diff -Nru cl-ironclad-0.45/debian/changelog cl-ironclad-0.47/debian/changelog --- cl-ironclad-0.45/debian/changelog 2019-02-21 11:23:45.000000000 +0000 +++ cl-ironclad-0.47/debian/changelog 2020-01-19 21:18:19.000000000 +0000 @@ -1,3 +1,10 @@ +cl-ironclad (0.47-1) unstable; urgency=medium + + * New upstream release + * Bump S-V to 4.4.1 + + -- Sébastien Villemot Sun, 19 Jan 2020 22:18:19 +0100 + cl-ironclad (0.45-1) unstable; urgency=medium * New upstream version 0.45 diff -Nru cl-ironclad-0.45/debian/control cl-ironclad-0.47/debian/control --- cl-ironclad-0.45/debian/control 2019-02-21 11:23:45.000000000 +0000 +++ cl-ironclad-0.47/debian/control 2020-01-19 21:17:56.000000000 +0000 @@ -4,7 +4,7 @@ Maintainer: Debian Common Lisp Team Uploaders: Sébastien Villemot Build-Depends: debhelper-compat (= 12) -Standards-Version: 4.3.0 +Standards-Version: 4.4.1 Homepage: https://github.com/sharplispers/ironclad Vcs-Git: https://salsa.debian.org/common-lisp-team/cl-ironclad.git Vcs-Browser: https://salsa.debian.org/common-lisp-team/cl-ironclad diff -Nru cl-ironclad-0.45/doc/ironclad.html cl-ironclad-0.47/doc/ironclad.html --- cl-ironclad-0.45/doc/ironclad.html 2019-02-18 14:08:52.000000000 +0000 +++ cl-ironclad-0.47/doc/ironclad.html 2019-09-11 12:36:21.000000000 +0000 @@ -1,7 +1,7 @@ - + Ironclad @@ -43,7 +43,7 @@ Most of the algorithms were written with efficiency for specific Common Lisp implementations in mind, although portable code is provided as an alternative in nearly all instances. The framework -should be flexible enough to accomodate implementation-specific +should be flexible enough to accommodate implementation-specific optimizations when possible.

@@ -73,8 +73,8 @@ -
-

Warnings

+
+

Warnings

Ironclad should not be considered safe against side channel attacks. @@ -116,17 +116,17 @@ you are using the threading functions of your Common Lisp implementation directly, you have to bind the *prng* special variable to a new PRNG in each thread. There is an example showing how it can be done in the section about -make-prng. +make-prng.

-
-

Installation

+
+

Installation

-The current version of Ironclad is 0.45. It can be downloaded -at https://github.com/sharplispers/ironclad/archive/v0.45.tar.gz. +The current version of Ironclad is 0.47. It can be downloaded +at https://github.com/sharplispers/ironclad/archive/v0.47.tar.gz. If you are feeling adventurous, you can download a bleeding-edge version at https://github.com/sharplispers/ironclad.

@@ -183,8 +183,8 @@
-
-

License

+
+

License

Ironclad is released under a MIT-like license; you can do pretty much @@ -193,11 +193,11 @@

-
-

Ciphers

+
+

Ciphers

-
(make-cipher name &key key mode initialization-vector padding tweak) => cipher
+
(make-cipher name &key key mode initialization-vector padding tweak) => cipher
 
@@ -206,7 +206,7 @@

-name denotes the encryption algorithm to use. list-all-ciphers will tell you +name denotes the encryption algorithm to use. list-all-ciphers will tell you the names of all supported ciphers. They are:

    @@ -267,7 +267,7 @@
  • ecb
  • cbc
  • ofb
  • -
  • cfb (note that Ironclad's CFB mode is n-bit CFB, where n is the block-length of the cipher)
  • +
  • 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
@@ -281,7 +281,7 @@

initialization-vector (IV) should be supplied only if mode requires one. initialization-vector should be a (simple-array (unsigned-byte 8) (*)). -The supplied IV should be the same length as the block-length of name. +The supplied IV should be the same length as the block-length of name. The Chacha and Salsa20 stream ciphers also use an initialization vector (nonce). It should be 8 bytes long for Chacha and Salsa20, and 24 bytes long for XChacha and XSalsa20. @@ -293,8 +293,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 +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. padding will only be used if the mode is ECB or CBC. The possible values for padding are :pkcs7, :ansi-x923 and :iso-7816-4.

@@ -306,7 +306,7 @@
-
(encrypt cipher plaintext ciphertext &key plaintext-start plaintext-end ciphertext-start handle-final-block) => n-bytes-consumed, n-bytes-produced
+
(encrypt cipher plaintext ciphertext &key plaintext-start plaintext-end ciphertext-start handle-final-block) => n-bytes-consumed, n-bytes-produced
 
@@ -318,7 +318,7 @@
-
(decrypt cipher ciphertext plaintext &key ciphertext-start ciphertext-end plaintext-start handle-final-block) => n-bytes-consumed, n-bytes-produced
+
(decrypt cipher ciphertext plaintext &key ciphertext-start ciphertext-end plaintext-start handle-final-block) => n-bytes-consumed, n-bytes-produced
 
@@ -330,11 +330,11 @@
-
(encrypt-in-place cipher text &key start end) => n-bytes-consumed, n-bytes-produced
+
(encrypt-in-place cipher text &key start end) => n-bytes-consumed, n-bytes-produced
 
-
(decrypt-in-place cipher text &key start end) => n-bytes-consumed, n-bytes-produced
+
(decrypt-in-place cipher text &key start end) => n-bytes-consumed, n-bytes-produced
 
@@ -348,17 +348,17 @@

-Note: encrypt-in-place and decrypt-in-place do not support -a handle-final-block parameter as encrypt and decrypt do. If you +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. +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. +encrypt-in-place or decrypt-in-place. This subtlely is also present in +encrypt or decrypt.

@@ -384,32 +384,32 @@

-
-

Inquiry functions

-
+
+

Inquiry functions

+
-
(list-all-ciphers) => list
+
(list-all-ciphers) => list
 

-Returns a list of cipher-names that may be validly passed to make-cipher. +Returns a list of cipher-names that may be validly passed to make-cipher.

-
(cipher-supported-p name) => boolean
+
(cipher-supported-p name) => boolean
 

-Returns t if name would be in the list returned by list-all-ciphers, +Returns t if name would be in the list returned by list-all-ciphers, nil otherwise.

-
(key-lengths cipher) => list
+
(key-lengths cipher) => list
 
@@ -419,7 +419,7 @@
-
(block-length cipher) => number
+
(block-length cipher) => number
 
@@ -430,9 +430,9 @@
-
-

Key stream position

-
+
+

Key stream position

+

Block ciphers in CTR mode and some stream ciphers have the ability to change the current position within the key stream in constant time instead of having to @@ -440,21 +440,21 @@

-
(keystream-position cipher &optional position) => number or boolean
+
(keystream-position cipher &optional position) => number or boolean
 

Return or change the current position within the key stream of a cipher. -When position is not supplied, keystream-position returns the current position +When position is not supplied, keystream-position returns the current position in the key stream, or nil if it can't be determined. When position is supplied, the key stream position of the cipher is set to that position if -possible. keystream-position returns t if the repositioning is performed +possible. keystream-position returns t if the repositioning is performed successfully, or nil otherwise.

-keystream-position can be used with the following ciphers: +keystream-position can be used with the following ciphers:

  • all the block ciphers (aes, twofish, etc.) in CTR mode
  • @@ -475,8 +475,8 @@
-
-

Digests

+
+

Digests

Digest functions, also known as hash functions, produce fixed-length @@ -504,14 +504,14 @@

-
(make-digest digest-name &rest keys &key &allow-other-keys) => digester
+
(make-digest digest-name &rest keys &key &allow-other-keys) => digester
 

Returns a digest object. digest-name is a keyword naming the algorithm you wish digester to use. The supported digest names can be found -by calling list-all-digests. They are: +by calling list-all-digests. They are:

  • adler32
  • @@ -577,7 +577,7 @@

-Like for make-cipher, digest-name should be a symbol in the +Like for make-cipher, digest-name should be a symbol in the keyword or ironclad packages.

@@ -593,7 +593,7 @@
-
(update-digest digester thing &key &allow-other-keys) => (values)
+
(update-digest digester thing &key &allow-other-keys) => (values)
 
@@ -633,14 +633,14 @@ 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 +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).

-
(produce-digest digester &key digest digest-start) => digest
+
(produce-digest digester &key digest digest-start) => digest
 
@@ -651,19 +651,19 @@

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 +(simple-array (unsigned-byte 8) (*)). An insufficient-buffer-space error will be signaled if there is insufficient space in digest.

-
-

High-level convenience functions

-
+
+

High-level convenience functions

+

Several high-level convenience functions that encapsulate common -sequences of make-digest, update-digest and produce-digest are +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 +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:

@@ -682,12 +682,12 @@

-The second form comes in handy if you plan on reusing the digest object. +The second form comes in handy if you plan on reusing the digest object.

-
(digest-sequence digest-spec sequence &rest args &key start end digest digest-start) => digest
+
(digest-sequence digest-spec sequence &rest args &key start end digest digest-start) => digest
 
@@ -695,12 +695,12 @@ 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. +are as in produce-digest.

-
(digest-stream digest-spec stream &rest args &key buffer start end digest digest-start) => digest
+
(digest-stream digest-spec stream &rest args &key buffer start end digest digest-start) => digest
 
@@ -709,7 +709,7 @@ 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. +digest and digest-start are as in produce-digest.

@@ -720,13 +720,13 @@

-
(digest-file digest-spec pathname &rest args &key buffer start end digest digest-start) => digest
+
(digest-file digest-spec pathname &rest args &key buffer start end digest digest-start) => digest
 

Returns the digest of the contents of the file named by pathname. -digest and digest-start are as in produce-digest. +digest and digest-start are as in produce-digest.

@@ -737,32 +737,32 @@

-
-

Inquiry functions

-
+
+

Inquiry functions

+
-
(list-all-digests) => list
+
(list-all-digests) => list
 

-Returns a list whose elements may be validly passed to make-digest. +Returns a list whose elements may be validly passed to make-digest.

-
(digest-supported-p name) => boolean
+
(digest-supported-p name) => boolean
 

-Returns t if name would be in the list returned by list-all-digests, +Returns t if name would be in the list returned by list-all-digests, nil otherwise.

-
(digest-length digest) => number
+
(digest-length digest) => number
 
@@ -773,9 +773,9 @@
-
-

Miscellaneous

-
+
+

Miscellaneous

+

Ironclad digests are CLOS objects; the interesting thing about this for most purposes is that functions like reinitialize-instance are @@ -800,9 +800,9 @@

-
-

Tree hashes

-
+
+

Tree hashes

+

Ironclad supports tree hashes, as described in Tree Hash EXchange format. You create tree hashes as if you were creating a digest: @@ -848,8 +848,8 @@

-
-

Message authentication codes

+
+

Message authentication codes

A message authentication code is a cryptographic function of some data @@ -873,14 +873,14 @@

-
(make-mac mac-name key &rest args) => mac
+
(make-mac mac-name key &rest args) => mac
 

Return a MAC object initialized with a secret key. mac-name is a keyword naming the algorithm you wish mac to use. The supported -MACs can be found by calling list-all-macs. They are: +MACs can be found by calling list-all-macs. They are:

  • blake2-mac
  • @@ -894,7 +894,7 @@

-Like for make-digest, mac-name should be a symbol in the keyword +Like for make-digest, mac-name should be a symbol in the keyword or ironclad packages.

@@ -914,35 +914,35 @@

