(: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 "&gt; (setf *prng* (make-prng :fortuna))
-&gt; #&lt;fortuna-prng&gt;
&gt; (random-data 16)
-&gt; #(61 145 133 130 220 200 90 86 0 101 62 169 0 40 101 78)
&gt; (setf *prng* (make-prng :fortuna :seed nil))
-&gt; #&lt;fortuna-prng&gt;
&gt; (crypto:read-os-random-seed :random)
-&gt; 1
&gt; (crypto:strong-random 16)
-&gt; 3
&gt; (write-seed #P\"/tmp/seed\")
-&gt; t
&gt; (crypto:read-seed #P\"/tmp/seed\")
-&gt; t
&gt; (crypto:random-bits 16)
-&gt; 41546
&gt; (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 ".")
