ironclad-v0.34/ 0000755 0000000 0000000 00000000000 13057521520 012117 5 ustar root root ironclad-v0.34/ironclad.asd 0000644 0000000 0000000 00000042311 13057521520 014404 0 ustar root root ;;;; -*- mode: lisp; indent-tabs-mode: nil -*-
(cl:defpackage #:ironclad-system
(:use :cl))
(cl:in-package #:ironclad-system)
;;; easy-to-type readmacro for creating s-boxes and the like
(defun array-reader (stream subchar arg)
(declare (ignore subchar))
(let ((array-data (read stream nil stream nil))
(array-element-type `(unsigned-byte ,arg)))
;; FIXME: need to make this work for multi-dimensional arrays
`(make-array ,(length array-data) :element-type ',array-element-type
:initial-contents ',array-data)))
(defparameter *ironclad-readtable*
(let ((readtable (copy-readtable nil)))
(set-dispatch-macro-character #\# #\@ #'array-reader readtable)
readtable))
(defclass ironclad-source-file (asdf:cl-source-file) ())
(defclass txt-file (asdf:doc-file) ((type :initform "txt")))
(defclass css-file (asdf:doc-file) ((type :initform "css")))
(asdf:defsystem :ironclad
:version "0.34"
:author "Nathan Froyd "
:maintainer "Nathan Froyd "
:description "A cryptographic toolkit written in pure Common Lisp"
:default-component-class ironclad-source-file
:depends-on (#+sbcl sb-rotate-byte #+sbcl sb-posix nibbles)
:components ((:static-file "README")
(:static-file "LICENSE")
(:static-file "TODO")
(:static-file "NEWS")
(:module "src"
:components
((:file "package")
(:file "conditions" :depends-on ("package"))
(:file "util" :depends-on ("package"))
(:file "macro-utils" :depends-on ("package"))
(:file "common" :depends-on ("package"))
;; FIXME: make this depend on :FEATURE :IRONCLAD-GRAY-STREAMS
#+(or lispworks sbcl openmcl cmu allegro)
(:file "octet-stream" :depends-on ("common"))
(:file "padding" :depends-on ("common"))
(:file "kdf-common" :depends-on ("package"))
(:file "pkcs5" :depends-on ("common" "kdf-common"))
(:file "scrypt" :depends-on ("kdf-common" "pkcs5"))
(:file "password-hash" :depends-on ("pkcs5" "prng"))
(:file "math" :depends-on ("prng"))
(:module "sbcl-opt"
:depends-on ("package" "common")
:components
((:file "fndb")
(:file "x86oid-vm" :depends-on ("fndb"))))
(:module "ciphers"
:depends-on ("common" "macro-utils" "sbcl-opt")
:components
(
;; block ciphers of various kinds
(:file "cipher")
(:file "modes" :depends-on ("cipher"))
(:file "make-cipher" :depends-on ("cipher"))
(:file "null-cipher" :depends-on ("cipher"))
(:file "aes" :depends-on ("cipher"))
(:file "des" :depends-on ("cipher"))
(:file "blowfish" :depends-on ("cipher"))
(:file "twofish" :depends-on ("cipher"))
(:file "threefish" :depends-on ("cipher"))
(:file "idea" :depends-on ("cipher"))
(:file "misty1" :depends-on ("cipher"))
(:file "square" :depends-on ("cipher"))
(:file "rc2" :depends-on ("cipher"))
(:file "rc5" :depends-on ("cipher"))
(:file "rc6" :depends-on ("cipher"))
(:file "tea" :depends-on ("cipher"))
(:file "xtea" :depends-on ("cipher"))
(:file "cast5" :depends-on ("cipher"))
(:file "serpent" :depends-on ("cipher"))
;; stream ciphers
(:file "arcfour" :depends-on ("cipher"))
(:file "salsa20" :depends-on ("cipher"))
(:file "chacha" :depends-on ("cipher"))))
(:module "digests"
:depends-on ("common" "macro-utils" "sbcl-opt" "ciphers")
:components
((:file "digest")
(:file "crc24" :depends-on ("digest"))
(:file "crc32" :depends-on ("digest"))
(:file "adler32" :depends-on ("digest"))
(:file "md2" :depends-on ("digest"))
(:file "md4" :depends-on ("digest"))
(:file "md5" :depends-on ("digest"))
(:file "md5-lispworks-int32" :depends-on ("digest"))
(:file "sha1" :depends-on ("digest"))
(:file "sha256" :depends-on ("digest"))
(:file "sha512" :depends-on ("digest"))
(:file "ripemd-128" :depends-on ("digest"))
(:file "ripemd-160" :depends-on ("digest"))
(:file "tiger" :depends-on ("digest"))
(:file "whirlpool" :depends-on ("digest"))
(:file "tree-hash" :depends-on ("digest"))
(:file "skein" :depends-on ("digest"))
(:file "sha3" :depends-on ("digest"))
(:file "blake2" :depends-on ("digest"))
(:file "blake2s" :depends-on ("digest"))))
(:module "macs"
:depends-on ("common" "digests")
:components
((:file "hmac")
(:file "cmac")
(:file "skein-mac")
(:file "poly1305")))
(:module "public-key"
:depends-on ("digests" "math")
:components
((:file "public-key")
(:file "dsa" :depends-on ("public-key"))
(:file "elgamal" :depends-on ("public-key"))
(:file "rsa" :depends-on ("public-key"))
(:file "pkcs1" :depends-on ("public-key"))
(:file "ed25519" :depends-on ("public-key"))))
(:module "prng"
:depends-on ("digests" "ciphers")
:components
((:file "prng")
(:file "fortuna" :depends-on ("prng"
"generator"))
(:file "generator")))))
(:module "doc"
:components
((:html-file "ironclad")
;; XXX ASDF bogosity
(:txt-file "ironclad-doc")
(:css-file "style")))))
(defun ironclad-implementation-features ()
#+sbcl
(list* sb-c:*backend-byte-order*
(if (= sb-vm:n-word-bits 32)
:32-bit
:64-bit)
:ironclad-fast-mod32-arithmetic
:ironclad-gray-streams
(when (member :x86-64 *features*)
'(:ironclad-fast-mod64-arithmetic)))
#+cmu
(list (c:backend-byte-order c:*target-backend*)
(if (= vm:word-bits 32)
:32-bit
:64-bit)
:ironclad-fast-mod32-arithmetic
:ironclad-gray-streams)
#+allegro
(list :ironclad-gray-streams)
#+lispworks
(list :ironclad-gray-streams
;; Disable due to problem reports from Lispworks users and
;; non-obviousness of the fix.
#+nil
(when (not (member :lispworks4 *features*))
'(:ironclad-md5-lispworks-int32)))
#+openmcl
(list :ironclad-gray-streams)
#-(or sbcl cmu allegro lispworks openmcl)
nil)
(macrolet ((do-silently (&body body)
`(handler-bind ((style-warning #'muffle-warning)
;; It's about as fast as we can make it,
;; and a number of the notes relate to code
;; that we're running at compile time,
;; which we don't care about the speed of
;; anyway...
#+sbcl (sb-ext:compiler-note #'muffle-warning))
,@body)))
(defmethod asdf:perform :around ((op asdf:compile-op) (c ironclad-source-file))
(let ((*readtable* *ironclad-readtable*)
(*print-base* 10) ; INTERN'ing FORMAT'd symbols
(*print-case* :upcase)
#+sbcl (sb-ext:*inline-expansion-limit* (max sb-ext:*inline-expansion-limit* 1000))
#+cmu (ext:*inline-expansion-limit* (max ext:*inline-expansion-limit* 1000))
(*features* (append (ironclad-implementation-features) *features*)))
(do-silently (call-next-method))))
(defmethod asdf:perform :around ((op asdf:load-op) (c ironclad-source-file))
(do-silently (call-next-method))))
(defmethod asdf:perform :after ((op asdf:load-op)
(c (eql (asdf:find-system :ironclad))))
(provide :ironclad))
;;; testing
(defclass test-vector-file (asdf:static-file)
((type :initform "testvec")))
(defpackage :ironclad-tests
(:nicknames :crypto-tests)
(:use :cl))
(defmethod asdf:perform ((op asdf:test-op)
(c (eql (asdf:find-system :ironclad))))
(asdf:oos 'asdf:test-op 'ironclad-tests))
(asdf:defsystem ironclad-tests
:depends-on (ironclad)
:version "0.6"
:in-order-to ((asdf:test-op (asdf:load-op :ironclad-tests)))
:components ((:module "testing"
:components
((:file "rt")
(:file "testfuns" :depends-on ("rt"))
(:module "test-vectors"
:depends-on ("testfuns")
:components
((:file "macs")
(:file "modes")
(:file "ciphers")
(:file "digests")
(:file "padding")
(:file "pkcs5")
(:file "scrypt")
(:file "ironclad")
(:file "prng-tests")
(:file "public-key")
;; test vectors
(:test-vector-file "crc24")
(:test-vector-file "crc32")
(:test-vector-file "adler32")
(:test-vector-file "md2")
(:test-vector-file "md4")
(:test-vector-file "md5")
(:test-vector-file "sha1")
(:test-vector-file "sha224")
(:test-vector-file "sha256")
(:test-vector-file "sha384")
(:test-vector-file "sha512")
(:test-vector-file "ripemd-128")
(:test-vector-file "ripemd-160")
(:test-vector-file "tiger")
(:test-vector-file "whirlpool")
(:test-vector-file "hmac")
(:test-vector-file "cmac")
(:test-vector-file "tree-hash")
(:test-vector-file "skein256")
(:test-vector-file "skein256-128")
(:test-vector-file "skein256-160")
(:test-vector-file "skein256-224")
(:test-vector-file "skein512")
(:test-vector-file "skein512-128")
(:test-vector-file "skein512-160")
(:test-vector-file "skein512-224")
(:test-vector-file "skein512-256")
(:test-vector-file "skein512-384")
(:test-vector-file "skein1024")
(:test-vector-file "skein1024-384")
(:test-vector-file "skein1024-512")
(:test-vector-file "skein-mac")
(:test-vector-file "poly1305")
(:test-vector-file "sha3")
(:test-vector-file "sha3-224")
(:test-vector-file "sha3-256")
(:test-vector-file "sha3-384")
(:test-vector-file "shake128")
(:test-vector-file "shake256")
(:test-vector-file "blake2")
(:test-vector-file "blake2-160")
(:test-vector-file "blake2-256")
(:test-vector-file "blake2-384")
(:test-vector-file "blake2s")
(:test-vector-file "blake2s-128")
(:test-vector-file "blake2s-160")
(:test-vector-file "blake2s-224")
;; block ciphers of various kinds
(:test-vector-file "null")
(:test-vector-file "aes")
(:test-vector-file "des")
(:test-vector-file "3des")
(:test-vector-file "blowfish")
(:test-vector-file "twofish")
(:test-vector-file "threefish256")
(:test-vector-file "threefish512")
(:test-vector-file "threefish1024")
(:test-vector-file "idea")
(:test-vector-file "misty1")
(:test-vector-file "square")
(:test-vector-file "rc2")
(:test-vector-file "rc5")
(:test-vector-file "rc6")
(:test-vector-file "tea")
(:test-vector-file "xtea")
(:test-vector-file "cast5")
(:test-vector-file "serpent")
;; modes
(:test-vector-file "cbc")
(:test-vector-file "ctr")
(:test-vector-file "ofb")
(:test-vector-file "cfb")
(:test-vector-file "cfb8")
;; stream ciphers
(:test-vector-file "arcfour")
(:test-vector-file "salsa20")
(:test-vector-file "salsa20-12")
(:test-vector-file "salsa20-8")
(:test-vector-file "chacha")
(:test-vector-file "chacha-12")
(:test-vector-file "chacha-8")
;; prng
(:test-vector-file "prng")
;; public key
(:test-vector-file "rsa-enc")
(:test-vector-file "rsa-sig")
(:test-vector-file "elgamal-enc")
(:test-vector-file "elgamal-sig")
(:test-vector-file "dsa")
(:test-vector-file "ed25519")))))))
(defmethod asdf:perform ((op asdf:test-op)
(c (eql (asdf:find-system :ironclad-tests))))
(or (funcall (intern "DO-TESTS" (find-package "RTEST")))
(error "TEST-OP failed for IRONCLAD-TESTS")))
ironclad-v0.34/ironclad-text.asd 0000644 0000000 0000000 00000000363 13057521520 015367 0 ustar root root ;;;; -*- mode: lisp; indent-tabs-mode: nil -*-
(asdf:defsystem :ironclad-text
:components
((:module :src
:serial t
:components
((:file "text")
)))
:depends-on (:ironclad :flexi-streams))
ironclad-v0.34/.gitignore 0000644 0000000 0000000 00000000210 13057521520 014100 0 ustar root root *.fasl
*.FASL
*.ufasl
*.ufsl
*.dx32fsl
*.dx64fsl
*.pfsl
*.dfsl
*.p64fsl
*.d64fsl
*.lx32fsl
*.lx64fsl
*.fx32fsl
*.fx64fsl
*.fas
*.lib
*~
ironclad-v0.34/TODO 0000644 0000000 0000000 00000021753 13057521520 012617 0 ustar root root hey emacs, show me an -*- mode: outline -*-
* misc
** cleanup octet-stream: positions are either INDEX or POSITION, not both
The latter requires :SHADOW adjustments in Ironclad's DEFPACKAGE.
** WRITE-SEQUENCE on crypting-streams
** HMACING-STREAM
* ciphers to add
CAST6
SEED
MARS
Serpent
Camellia
...others?...
* add OpenPGP functions
This would involve writing some simple base64 encoding and decoding.
I'm not sure how to add this and achieve the right amount of generality
and functionality. Just writing an OpenPGP parser is straightforward,
but determining what to do with the data is a little more complicated.
* add {EN,DE}CRYPTING-STREAM capabilities
Encrypts data written to it before writing the (now-encrypted) data to
an underlying stream. Of course, the equivalent functionality is
available for decrypting. This might finesse the current lack of
padding functionality in the library: when the stream is closed, the
appropriate amount and kind of padding is added. Not sure that this is
necessarily the right idea, although when people are encrypting data,
they are generally doing so before writing said data someplace else. I
don't encrypt data in memory just for the fun of it.
(These are partially written, see octet-stream.lisp.)
* make a condition hierarchy for the library
Some conditions have been added as of 0.7, but there probably need to be
more, with helpful messages along the way.
* move generic function definitions to their own file
There's a lot of generic function definitions floating around as of
version 0.5 and there are likely to be many more as the library grows.
Perhaps the DEFGENERIC forms should be moved into their own file;
such a change would make the source tree cleaner and provide a good
starting point for people interested in examining the library's
functionality.
* add functional version of encryption and decryption
{ENCRYPT,DECRYPT}-MESSAGE would be perfect for this; although they were
originally conceived for public-key operations (and therefore they have
to return a fresh octet vector), they make perfect candidates for
providing functional interfaces to encryption and decryption as well.
Figuring out short block interfaces for this and padding issues,
however, will be a little tricky.
* remove generic function calls from inner loops
ENCRYPT-WITH-MODE, by virtue of calling -ENCRYPT-BLOCK on
every iteration of the loop, requires -ENCRYPT-BLOCK to
repeatedly extract the cipher-specific data from the provided cipher
context. No idea if this is a bottleneck (probably not).
Doing this extraction on every iteration of the loop is overhead,
though--unnecessary overhead compared to a C implementation. Pulling
the extraction out of the loop in a clean, cipher-independent manner is
probably pretty hairy, however.
* figure out interface for extra cipher creation parameters
RC5, for instance, is configurable in the number of rounds. The number
of rounds must be fixed the way the current system works. Configuring
the number of rounds for TEA and XTEA would be another example of where
this is useful. Surely there are other reasons, too.
* add more cipher modes of operation
CBC with ciphertext stealing (CBC-CTS) (this would require some sort
of ENCRYPT-FINAL-BLOCK interface so we would know when to perform the
ciphertext stealing)
PCBC, for all those Lispers who want to implement Kerberos 4
OpenPGP CFB
* add padding primitives
This should be combined, somehow, with an ENCRYPT-FINAL-BLOCK generic
function. This lets the caller do padding and encryption in one shot.
An alternative to ENCRYPT-FINAL-BLOCK is to provide a
:PROCESS-FINAL-BLOCK argument to {ENCRYPT,DECRYPT}, which seems cleaner
and easier to add.
* efficient 32-bit implementations of SHA2-{512,384}
These digests are currently implemented with straight 64-bit arithmetic,
which is elegant, but causes a lot of consing on 32-bit platforms. Is
it worth spending the time to tweak these?
* add other MACs
The NESSIE project has a boatload of MAC recommendations with test
vectors and everything. Adding other MACs would help with cleaning up
the MAC interface (or lack thereof).
** unified interface for MAC construction, updating, etc.
* add other digests
Are there any other reputable hash functions?
* figure out interface for stream ciphers
WiderWake 4+1
SEAL (intellectual property issues, although that doesn't seem to stop
many other crypto libraries)
* add public-key crypto
DSA signing and verification routines have been added as of version
0.11. RSA, Elgamal, elliptic-curve crypto, and others remain to be
added. One issue that has yet to be resolved is how to handle the
myriad signature/encryption schemes for RSA.
* random number generation
Doing this right would require adding an interface to get at entropy
sources on various operating systems. We could just punt this out to
the user and have an ADD-ENTROPY function that the user would need to
call from time to time. Very inconvenient, though.
* blue-sky ideas
** implementation-specific algorithm implementations
Ironclad is written with implementations that are capable of performing
32-bit unboxed arithmetic in mind. However, there is no reason to leave
other implementations out in the cold. I particularly have in mind
ABCL, whose clean Java integration could facilitate using Java's crypto
routines for many of the algorithms Ironclad provides.
Using 32-bit integer arithmetic in LispWorks (SYSTEM:INT32-*) and
DEF*LAPFUNCTION in ClozureCL, along with architecture-specific VOPs
and/or DEF{KNOWN,OPTIMIZER,TRANSFORM} in SBCL/CMUCL are other ways of
doing this.
** compilable encryption and decryption routines
One trick that's commonly used in high-performance crypto libraries is
unrolling inner loops of ciphers, which are generally Feistel networks
of some sort. This transformation has many benefits: avoiding jumps and
compiling more efficient code for structure accesses, to name two.
Ironclad features unrolled loops in various places (e.g. the CAST5 and
Blowfish ciphers). Unrolled loops can be particularly helpful in Common
Lisp because the compiler can often optimize away things like array
bounds checks.
Unfortunately, this tends to limit the libraries to only handling a
fixed number of rounds (or other configurable parameters of block
ciphers). Accomodating variable numbers of rounds makes life easier for
the truly paranoid and is more in the spirit of the ciphers as
originally designed.
One way of getting around this limitation would be to use Common Lisp's
provision of the COMPILE function at create-the-cipher time. One could
compile--on demand--specialized encryption/decryption routines that only
accomodated a fixed number of rounds (or other static configuration
parameters). This facility would be particularly helpful for those
ciphers whose round keys are variable in the number of rounds chosen;
bounds checking for accesses to these keys could be done away with
entirely (except perhaps for a check to ensure the round key vector is
of the proper length and type). Furthermore, the round keys could
actually be compiled directly into the code--as 32-bit constant
arguments on the x86, for example. Such a change would necessarily make
the compiled routines key-specific, but perhaps that would be OK.
(When reading the specifications for Blowfish and Twofish, the authors
make much of this capability by citing screaming performance numbers for
this scenario while noting that modifying the code for each individual
key is difficult. In Ironclad, providing three differing levels of
generality--general, n-rounds-specific, and key-specific--would be
relatively easy compared to the difficulty of doing it in C.)
Changes to MAKE-CIPHER--a keyword argument specifying the level of
specialization--would be necessary. Extensive changes to the individual
block ciphers would also be necessary. Perhaps this sort of facility
need not be added to all ciphers; the common ones could come first
(e.g. AES) and less-common ones would be converted as people contributed
patches or needs were found for them.
** use word-aligned loads when possible
The current code uses byte loads + shifts and ors to fetch
(UNSIGNED-BYTE 32) values out of a (SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*)).
Some implementations (I have in mind particularly SBCL and CMUCL) could
use a more efficient mechanism if the offset of the (UNSIGNED-BYTE 32)
value was known to be four-byte aligned. Once this was done for each
encryption/decryption function for a particular block cipher, the
high-level generic functions could call efficient versions if possible,
eliminating some overhead.
There is actually some (SBCL) support for this in cipher.lisp, in the
alternate definition for WITH-WORDS. It doesn't make that much of a
difference--at least not in the case where WITH-WORDS needs to check for
alignment and safety. It might be a win if there were several
different *-ENCRYPT-BLOCK functions and the high-level ones knew which
one to call, but...
* add option to allow INTEGER-TO-OCTETS to output directly to a buffer
This could be handy in certain internals, e.g. in GENERATE-BLOCKS.
ironclad-v0.34/doc/ 0000755 0000000 0000000 00000000000 13057521520 012664 5 ustar root root ironclad-v0.34/doc/style.css 0000644 0000000 0000000 00000001756 13057521520 014547 0 ustar root root body {
margin: 1em 5% 1em 5%;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
pre {
padding: 0;
margin: 0;
}
h1, h2 {
border-bottom: 2px solid #449977;
}
h1, h2, h3, h4, h5, h6 {
font-family: sans-serif;
line-height: 1.3;
}
a:link {
color: #449977;
}
a:visited {
color: purple;
}
a {
text-decoration: none;
padding: 1px 2px;
}
a:hover {
text-decoration: none;
padding: 1px;
border: 1px solid #000000;
}
.lisp-symbol {
margin-right: 10%;
margin-top: 1.5em;
margin-bottom: 1.5em;
border: 1px solid #449977;
background: #eeeeee;
padding: 0.5em;
}
.note {
margin-right: 10%;
margin-top: 1.5em;
margin-bottom: 1.5em;
}
td.content {
padding: 0;
}
td.title {
font-family: sans-serif;
font-size: 1.1em;
font-weight: bold;
text-align: left;
vertical-align: top;
text-decoration: underline;
padding-right: 0.5em;
margin-top: 0.0em;
margin-bottom: 0.5em;
}
.note td.content {
padding-left: 0.5em;
border-left: 2px solid #449977;
}
ironclad-v0.34/doc/ironclad-doc.txt 0000644 0000000 0000000 00000111557 13057521520 015775 0 ustar root root (:author "Nathan Froyd"
:email "froydnj@gmail.com"
:package "Ironclad"
:cl-package "IRONCLAD"
:version #.(asdf:component-version (asdf:find-system :ironclad))
:homepage "http://www.method-combination.net/lisp/ironclad/"
:source-code "http://github.com/froydnj/ironclad"
:download "http://www.method-combination.net/lisp/files/ironclad.tar.gz")
(:h1 ${package})
(:p ${package} " is a cryptography library written entirely in Common
Lisp. It includes support for several popular " (:xref "ciphers"
"ciphers") ", " (:xref "digests" "digests") ", and " (:xref "macs"
"MACs") ". Rudimentary support for " (:xref "public-key" "public-key
cryptography") " is included. For several implementations that support
Gray Streams, " (:xref "gray-streams" "support") " is included for
convenient stream wrappers.")
(:p ${package} " was written primarily by " ${author} " (" ${email} ").")
(:h2 "Installation")
(:p ${package} " can be downloaded at " (:url ${download} ${download})
". The latest released version is " ${version} ". If you are feeling
adventurous, you can download a bleeding-edge version at " (:url
${source-code} ${source-code}) ".")
(:p "It comes with an ASDF system definition, so " `(ASDF:OOS
'ASDF:LOAD-OP :IRONCLAD)` " should be all that you need to get started.
The testsuite can be run by substituting " `ASDF:TEST-OP` " for "
`ASDF:LOAD-OP` " in the form above.")
(:p ${package} " has been tested in the following implementations:")
(:ul
(:li "SBCL x86/linux, x86-64/linux (primary development platforms)")
(:li "SBCL x86-64/solaris, x86/darwin")
(:li "CMUCL x86/linux")
(:li "ABCL with Sun's 1.5.0 JVM")
(:li "Lispworks 5.0.1 x86/linux")
(:li "Lispworks 5.1.2 x86-64/darwin x86/windows")
(:li "Allegro 8.0 x86/linux")
(:li "Allegro 8.1 x86/linux, x86-64/linux, sparc/solaris")
(:li "CLISP 2.41 x86/linux, x86/cygwin")
(:li "Clozure Common Lisp 1.2 x86-64/linux")
(:li "Clozure Common Lisp 1.10 x86-64/darwin"))
(:p "All included tests should pass successfully. If you use a platform
not listed above, please send your platform information to the author so
that he can add it to the above list. If the tests do not all pass, you
have found a bug; please report it.")
(:h2 "License")
(:p ${package} " is released under a MIT-like license; you can do pretty
much anything you want to with the code except claim that you wrote
it.")
((:h2 id "ciphers") "Ciphers")
(:describe :function (ironclad:make-cipher cipher))
(:p "Return a cipher object suitable for use for both encryption and
decryption.")
(:p 'name' " denotes the encryption algorithm to use. "
@list-all-ciphers " will tell you the names of all supported ciphers;
the short list of ones you are likely to be interested in is:")
(:ul
(:li "AES")
(:li "DES")
(:li "3DES")
(:li "Blowfish")
(:li "Twofish")
(:li "RC5")
(:li "RC6")
(:li "Arcfour (RC4)"))
(:p 'name' " can be a symbol in the " `KEYWORD` " package or the "
`IRONCLAD` " package; " `:AES` " for AES, " `IRONCLAD:ARCFOUR` " for
RC4, and so forth.")
(:p 'mode' " describes the mode of operation for the cipher. Stream
ciphers such as Arcfour can operate in only one mode, " `stream` ".
Block ciphers such as AES and DES can operate in several different
modes:")
(:ul
(:li "ECB")
(:li "CBC")
(:li "OFB")
(:li "CFB (note that Ironclad's CFB mode is 'n'-bit CFB, where 'n' is
the " @block-length " of the cipher)")
(:li "CFB8 (this seems to be the mode other crypto packages call
'CFB')")
(:li "CTR"))
(:p 'mode' " should be a symbol in the " `KEYWORD` " or " `IRONCLAD` "
packages; " `:STREAM` ", " `IRONCLAD:OFB` ", and so forth. An error
will be signaled if " 'mode' " is not appropriate for the cipher "
'name' ".")
(:p 'initialization-vector' " (IV) should be supplied only if " 'mode' "
requires one. " 'initialization-vector' " should be a " `(VECTOR
(UNSIGNED-BYTE 8))` ". The supplied IV should be the same length as the
" @block-length " of " 'name' ".")
(:p 'key' " is, of course, the key for the cipher. " 'key' " should be
a " `(VECTOR (UNSIGNED-BYTE 8))` ".")
(:p "If " 'padding' " is supplied, the specified padding method will be
used by " @encrypt " and " @decrypt " to handle short blocks when the "
`:HANDLE-FINAL-BLOCK` " argument is supplied. Depending on the mode
specified, " 'padding' " may be ignored (e.g. OFB and CFB modes do not
care about short blocks; neither do stream ciphers).")
(:note 'padding' " is currently ignored in all modes (and, by extension,
so is " `:HANDLE-FINAL-BLOCK` "). This oversight is expected to be
corrected in a future release.")
(:describe :function (ironclad:encrypt (values n-bytes-consumed n-bytes-produced)))
(:p "Encrypts data according to " 'cipher' " from " 'plaintext' "
starting at " 'plaintext-start' " and continuing until "
'plaintext-end' ". The encrypted data is placed in "
'ciphertext' " starting at " 'ciphertext-start' ".")
(:describe :function (ironclad:decrypt (values n-bytes-consumed n-bytes-produced)))
(:p "Decrypts data according to " 'cipher' " from " 'ciphertext' "
starting at " 'ciphertext-start' " and continuing until "
'ciphertext-end' ". The decrypted data is placed in " 'plaintext' "
starting at " 'plaintext-start' ".")
(:describe :function (ironclad:encrypt-in-place (values n-bytes-consumed n-bytes-produced))
(ironclad:decrypt-in-place (values n-bytes-consumed n-bytes-produced)))
(:p "Encrypts or decrypts data in " 'text' " between " 'start' " and "
'end' " \"in-place\" according to " 'cipher' ". These functions are
shorthand for:")
(:pre "(encrypt cipher text text :plaintext-start start :plaintext-end end :ciphertext-start start)
(decrypt cipher text text :ciphertext-start start :ciphertext-end end :plaintext-start start)")
(:note @encrypt-in-place " and " @decrypt-in-place " do not support a "
'handle-final-block' " parameter as " @encrypt " and " @decrypt " do.
If you need the functionality that " 'handle-final-block' " provides,
then you need to use " @encrypt " and " @decrypt ".")
(:note 'n-bytes-consumed' " and " 'n-bytes-produced' " may not always be
equal to the length of the data specified in the call to "
@encrypt-in-place " or " @decrypt-in-place ". This subtlely is also
present in " @encrypt " or " @decrypt ".")
(:h3 "Inquiry functions")
(:describe :function (ironclad:list-all-ciphers list))
(:p "Returns a list of cipher-names that may be validly passed to "
@make-cipher ".")
(:describe :function (ironclad:cipher-supported-p boolean))
(:p "Returns T if " 'name' " would be in the list returned by "
@list-all-ciphers ", NIL otherwise.")
(:describe :function (ironclad:key-lengths list))
(:p "Return a list of valid key lengths for " 'cipher' ".")
(:describe :function (ironclad:block-length number))
(:p "Return the number of octets " 'cipher' " processes at a time. This
function always returns 1 for stream ciphers.")
((:h2 id "digests") "Digests")
(:p "Digest functions, also known as hash functions, produce
fixed-length output (a " 'digest' " or " 'hash' ") from a
variable-length message. The simplest example of a digest function is
one that adds up all the bytes in the message modulo 256. This digest
function fails one test of a cryptographically secure hash function: it
must be difficult to find a message with a given digest. It also fails
the other test: it must be difficult to find two messages with the same
digest.")
(:p "Ironclad provides several cryptographically secure digest functions
and several non-cryptographically secure digest functions.")
(:note "In the functions below, messages or parts thereof are provided
as octet vectors; Ironclad has no facilities for producing digests of
strings. If you need to obtain the digest of a string, then you need to
figure out how to convert it to an octet vector first. This is a
deliberate design decision. Characters are not equivalent to bytes.
See your local Unicode guru for more details.")
(:describe :function (ironclad:make-digest digester))
(:p "Returns a digest object. " 'digest-name' " is a keyword naming the
algorithm you wish " 'digester' " to use. The algorithms you are likely
to want to use are:")
(:ul
(:li "MD4")
(:li "MD5")
(:li "SHA1")
(:li "SHA256")
(:li "Tiger")
(:li "Adler32")
(:li "CRC32"))
(:p "Other legitimate digest names can be found by calling "
@list-all-digests ". Like " @make-cipher ", " 'digest-name' " should be
a symbol in the " `KEYWORD` " or " `IRONCLAD` " packages.")
(:describe :generic-function (ironclad:update-digest (values)))
(:p "Updates the internal state of " 'digester' " with the contents of "
'thing' ". The exact method is determined by the type of THING.")
(:p "There are several methods defined on this generic function that
take a particular digester and a " `(SIMPLE-ARRAY (UNSIGNED-BYTE 8)
(*))` " as well as the usual " 'start' " and " 'end' " keyword
arguments. These methods update the state of " 'digester' " with the
subsequence of the array denoted by " 'start' " and " 'end' ". They are
not listed here because there's one method for every type of digest
that " ${package} " provides, and listing them would get very tedious
for no benefit. An example should suffice.")
(:pre
"(let ((digester (ironclad:make-digest :sha1))
(array (make-array 16 :element-type '(unsigned-byte 8) :initial-element 0)))
;; Update with 16 zeroes.
(ironclad:update-digest digester array)
;; Update with 8 ones.
(fill array 1 :start 2 :end 10)
(ironclad:update-digest digester array :start 2 :end 10))")
(:describe :method (ironclad:update-digest (t stream) digester))
(:p "Update the internal state of " 'digester' " with the contents of "
'stream' ", which must respond to " `READ-BYTE` " or " `READ-SEQUENCE` "
with a " `(SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*))` " and return "
'digester' ". It differs from " @digest-stream ", below, in that you
may need to digest data before or after the contents of " 'stream' "
(this happens, for instance, when signing the contents of some file).")
(:describe :generic-function (ironclad:produce-digest digest))
(:p "Return the digest of the data processed by " 'digester' " so far.
The internal state of " 'digester' " is modified; if you wish to retain
a copy of the digest, you must call " @copy-digest ".")
(:p "If " 'digest' " is provided, the computed digest will be placed
into " 'digest' " starting at " 'digest-start' ". " 'digest' " must be
a " `(SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*))` ". An "
@insufficient-buffer-space " error will be signaled if there is
insufficient space in " 'digest' ".")
(:h3 "High-level convenience functions")
(:p "Several high-level convenience functions that encapsulate common
sequences of " @make-digest ", " @update-digest " and " @produce-digest
" are provided by Ironclad as well. They come in two flavors: the first
takes a digest name as would be provided to " @make-digest ". The
second way to call these functions is to provide an actual digest object
as the first argument. So one can say:")
(:pre "(ironclad:digest-sequence :md5 *buffer*)")
(:p "or, equivalently:")
(:pre
"(let ((digester (make-digest :md5)))
(ironclad:digest-sequence digester *buffer*))")
(:p "The second form comes in handy if you plan on " (:xref
"digest-tips" "reusing the digest object") ".")
(:describe :generic-function (ironclad:digest-sequence digest))
(:p "Returns the digest of the subsequence of " 'sequence' " bounded by
" 'start' " and " 'end' ", according to " 'digest-name' ". " 'sequence'
" must be a " `(SIMPLE-ARRAY (UNSIGNED-BYTE 8))` ". " 'digest' " and "
'digest-start' " are as in " @produce-digest ".")
(:describe :generic-function (ironclad:digest-stream digest))
(:p "Returns the digest of the contents of the stream specified by "
'stream' ". " `READ-BYTE` " must be a legal operation on " 'stream' "
and return an " `(UNSIGNED-BYTE 8)` ". In a similar fashion, "
`READ-SEQUENCE` " on " 'stream' " must support reading into a "
`(SIMPLE-ARRAY (UNSIGNED-BYTE 8))` ". " 'digest' " and " 'digest-start'
" are as in " @produce-digest ".")
(:p "If " 'buffer' " is provided, it must be a " `(SIMPLE-ARRAY
(UNSIGNED-BYTE 8) (*))` "; the portion of " 'buffer' " between " 'start'
" and " 'end' " will be used to read the data from the stream.")
(:describe :generic-function (ironclad:digest-file digest))
(:p "Returns the digest of the contents of the file named by "
'pathname' ". " 'digest' " and " 'digest-start' " are as in "
@produce-digest ".")
(:p "If " 'buffer' " is provided, it must be a " `(SIMPLE-ARRAY
(UNSIGNED-BYTE 8) (*))` "; the portion of " 'buffer' " between " 'start'
" and " 'end' " will be used to read the data from the stream.")
(:h3 "Inquiry functions")
(:describe :function (ironclad:list-all-digests list))
(:p "Returns a list whose elements may be validly passed to " @make-digest ".")
(:describe :function (ironclad:digest-supported-p boolean))
(:p "Returns T if " 'name' " would be in the list returned by "
@list-all-digests ", NIL otherwise.")
(:describe :function (ironclad:digest-length number))
(:p "Returns the length of the digest computed by " 'digest' ", which
may be a digest-name or a digest instance.")
((:h3 id "digest-tips") "Miscellaneous")
(:p "Ironclad digests are CLOS objects; the interesting thing about this
for most purposes is that functions like " `REINITIALIZE-INSTANCE` " are
supported. This means one can write a fairly efficient clone of the "
`md5sum` " program like so:")
(:pre
"(defun digest-sum-files (digest-name &rest files)
(unless files
(error \"no files given to digest\"))
(loop with buffer = (make-array 8192 :element-type '(unsigned-byte 8))
with digest = (make-array (ironclad:digest-length digest-name)
:element-type '(unsigned-byte 8))
for file in files
for digester = (ironclad:make-digest digest-name)
then (reinitialize-instance digester)
do (ironclad:digest-file digester file :buffer buffer :digest digest)
(format t \"~A ~A~%\" (file-namestring file)
(ironclad:byte-array-to-hex-string digest))))")
(:h3 "Tree hashes")
(:p "Ironclad supports tree hashes, as described in " (:url
"http://web.archive.org/web/20080316033726/http://www.open-content.net/specs/draft-jchapweske-thex-02.html"
"Tree Hash EXchange format") ". You create tree hashes as if you were
creating a digest:")
(:pre "(ironclad:make-digest :tree-hash)")
(:p "By default, this creates a tree hash that uses the Tiger digest
algorithm internally and a segment size of 1024. Since using the Tiger
digest algorithm is so common, a convenience function that makes your
intent obvious:")
(:pre "(ironclad:make-tiger-tree-hash)")
(:p "has also been provided.")
(:p "You may indicate that you wish to use a different algorithm than
Tiger:")
(:pre "(ironclad:make-digest '(:treehash :digest :sha256))")
(:p "Or you might wish to use a different segment size:")
(:pre "(ironclad:make-digest '(:tree-hash :block-length 16384))")
(:p "There is currently no interface for obtaining the intermediate
hashes computed while computing the final tree hash.")
((:h2 id "macs") "Message authentication codes")
(:p "A message authentication code is a cryptographic function of some
data and a user-specified key. Only a person knowing the key can
recompute the MAC for the given message. A MAC is useful where
maintaining data integrity is required, but the secrecy of the data is
not paramount.")
(:p "Ironclad provides three different kinds of MACs: HMACs, specified in
" (:url "http://www.ietf.org/rfc/rfc2109.txt" "RFC 2104") ", CMACs,
specified in " (:url "http://www.ietf.org/rfc/rfc4493.txt" "RFC 4493") "
and NIST document 800-38B and Skein-MACs.")
(:h3 "HMACs")
(:p "Instances of HMACs are constructed by specifying a secret key and a
digest-name.")
(:describe :function (ironclad:make-hmac hmac))
(:p "Return an HMAC instance based on the hash function " 'digest-name'
" with secret key " 'key' ".")
(:p "The returned object supports " `REINITIALIZE-INSTANCE` ":")
(:describe :method (cl:reinitialize-instance (ironclad::hmac) hmac))
(:p "The " `:KEY` " argument is the secret key, as provided to "
@make-hmac ".")
(:describe :function (ironclad:update-hmac hmac))
(:p "Update the internal state of " 'hmac' " with the data in "
'sequence' " bounded by " 'start' " and " 'end' ". " 'sequence' " must
be a " `(SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*))` ".")
(:describe :function (ironclad:hmac-digest digest))
(:p "Returns the MAC (" 'digest' ") computed by " 'hmac' " thus far.
The internal state of " 'hmac' " is not modified; this feature makes it
possible to compute a \"rolling MAC\" of a document. The length of "
'digest' " is determined by the " @digest-length " of " 'digest-name' "
passed to " @make-hmac " when " 'hmac' " was constructed.")
(:p "If " 'buffer' " is provided, the computed MAC will be placed into "
'buffer' " starting at " 'buffer-start' ". " 'buffer' " must be a "
`(SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*))` ". An "
@insufficient-buffer-space " error will be signaled if there is
insufficient space in " 'buffer' ".")
(:h3 "CMACs")
(:p "Instances of CMACs are constructed by specifying a secret key and a
cipher-name.")
(:describe :function (ironclad:make-cmac cmac))
(:p "Return a CMAC instance based on the cipher " 'cipher-name' " with
secret key " 'key' ". " 'cipher-name' " must have a " @block-length "
of either 8 or 16; this restriction is satisfied by most ciphers in
Ironclad with the notable exception of stream ciphers. " 'key' " must
be an acceptable key for " 'cipher-name' ".")
(:describe :function (ironclad:update-cmac cmac))
(:p "Update the internal state of " 'cmac' " with the data in "
'sequence' " bounded by " 'start' " and " 'end' ". " 'sequence' " must
be a " `(SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*))` ".")
(:describe :function (ironclad:cmac-digest digest))
(:p "Returns the MAC (" 'digest' ") computed by " 'cmac' " thus far.
The internal state of " 'cmac' " is not modified; this feature makes it
possible to compute a \"rolling MAC\" of a document. The length of "
'digest' " is determined by the " @block-length " of " 'cipher-name' "
passed to " @make-cmac " when " 'cmac' " was constructed.")
(:h3 "Skein-MACs")
(:p "Instances of Skein-MACs are constructed by specifying a secret key and
optionally a block length and a digest-length.")
(:describe :function (ironclad:make-skein-mac shein-mac))
(:p "Return a Skein-MAC instance with secret key " 'key' ". " 'block-length' "
can be 32 (to use the Skein256 hash function internally), 64 (to use Skein512)
or 128 (to use Skein1024). " 'digest-length' " can be any length you want the
computed MAC (" 'digest' ") to be. By default, " 'block-length' " is 64
and " 'digest-length' " is 64.")
(:describe :function (ironclad:update-skein-mac skein-mac))
(:p "Update the internal state of " 'skein-mac' " with the data in "
'sequence' " bounded by " 'start' " and " 'end' ". " 'sequence' " must
be a " `(SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*))` ".")
(:describe :function (ironclad:skein-mac-digest digest))
(:p "Returns the MAC (" 'digest' ") computed by " 'skein-mac' " thus far.
The internal state of " 'skein-mac' " is not modified; this feature makes it
possible to compute a \"rolling MAC\" of a document. The length of "
'digest' " is determined by the " 'digest-length' " key parameter
passed to " @make-skein-mac " when " 'skein-mac' " was constructed.")
((:h2 id "kdfs") "Key derivation functions")
(:p "Ironclad comes with the basic PBKDF1 and PBKDF2 algorithms, as well as
an implementation of the scrypt key derivation function as defined by
Colin Percival's " (:url
"http://www.tarsnap.com/scrypt.html"
"The scrypt key derivation function."))
(:describe :function (ironclad:derive-key digest))
(:p "Given a key derivation function object (produced by " `make-kdf` "),
a password and salt (both must be of type " `(SIMPLE-ARRAY
(UNSIGNED-BYTE 8) (*))` "), and number of digest iterations, returns
the password digest as a byte array of length " 'key-length' ".")
(:p "Scrypt ignores " 'iteration-count' " parameter.")
(:describe :function (ironclad:make-kdf kdf))
(:p "Returns a key derivation function instance (" 'kind' " must
either be " `PBKDF1` ", " `PBKDF2` " or " `SCRYPT-KDF` "). The PBKDF
algorithms use " 'digest' ". The scrypt key derivation uses cost parameters "
'N' ", " 'r' " and " 'p' " (" 'N' " is a CPU cost parameter that must be a
power of 2, " 'r' " and " 'p' " are memory cost parameters that must be defined
such that " 'r' " * " 'p' " <= 2^30.)")
(:p "The default parameters are " 'N' " = 4096, " 'r' " = 8, and " 'p' "
= 2. Please note that depending on the values of " 'N' " and " 'r' ", "
@derive-key " may not be able to allocate sufficient space for its
temporary arrays.")
(:h3 "PBKDF Convenience functions")
(:p "Ironclad comes with convenience functions for using PBKDF1 and
PBKDF2 to store passwords.")
(:describe :function (ironclad:pbkdf2-hash-password password))
(:p "Convenience function for hashing passwords using the PBKDF2
algorithm. Returns the derived hash of the password, and the original
salt, as byte vectors.")
(:describe :function (ironclad:pbkdf2-hash-password-to-combined-string password))
(:p "Convenience function for hashing passwords using the PBKDF2
algorithm. Returns the derived hash of the password as a single string
that encodes the given salt and PBKDF2 algorithm parameters.")
(:describe :function (ironclad:pbkdf2-check-password boolean))
(:p "Given a " 'password' " byte vector and a combined salt and digest string
produced by " `pbkdf2-hash-password-to-combined-string` ", checks whether
the password is valid.")
((:h2 id "public-key") "Public-key Operations")
(:p "Ironclad includes support for a few public key cryptography algorithms.")
(:p "Encryption algorithms:")
(:ul
(:li "Elgamal")
(:li "RSA"))
(:p "Signature algorithms:")
(:ul
(:li "DSA")
(:li "Ed25519")
(:li "Elgamal")
(:li "RSA"))
(:h3 "Key pair generation")
(:describe :generic-function (ironclad:generate-key-pair private-key,public-key))
(:p "Return a key pair according to " 'kind' ". If " 'kind' " is :DSA,
:ELGAMAL or :RSA, the " 'num-bits' "key argument indicating the size of
the keys to generate must be specified.
The generation of DSA, Elgamal and RSA key pairs can take a very long time.")
(:h3 "Key construction")
(:describe :generic-function (ironclad:make-public-key public-key))
(:p "Return a public key according to " 'kind' ". The " '&key' "
arguments vary according to " 'kind' ". The interesting bits are in the
methods that specialize on " 'kind' ", below.")
(:describe :method (ironclad:make-public-key ((eql :dsa)) public-key))
(:p "Return a DSA public key. " 'p' ", " 'q' ", " 'g' ", and " 'y' "
are the usual parameters for DSA keys discussed in the literature.")
(:describe :method (ironclad:make-public-key ((eql :ed25519)) public-key))
(:p "Return a Ed25519 public key. " 'y' and 'x' " are the usual parameters
for Ed25519 keys discussed in the literature.")
(:describe :method (ironclad:make-public-key ((eql :elgamal)) public-key))
(:p "Return a Elgamal public key. " 'p' ", " 'g' ", and " 'y' "
are the usual parameters for Elgamal keys discussed in the literature.")
(:describe :method (ironclad:make-public-key ((eql :rsa)) public-key))
(:p "Return a RSA public key. " 'e' and 'n' " are the usual parameters
for RSA keys discussed in the literature.")
(:describe :generic-function (ironclad:make-private-key private-key))
(:p "Return a private key according to " 'kind' ". The " '&key' "
arguments vary according to " 'kind' ". The interesting bits are in the
methods that specialize on " 'kind' ", below.")
(:describe :method (ironclad:make-private-key ((eql :dsa)) private-key))
(:p "Return a DSA private key. " 'p' ", " 'q' ", " 'g' ", " 'y' ", and
" 'x' " are the usual parameters for DSA keys discussed in the
literature.")
(:describe :method (ironclad:make-private-key ((eql :ed25519)) private-key))
(:p "Return a Ed25519 private key. " 'y' and 'x' " are the usual parameters
for Ed25519 keys discussed in the literature.")
(:describe :method (ironclad:make-private-key ((eql :elgamal)) private-key))
(:p "Return a Elgamal private key. " 'p' ", " 'g' ", " 'y' ", and
" 'x' " are the usual parameters for Elgamal keys discussed in the
literature.")
(:describe :method (ironclad:make-private-key ((eql :rsa)) private-key))
(:p "Return a RSA private key. " 'd' and 'n' " are the usual parameters
for RSA keys discussed in the literature.")
(:h3 "Digital signatures")
(:describe :generic-function (ironclad:sign-message signature))
(:p "Return a signature of " 'message' " between " 'start' " and " 'end'
" signed with " 'key' "; the class of " 'key' " determines the class of
" 'signature' ".")
(:describe :generic-function (ironclad:verify-signature boolean))
(:p "Verify whether " 'signature' " is the signature of " 'message' "
between " 'start' " and " 'end' " using " 'key' ". Return T or NIL
depending on the result of verification.")
(:h4 "Padding")
(:p "To be secure, RSA signature requires the message to be padded.
The " 'pss' "key parameter is provided to pad (or unpad) the message
during signature (or verification) with the PSS scheme of PKCS-1.")
(:describe :method (ironclad:sign-message ((eql :rsa)) signature))
(:describe :method (ironclad:verify-signature ((eql :rsa)) boolean))
(:h4 "Format of signatures")
(:p @sign-message " returns signatures as octet vectors.
When the signature contains several values (e.g. the R and S values
of DSA and Elgamal signatures), the octet vector is the concatenation
of these values (e.g. the first half of the vector is the R value,
the second half is the S value.")
(:h3 "Encryption and decryption")
(:describe :generic-function (ironclad:encrypt-message encrypted-message))
(:describe :generic-function (ironclad:decrypt-message decrypted-message))
(:h4 "Padding")
(:p "To be secure, RSA encryption requires the message to be padded.
The " 'oaep' "key parameter is provided to pad (or unpad) the message
during encryption (or decryption) with the OAEP scheme of PKCS-1.")
(:describe :method (ironclad:encrypt-message ((eql :rsa)) encrypted-message))
(:describe :method (ironclad:decrypt-message ((eql :rsa)) decrypted-message))
((:h2 id "prng") "Pseudo-random Number Generation")
(:h3 "Example")
(:p (:pre "> (setf *prng* (make-prng :fortuna))
-> #<fortuna-prng>
> (random-data 16)
-> #(61 145 133 130 220 200 90 86 0 101 62 169 0 40 101 78)
> (setf *prng* (make-prng :fortuna :seed nil))
-> #<fortuna-prng>
> (crypto:read-os-random-seed :random)
-> 1
> (crypto:strong-random 16)
-> 3
> (write-seed #P\"/tmp/seed\")
-> t
> (crypto:read-seed #P\"/tmp/seed\")
-> t
> (crypto:random-bits 16)
-> 41546
> (add-random-event 0 0 (string-to-octets \"foobar\"))"))
(:p "See details about " @add-random-event " below.")
(:describe :function (ironclad:make-prng prng))
(:p "Create an unseeded pseudo-random number generator.")
(:p 'name' " denotes the style of PRNG to use. " @list-all-prngs " will
tell you the names of all supported PRNGs. Currently only Fortuna is
supported. " 'name' " can be a symbol in the " `KEYWORD` " package or
the " `IRONCLAD` " package.")
(:p 'seed' " is a " (:em "seed descriptor") ". If NIL, the PRNG will
not be seeded (which may prevent it from generating output until it is
seeded, depending on the generator). If " `:RANDOM` " then the PRNG
will be seeded with the OS's cryptographically-secure PRNG. If "
`:URANDOM` " then the PRNG will be seeded with the OS's
fast-but-potentially-less-secure PRNG, if available (if not, will
fallback to " `:RANDOM` "). If it is a pathname indicator, a seed will
be read from the indicated file, then a new seed will be generated and
written back to the file (over-writing the old seed). Finally, if it is
a byte vector, it will be used to seed the PRNG.")
(:describe :function (ironclad:list-all-prngs list))
(:p "List all known PRNG types.")
(:describe :function (ironclad:random-data bytes))
(:p "Generate " 'num-bytes' " bytes of random data from "
'pseudo-random-number-generator' ". Updates the state of the generator.")
(:describe :function (ironclad:read-os-random-seed reseed-count))
(:p "Reads an OS-provided random seed (from /dev/urandom or /dev/random
on Unix; CryptGenRandom on Windows) and reseed " 'pseudo-random-number-generator' ".")
(:p 'source' " may be " `:RANDOM` ", which indicates /dev/random or "
`:URANDOM` ", which indicates /dev/urandom. On Windows, CryptGenRandom
is always used.")
(:describe :function (ironclad:read-seed t))
(:p "Read enough bytes from " 'path' " to reseed "
'pseudo-random-number-generator' ", then generate a pseudo-random seed
and write it back to " 'path' ". If " 'path' " doesn't exist, calls "
@read-os-random-seed " to get a truly random seed from the OS. Note
that reseeding does " (:em "not") " reset the generator's state to the
seed value; rather, it " (:em "combines" ) " the generator's state with
the seed to form a new state.")
(:describe :function (ironclad:write-seed t))
(:p "Generate enough random data to reseed "
'pseudo-random-number-generator' ", then write it to " 'path' ".")
(:describe :function (ironclad:random-bits integer))
(:p "Generate and integer with " 'num-bits' " bits.")
(:describe :function (ironclad:strong-random number))
(:p "A drop-in replacement for " 'COMMON-LISP:RANDOM' ", "
`STRONG-RANDOM` " generates a number (and integer if " 'limit' " is an
integer and a float if it is a float) between 0 and " 'limit' "-1 in an
unbiased fashion.")
(:h3 "Fortuna")
(:p "Fortuna is a cryptographically-secure random number presented by
Ferguson, Schneier and Kohno in " (:cite "Cryptography Engineering")
". It is built around 32 entropy pools, which are used with decreasing
frequency for each reseed (e.g. pool 0 is used in each reseed, pool 1 in
every other reseed, pool 2 in every fourth reseed and so forth). Pools
are seeded with data from up to 256 sources.")
(:p "Each application should have one or more entropy sources (say, one
for each OS random number source, one for the low bits of the current
time, one for the output of a particular command or group of commands
and so forth). A source should be used to add randomness to each pool
in order, so source 0 should top up pool 0, then pool 1, and so forth up
to pool 31, then loop back to pool 1 again. Be very careful to spread
entropy across all 32 pools.")
(:p "Fortuna automatically feeds entropy from the pools back into its
random state when " @random-data " is called, using a method designed to
make it resistant to various avenues of attack; even in case of
generator compromise it will return to a safe state within a bounded
time.")
(:p "For purposes of reseeding, Fortuna will not reseed until the first
pool contains 128 bits of entropy; " '+min-pool-size+' " sets the number
of bytes this is; it defaults to a very conservative 128, meaning that
by default each byte of event is assumed to contain a single bit of
randomness.")
(:p "It also will not reseed more than ten times per second.")
(:describe :function (ironclad:add-random-event pool-length))
(:p "Add entropy to " 'pseudo-random-number-generator' ".")
;; FIXME: how to create an ndash in this formatting language?
(:p 'source' " is an integer in the range 0-255 specifiying the event's
application-defined source.")
(:p 'pool-id' " is an integer in the range 0-31 specifying the pool to top up.")
(:p 'event' " is up to 32 bytes of data (for longer events, hash them
down or break them up into chunks).")
((:h2 id "gray-streams") "Gray Streams")
(:p "Ironclad includes support for several convenient stream
abstractions based on Gray streams. Gray streams support in Ironclad is
included for SBCL, CMUCL, OpenMCL, Lispworks, and Allegro.")
(:h3 "Octet streams")
(:p "Octet streams are very similar to Common Lisp's " @string-stream ",
except they deal in octets instead of characters.")
(:describe :function (ironclad:make-octet-input-stream octet-input-stream))
(:p "As " @make-string-input-stream ", only with octets instead of characters.")
(:describe :function (ironclad:make-octet-output-stream octet-output-stream))
(:p "As " @make-string-output-stream ", only with octets instead of characters.")
(:describe :function (ironclad:get-output-stream-octets octet-vector))
(:p "As " @get-output-stream-string ", only with an octet output-steam
instead of a string output-stream.")
(:h3 "Digest streams")
(:p "Digest streams compute a digest of the data written to them
according to a specific digest algorithm.")
(:p "Example:")
(:pre "(defun frobbing-function (stream)
;; We want to compute a digest of the data being written to STREAM
;; without involving our callees in the process.
(let* ((digesting-stream (crypto:make-digesting-stream :sha1))
(stream (make-broadcast-stream stream digesting-stream)))
;; Feed data to STREAM.
(frob-guts stream)
;; Do something with the digest computed.
(... (crypto:produce-digest digesting-stream) ...)
...))")
(:describe :function (ironclad:make-digesting-stream stream))
(:p "Make a stream that computes a digest of the data written to it
according to the algorithm " 'digest-name' ". " @produce-digest " may
be used to obtain a digest of all the data written to the stream.")
(:note "Calling " @produce-digest " on a digest stream does not alter
the internal state of the digest.")
(:h2 "Utility Functions")
(:describe :accessor (ironclad:ub16ref/le value)
(ironclad:ub32ref/le value)
(ironclad:ub64ref/le value))
(:p "This family of functions accesses an unsigned 16-bit, 32-bit or
64-bit value stored in little-endian order starting at " 'index' " in "
'array' ". " 'array' " must be a " `(SIMPLE-ARRAY (UNSIGNED-BYTE 8)
(*))` ". These functions are SETFable.")
(:describe :accessor (ironclad:ub16ref/be value)
(ironclad:ub32ref/be value)
(ironclad:ub64ref/be value))
(:p "As the above, only the value is stored in big-endian order.")
(:describe :function (ironclad:byte-array-to-hex-string string)
(ironclad:hex-string-to-byte-array string)
(ironclad:ascii-string-to-byte-array vector))
(:p `byte-array-to-hex-string` " converts the bytes of " 'vector' "
between " 'start' " and " 'end' " into a hexadecimal string. It is
useful for converting digests to a more readable form. " 'element-type'
" indicates the element-type of the returned string.")
(:p `hex-string-to-byte-array` " parses a substring of " 'string' "
delimited " 'start' " and " 'end' " of hexadecimal digits into a byte
array.")
(:p `ascii-string-to-byte-array` " is provided as a quick and dirty way
to convert a string to a byte array suitable for feeding to "
@update-digest " or " @encrypt ". Care should be taken to ensure that
the provided string is actually an ASCII string. " 'start' " and "
'end' " have their usual interpretations.")
(:describe :function (ironclad:octets-to-integer number)
(ironclad:integer-to-octets vector))
(:p `octets-to-integer` " converts the bytes of " 'octet-vec' " between
" 'start' " and " 'end' " to an integer as though the bytes denoted a
number in base 256. " 'big-endian' " is a boolean indicating whether
the bytes are to be read in big-endian or little-endian order. "
'n-bits' " specifies how many bits should be considered as significant
in the resulting number.")
(:p `integer-to-octets` " is the reverse operation.")
(:describe :function (ironclad:expt-mod number))
(:p "Raises " 'n' " to the " 'exponent' " power modulo " 'modulus' " in
a more efficient fashion than " `(MOD (EXPT N EXPONENT) MODULUS)` ".")
(:describe :function (ironclad:make-random-salt size))
(:p "Generate a byte vector of " 'size' " (default 16) random bytes, suitable
for use as a password salt.")
(:h2 "Conditions")
(:describe :condition ironclad-error)
(:p "All errors signaled by Ironclad are of this type. This type is a
direct subtype of " `SIMPLE-ERROR` " without any extra slots or
options.")
(:describe :condition initialization-vector-not-supplied)
(:p "This error is signaled by " @make-cipher " when an initialization
vector is not provided and the requested mode requires an initialization
vector.")
(:describe :condition invalid-initialization-vector)
(:p "This error is signaled when an invalid initialization vector is
supplied to " @make-cipher " (e.g. when the length of the initialization
vector does not match the block length of the cipher).")
(:describe :condition invalid-key-length)
(:p "This error is signaled when the key provided to " @make-cipher " is
not of an acceptable length for the requested cipher.")
(:describe :condition unsupported-cipher)
(:p "This error is signaled when the " 'cipher-name' " provided to "
@make-cipher " is not " @cipher-supported-p ".")
(:describe :condition unsupported-mode)
(:p "This error is signaled when the " 'mode' " provided to "
@make-cipher " is not " @mode-supported-p ".")
(:describe :condition unsupported-digest)
(:p "This error is signaled when the " 'digest-name' " provided to "
@make-digest " is not " @digest-supported-p ".")
(:describe :condition insufficient-buffer-space)
(:p "This error is signaled when Ironclad needs to stuff some data into a
buffer (e.g. when the user provides " 'digest' " to " @produce-digest ") and
there is insufficient space.")
(:describe :condition key-not-supplied)
(:p "This error is signaled when a " `:KEY` " argument is not provided
to " @make-cipher ".")
ironclad-v0.34/doc/index.html 0000644 0000000 0000000 00000137325 13057521520 014674 0 ustar root root
Ironclad
Ironclad
Ironclad is a cryptography library written entirely in Common
Lisp. It includes support for several popular ciphers, digests, and MACs. Rudimentary support for public-key
cryptography is included. For several implementations that support
Gray Streams, support is included for
convenient stream wrappers.
Ironclad was written primarily by Nathan Froyd (froydnj@gmail.com).
It comes with an ASDF system definition, so (ASDF:OOS
'ASDF:LOAD-OP :IRONCLAD) should be all that you need to get started.
The testsuite can be run by substituting ASDF:TEST-OP for ASDF:LOAD-OP in the form above.
Ironclad has been tested in the following implementations:
SBCL x86/linux, x86-64/linux (primary development platforms)
All included tests should pass successfully. If you use a platform
not listed above, please send your platform information to the author so
that he can add it to the above list. If the tests do not all pass, you
have found a bug; please report it.
License
Ironclad is released under a MIT-like license; you can do pretty
much anything you want to with the code except claim that you wrote
it.
Return a cipher object suitable for use for both encryption and
decryption.
name denotes the encryption algorithm to use. list-all-ciphers will tell you the names of all supported ciphers;
the short list of ones you are likely to be interested in is:
AES
DES
3DES
Blowfish
Twofish
RC5
RC6
Arcfour (RC4)
name can be a symbol in the KEYWORD package or the IRONCLAD package; :AES for AES, IRONCLAD:ARCFOUR for
RC4, and so forth.
mode describes the mode of operation for the cipher. Stream
ciphers such as Arcfour can operate in only one mode, stream.
Block ciphers such as AES and DES can operate in several different
modes:
ECB
CBC
OFB
CFB (note that Ironclad's CFB mode is 'n'-bit CFB, where 'n' is
the block-length of the cipher)
CFB8 (this seems to be the mode other crypto packages call
'CFB')
CTR
mode should be a symbol in the KEYWORD or IRONCLAD
packages; :STREAM, IRONCLAD:OFB, and so forth. An error
will be signaled if mode is not appropriate for the cipher name.
initialization-vector (IV) should be supplied only if mode
requires one. initialization-vector should be a (VECTOR
(UNSIGNED-BYTE 8)). The supplied IV should be the same length as the
block-length of name.
key is, of course, the key for the cipher. key should be
a (VECTOR (UNSIGNED-BYTE 8)).
If padding is supplied, the specified padding method will be
used by encrypt and decrypt to handle short blocks when the :HANDLE-FINAL-BLOCK argument is supplied. Depending on the mode
specified, padding may be ignored (e.g. OFB and CFB modes do not
care about short blocks; neither do stream ciphers).
Note
padding is currently ignored in all modes (and, by extension,
so is :HANDLE-FINAL-BLOCK). This oversight is expected to be
corrected in a future release.
Encrypts data according to cipher from plaintext
starting at plaintext-start and continuing until plaintext-end. The encrypted data is placed in ciphertext starting at ciphertext-start.
Decrypts data according to cipher from ciphertext
starting at ciphertext-start and continuing until ciphertext-end. The decrypted data is placed in plaintext
starting at plaintext-start.
Encrypts or decrypts data in text between start and end "in-place" according to cipher. These functions are
shorthand for:
(encrypt cipher text text :plaintext-start start :plaintext-end end :ciphertext-start start)
(decrypt cipher text text :ciphertext-start start :ciphertext-end end :plaintext-start start)
n-bytes-consumed and n-bytes-produced may not always be
equal to the length of the data specified in the call to encrypt-in-place or decrypt-in-place. This subtlely is also
present in encrypt or decrypt.
Inquiry functions
list-all-ciphers => list
Returns a list of cipher-names that may be validly passed to make-cipher.
cipher-supported-pname => boolean
Returns T if name would be in the list returned by list-all-ciphers, NIL otherwise.
key-lengthscipher => list
Return a list of valid key lengths for cipher.
block-lengthcipher => number
Return the number of octets cipher processes at a time. This
function always returns 1 for stream ciphers.
Digests
Digest functions, also known as hash functions, produce
fixed-length output (a digest or hash) from a
variable-length message. The simplest example of a digest function is
one that adds up all the bytes in the message modulo 256. This digest
function fails one test of a cryptographically secure hash function: it
must be difficult to find a message with a given digest. It also fails
the other test: it must be difficult to find two messages with the same
digest.
Ironclad provides several cryptographically secure digest functions
and several non-cryptographically secure digest functions.
Note
In the functions below, messages or parts thereof are provided
as octet vectors; Ironclad has no facilities for producing digests of
strings. If you need to obtain the digest of a string, then you need to
figure out how to convert it to an octet vector first. This is a
deliberate design decision. Characters are not equivalent to bytes.
See your local Unicode guru for more details.
Returns a digest object. digest-name is a keyword naming the
algorithm you wish digester to use. The algorithms you are likely
to want to use are:
MD4
MD5
SHA1
SHA256
Tiger
Adler32
CRC32
Other legitimate digest names can be found by calling list-all-digests. Like make-cipher, digest-name should be
a symbol in the KEYWORD or IRONCLAD packages.
Updates the internal state of digester with the contents of thing. The exact method is determined by the type of THING.
There are several methods defined on this generic function that
take a particular digester and a (SIMPLE-ARRAY (UNSIGNED-BYTE 8)
(*)) as well as the usual start and end keyword
arguments. These methods update the state of digester with the
subsequence of the array denoted by start and end. They are
not listed here because there's one method for every type of digest
that Ironclad provides, and listing them would get very tedious
for no benefit. An example should suffice.
Update the internal state of digester with the contents of stream, which must respond to READ-BYTE or READ-SEQUENCE
with a (SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*)) and return digester. It differs from digest-stream, below, in that you
may need to digest data before or after the contents of stream
(this happens, for instance, when signing the contents of some file).
Return the digest of the data processed by digester so far.
The internal state of digester is modified; if you wish to retain
a copy of the digest, you must call copy-digest.
If digest is provided, the computed digest will be placed
into digest starting at digest-start. digest must be
a (SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*)). An insufficient-buffer-space error will be signaled if there is
insufficient space in digest.
High-level convenience functions
Several high-level convenience functions that encapsulate common
sequences of make-digest, update-digest and produce-digest are provided by Ironclad as well. They come in two flavors: the first
takes a digest name as would be provided to make-digest. The
second way to call these functions is to provide an actual digest object
as the first argument. So one can say:
Returns the digest of the subsequence of sequence bounded by
start and end, according to digest-name. sequence must be a (SIMPLE-ARRAY (UNSIGNED-BYTE 8)). digest and digest-start are as in produce-digest.
Returns the digest of the contents of the stream specified by stream. READ-BYTE must be a legal operation on stream
and return an (UNSIGNED-BYTE 8). In a similar fashion, READ-SEQUENCE on stream must support reading into a (SIMPLE-ARRAY (UNSIGNED-BYTE 8)). digest and digest-start are as in produce-digest.
If buffer is provided, it must be a (SIMPLE-ARRAY
(UNSIGNED-BYTE 8) (*)); the portion of buffer between start and end will be used to read the data from the stream.
Returns the digest of the contents of the file named by pathname. digest and digest-start are as in produce-digest.
If buffer is provided, it must be a (SIMPLE-ARRAY
(UNSIGNED-BYTE 8) (*)); the portion of buffer between start and end will be used to read the data from the stream.
Inquiry functions
list-all-digests => list
Returns a list whose elements may be validly passed to make-digest.
digest-supported-pname => boolean
Returns T if name would be in the list returned by list-all-digests, NIL otherwise.
digest-lengthdigest => number
Returns the length of the digest computed by digest, which
may be a digest-name or a digest instance.
Miscellaneous
Ironclad digests are CLOS objects; the interesting thing about this
for most purposes is that functions like REINITIALIZE-INSTANCE are
supported. This means one can write a fairly efficient clone of the md5sum program like so:
(defun digest-sum-files (digest-name &rest files)
(unless files
(error "no files given to digest"))
(loop with buffer = (make-array 8192 :element-type '(unsigned-byte 8))
with digest = (make-array (ironclad:digest-length digest-name)
:element-type '(unsigned-byte 8))
for file in files
for digester = (ironclad:make-digest digest-name)
then (reinitialize-instance digester)
do (ironclad:digest-file digester file :buffer buffer :digest digest)
(format t "~A ~A~%" (file-namestring file)
(ironclad:byte-array-to-hex-string digest))))
Tree hashes
Ironclad supports tree hashes, as described in Tree Hash EXchange format. You create tree hashes as if you were
creating a digest:
(ironclad:make-digest :tree-hash)
By default, this creates a tree hash that uses the Tiger digest
algorithm internally and a segment size of 1024. Since using the Tiger
digest algorithm is so common, a convenience function that makes your
intent obvious:
(ironclad:make-tiger-tree-hash)
has also been provided.
You may indicate that you wish to use a different algorithm than
Tiger:
There is currently no interface for obtaining the intermediate
hashes computed while computing the final tree hash.
Message authentication codes
A message authentication code is a cryptographic function of some
data and a user-specified key. Only a person knowing the key can
recompute the MAC for the given message. A MAC is useful where
maintaining data integrity is required, but the secrecy of the data is
not paramount.
Ironclad provides two different kinds of MACs: HMACs, specified in
RFC 2104, and CMACs,
specified in RFC 4493
and NIST document 800-38B.
HMACs
Instances of HMACs are constructed by specifying a secret key and a
digest-name.
make-hmackeydigest-name => hmac
Return an HMAC instance based on the hash function digest-name with secret key key.
The returned object supports REINITIALIZE-INSTANCE:
The :KEY argument is the secret key, as provided to make-hmac.
update-hmachmacsequence&keystartend => hmac
Update the internal state of hmac with the data in sequence bounded by start and end. sequence must
be a (SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*)).
hmac-digesthmac&keybufferbuffer-start => digest
Returns the MAC (digest) computed by hmac thus far.
The internal state of hmac is not modified; this feature makes it
possible to compute a "rolling MAC" of a document. The length of digest is determined by the digest-length of digest-name
passed to make-hmac when hmac was constructed.
If buffer is provided, the computed MAC will be placed into buffer starting at buffer-start. buffer must be a (SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*)). An insufficient-buffer-space error will be signaled if there is
insufficient space in buffer.
Key derivation functions
Ironclad comes with the basic PBKDF1 and PBKDF2 algorithms, as well as
an implementation of the scrypt key derivation function as defined by
Colin Percival's The scrypt key derivation function.
Given a key derivation function object (produced by make-kdf),
a password and salt (both must be of type (SIMPLE-ARRAY
(UNSIGNED-BYTE 8) (*))), and number of digest iterations, returns
the password digest as a byte array of length key-length.
Scrypt ignores iteration-count parameter.
make-kdfkind&keydigestnrp => kdf
Returns a key derivation function instance (kind must
either be PBKDF1, PBKDF2 or SCRYPT-KDF). The PBKDF
algorithms use digest. The scrypt key derivation uses cost parameters N, r and p (N is a CPU cost parameter that must be a
power of 2, r and p are memory cost parameters that must be defined
such that r * p <= 2^30.)
The default parameters are N = 4096, r = 8, and p
= 2. Please note that depending on the values of N and r, derive-key may not be able to allocate sufficient space for its
temporary arrays.
PBKDF Convenience functions
Ironclad comes with convenience functions for using PBKDF1 and
PBKDF2 to store passwords.
Convenience function for hashing passwords using the PBKDF2
algorithm. Returns the derived hash of the password, and the original
salt, as byte vectors.
Convenience function for hashing passwords using the PBKDF2
algorithm. Returns the derived hash of the password as a single string
that encodes the given salt and PBKDF2 algorithm parameters.
Given a password byte vector and a combined salt and digest string
produced by pbkdf2-hash-password-to-combined-string, checks whether
the password is valid.
CMACs
Instances of CMACs are constructed by specifying a secret key and a
cipher-name.
make-cmackeycipher-name => cmac
Return a CMAC instance based on the cipher cipher-name with
secret key key. cipher-name must have a block-length
of either 8 or 16; this restriction is satisfied by most ciphers in
Ironclad with the notable exception of stream ciphers. key must
be an acceptable key for cipher-name.
update-cmaccmacsequence&keystartend => cmac
Update the internal state of cmac with the data in sequence bounded by start and end. sequence must
be a (SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*)).
cmac-digestcmac => digest
Returns the MAC (digest) computed by cmac thus far.
The internal state of cmac is not modified; this feature makes it
possible to compute a "rolling MAC" of a document. The length of digest is determined by the block-length of cipher-name
passed to make-cmac when cmac was constructed.
Public-key Operations
Ironclad includes support for DSA signing and verification.
Support for RSA encryption and decryption is provided as well, but it is
"raw"--the various formatting schemes (e.g. PKCS-1) must be implemented
by the user at this time.
Return a public key according to kind. The &key
arguments vary according to kind. The interesting bits are in the
methods that specialize on kind, below.
Return a private key according to kind. The &key
arguments vary according to kind. The interesting bits are in the
methods that specialize on kind, below.
This method places an additional constraint on the size of message specified by start and end: it must be exactly
20 bytes long (the length of a SHA-1 digest). signature is a dsa-signature object.
There is no one "right" way to format signatures into octet
vectors; different applications may have different requirements. sign-message therefore returns objects and lets the user determine
how to best format the values contained therein.
dsa-signature
A DSA signature object.
make-dsa-signaturers => signature
Returns a DSA signature with the provided r and s
values. r and s may be either integers or they may be
20-byte octet vectors.
dsa-signature-robject => integer
Returns the r value of the provided DSA signature.
dsa-signature-sobject => integer
Returns the s value of the provided DSA signature.
Create an unseeded pseudo-random number generator.
name denotes the style of PRNG to use. list-all-prngs will
tell you the names of all supported PRNGs. Currently only Fortuna is
supported. name can be a symbol in the KEYWORD package or
the IRONCLAD package.
seed is a seed descriptor. If NIL, the PRNG will
not be seeded (which may prevent it from generating output until it is
seeded, depending on the generator). If :RANDOM then the PRNG
will be seeded with the OS's cryptographically-secure PRNG. If :URANDOM then the PRNG will be seeded with the OS's
fast-but-potentially-less-secure PRNG, if available (if not, will
fallback to :RANDOM). If it is a pathname indicator, a seed will
be read from the indicated file, then a new seed will be generated and
written back to the file (over-writing the old seed). Finally, if it is
a byte vector, it will be used to seed the PRNG.
Reads an OS-provided random seed (from /dev/urandom or /dev/random
on Unix; CryptGenRandom on Windows) and reseed pseudo-random-number-generator.
source may be :RANDOM, which indicates /dev/random or :URANDOM, which indicates /dev/urandom. On Windows, CryptGenRandom
is always used.
read-seedpath&optionalpseudo-random-number-generator => t
Read enough bytes from path to reseed pseudo-random-number-generator, then generate a pseudo-random seed
and write it back to path. If path doesn't exist, calls read-os-random-seed to get a truly random seed from the OS. Note
that reseeding does not reset the generator's state to the
seed value; rather, it combines the generator's state with
the seed to form a new state.
write-seedpath&optionalprng => t
Generate enough random data to reseed pseudo-random-number-generator, then write it to path.
A drop-in replacement for COMMON-LISP:RANDOM, STRONG-RANDOM generates a number (and integer if limit is an
integer and a float if it is a float) between 0 and limit-1 in an
unbiased fashion.
Fortuna
Fortuna is a cryptographically-secure random number presented by
Ferguson, Schneier and Kohno in Cryptography Engineering. It is built around 32 entropy pools, which are used with decreasing
frequency for each reseed (e.g. pool 0 is used in each reseed, pool 1 in
every other reseed, pool 2 in every fourth reseed and so forth). Pools
are seeded with data from up to 256 sources.
Each application should have one or more entropy sources (say, one
for each OS random number source, one for the low bits of the current
time, one for the output of a particular command or group of commands
and so forth). A source should be used to add randomness to each pool
in order, so source 0 should top up pool 0, then pool 1, and so forth up
to pool 31, then loop back to pool 1 again. Be very careful to spread
entropy across all 32 pools.
Fortuna automatically feeds entropy from the pools back into its
random state when random-data is called, using a method designed to
make it resistant to various avenues of attack; even in case of
generator compromise it will return to a safe state within a bounded
time.
For purposes of reseeding, Fortuna will not reseed until the first
pool contains 128 bits of entropy; +min-pool-size+ sets the number
of bytes this is; it defaults to a very conservative 128, meaning that
by default each byte of event is assumed to contain a single bit of
randomness.
It also will not reseed more than ten times per second.
source is an integer in the range 0-255 specifiying the event's
application-defined source.
pool-id is an integer in the range 0-31 specifying the pool to top up.
event is up to 32 bytes of data (for longer events, hash them
down or break them up into chunks).
Gray Streams
Ironclad includes support for several convenient stream
abstractions based on Gray streams. Gray streams support in Ironclad is
included for SBCL, CMUCL, OpenMCL, Lispworks, and Allegro.
Octet streams
Octet streams are very similar to Common Lisp's string-stream,
except they deal in octets instead of characters.
Digest streams compute a digest of the data written to them
according to a specific digest algorithm.
Example:
(defun frobbing-function (stream)
;; We want to compute a digest of the data being written to STREAM
;; without involving our callees in the process.
(let* ((digesting-stream (crypto:make-digesting-stream :sha1))
(stream (make-broadcast-stream stream digesting-stream)))
;; Feed data to STREAM.
(frob-guts stream)
;; Do something with the digest computed.
(... (crypto:produce-digest digesting-stream) ...)
...))
make-digesting-streamdigest => stream
Make a stream that computes a digest of the data written to it
according to the algorithm digest-name. produce-digest may
be used to obtain a digest of all the data written to the stream.
Note
Calling produce-digest on a digest stream does not alter
the internal state of the digest.
Utility Functions
ub16ref/levectorindex => value ub32ref/levectorindex => value ub64ref/levectorindex => value
This family of functions accesses an unsigned 16-bit, 32-bit or
64-bit value stored in little-endian order starting at index in array. array must be a (SIMPLE-ARRAY (UNSIGNED-BYTE 8)
(*)). These functions are SETFable.
ub16ref/bevectorindex => value ub32ref/bevectorindex => value ub64ref/bevectorindex => value
As the above, only the value is stored in big-endian order.
byte-array-to-hex-string converts the bytes of vector
between start and end into a hexadecimal string. It is
useful for converting digests to a more readable form. element-type indicates the element-type of the returned string.
hex-string-to-byte-array parses a substring of string
delimited start and end of hexadecimal digits into a byte
array.
ascii-string-to-byte-array is provided as a quick and dirty way
to convert a string to a byte array suitable for feeding to update-digest or encrypt. Care should be taken to ensure that
the provided string is actually an ASCII string. start and end have their usual interpretations.
octets-to-integeroctet-vec&keystartendbig-endiann-bits => number integer-to-octetsbignum&keyn-bitsbig-endian => vector
octets-to-integer converts the bytes of octet-vec between
start and end to an integer as though the bytes denoted a
number in base 256. big-endian is a boolean indicating whether
the bytes are to be read in big-endian or little-endian order. n-bits specifies how many bits should be considered as significant
in the resulting number.
integer-to-octets is the reverse operation.
expt-mod => number
Raises n to the exponent power modulo modulus in
a more efficient fashion than (MOD (EXPT N EXPONENT) MODULUS).
make-random-salt&optionalsize => size
Generate a byte vector of size (default 16) random bytes, suitable
for use as a password salt.
Conditions
ironclad-error
All errors signaled by Ironclad are of this type. This type is a
direct subtype of SIMPLE-ERROR without any extra slots or
options.
initialization-vector-not-supplied
This error is signaled by make-cipher when an initialization
vector is not provided and the requested mode requires an initialization
vector.
invalid-initialization-vector
This error is signaled when an invalid initialization vector is
supplied to make-cipher (e.g. when the length of the initialization
vector does not match the block length of the cipher).
invalid-key-length
This error is signaled when the key provided to make-cipher is
not of an acceptable length for the requested cipher.
This error is signaled when Ironclad needs to stuff some data into a
buffer (e.g. when the user provides digest to produce-digest) and
there is insufficient space.
key-not-supplied
This error is signaled when a :KEY argument is not provided
to make-cipher.