-When making a Blake2 MAC, the length of the key passed to make-mac +When making a Blake2 MAC, the length of the key passed to make-mac must be 64 bytes.

-When making a Blake2s MAC, the length of the key passed to make-mac +When making a Blake2s MAC, the length of the key passed to make-mac must be 32 bytes.

-When making a CMAC, cipher-name must have a block-length of either 8, 16, 32, +When making a CMAC, cipher-name must have a block-length of either 8, 16, 32, 64 or 128; this restriction is satisfied by many ciphers in Ironclad with the notable exception of stream ciphers. key must be an acceptable key for cipher-name.

-When making a GMAC, cipher-name must have a block-length of 16. key must be +When making a GMAC, cipher-name must have a block-length of 16. key must be an acceptable key for cipher-name. The length of initialization-vector must be 12 bytes.

-When making a Poly1305 MAC, the length of the key passed to make-mac must be +When making a Poly1305 MAC, the length of the key passed to make-mac must be 32 bytes.

-When making a SipHash MAC, the length of the key passed to make-mac must be 16 +When making a SipHash MAC, the length of the key passed to make-mac must be 16 bytes. digest-length is 8 by default, but it can also be set to 16. By default, compression-rounds is 2 and finalization-rounds is 4.

@@ -966,12 +966,12 @@

-The :key argument is the secret key, as provided to make-mac. +The :key argument is the secret key, as provided to make-mac.

-
(update-mac mac thing &key &allow-other-keys) => (values)
+
(update-mac mac thing &key &allow-other-keys) => (values)
 
@@ -1004,7 +1004,7 @@
-
(produce-mac mac &key digest digest-start) => digest
+
(produce-mac mac &key digest digest-start) => digest
 
@@ -1017,57 +1017,57 @@

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 +(simple-array (unsigned-byte 8) (*)). An insufficient-buffer-space error will be signaled if there is insufficient space in digest.

-The length of the digest returned by produce-mac is determined by the -kind of MAC and the extra arguments passed to make-mac: +The length of the digest returned by produce-mac is determined by the +kind of MAC and the extra arguments passed to make-mac:

  • blake2-mac: from 1 to 64 bytes (64 by default)
  • blake2s-mac: from 1 to 32 bytes (32 by default)
  • -
  • cmac: block-length of the cipher-name passed to make-mac
  • +
  • cmac: block-length of the cipher-name passed to make-mac
  • gmac: 16 bytes
  • -
  • hmac: digest-length of the digest-name passed to make-mac
  • +
  • hmac: digest-length of the digest-name passed to make-mac
  • poly1305: 16 bytes
  • -
  • siphash: digest-length passed to make-mac (8 by default)
  • -
  • skein-mac: digest-length passed to make-mac (64 by default)
  • +
  • siphash: digest-length passed to make-mac (8 by default)
  • +
  • skein-mac: digest-length passed to make-mac (64 by default)
-
-

Inquiry functions

-
+
+

Inquiry functions

+
-
(list-all-macs) => list
+
(list-all-macs) => list
 

-Returns a list whose elements may be validly passed to make-mac. +Returns a list whose elements may be validly passed to make-mac.

-
(mac-supported-p name) => boolean
+
(mac-supported-p name) => boolean
 

-Returns t if name would be in the list returned by list-all-macs, +Returns t if name would be in the list returned by list-all-macs, nil otherwise.

-
-

Authenticated encryption

+
+

Authenticated encryption

-
(make-authenticated-encryption-mode name &rest args) => mode
+
(make-authenticated-encryption-mode name &rest args) => mode
 
@@ -1077,7 +1077,7 @@

-name denotes the mode to use. list-all-authenticated-encryption-modes will +name denotes the mode to use. list-all-authenticated-encryption-modes will tell you the names of all the supported modes. They are:

    @@ -1104,8 +1104,8 @@ If tag is specified, it will be used at the end of decryption (when the handle-final-block flag is t) to check the authenticity of the data. A bad-authentication-tag error will be signaled if the data is not authentic. -If you don't specify it, you will have to call produce-tag after decryption and -check that the tags match (e.g. using constant-time-equal). +If you don't specify it, you will have to call produce-tag after decryption and +check that the tags match (e.g. using constant-time-equal).

    @@ -1113,18 +1113,18 @@

    -When using ETM, cipher must be a cipher object created by make-cipher. -mac must be a mac object created by make-mac. +When using ETM, cipher must be a cipher object created by make-cipher. +mac must be a mac object created by make-mac.

    -When using GCM, cipher-name must have a block-length of 16 bytes. key must be a suitable key +When using GCM, cipher-name must have a block-length of 16 bytes. key must be a suitable key for the chosen cipher. The length of initialization-vector must be 12 bytes.

    -
    (process-associated-data mode data &key start end) => (values)
    +
    (process-associated-data mode data &key start end) => (values)
     
    @@ -1135,8 +1135,8 @@

    -An authenticated encryption object can be used with the encrypt, decrypt, -encrypt-message and decrypt-message functions. +An authenticated encryption object can be used with the encrypt, decrypt, +encrypt-message and decrypt-message functions.

    @@ -1148,7 +1148,7 @@
     
     
     
    -
    (produce-tag mode &key tag tag-start) => tag
    +
    (produce-tag mode &key tag tag-start) => tag
     
    @@ -1156,78 +1156,97 @@ Return the authentication tag of the data processed by mode so far. If tag is provided, the computed tag will be placed into tag starting at tag-start. tag must be a (simple-array (unsigned-byte 8) (*)). An -insufficient-buffer-space error will be signaled if there is insufficient space +insufficient-buffer-space error will be signaled if there is insufficient space in tag.

-
-

Inquiry functions

-
+
+

Inquiry functions

+
-
(list-all-authenticated-encryption-modes) => list
+
(list-all-authenticated-encryption-modes) => list
 

Returns a list whose elements may be validly passed to -make-authenticated-encryption-mode. +make-authenticated-encryption-mode.

-
(authenticated-encryption-mode-supported-p name) => boolean
+
(authenticated-encryption-mode-supported-p name) => boolean
 

Returns t if name would be in the list returned by -list-all-authenticated-encryption-modes nil otherwise. +list-all-authenticated-encryption-modes nil otherwise.

-
-

Key derivation functions

+
+

Key derivation functions

Ironclad comes with a few key derivation functions:

    -
  • Argon2i
  • +
  • Argon2 (only Argon2d and Argon2i without parallelism are implemented)
  • PBKDF1
  • PBKDF2
  • Scrypt
  • +
  • HMAC
-
(derive-key kdf passphrase salt iteration-count key-length) => digest
+
(derive-key kdf passphrase salt iteration-count key-length) => digest
 

-Given a key derivation function object (produced by make-kdf), +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 iterations, returns the password digest as a byte array of length key-length.

-Scrypt ignores the iteration-count parameter. +Scrypt and HMAC ignore the iteration-count parameter.

-
(make-kdf kind &key digest n r p block-count additional-key additional-data) => kdf
+
(make-kdf kind &key digest n r p block-count additional-key additional-data) => kdf
 

-Returns a key derivation function instance (kind must either be -ARGON2I, PBKDF1, PBKDF2 or SCRYPT-KDF). -The Argon2i key derivation uses the block-count, additional-key +Returns a key derivation function instance. +

+ +

+kind denotes the key derivation function to use. They are: +

+
    +
  • argon2d
  • +
  • argon2i
  • +
  • pbkdf1
  • +
  • pbkdf2
  • +
  • scrypt-kdf
  • +
  • hmac-kdf
  • +
+ +

+kind can be a symbol in the keyword or ironclad packages. +

+ +

+The Argon2 key derivations use the block-count, additional-key and additional-data parameters (block-count is the number of 1 KiB memory blocks used by the function and it must be at least 8, additional-key and additional-data are optional). @@ -1241,14 +1260,20 @@

The default Scrypt 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 +derive-key may not be able to allocate sufficient space for its temporary arrays.

+ +

+The HMAC-KDF algorithm uses the digest parameter to precise what +hash function is used. It also optionally uses the additional-data +to precise the info vector from the RFC. +

-
-

PBKDF convenience functions

-
+
+

PBKDF convenience functions

+

Ironclad comes with convenience functions for using PBKDF1 and PBKDF2 to store passwords. @@ -1256,7 +1281,7 @@

-
(pbkdf2-hash-password password &key salt digest iterations) => password
+
(pbkdf2-hash-password password &key salt digest iterations) => password
 
@@ -1268,7 +1293,7 @@
-
(pbkdf2-hash-password-to-combined-string password &key salt digest iterations) => password
+
(pbkdf2-hash-password-to-combined-string password &key salt digest iterations) => password
 
@@ -1280,21 +1305,21 @@
-
(pbkdf2-check-password password combined-salt-and-digest) => boolean
+
(pbkdf2-check-password password combined-salt-and-digest) => boolean
 

Given a password byte vector and a combined salt and digest string -produced by pbkdf2-hash-password-to-combined-string, checks whether +produced by pbkdf2-hash-password-to-combined-string, checks whether the password is valid.

-
-

Public key cryptography

+
+

Public key cryptography

Ironclad includes support for a few public key cryptography algorithms. @@ -1329,11 +1354,11 @@

-
-

Key pair generation

-
+
+

Key pair generation

+
-
(generate-key-pair kind &key num-bits &allow-other-keys) => private-key, public-key
+
(generate-key-pair kind &key num-bits &allow-other-keys) => private-key, public-key
 
@@ -1356,11 +1381,11 @@
-
-

Key construction

-
+
+

Key construction

+
-
(make-public-key kind &key &allow-other-keys) => public-key
+
(make-public-key kind &key &allow-other-keys) => public-key
 
@@ -1382,7 +1407,7 @@
-
(make-private-key kind &key &allow-other-keys) => private-key
+
(make-private-key kind &key &allow-other-keys) => private-key
 
@@ -1434,43 +1459,43 @@
-
-

Key destructuring

-
+
+

Key destructuring

+

-The destructure-public-key and destructure-private-key functions can +The destructure-public-key and destructure-private-key functions can be useful if you need to store keys somewhere for future use.

-
(destructure-public-key public-key) => plist
+
(destructure-public-key public-key) => plist
 

Return the elements of a public key in a plist. The indicators of the -plist match the &key arguments of the make-public-key method. +plist match the &key arguments of the make-public-key method.

-
(destructure-private-key private-key) => plist
+
(destructure-private-key private-key) => plist
 

Return the elements of a private key in a plist. The indicators of the -plist match the &key arguments of the make-private-key method. +plist match the &key arguments of the make-private-key method.

-
-

Digital signatures

-
+
+

Digital signatures

+
-
(sign-message key message &key start end &allow-other-keys) => signature
+
(sign-message key message &key start end &allow-other-keys) => signature
 
@@ -1481,14 +1506,14 @@

-Note: The sign-message does not perform the hashing of the data. You +Note: The sign-message does not perform the hashing of the data. You should hash your data using your favorite hash function, and then use -this hash as the message passed to sign-message. +this hash as the message passed to sign-message.

-
(verify-signature key message signature &key start end &allow-other-keys) => boolean
+
(verify-signature key message signature &key start end &allow-other-keys) => boolean
 
@@ -1499,9 +1524,9 @@

-
-

Padding

-
+
+

Padding

+

To be secure, RSA signature requires the message to be padded. The pss key parameter is provided to pad (or unpad) the message @@ -1522,22 +1547,22 @@

-
-

Format of signatures

-
+
+

Format of signatures

+

-sign-message returns signatures as octet vectors. When the signature +sign-message returns signatures as octet vectors. When the signature contains several values (e.g. the R and S values of DSA 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). -You can use the make-signature and destructure-signature functions if +You can use the make-signature and destructure-signature functions if you need access to the elements of a signature (e.g. to use a different kind of serialization).

