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 (email@example.com).
Ironclad can be downloaded at http://www.method-combination.net/lisp/files/ironclad.tar.gz. The latest released version is 0.32. If you are feeling adventurous, you can download a bleeding-edge version at http://github.com/froydnj/ironclad.
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:
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.
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:
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:
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)
|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.|
Returns a list of cipher-names that may be validly passed to make-cipher.
Returns T if name would be in the list returned by list-all-ciphers, NIL otherwise.
Return a list of valid key lengths for cipher.
Return the number of octets cipher processes at a time. This function always returns 1 for stream ciphers.
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:
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.
(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))
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.
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:
(ironclad:digest-sequence :md5 *buffer*)
(let ((digester (make-digest :md5))) (ironclad:digest-sequence digester *buffer*))
The second form comes in handy if you plan on reusing the digest object.
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.
Returns a list whose elements may be validly passed to make-digest.
Returns T if name would be in the list returned by list-all-digests, NIL otherwise.
Returns the length of the digest computed by digest, which may be a digest-name or a digest instance.
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))))
Ironclad supports tree hashes, as described in Tree Hash EXchange format. You create tree hashes as if you were creating a digest:
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:
has also been provided.
You may indicate that you wish to use a different algorithm than Tiger:
(ironclad:make-digest '(:treehash :digest :sha256))
Or you might wish to use a different segment size:
(ironclad:make-digest '(:tree-hash :block-length 16384))
There is currently no interface for obtaining the intermediate hashes computed while computing the final tree hash.
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.
Instances of HMACs are constructed by specifying a secret key and a digest-name.
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 the internal state of hmac with the data in sequence bounded by start and end. sequence must be a (SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*)).
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.
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.
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.
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.
Instances of CMACs are constructed by specifying a secret key and a cipher-name.
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 the internal state of cmac with the data in sequence bounded by start and end. sequence must be a (SIMPLE-ARRAY (UNSIGNED-BYTE 8) (*)).
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.
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 DSA public key. p, q, g, and y are the usual parameters for DSA keys discussed in the literature.
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.
Return a DSA private key. p, q, g, y, and x are the usual parameters for DSA keys discussed in the literature.
Return a signature of message between start and end signed with key; the class of key determines the class of signature.
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.
Verify whether signature is the signature of message between start and end using key. Return T or NIL depending on the result of verification.
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.
A DSA signature object.
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.
Returns the r value of the provided DSA signature.
Returns the s value of the provided DSA signature.
> (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"))
See details about add-random-event below.
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.
List all known PRNG types.
Generate num-bytes bytes of random data from pseudo-random-number-generator. Updates the state of the generator.
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 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.
Generate enough random data to reseed pseudo-random-number-generator, then write it to path.
Generate and integer with num-bits bits.
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 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.
Add entropy to pseudo-random-number-generator.
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).
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 are very similar to Common Lisp's string-stream, except they deal in octets instead of characters.
As make-string-input-stream, only with octets instead of characters.
As make-string-output-stream, only with octets instead of characters.
As get-output-stream-string, only with an octet output-steam instead of a string output-stream.
Digest streams compute a digest of the data written to them according to a specific digest algorithm.
(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 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.|
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.
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-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.
Raises n to the exponent power modulo modulus in a more efficient fashion than (MOD (EXPT N EXPONENT) MODULUS).
Generate a byte vector of size (default 16) random bytes, suitable for use as a password salt.
All errors signaled by Ironclad are of this type. This type is a direct subtype of SIMPLE-ERROR without any extra slots or options.
This error is signaled by make-cipher when an initialization vector is not provided and the requested mode requires an 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).
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 the cipher-name provided to make-cipher is not cipher-supported-p.
This error is signaled when the mode provided to make-cipher is not mode-supported-p.
This error is signaled when the digest-name provided to make-digest is not digest-supported-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.
This error is signaled when a :KEY argument is not provided to make-cipher.