-
(make-signature kind &key &allow-other-keys) => signature
+
(make-signature kind &key &allow-other-keys) => signature
 
@@ -1572,23 +1597,23 @@
-
(destructure-signature kind signature) => plist
+
(destructure-signature kind signature) => plist
 

Return the elements of a signature in a plist. The indicators of the -plist match the &key arguments of the make-signature method. +plist match the &key arguments of the make-signature method.

-
-

Encryption and decryption

-
+
+

Encryption and decryption

+
-
(encrypt-message key message &key start end &allow-other-keys) => encrypted-message
+
(encrypt-message key message &key start end &allow-other-keys) => encrypted-message
 
@@ -1599,7 +1624,7 @@
-
(decrypt-message key message &key start end n-bits &allow-other-keys) => decrypted-message
+
(decrypt-message key message &key start end n-bits &allow-other-keys) => decrypted-message
 
@@ -1612,9 +1637,9 @@

-
-

Padding

-
+
+

Padding

+

To be secure, RSA encryption requires the message to be padded. The oaep key parameter is provided to pad (or unpad) the message during @@ -1635,23 +1660,23 @@

-
-

Format of messages

-
+
+

Format of messages

+

-encrypt-message returns encrypted messages as octet vectors. When the +encrypt-message returns encrypted messages as octet vectors. When the message contains several values (e.g. the C1 and C2 values of Elgamal messages), the octet vector is the concatenation of these values (e.g. the first half of the vector is the big-endian representation of the C1 value, the second half is the C2 value). You can use the -make-message and destructure-message functions if you need access to +make-message and destructure-message functions if you need access to the elements of a message (e.g. to use a different kind of serialization).

-
(make-message kind &key &allow-other-keys) => message
+
(make-message kind &key &allow-other-keys) => message
 
@@ -1678,23 +1703,23 @@
-
(destructure-message kind message) => plist
+
(destructure-message kind message) => plist
 

Return the elements of a message in a plist. The indicators of the -plist match the &key arguments of the make-message method. +plist match the &key arguments of the make-message method.

-
-

Diffie-Hellman key exchange

-
+
+

Diffie-Hellman key exchange

+
-
(diffie-hellman private-key public-key) => bytes
+
(diffie-hellman private-key public-key) => bytes
 
@@ -1707,8 +1732,8 @@
-
-

Pseudo-random number generation

+
+

Pseudo-random number generation

The *prng* special variable indicates which pseudo-random number @@ -1729,7 +1754,7 @@

-
(make-prng name &key seed) => prng
+
(make-prng name &key seed) => prng
 
@@ -1738,7 +1763,7 @@

-name denotes the style of PRNG to use. list-all-prngs will tell you +name denotes the style of PRNG to use. list-all-prngs will tell you the names of all supported PRNGs. Currently supported PRNGs are:

    @@ -1766,7 +1791,7 @@

    -In single-threaded applications, you should very rarely need to call make-prng; +In single-threaded applications, you should very rarely need to call make-prng; the default OS-provided PRNG should be appropriate in nearly all cases.

    @@ -1792,7 +1817,7 @@
    -
    (list-all-prngs) => list
    +
    (list-all-prngs) => list
     
    @@ -1802,7 +1827,7 @@
    -
    (random-data num-bytes &optional prng) => bytes
    +
    (random-data num-bytes &optional prng) => bytes
     
    @@ -1813,7 +1838,7 @@
    -
    (random-bits num-bits &optional prng) => integer
    +
    (random-bits num-bits &optional prng) => integer
     
    @@ -1823,19 +1848,19 @@
    -
    (strong-random limit &optional prng) => number
    +
    (strong-random limit &optional prng) => number
     

    -A drop-in replacement for common-lisp:random, strong-random +A drop-in replacement for common-lisp:random, strong-random generates a number (an integer if limit is an integer and a float if it is a float) between 0 and limit - 1 in an unbiased fashion.

    -
    (read-os-random-seed source &optional prng) => reseed-count
    +
    (read-os-random-seed source &optional prng) => reseed-count
     
    @@ -1852,14 +1877,14 @@
    -
    (read-seed path &optional prng) => t
    +
    (read-seed path &optional prng) => t
     

    Read enough bytes from path to reseed prng, 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 +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. @@ -1867,7 +1892,7 @@

    -
    (write-seed path &optional prng) => t
    +
    (write-seed path &optional prng) => t
     
    @@ -1876,9 +1901,9 @@

-
-

Example

-
+
+

Example

+
 (crypto:random-data 16)
 => #(61 145 133 130 220 200 90 86 0 101 62 169 0 40 101 78)
@@ -1892,9 +1917,9 @@
 
-
-

Fortuna

-
+
+

Fortuna

+

You should only use the Fortuna PRNG if your OS does not provided a sufficiently-good PRNG. If you use a Unix or Unix-like OS (e.g. @@ -1923,7 +1948,7 @@

Fortuna automatically feeds entropy from the pools back into its -random state when random-data is called, using a method designed to +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. @@ -1943,7 +1968,7 @@

-
(add-random-event source pool-id event &optional prng) => pool-length
+
(add-random-event source pool-id event &optional prng) => pool-length
 
@@ -1969,8 +1994,8 @@
-
-

Gray streams

+
+

Gray streams

Ironclad includes support for several convenient stream abstractions @@ -1979,9 +2004,9 @@

-
-

Octet streams

-
+
+

Octet streams

+

Octet streams are very similar to Common Lisp's string-stream except they deal in octets instead of characters. @@ -1989,7 +2014,7 @@

-
(make-octet-input-stream buffer &optional start end) => octet-input-stream
+
(make-octet-input-stream buffer &optional start end) => octet-input-stream
 
@@ -1999,7 +2024,7 @@
-
(make-octet-output-stream) => octet-output-stream
+
(make-octet-output-stream) => octet-output-stream
 
@@ -2009,7 +2034,7 @@
-
(get-output-stream-octets stream) => octet-vector
+
(get-output-stream-octets stream) => octet-vector
 
@@ -2020,7 +2045,7 @@
-
(with-octet-input-stream ((var buffer &optional (start 0) end) &body body))
+
(with-octet-input-stream ((var buffer &optional (start 0) end) &body body))
 
@@ -2032,7 +2057,7 @@
-
(with-octet-output-stream ((var) &body body)) => bytes
+
(with-octet-output-stream ((var) &body body)) => bytes
 
@@ -2040,14 +2065,14 @@ Within body, var is bound to an octet output stream. After all the forms in body have been executed, the data that has been written to var (and that hasn't been consumed by a call to -get-output-stream-octets within body) is returned. +get-output-stream-octets within body) is returned.

-
-

Digest streams

-
+
+

Digest streams

+

Digest streams compute a digest of the data written to them according to a specific digest algorithm. @@ -2071,7 +2096,7 @@

-
(make-digesting-stream digest &rest args) => stream
+
(make-digesting-stream digest &rest args) => stream
 
@@ -2079,18 +2104,18 @@ Make a stream that computes a digest of the data written to it according to the algorithm digest. The parameters that can be used by some algorithms can be specified as args. -produce-digest may be used to obtain a digest of all the data written +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 +Note: Calling produce-digest on a digest stream does not alter the internal state of the digest.

-
(with-digesting-stream (var digest-name &rest args) &body body) => digest
+
(with-digesting-stream (var digest-name &rest args) &body body) => digest
 
@@ -2103,9 +2128,9 @@
-
-

Cipher streams

-
+
+

Cipher streams

+

Cipher streams encrypt or decrypt the data written to or read from them according to a specific cipher algorithm. @@ -2113,7 +2138,7 @@

-
(make-encrypting-stream stream cipher mode key &key initialization-vector direction) => stream
+
(make-encrypting-stream stream cipher mode key &key initialization-vector direction) => stream
 
@@ -2129,7 +2154,7 @@
-
(make-decrypting-stream stream cipher mode key &key initialization-vector direction) => stream
+
(make-decrypting-stream stream cipher mode key &key initialization-vector direction) => stream
 
@@ -2146,12 +2171,12 @@

Note: Only stream ciphers and block ciphers in CTR, CFB, CFB8 or OFB mode are -supported by make-encrypting-stream and make-decrypting-stream. +supported by make-encrypting-stream and make-decrypting-stream.

-
(with-encrypting-stream ((var stream cipher mode key &key initialization-vector direction) &body body))
+
(with-encrypting-stream ((var stream cipher mode key &key initialization-vector direction) &body body))
 
@@ -2162,7 +2187,7 @@
-
(with-decrypting-stream ((var stream cipher mode key &key initialization-vector direction) &body body))
+
(with-decrypting-stream ((var stream cipher mode key &key initialization-vector direction) &body body))
 
@@ -2173,16 +2198,16 @@
-
-

MAC streams

-
+
+

MAC streams

+

MAC streams compute a message authentication code of the data written to them according to a specific MAC algorithm.

-
(make-authenticating-stream mac key &rest args) => stream
+
(make-authenticating-stream mac key &rest args) => stream
 
@@ -2190,12 +2215,12 @@ Make a stream that computes a MAC of the data written to it according to the algorithm mac initialized with a key. The parameters used to create the MAC can be specified as args. -produce-mac may be used to obtain a MAC of all the data written to the +produce-mac may be used to obtain a MAC of all the data written to the stream.

-Note: Calling produce-mac on a MAC stream does not alter the +Note: Calling produce-mac on a MAC stream does not alter the internal state of the MAC.

@@ -2226,7 +2251,7 @@
-
(with-authenticating-stream (var mac-name key &rest args) &body body) => mac
+
(with-authenticating-stream (var mac-name key &rest args) &body body) => mac
 
@@ -2240,11 +2265,11 @@
-
-

Utility functions

+
+

Utility functions

-
(ub16ref/le vector index) => value
+
(ub16ref/le vector index) => value
 (ub32ref/le vector index) => value
 (ub64ref/le vector index) => value
 
@@ -2259,7 +2284,7 @@
-
(ub16ref/be vector index) => value
+
(ub16ref/be vector index) => value
 (ub32ref/be vector index) => value
 (ub64ref/be vector index) => value
 
@@ -2271,41 +2296,41 @@
-
(byte-array-to-hex-string vector &key start end element-type) => string
+
(byte-array-to-hex-string vector &key start end element-type) => string
 (hex-string-to-byte-array string &key start end) => string
 (ascii-string-to-byte-array string &key start end) => vector
 

-byte-array-to-hex-string converts the bytes of vector between +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 +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 +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 octet-vec &key start end big-endian n-bits) => number
+
(octets-to-integer octet-vec &key start end big-endian n-bits) => number
 (integer-to-octets bignum &key n-bits big-endian) => vector
 

-octets-to-integer converts the bytes of octet-vec between start +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 @@ -2314,12 +2339,12 @@

-integer-to-octets is the reverse operation. +integer-to-octets is the reverse operation.

-
(expt-mod n exponent modulus) => number
+
(expt-mod n exponent modulus) => number
 (expt-mod/unsafe n exponent modulus) => number
 
@@ -2327,15 +2352,15 @@

Raises n to the exponent power modulo modulus in a more efficient fashion than (mod (expt n exponent) modulus). -expt-mod is using the Montgomery ladder algorithm to be more robust +expt-mod is using the Montgomery ladder algorithm to be more robust against timing attacks. -expt-mod/unsafe runs faster than expt-mod but is not safe against +expt-mod/unsafe runs faster than expt-mod but is not safe against timing attacks; don't use it on secret data.

-
make-random-salt &optional size => bytes
+
make-random-salt &optional size => bytes
 
@@ -2346,7 +2371,7 @@
-
constant-time-equal data1 data2 => boolean
+
constant-time-equal data1 data2 => boolean
 
@@ -2359,11 +2384,11 @@
-
-

Conditions

+
+

Conditions

-
ironclad-error
+
ironclad-error
 
@@ -2374,152 +2399,152 @@
-
initialization-vector-not-supplied
+
initialization-vector-not-supplied
 

-This error is signaled by make-cipher when an initialization vector is +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
+
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 +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
+
invalid-key-length
 

-This error is signaled when the key provided to make-cipher is not of +This error is signaled when the key provided to make-cipher is not of an acceptable length for the requested cipher.

-
unsupported-cipher
+
unsupported-cipher
 

-This error is signaled when the cipher-name provided to make-cipher -is not cipher-supported-p. +This error is signaled when the cipher-name provided to make-cipher +is not cipher-supported-p.

-
unsupported-mode
+
unsupported-mode
 

This error is signaled when the mode provided to -make-cipher is not mode-supported-p. +make-cipher is not mode-supported-p.

-
unsupported-padding
+
unsupported-padding
 

-This error is signaled when the padding provided to make-cipher is not +This error is signaled when the padding provided to make-cipher is not supported.

-
unsupported-digest
+
unsupported-digest
 

This error is signaled when the digest-name provided to -make-digest is not digest-supported-p. +make-digest is not digest-supported-p.

-
unsupported-mac
+
unsupported-mac
 

This error is signaled when the mac-name provided to -make-mac is not mac-supported-p. +make-mac is not mac-supported-p.

-
insufficient-buffer-space
+
insufficient-buffer-space
 

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 +a buffer (e.g. when the user provides digest to produce-digest and there is insufficient space).

-
key-not-supplied
+
key-not-supplied
 

This error is signaled when a :key argument is not provided -to make-cipher. +to make-cipher.

-
unsupported-kdf
+
unsupported-kdf
 

This error is signaled when an invalid KDF name is provided -to make-kdf. +to make-kdf.

-
unsupported-scrypt-cost-factors
+
unsupported-scrypt-cost-factors
 

This error is signaled when invalid Scrypt cost factors are provided -to make-kdf. +to make-kdf.

-
unsupported-argon2i-cost-factors
+
unsupported-argon2-cost-factors
 

-This error is signaled when invalid Argon2i parameters are provided -to make-kdf. +This error is signaled when invalid Argon2 parameters are provided +to make-kdf.

-
invalid-padding
+
invalid-padding
 
@@ -2530,84 +2555,84 @@
-
invalid-mac-parameter
+
invalid-mac-parameter
 

This error is signaled when an invalid parameter is provided -to make-mac. +to make-mac.

-
invalid-signature-length
+
invalid-signature-length
 

This error is signaled when a signature with an invalid length is provided -to verify-signature or destructure-signature. +to verify-signature or destructure-signature.

-
invalid-message-length
+
invalid-message-length
 

This error is signaled when a message with an invalid length is provided -to encrypt-message, decrypt-message or destructure-message. +to encrypt-message, decrypt-message or destructure-message.

-
missing-key-parameter
+
missing-key-parameter
 

This error is signaled when it is determined that a parameter is -missing in a call to make-public-key or make-private-key. +missing in a call to make-public-key or make-private-key.

-
missing-message-parameter
+
missing-message-parameter
 

This error is signaled when it is determined that a parameter is -missing in a call to make-message. +missing in a call to make-message.

-
missing-signature-parameter
+
missing-signature-parameter
 

This error is signaled when it is determined that a parameter is -missing in a call to make-signature. +missing in a call to make-signature.

-
incompatible-keys
+
incompatible-keys
 

This error is signaled when incompatible keys are provided to -diffie-hellman. +diffie-hellman.

-
invalid-curve-point
+
invalid-curve-point
 
@@ -2617,18 +2642,18 @@
-
invalid-public-key-length
+
invalid-public-key-length
 

This error is signaled when a public key with an invalid length is -provided to verify-signature. +provided to verify-signature.

-
oaep-decoding-error
+
oaep-decoding-error
 
@@ -2638,18 +2663,18 @@
-
unsupported-authenticated-encryption-mode
+
unsupported-authenticated-encryption-mode
 

This error is signaled when an invalid mode name is provided to -make-authenticated-encryption-mode. +make-authenticated-encryption-mode.

-
bad-authentication-tag
+
bad-authentication-tag
 
diff -Nru cl-ironclad-0.45/ironclad.asd cl-ironclad-0.47/ironclad.asd --- cl-ironclad-0.45/ironclad.asd 2019-02-18 14:08:52.000000000 +0000 +++ cl-ironclad-0.47/ironclad.asd 2019-09-11 12:36:21.000000000 +0000 @@ -8,7 +8,7 @@ (defclass ironclad-source-file (cl-source-file) ()) (defsystem "ironclad" - :version "0.45" + :version "0.47" :author "Nathan Froyd " :maintainer "Guillaume LE VAILLANT " :description "A cryptographic toolkit written in pure Common Lisp" @@ -102,11 +102,12 @@ (:file "whirlpool" :depends-on ("digest")))) (:module "kdf" :depends-on ("ciphers" "common" "conditions" "digests" "generic" "macs" "package" "prng" "util") - :components ((:file "argon2i" :depends-on ("kdf-common")) + :components ((:file "argon2" :depends-on ("kdf-common")) (:file "kdf-common") (:file "password-hash" :depends-on ("pkcs5")) (:file "pkcs5" :depends-on ("kdf-common")) - (:file "scrypt" :depends-on ("kdf-common" "pkcs5")))) + (:file "scrypt" :depends-on ("kdf-common" "pkcs5")) + (:file "hmac" :depends-on ("kdf-common")))) (:module "macs" :depends-on ("ciphers" "common" "conditions" "digests" "generic" "opt" "package") :components ((:file "blake2-mac" :depends-on ("mac")) @@ -183,7 +184,7 @@ (defsystem "ironclad/tests" :depends-on ("ironclad" "rt") - :version "0.45" + :version "0.47" :in-order-to ((test-op (load-op "ironclad/tests"))) :perform (test-op (o s) (or (funcall (intern "DO-TESTS" (find-package "RTEST"))) @@ -311,9 +312,11 @@ (:test-vector-file "tree-hash") (:test-vector-file "whirlpool") ;; kdf + (:file "argon2d") (:file "argon2i") (:file "pkcs5") (:file "scrypt") + (:file "hmac-kdf") ;; macs (:file "macs") (:test-vector-file "blake2-mac") diff -Nru cl-ironclad-0.45/NEWS cl-ironclad-0.47/NEWS --- cl-ironclad-0.45/NEWS 2019-02-18 14:08:52.000000000 +0000 +++ cl-ironclad-0.47/NEWS 2019-09-11 12:36:21.000000000 +0000 @@ -1,5 +1,25 @@ -*- mode: outline -*- +* Version 0.47, released 2019-09-11 + +** bug fixes + +Allow running in ECL even if there is no C compiler available. + +** new features + +Added the HMAC-KDF key derivation function. + +* Version 0.46, released 2019-06-04 + +** bug fixes + +Allow the 'digest' parameter of the 'make-kdf' function to be a keyword. + +** new features + +Added the Argon2d key derivation function (without parallelism). + * Version 0.45, released 2019-02-18 ** bug fixes @@ -727,7 +747,7 @@ DSA signing routine requires *random* numbers; it currently uses CL:RANDOM for this purpose, which is probably *NOT* secure enough for your needs. Please make any changes you deem necessary to the signing -routine to accomodate your security level. +routine to accommodate your security level. Feedback is sought on the interface to the routines: diff -Nru cl-ironclad-0.45/README.org cl-ironclad-0.47/README.org --- cl-ironclad-0.45/README.org 2019-02-18 14:08:52.000000000 +0000 +++ cl-ironclad-0.47/README.org 2019-09-11 12:36:21.000000000 +0000 @@ -31,7 +31,7 @@ Most of the algorithms were written with efficiency for specific Common Lisp implementations in mind, although portable code is provided as an alternative in nearly all instances. The framework -should be flexible enough to accomodate implementation-specific +should be flexible enough to accommodate implementation-specific optimizations when possible. Test vectors for many of the algorithms are included to provide @@ -80,8 +80,8 @@ :CUSTOM_ID: installation :END: -The current version of Ironclad is 0.45. It can be downloaded -at [[https://github.com/sharplispers/ironclad/archive/v0.45.tar.gz]]. +The current version of Ironclad is 0.47. It can be downloaded +at [[https://github.com/sharplispers/ironclad/archive/v0.47.tar.gz]]. If you are feeling adventurous, you can download a bleeding-edge version at [[https://github.com/sharplispers/ironclad]]. @@ -916,10 +916,11 @@ :END: Ironclad comes with a few key derivation functions: - - Argon2i + - Argon2 (only Argon2d and Argon2i without parallelism are implemented) - PBKDF1 - PBKDF2 - Scrypt + - HMAC #+NAME: derive-key @@ -932,7 +933,7 @@ ~(simple-array (unsigned-byte 8) (*))~), and number of iterations, returns the password digest as a byte array of length /key-length/. -Scrypt ignores the /iteration-count/ parameter. +Scrypt and HMAC ignore the /iteration-count/ parameter. #+NAME: make-kdf @@ -940,9 +941,19 @@ (make-kdf kind &key digest n r p block-count additional-key additional-data) => kdf #+END_SRC -Returns a key derivation function instance (/kind/ must either be -/ARGON2I/, /PBKDF1/, /PBKDF2/ or /SCRYPT-KDF/). -The Argon2i key derivation uses the /block-count/, /additional-key/ +Returns a key derivation function instance. + +/kind/ denotes the key derivation function to use. They are: + - argon2d + - argon2i + - pbkdf1 + - pbkdf2 + - scrypt-kdf + - hmac-kdf + +/kind/ can be a symbol in the ~keyword~ or ~ironclad~ packages. + +The Argon2 key derivations use the /block-count/, /additional-key/ and /additional-data/ parameters (/block-count/ is the number of 1 KiB memory blocks used by the function and it must be at least 8, /additional-key/ and /additional-data/ are optional). @@ -957,6 +968,10 @@ [[derive-key][derive-key]] may not be able to allocate sufficient space for its temporary arrays. +The HMAC-KDF algorithm uses the /digest/ parameter to precise what +hash function is used. It also optionally uses the /additional-data/ +to precise the ~info~ vector from the [[https://tools.ietf.org/html/rfc5869][RFC]]. + ** PBKDF convenience functions Ironclad comes with convenience functions for using PBKDF1 and PBKDF2 @@ -1932,12 +1947,12 @@ to [[make-kdf][make-kdf]]. -#+NAME: unsupported-argon2i-cost-factors +#+NAME: unsupported-argon2-cost-factors #+BEGIN_SRC lisp -unsupported-argon2i-cost-factors +unsupported-argon2-cost-factors #+END_SRC -This error is signaled when invalid Argon2i parameters are provided +This error is signaled when invalid Argon2 parameters are provided to [[make-kdf][make-kdf]]. diff -Nru cl-ironclad-0.45/src/conditions.lisp cl-ironclad-0.47/src/conditions.lisp --- cl-ironclad-0.45/src/conditions.lisp 2019-02-18 14:08:52.000000000 +0000 +++ cl-ironclad-0.47/src/conditions.lisp 2019-09-11 12:36:21.000000000 +0000 @@ -94,18 +94,18 @@ (cost-N condition) (cost-r condition) (cost-p condition)))) (:documentation "Signaled when invalid Scrypt cost factors are provided to MAKE-KDF.")) -(define-condition unsupported-argon2i-parameters (ironclad-error) +(define-condition unsupported-argon2-parameters (ironclad-error) () (:report (lambda (condition stream) - (format stream "Argon2i parameters not supported. block-count must be at least 8, key-length must be at least 4, salt must be at least 8 bytes long and iteration-count must be at least 1."))) - (:documentation "Signaled when invalid Argon2i parameters are provided to MAKE-KDF.")) + (format stream "Argon2 parameters not supported. block-count must be at least 8, key-length must be at least 4, salt must be at least 8 bytes long and iteration-count must be at least 1."))) + (:documentation "Signaled when invalid Argon2 parameters are provided to MAKE-KDF.")) (define-condition insufficient-buffer-space (ironclad-error) ((buffer :initarg :buffer :reader insufficient-buffer-space-buffer) (start :initarg :start :reader insufficient-buffer-space-start) (length :initarg :length :reader insufficient-buffer-space-length)) (:report (lambda (condition stream) - (format stream "Buffer ~A cannot accomodate ~D elements starting at index ~D." + (format stream "Buffer ~A cannot accommodate ~D elements starting at index ~D." (insufficient-buffer-space-buffer condition) (insufficient-buffer-space-length condition) (insufficient-buffer-space-start condition)))) diff -Nru cl-ironclad-0.45/src/kdf/argon2i.lisp cl-ironclad-0.47/src/kdf/argon2i.lisp --- cl-ironclad-0.45/src/kdf/argon2i.lisp 2019-02-18 14:08:52.000000000 +0000 +++ cl-ironclad-0.47/src/kdf/argon2i.lisp 1970-01-01 00:00:00.000000000 +0000 @@ -1,297 +0,0 @@ -;;;; -*- mode: lisp; indent-tabs-mode: nil -*- -;;;; argon2i.lisp -- implementation of the Argon2i key derivation function - -;;; Based on the Argon2i implementation present in the Monocypher -;;; crypto library (http://loup-vaillant.fr/projects/monocypher/) - -(in-package :crypto) - - -(defconstant +argon2i-block-size+ 128) - -(deftype argon2i-block () - '(simple-array (unsigned-byte 64) (128))) - - -(defun argon2i-load-block (b bytes) - (declare (type (simple-array (unsigned-byte 64) (*)) b) - (type (simple-array (unsigned-byte 8) (*)) bytes)) - (dotimes (i +argon2i-block-size+) - (setf (aref b i) (ub64ref/le bytes (* 8 i)))) - (values)) - -(defun argon2i-store-block (bytes b &key (start2 0)) - (declare (type (simple-array (unsigned-byte 64) (*)) b) - (type (simple-array (unsigned-byte 8) (*)) bytes)) - (dotimes (i +argon2i-block-size+) - (setf (ub64ref/le bytes (* 8 i)) (aref b (+ (* +argon2i-block-size+ start2) i)))) - (values)) - -(defun argon2i-copy-block (b1 b2 &key (start1 0) (start2 0)) - (declare (type (simple-array (unsigned-byte 64) (*)) b1 b2)) - (dotimes (i +argon2i-block-size+) - (setf (aref b1 (+ (* +argon2i-block-size+ start1) i)) - (aref b2 (+ (* +argon2i-block-size+ start2) i)))) - (values)) - -(defun argon2i-xor-block (b1 b2 &key (start1 0) (start2 0)) - (declare (type (simple-array (unsigned-byte 64) (*)) b1 b2)) - (dotimes (i +argon2i-block-size+) - (setf (aref b1 (+ (* +argon2i-block-size+ start1) i)) - (logxor (aref b1 (+ (* +argon2i-block-size+ start1) i)) - (aref b2 (+ (* +argon2i-block-size+ start2) i))))) - (values)) - -(defun argon2i-update-digester-32 (digester input) - (update-mac digester (integer-to-octets input :n-bits 32 :big-endian nil)) - (values)) - -(defun argon2i-extended-hash (state digest digest-size input input-size) - (declare (type (simple-array (unsigned-byte 8) (*)) digest input)) - (let ((no-key (make-array 0 :element-type '(unsigned-byte 8))) - (digester (argon2i-digester state))) - (reinitialize-instance digester :key no-key :digest-length (min digest-size 64)) - (argon2i-update-digester-32 digester digest-size) - (update-mac digester input :end input-size) - (produce-mac digester :digest digest) - (when (> digest-size 64) - (let ((r (- (ceiling digest-size 32) 2)) - (i 1) - (in 0) - (out 32)) - (loop while (< i r) do - (reinitialize-instance digester :key no-key :digest-length 64) - (update-mac digester digest :start in :end (+ in 64)) - (produce-mac digester :digest digest :digest-start out) - (incf i 1) - (incf in 32) - (incf out 32)) - (reinitialize-instance digester :key no-key :digest-length (- digest-size (* 32 r))) - (update-mac digester digest :start in :end (+ in 64)) - (produce-mac digester :digest digest :digest-start out)))) - (values)) - -(defmacro argon2i-g (a b c d) - `(setf ,a (mod64+ ,a (mod64+ ,b (mod64* 2 (mod64* (logand ,a #xffffffff) (logand ,b #xffffffff))))) - ,d (ror64 (logxor ,d ,a) 32) - ,c (mod64+ ,c (mod64+ ,d (mod64* 2 (mod64* (logand ,c #xffffffff) (logand ,d #xffffffff))))) - ,b (ror64 (logxor ,b ,c) 24) - ,a (mod64+ ,a (mod64+ ,b (mod64* 2 (mod64* (logand ,a #xffffffff) (logand ,b #xffffffff))))) - ,d (ror64 (logxor ,d ,a) 16) - ,c (mod64+ ,c (mod64+ ,d (mod64* 2 (mod64* (logand ,c #xffffffff) (logand ,d #xffffffff))))) - ,b (ror64 (logxor ,b ,c) 63))) - -(defmacro argon2i-round (v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15) - `(progn - (argon2i-g ,v0 ,v4 ,v8 ,v12) - (argon2i-g ,v1 ,v5 ,v9 ,v13) - (argon2i-g ,v2 ,v6 ,v10 ,v14) - (argon2i-g ,v3 ,v7 ,v11 ,v15) - (argon2i-g ,v0 ,v5 ,v10 ,v15) - (argon2i-g ,v1 ,v6 ,v11 ,v12) - (argon2i-g ,v2 ,v7 ,v8 ,v13) - (argon2i-g ,v3 ,v4 ,v9 ,v14))) - -(defun argon2i-g-rounds (work-block) - (declare (type argon2i-block work-block)) - (loop for i from 0 below 128 by 16 do - (argon2i-round (aref work-block i) - (aref work-block (+ i 1)) - (aref work-block (+ i 2)) - (aref work-block (+ i 3)) - (aref work-block (+ i 4)) - (aref work-block (+ i 5)) - (aref work-block (+ i 6)) - (aref work-block (+ i 7)) - (aref work-block (+ i 8)) - (aref work-block (+ i 9)) - (aref work-block (+ i 10)) - (aref work-block (+ i 11)) - (aref work-block (+ i 12)) - (aref work-block (+ i 13)) - (aref work-block (+ i 14)) - (aref work-block (+ i 15)))) - (loop for i from 0 below 16 by 2 do - (argon2i-round (aref work-block i) - (aref work-block (+ i 1)) - (aref work-block (+ i 16)) - (aref work-block (+ i 17)) - (aref work-block (+ i 32)) - (aref work-block (+ i 33)) - (aref work-block (+ i 48)) - (aref work-block (+ i 49)) - (aref work-block (+ i 64)) - (aref work-block (+ i 65)) - (aref work-block (+ i 80)) - (aref work-block (+ i 81)) - (aref work-block (+ i 96)) - (aref work-block (+ i 97)) - (aref work-block (+ i 112)) - (aref work-block (+ i 113)))) - (values)) - -(defun argon2i-g-copy (work-area r x y) - (declare (type (simple-array (unsigned-byte 64) (*)) work-area)) - (let ((tmp (make-array +argon2i-block-size+ :element-type '(unsigned-byte 64)))) - (declare (type argon2i-block tmp) - (dynamic-extent tmp)) - (argon2i-copy-block tmp work-area :start2 x) - (argon2i-xor-block tmp work-area :start2 y) - (argon2i-copy-block work-area tmp :start1 r) - (argon2i-g-rounds tmp) - (argon2i-xor-block work-area tmp :start1 r)) - (values)) - -(defun argon2i-g-xor (work-area r x y) - (declare (type (simple-array (unsigned-byte 64) (*)) work-area)) - (let ((tmp (make-array +argon2i-block-size+ :element-type '(unsigned-byte 64)))) - (declare (type argon2i-block tmp) - (dynamic-extent tmp)) - (argon2i-copy-block tmp work-area :start2 x) - (argon2i-xor-block tmp work-area :start2 y) - (argon2i-xor-block work-area tmp :start1 r) - (argon2i-g-rounds tmp) - (argon2i-xor-block work-area tmp :start1 r)) - (values)) - -(defun argon2i-unary-g (work-block) - (declare (type argon2i-block work-block)) - (let ((tmp (make-array +argon2i-block-size+ :element-type '(unsigned-byte 64)))) - (declare (type argon2i-block tmp) - (dynamic-extent tmp)) - (argon2i-copy-block tmp work-block) - (argon2i-g-rounds work-block) - (argon2i-xor-block work-block tmp)) - (values)) - -(defun argon2i-gidx-refresh (state) - (let ((b (argon2i-block state))) - (setf (aref b 0) (argon2i-pass-number state) - (aref b 1) 0 - (aref b 2) (argon2i-slice-number state) - (aref b 3) (argon2i-nb-blocks state) - (aref b 4) (argon2i-nb-iterations state) - (aref b 5) 1 - (aref b 6) (argon2i-counter state)) - (fill b 0 :start 7) - (argon2i-unary-g b) - (argon2i-unary-g b) - (values))) - -(defun argon2i-gidx-init (state pass-number slice-number nb-blocks nb-iterations) - (setf (argon2i-pass-number state) pass-number - (argon2i-slice-number state) slice-number - (argon2i-nb-blocks state) nb-blocks - (argon2i-nb-iterations state) nb-iterations - (argon2i-counter state) 0) - (if (and (zerop pass-number) (zerop slice-number)) - (progn - (setf (argon2i-offset state) 2) - (incf (argon2i-counter state)) - (argon2i-gidx-refresh state)) - (setf (argon2i-offset state) 0)) - (values)) - -(defun argon2i-gidx-next (state) - (when (zerop (mod (argon2i-offset state) +argon2i-block-size+)) - (incf (argon2i-counter state)) - (argon2i-gidx-refresh state)) - (let* ((offset (argon2i-offset state)) - (index (mod offset +argon2i-block-size+)) - (first-pass (zerop (argon2i-pass-number state))) - (nb-blocks (argon2i-nb-blocks state)) - (slice-size (floor nb-blocks 4)) - (slice-number (argon2i-slice-number state)) - (nb-segments (if first-pass slice-number 3)) - (area-size (- (+ (* nb-segments slice-size) offset) 1)) - (next-slice (* (mod (+ slice-number 1) 4) slice-size)) - (start-pos (if first-pass 0 next-slice)) - (j1 (logand (aref (argon2i-block state) index) #xffffffff)) - (x (ash (* j1 j1) -32)) - (y (ash (* area-size x) -32)) - (z (- area-size 1 y))) - (incf (argon2i-offset state)) - (mod (+ start-pos z) nb-blocks))) - -(defmethod shared-initialize ((kdf argon2i) slot-names &rest initargs - &key block-count additional-key additional-data &allow-other-keys) - (let ((no-data (make-array 0 :element-type '(unsigned-byte 8)))) - (setf (argon2i-block kdf) (make-array +argon2i-block-size+ - :element-type '(unsigned-byte 64)) - (argon2i-block-count kdf) (max 8 (or block-count 4096)) - (argon2i-additional-key kdf) (or additional-key no-data) - (argon2i-additional-data kdf) (or additional-data no-data) - (argon2i-work-area kdf) (make-array (* +argon2i-block-size+ block-count) - :element-type '(unsigned-byte 64)) - (argon2i-digester kdf) (make-mac :blake2-mac no-data))) - kdf) - -(defmethod derive-key ((kdf argon2i) passphrase salt iteration-count key-length) - (declare (type (simple-array (unsigned-byte 8) (*)) passphrase salt)) - (when (or (< key-length 4) (< iteration-count 1) (< (length salt) 8)) - (error 'unsupported-argon2i-parameters)) - (setf (argon2i-nb-iterations kdf) iteration-count) - (let ((work-area (argon2i-work-area kdf)) - (block-count (argon2i-block-count kdf)) - (additional-key (argon2i-additional-key kdf)) - (additional-data (argon2i-additional-data kdf)) - (digester (argon2i-digester kdf)) - (no-key (make-array 0 :element-type '(unsigned-byte 8))) - (tmp-area (make-array 1024 :element-type '(unsigned-byte 8)))) - (declare (type (simple-array (unsigned-byte 64) (*)) work-area) - (type (simple-array (unsigned-byte 8) (1024)) tmp-area) - (dynamic-extent tmp-area)) - (reinitialize-instance digester :key no-key :digest-length 64) - (argon2i-update-digester-32 digester 1) - (argon2i-update-digester-32 digester key-length) - (argon2i-update-digester-32 digester block-count) - (argon2i-update-digester-32 digester iteration-count) - (argon2i-update-digester-32 digester #x13) - (argon2i-update-digester-32 digester 1) - (argon2i-update-digester-32 digester (length passphrase)) - (update-mac digester passphrase) - (argon2i-update-digester-32 digester (length salt)) - (update-mac digester salt) - (argon2i-update-digester-32 digester (length additional-key)) - (update-mac digester additional-key) - (argon2i-update-digester-32 digester (length additional-data)) - (update-mac digester additional-data) - (let ((initial-hash (make-array 72 :element-type '(unsigned-byte 8))) - (tmp-block (make-array +argon2i-block-size+ :element-type '(unsigned-byte 64)))) - (declare (type (simple-array (unsigned-byte 8) (72)) initial-hash) - (type argon2i-block tmp-block) - (dynamic-extent initial-hash tmp-block)) - (produce-mac digester :digest initial-hash) - - (setf (ub32ref/le initial-hash 64) 0 - (ub32ref/le initial-hash 68) 0) - (argon2i-extended-hash kdf tmp-area 1024 initial-hash 72) - (argon2i-load-block tmp-block tmp-area) - (argon2i-copy-block work-area tmp-block) - - (setf (ub32ref/le initial-hash 64) 1) - (argon2i-extended-hash kdf tmp-area 1024 initial-hash 72) - (argon2i-load-block tmp-block tmp-area) - (argon2i-copy-block work-area tmp-block :start1 1)) - - (let* ((nb-blocks (- block-count (mod block-count 4))) - (segment-size (floor nb-blocks 4))) - (dotimes (pass-number iteration-count) - (let ((first-pass (zerop pass-number))) - (dotimes (segment 4) - (argon2i-gidx-init kdf pass-number segment nb-blocks iteration-count) - (let* ((start-offset (if (and first-pass (zerop segment)) 2 0)) - (segment-start (+ (* segment segment-size) start-offset)) - (segment-end (* (+ segment 1) segment-size))) - (loop for current-block from segment-start below segment-end do - (let ((reference-block (argon2i-gidx-next kdf)) - (previous-block (if (zerop current-block) - (- nb-blocks 1) - (- current-block 1)))) - (if first-pass - (argon2i-g-copy work-area current-block previous-block reference-block) - (argon2i-g-xor work-area current-block previous-block reference-block)))))))) - (let ((hash (make-array key-length :element-type '(unsigned-byte 8)))) - (argon2i-store-block tmp-area work-area :start2 (- nb-blocks 1)) - (argon2i-extended-hash kdf hash key-length tmp-area 1024) - hash)))) diff -Nru cl-ironclad-0.45/src/kdf/argon2.lisp cl-ironclad-0.47/src/kdf/argon2.lisp --- cl-ironclad-0.45/src/kdf/argon2.lisp 1970-01-01 00:00:00.000000000 +0000 +++ cl-ironclad-0.47/src/kdf/argon2.lisp 2019-09-11 12:36:21.000000000 +0000 @@ -0,0 +1,331 @@ +;;;; -*- mode: lisp; indent-tabs-mode: nil -*- +;;;; argon2.lisp -- implementation of the Argon2 key derivation function + +;;; Based on the Argon2i implementation present in the Monocypher +;;; crypto library (http://loup-vaillant.fr/projects/monocypher/) + +(in-package :crypto) + + +(defconstant +argon2-block-size+ 128) + +(deftype argon2-block () + '(simple-array (unsigned-byte 64) (128))) + + +(defun argon2-load-block (b bytes) + (declare (type (simple-array (unsigned-byte 64) (*)) b) + (type (simple-array (unsigned-byte 8) (*)) bytes)) + (dotimes (i +argon2-block-size+) + (setf (aref b i) (ub64ref/le bytes (* 8 i)))) + (values)) + +(defun argon2-store-block (bytes b &key (start2 0)) + (declare (type (simple-array (unsigned-byte 64) (*)) b) + (type (simple-array (unsigned-byte 8) (*)) bytes)) + (dotimes (i +argon2-block-size+) + (setf (ub64ref/le bytes (* 8 i)) (aref b (+ (* +argon2-block-size+ start2) i)))) + (values)) + +(defun argon2-copy-block (b1 b2 &key (start1 0) (start2 0)) + (declare (type (simple-array (unsigned-byte 64) (*)) b1 b2)) + (dotimes (i +argon2-block-size+) + (setf (aref b1 (+ (* +argon2-block-size+ start1) i)) + (aref b2 (+ (* +argon2-block-size+ start2) i)))) + (values)) + +(defun argon2-xor-block (b1 b2 &key (start1 0) (start2 0)) + (declare (type (simple-array (unsigned-byte 64) (*)) b1 b2)) + (dotimes (i +argon2-block-size+) + (setf (aref b1 (+ (* +argon2-block-size+ start1) i)) + (logxor (aref b1 (+ (* +argon2-block-size+ start1) i)) + (aref b2 (+ (* +argon2-block-size+ start2) i))))) + (values)) + +(defun argon2-update-digester-32 (digester input) + (update-mac digester (integer-to-octets input :n-bits 32 :big-endian nil)) + (values)) + +(defun argon2-extended-hash (state digest digest-size input input-size) + (declare (type (simple-array (unsigned-byte 8) (*)) digest input)) + (let ((no-key (make-array 0 :element-type '(unsigned-byte 8))) + (digester (argon2-digester state))) + (reinitialize-instance digester :key no-key :digest-length (min digest-size 64)) + (argon2-update-digester-32 digester digest-size) + (update-mac digester input :end input-size) + (produce-mac digester :digest digest) + (when (> digest-size 64) + (let ((r (- (ceiling digest-size 32) 2)) + (i 1) + (in 0) + (out 32)) + (loop while (< i r) do + (reinitialize-instance digester :key no-key :digest-length 64) + (update-mac digester digest :start in :end (+ in 64)) + (produce-mac digester :digest digest :digest-start out) + (incf i 1) + (incf in 32) + (incf out 32)) + (reinitialize-instance digester :key no-key :digest-length (- digest-size (* 32 r))) + (update-mac digester digest :start in :end (+ in 64)) + (produce-mac digester :digest digest :digest-start out)))) + (values)) + +(defmacro argon2-g (a b c d) + `(setf ,a (mod64+ ,a (mod64+ ,b (mod64* 2 (mod64* (logand ,a #xffffffff) (logand ,b #xffffffff))))) + ,d (ror64 (logxor ,d ,a) 32) + ,c (mod64+ ,c (mod64+ ,d (mod64* 2 (mod64* (logand ,c #xffffffff) (logand ,d #xffffffff))))) + ,b (ror64 (logxor ,b ,c) 24) + ,a (mod64+ ,a (mod64+ ,b (mod64* 2 (mod64* (logand ,a #xffffffff) (logand ,b #xffffffff))))) + ,d (ror64 (logxor ,d ,a) 16) + ,c (mod64+ ,c (mod64+ ,d (mod64* 2 (mod64* (logand ,c #xffffffff) (logand ,d #xffffffff))))) + ,b (ror64 (logxor ,b ,c) 63))) + +(defmacro argon2-round (v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15) + `(progn + (argon2-g ,v0 ,v4 ,v8 ,v12) + (argon2-g ,v1 ,v5 ,v9 ,v13) + (argon2-g ,v2 ,v6 ,v10 ,v14) + (argon2-g ,v3 ,v7 ,v11 ,v15) + (argon2-g ,v0 ,v5 ,v10 ,v15) + (argon2-g ,v1 ,v6 ,v11 ,v12) + (argon2-g ,v2 ,v7 ,v8 ,v13) + (argon2-g ,v3 ,v4 ,v9 ,v14))) + +(defun argon2-g-rounds (work-block) + (declare (type argon2-block work-block)) + (loop for i from 0 below 128 by 16 do + (argon2-round (aref work-block i) + (aref work-block (+ i 1)) + (aref work-block (+ i 2)) + (aref work-block (+ i 3)) + (aref work-block (+ i 4)) + (aref work-block (+ i 5)) + (aref work-block (+ i 6)) + (aref work-block (+ i 7)) + (aref work-block (+ i 8)) + (aref work-block (+ i 9)) + (aref work-block (+ i 10)) + (aref work-block (+ i 11)) + (aref work-block (+ i 12)) + (aref work-block (+ i 13)) + (aref work-block (+ i 14)) + (aref work-block (+ i 15)))) + (loop for i from 0 below 16 by 2 do + (argon2-round (aref work-block i) + (aref work-block (+ i 1)) + (aref work-block (+ i 16)) + (aref work-block (+ i 17)) + (aref work-block (+ i 32)) + (aref work-block (+ i 33)) + (aref work-block (+ i 48)) + (aref work-block (+ i 49)) + (aref work-block (+ i 64)) + (aref work-block (+ i 65)) + (aref work-block (+ i 80)) + (aref work-block (+ i 81)) + (aref work-block (+ i 96)) + (aref work-block (+ i 97)) + (aref work-block (+ i 112)) + (aref work-block (+ i 113)))) + (values)) + +(defun argon2-g-copy (work-area r x y) + (declare (type (simple-array (unsigned-byte 64) (*)) work-area)) + (let ((tmp (make-array +argon2-block-size+ :element-type '(unsigned-byte 64)))) + (declare (type argon2-block tmp) + (dynamic-extent tmp)) + (argon2-copy-block tmp work-area :start2 x) + (argon2-xor-block tmp work-area :start2 y) + (argon2-copy-block work-area tmp :start1 r) + (argon2-g-rounds tmp) + (argon2-xor-block work-area tmp :start1 r)) + (values)) + +(defun argon2-g-xor (work-area r x y) + (declare (type (simple-array (unsigned-byte 64) (*)) work-area)) + (let ((tmp (make-array +argon2-block-size+ :element-type '(unsigned-byte 64)))) + (declare (type argon2-block tmp) + (dynamic-extent tmp)) + (argon2-copy-block tmp work-area :start2 x) + (argon2-xor-block tmp work-area :start2 y) + (argon2-xor-block work-area tmp :start1 r) + (argon2-g-rounds tmp) + (argon2-xor-block work-area tmp :start1 r)) + (values)) + +(defun argon2-unary-g (work-block) + (declare (type argon2-block work-block)) + (let ((tmp (make-array +argon2-block-size+ :element-type '(unsigned-byte 64)))) + (declare (type argon2-block tmp) + (dynamic-extent tmp)) + (argon2-copy-block tmp work-block) + (argon2-g-rounds work-block) + (argon2-xor-block work-block tmp)) + (values)) + +(defun argon2i-gidx-refresh (state) + (let ((b (argon2-block state))) + (setf (aref b 0) (argon2-pass-number state) + (aref b 1) 0 + (aref b 2) (argon2-slice-number state) + (aref b 3) (argon2-nb-blocks state) + (aref b 4) (argon2-nb-iterations state) + (aref b 5) 1 + (aref b 6) (argon2-counter state)) + (fill b 0 :start 7) + (argon2-unary-g b) + (argon2-unary-g b) + (values))) + +(defun argon2i-gidx-init (state pass-number slice-number nb-blocks nb-iterations) + (setf (argon2-pass-number state) pass-number + (argon2-slice-number state) slice-number + (argon2-nb-blocks state) nb-blocks + (argon2-nb-iterations state) nb-iterations + (argon2-counter state) 0) + (if (and (zerop pass-number) (zerop slice-number)) + (progn + (setf (argon2-offset state) 2) + (incf (argon2-counter state)) + (argon2i-gidx-refresh state)) + (setf (argon2-offset state) 0)) + (values)) + +(defun argon2i-gidx-next (state) + (when (zerop (mod (argon2-offset state) +argon2-block-size+)) + (incf (argon2-counter state)) + (argon2i-gidx-refresh state)) + (let* ((offset (argon2-offset state)) + (index (mod offset +argon2-block-size+)) + (first-pass (zerop (argon2-pass-number state))) + (nb-blocks (argon2-nb-blocks state)) + (slice-size (floor nb-blocks 4)) + (slice-number (argon2-slice-number state)) + (nb-segments (if first-pass slice-number 3)) + (area-size (- (+ (* nb-segments slice-size) offset) 1)) + (next-slice (* (mod (+ slice-number 1) 4) slice-size)) + (start-pos (if first-pass 0 next-slice)) + (j1 (logand (aref (argon2-block state) index) #xffffffff)) + (x (ash (* j1 j1) -32)) + (y (ash (* area-size x) -32)) + (z (- area-size 1 y))) + (incf (argon2-offset state)) + (mod (+ start-pos z) nb-blocks))) + +(defun argon2d-gidx-init (state pass-number slice-number nb-blocks nb-iterations) + (setf (argon2-pass-number state) pass-number + (argon2-slice-number state) slice-number + (argon2-nb-blocks state) nb-blocks + (argon2-nb-iterations state) nb-iterations + (argon2-counter state) 0) + (if (and (zerop pass-number) (zerop slice-number)) + (setf (argon2-offset state) 2) + (setf (argon2-offset state) 0)) + (values)) + +(defun argon2d-gidx-next (state previous-block) + (let* ((offset (argon2-offset state)) + (index (* +argon2-block-size+ previous-block)) + (first-pass (zerop (argon2-pass-number state))) + (nb-blocks (argon2-nb-blocks state)) + (slice-size (floor nb-blocks 4)) + (slice-number (argon2-slice-number state)) + (nb-segments (if first-pass slice-number 3)) + (area-size (- (+ (* nb-segments slice-size) offset) 1)) + (next-slice (* (mod (+ slice-number 1) 4) slice-size)) + (start-pos (if first-pass 0 next-slice)) + (j1 (logand (aref (argon2-work-area state) index) #xffffffff)) + (x (ash (* j1 j1) -32)) + (y (ash (* area-size x) -32)) + (z (- area-size 1 y))) + (incf (argon2-offset state)) + (mod (+ start-pos z) nb-blocks))) + +(defmethod shared-initialize ((kdf argon2) slot-names &rest initargs + &key block-count additional-key additional-data &allow-other-keys) + (let ((no-data (make-array 0 :element-type '(unsigned-byte 8)))) + (setf (argon2-block kdf) (make-array +argon2-block-size+ + :element-type '(unsigned-byte 64)) + (argon2-block-count kdf) (max 8 (or block-count 4096)) + (argon2-additional-key kdf) (or additional-key no-data) + (argon2-additional-data kdf) (or additional-data no-data) + (argon2-work-area kdf) (make-array (* +argon2-block-size+ block-count) + :element-type '(unsigned-byte 64)) + (argon2-digester kdf) (make-mac :blake2-mac no-data))) + kdf) + +(defmethod derive-key ((kdf argon2) passphrase salt iteration-count key-length) + (declare (type (simple-array (unsigned-byte 8) (*)) passphrase salt)) + (when (or (< key-length 4) (< iteration-count 1) (< (length salt) 8)) + (error 'unsupported-argon2-parameters)) + (setf (argon2-nb-iterations kdf) iteration-count) + (let ((data-independent-p (typep kdf 'argon2i)) + (work-area (argon2-work-area kdf)) + (block-count (argon2-block-count kdf)) + (additional-key (argon2-additional-key kdf)) + (additional-data (argon2-additional-data kdf)) + (digester (argon2-digester kdf)) + (no-key (make-array 0 :element-type '(unsigned-byte 8))) + (tmp-area (make-array 1024 :element-type '(unsigned-byte 8)))) + (declare (type (simple-array (unsigned-byte 64) (*)) work-area) + (type (simple-array (unsigned-byte 8) (1024)) tmp-area) + (dynamic-extent tmp-area)) + (reinitialize-instance digester :key no-key :digest-length 64) + (argon2-update-digester-32 digester 1) + (argon2-update-digester-32 digester key-length) + (argon2-update-digester-32 digester block-count) + (argon2-update-digester-32 digester iteration-count) + (argon2-update-digester-32 digester #x13) + (argon2-update-digester-32 digester (if data-independent-p 1 0)) + (argon2-update-digester-32 digester (length passphrase)) + (update-mac digester passphrase) + (argon2-update-digester-32 digester (length salt)) + (update-mac digester salt) + (argon2-update-digester-32 digester (length additional-key)) + (update-mac digester additional-key) + (argon2-update-digester-32 digester (length additional-data)) + (update-mac digester additional-data) + (let ((initial-hash (make-array 72 :element-type '(unsigned-byte 8))) + (tmp-block (make-array +argon2-block-size+ :element-type '(unsigned-byte 64)))) + (declare (type (simple-array (unsigned-byte 8) (72)) initial-hash) + (type argon2-block tmp-block) + (dynamic-extent initial-hash tmp-block)) + (produce-mac digester :digest initial-hash) + + (setf (ub32ref/le initial-hash 64) 0 + (ub32ref/le initial-hash 68) 0) + (argon2-extended-hash kdf tmp-area 1024 initial-hash 72) + (argon2-load-block tmp-block tmp-area) + (argon2-copy-block work-area tmp-block) + + (setf (ub32ref/le initial-hash 64) 1) + (argon2-extended-hash kdf tmp-area 1024 initial-hash 72) + (argon2-load-block tmp-block tmp-area) + (argon2-copy-block work-area tmp-block :start1 1)) + + (let* ((nb-blocks (- block-count (mod block-count 4))) + (segment-size (floor nb-blocks 4))) + (dotimes (pass-number iteration-count) + (let ((first-pass (zerop pass-number))) + (dotimes (segment 4) + (if data-independent-p + (argon2i-gidx-init kdf pass-number segment nb-blocks iteration-count) + (argon2d-gidx-init kdf pass-number segment nb-blocks iteration-count)) + (let* ((start-offset (if (and first-pass (zerop segment)) 2 0)) + (segment-start (+ (* segment segment-size) start-offset)) + (segment-end (* (+ segment 1) segment-size))) + (loop for current-block from segment-start below segment-end do + (let* ((previous-block (if (zerop current-block) + (- nb-blocks 1) + (- current-block 1))) + (reference-block (if data-independent-p + (argon2i-gidx-next kdf) + (argon2d-gidx-next kdf previous-block)))) + (if first-pass + (argon2-g-copy work-area current-block previous-block reference-block) + (argon2-g-xor work-area current-block previous-block reference-block)))))))) + (let ((hash (make-array key-length :element-type '(unsigned-byte 8)))) + (argon2-store-block tmp-area work-area :start2 (- nb-blocks 1)) + (argon2-extended-hash kdf hash key-length tmp-area 1024) + hash)))) diff -Nru cl-ironclad-0.45/src/kdf/hmac.lisp cl-ironclad-0.47/src/kdf/hmac.lisp --- cl-ironclad-0.45/src/kdf/hmac.lisp 1970-01-01 00:00:00.000000000 +0000 +++ cl-ironclad-0.47/src/kdf/hmac.lisp 2019-09-11 12:36:21.000000000 +0000 @@ -0,0 +1,34 @@ +;;;; -*- mode: lisp; indent-tabs-mode: nil -*- +;;; implementation of RFC 5869 +(in-package :crypto) + +(defmethod shared-initialize ((kdf hmac-kdf) slot-names &rest initargs + &key digest info &allow-other-keys) + (declare (ignore slot-names initargs)) + (setf (slot-value kdf 'digest-name) digest + (hmac-kdf-info kdf) (or info (make-array 0 :element-type '(unsigned-byte 8)))) + kdf) + +(defun hkdf-extract (digest salt ikm) + (let ((hmac (make-hmac salt digest))) + (update-hmac hmac ikm) + (produce-mac hmac))) + +(defun hkdf-expand (digest prk info key-length) + (let ((digest-length (digest-length digest))) + (assert (<= key-length (* 255 digest-length))) + (subseq + (apply #'concatenate '(vector (unsigned-byte 8)) + (loop with tmp = (make-array 0 :element-type '(unsigned-byte 8)) + for i below (ceiling key-length digest-length) + collect + (setf tmp (hkdf-extract digest prk + (concatenate '(vector (unsigned-byte 8)) tmp info (vector (1+ i))))))) + 0 key-length))) + +(defun hmac-derive-key (digest passphrase salt info key-length) + (let ((prk (hkdf-extract digest salt passphrase))) + (hkdf-expand digest prk info key-length))) + +(defmethod derive-key ((kdf hmac-kdf) passphrase salt iteration-count key-length) + (hmac-derive-key (kdf-digest kdf) passphrase salt (hmac-kdf-info kdf) key-length)) diff -Nru cl-ironclad-0.45/src/kdf/kdf-common.lisp cl-ironclad-0.47/src/kdf/kdf-common.lisp --- cl-ironclad-0.45/src/kdf/kdf-common.lisp 2019-02-18 14:08:52.000000000 +0000 +++ cl-ironclad-0.47/src/kdf/kdf-common.lisp 2019-09-11 12:36:21.000000000 +0000 @@ -7,24 +7,35 @@ (defclass pbkdf2 () ((digest-name :initarg :digest :reader kdf-digest))) +(defclass hmac-kdf () + ((digest-name :initarg :digest :reader kdf-digest) + (info :initarg :info :accessor hmac-kdf-info :type (simple-array (unsigned-byte 8) (*)) + :documentation "Optional context and application specific information"))) + (defclass scrypt-kdf () ((N :initarg :N :reader scrypt-kdf-N) (r :initarg :r :reader scrypt-kdf-r) (p :initarg :p :reader scrypt-kdf-p))) -(defclass argon2i () - ((block :accessor argon2i-block :type (simple-array (unsigned-byte 64) (128))) - (pass-number :accessor argon2i-pass-number) - (slice-number :accessor argon2i-slice-number) - (nb-blocks :accessor argon2i-nb-blocks) - (block-count :accessor argon2i-block-count) - (nb-iterations :accessor argon2i-nb-iterations) - (counter :accessor argon2i-counter) - (offset :accessor argon2i-offset) - (additional-key :accessor argon2i-additional-key :type (simple-array (unsigned-byte 8) (*))) - (additional-data :accessor argon2i-additional-data :type (simple-array (unsigned-byte 8) (*))) - (work-area :accessor argon2i-work-area :type (simple-array (unsigned-byte 64) (*))) - (digester :accessor argon2i-digester))) +(defclass argon2 () + ((block :accessor argon2-block :type (simple-array (unsigned-byte 64) (128))) + (pass-number :accessor argon2-pass-number) + (slice-number :accessor argon2-slice-number) + (nb-blocks :accessor argon2-nb-blocks) + (block-count :accessor argon2-block-count) + (nb-iterations :accessor argon2-nb-iterations) + (counter :accessor argon2-counter) + (offset :accessor argon2-offset) + (additional-key :accessor argon2-additional-key :type (simple-array (unsigned-byte 8) (*))) + (additional-data :accessor argon2-additional-data :type (simple-array (unsigned-byte 8) (*))) + (work-area :accessor argon2-work-area :type (simple-array (unsigned-byte 64) (*))) + (digester :accessor argon2-digester))) + +(defclass argon2i (argon2) + ()) + +(defclass argon2d (argon2) + ()) (defun make-kdf (kind &key digest (N 4096) (r 8) (p 2) @@ -33,28 +44,42 @@ "digest is used for pbkdf1 and pbkdf2. N, p, and r are cost factors for scrypt. block-count, additional-key and additional-data are parameters for -argon2i" - (case kind - ((pbkdf1 :pbkdf1) - (unless (digestp digest) - (error 'unsupported-digest :name digest)) - (make-instance 'pbkdf1 :digest digest)) - ((pbkdf2 :pbkdf2) - (unless (digestp digest) - (error 'unsupported-digest :name digest)) - (make-instance 'pbkdf2 :digest digest)) - ((scrypt-kdf :scrypt-kdf) +argon2" + (case (massage-symbol kind) + (pbkdf1 + (let ((digest-name (massage-symbol digest))) + (unless (digestp digest-name) + (error 'unsupported-digest :name digest)) + (make-instance 'pbkdf1 :digest digest-name))) + (pbkdf2 + (let ((digest-name (massage-symbol digest))) + (unless (digestp digest-name) + (error 'unsupported-digest :name digest)) + (make-instance 'pbkdf2 :digest digest-name))) + (hmac-kdf + (let ((digest-name (massage-symbol digest))) + (unless (digestp digest-name) + (error 'unsupported-digest :name digest)) + (make-instance 'hmac-kdf :digest digest-name :info additional-data))) + (scrypt-kdf (when (or (<= N 1) (not (zerop (logand N (1- N)))) (>= (* r p) (expt 2 30))) (error 'unsupported-scrypt-cost-factors :N N :r r :p p)) (make-instance 'scrypt-kdf :N N :r r :p p)) - ((argon2i :argon2i) + (argon2i (when (< block-count 8) - (error 'unsupported-argon2i-parameters)) + (error 'unsupported-argon2-parameters)) (make-instance 'argon2i :block-count block-count :additional-key additional-key :additional-data additional-data)) + (argon2d + (when (< block-count 8) + (error 'unsupported-argon2-parameters)) + (make-instance 'argon2d + :block-count block-count + :additional-key additional-key + :additional-data additional-data)) (t (error 'unsupported-kdf :kdf kind)))) diff -Nru cl-ironclad-0.45/src/package.lisp cl-ironclad-0.47/src/package.lisp --- cl-ironclad-0.45/src/package.lisp 2019-02-18 14:08:52.000000000 +0000 +++ cl-ironclad-0.47/src/package.lisp 2019-09-11 12:36:21.000000000 +0000 @@ -57,7 +57,7 @@ #:gcm #:etm #:eax ;; KDFs - #:pbkdf1 #:pbkdf2 #:scrypt-kdf #:argon2i + #:pbkdf1 #:pbkdf2 #:hmac-kdf #:scrypt-kdf #:argon2i #:argon2d #:make-kdf #:derive-key ;; KDF convenience functions @@ -110,7 +110,7 @@ #:unsupported-cipher #:unsupported-mode #:unsupported-padding #:unsupported-digest #:unsupported-kdf #:unsupported-scrypt-cost-factors - #:unsupported-argon2i-parameters + #:unsupported-argon2-parameters #:insufficient-buffer-space #:invalid-padding #:key-not-supplied #:unsupported-mac #:invalid-mac-parameter #:invalid-signature-length @@ -230,4 +230,5 @@ (pushnew f *features*)) ;; Enable assembly optimizations +#-ecl-bytecmp (pushnew :ironclad-assembly *features*) diff -Nru cl-ironclad-0.45/testing/test-vectors/argon2d.lisp cl-ironclad-0.47/testing/test-vectors/argon2d.lisp --- cl-ironclad-0.45/testing/test-vectors/argon2d.lisp 1970-01-01 00:00:00.000000000 +0000 +++ cl-ironclad-0.47/testing/test-vectors/argon2d.lisp 2019-09-11 12:36:21.000000000 +0000 @@ -0,0 +1,39 @@ +;;;; -*- mode: lisp; indent-tabs-mode: nil -*- +(in-package :crypto-tests) + + +(rtest:deftest argon2d-1 + (run-kdf-test (crypto:make-kdf 'crypto:argon2d :block-count 12) + (crypto:ascii-string-to-byte-array "somepassword") + (crypto:ascii-string-to-byte-array "somesalt") + 3 + 32 + (ironclad:hex-string-to-byte-array "4b2506d85e002568380d0f4332b39fd0e6d17a36ceb8ea6cda4b42328715316a")) + t) + +(rtest:deftest argon2d-2 + (run-kdf-test (crypto:make-kdf 'crypto:argon2d :block-count 32) + (crypto:ascii-string-to-byte-array "0123456789abcdefgh") + (crypto:ascii-string-to-byte-array "0123456789") + 3 + 32 + (ironclad:hex-string-to-byte-array "74093b9772cc719dccf296bcdafc6d198beeb3b4ccb103f275489581497774de")) + t) + +(rtest:deftest argon2d-3 + (run-kdf-test (crypto:make-kdf 'crypto:argon2d :block-count 64) + (crypto:ascii-string-to-byte-array "0000000000000000") + (crypto:ascii-string-to-byte-array "00000000") + 4 + 32 + (ironclad:hex-string-to-byte-array "11180186e2608884c32539561128f6870f077319dfa29316ea4c065c815d0637")) + t) + +(rtest:deftest argon2d-4 + (run-kdf-test (crypto:make-kdf 'crypto:argon2d :block-count 128) + (crypto:ascii-string-to-byte-array "zzzzzzzzyyyyyyyyxxxxx") + (crypto:ascii-string-to-byte-array "wwwwwwwwvvvvv") + 3 + 111 + (ironclad:hex-string-to-byte-array "b98e59af3b82d241ee76f1a21262e8be0adcf9a673cbee7ff2b2e61ed938b2f2d709c925e067ef61f94b00478f91e9c773e79e66263ac6b8935e81afae94f44e1bb9daeae34e732e6be82438900ba1a865c159e16de16df2a738f00fdf1b4cf5e5c8b7a79703471c52b48152b2d55c")) + t) diff -Nru cl-ironclad-0.45/testing/test-vectors/hmac-kdf.lisp cl-ironclad-0.47/testing/test-vectors/hmac-kdf.lisp --- cl-ironclad-0.45/testing/test-vectors/hmac-kdf.lisp 1970-01-01 00:00:00.000000000 +0000 +++ cl-ironclad-0.47/testing/test-vectors/hmac-kdf.lisp 2019-09-11 12:36:21.000000000 +0000 @@ -0,0 +1,79 @@ +;;;; -*- mode: lisp; indent-tabs-mode: nil -*- +(in-package :crypto-tests) +;;; from RFC 5869 + +;;; with SHA-256 +(rtest:deftest hmac-kdf-1 + (run-kdf-test (crypto:make-kdf 'crypto:hmac-kdf + :digest :sha256 + :additional-data (ironclad:hex-string-to-byte-array "f0f1f2f3f4f5f6f7f8f9")) + (ironclad:hex-string-to-byte-array "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") + (ironclad:hex-string-to-byte-array "000102030405060708090a0b0c") + 0 + 42 + (ironclad:hex-string-to-byte-array "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865")) + t) + +(rtest:deftest hmac-kdf-2 + (run-kdf-test (crypto:make-kdf 'crypto:hmac-kdf + :digest :sha256 + :additional-data (ironclad:hex-string-to-byte-array "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")) + (ironclad:hex-string-to-byte-array "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f") + (ironclad:hex-string-to-byte-array "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf") + 0 + 82 + (ironclad:hex-string-to-byte-array "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87")) + t) + +(rtest:deftest hmac-kdf-3 + (run-kdf-test (crypto:make-kdf 'crypto:hmac-kdf + :digest :sha256) + (ironclad:hex-string-to-byte-array "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") + (make-array 0 :element-type '(unsigned-byte 8)) + 0 + 42 + (ironclad:hex-string-to-byte-array "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8")) + t) + +;;; with SHA-1 +(rtest:deftest hmac-kdf-4 + (run-kdf-test (crypto:make-kdf 'crypto:hmac-kdf + :digest :sha1 + :additional-data (ironclad:hex-string-to-byte-array "f0f1f2f3f4f5f6f7f8f9")) + (ironclad:hex-string-to-byte-array "0b0b0b0b0b0b0b0b0b0b0b") + (ironclad:hex-string-to-byte-array "000102030405060708090a0b0c") + 0 + 42 + (ironclad:hex-string-to-byte-array "085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896")) + t) + +(rtest:deftest hmac-kdf-5 + (run-kdf-test (crypto:make-kdf 'crypto:hmac-kdf + :digest :sha1 + :additional-data (ironclad:hex-string-to-byte-array "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")) + (ironclad:hex-string-to-byte-array "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f") + (ironclad:hex-string-to-byte-array "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf") + 0 + 82 + (ironclad:hex-string-to-byte-array "0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e927336d0441f4c4300e2cff0d0900b52d3b4")) + t) + +(rtest:deftest hmac-kdf-6 + (run-kdf-test (crypto:make-kdf 'crypto:hmac-kdf + :digest :sha1) + (ironclad:hex-string-to-byte-array "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") + (make-array 0 :element-type '(unsigned-byte 8)) + 0 + 42 + (ironclad:hex-string-to-byte-array "0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0ea00033de03984d34918")) + t) + +(rtest:deftest hmac-kdf-7 + (run-kdf-test (crypto:make-kdf 'crypto:hmac-kdf + :digest :sha1) + (ironclad:hex-string-to-byte-array "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c") + (make-array 0 :element-type '(unsigned-byte 8)) + 0 + 42 + (ironclad:hex-string-to-byte-array "2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48")) + t) diff -Nru cl-ironclad-0.45/TODO cl-ironclad-0.47/TODO --- cl-ironclad-0.45/TODO 2019-02-18 14:08:52.000000000 +0000 +++ cl-ironclad-0.47/TODO 2019-09-11 12:36:21.000000000 +0000 @@ -106,7 +106,7 @@ 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 +accommodated 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