6 Key Derivation and Password Hashing
A key derivation function (KDF) can be used to derive keys from secret material that is not directly suitable for use as a key, such as a passphrase or the result of a key agreement algorithm. Differnt KDFs have different additional parameters such as work factors and context information fields.
KDFs with adjustable work factors are also used to store passwords [OWASP, DUB]. A KDF is preferable to a simple digest function (even with a salt) because the work factor can be chosen to make exhaustively searching the space of likely passwords costly.
There are two groups of KDF specifiers. The following KDFs have work factors and are suitable for producing keys from passwords and storing passwords:
(list 'pbkdf2 'hmac digest-spec) —
the PBKDF2 function from PKCS#5 [PKCS5] using HMAC of digest-spec (see digest-spec?). 'scrypt —
scrypt, with work factors for both time and memory [scrypt] 'argon2d, 'argon2i, 'argon2id —
variants of Argon2, designed primarily for password hashing [Argon2, PHC]
The following KDFs are suitable for producing keys from the results of key-agreement algorithms. They are not suitable for storing passwords.
(list 'hkdf digest-spec) —
the HKDF extract-then-expand function [HKDF] (list 'concat digest-spec) —
the Concatentation (also called One-Step) KDF from NIST SP 800-56C [SP800-56C] using a plain digest (list 'concat 'hmac digest-spec) —
the Concatentation KDF [SP800-56C] using HMAC-digest-spec (list 'sp800-108-counter 'hmac digest-spec), (list 'sp800-108-feedback 'hmac digest-spec), and (list 'sp800-108-double-pipeline 'hmac digest-spec) —
KDF constructions from NIST SP 800-108 [SP800-108]; this library only supports 32-bit counters and the standard ordering of components (list 'ans-x9.63 digest-spec) —
similar to the concatenation KDF but with a different order of components, defined by ANSI [X963]
Changed in version 1.3 of package crypto-lib: Added support for 'hkdf, 'concat, 'sp800-108-*, and 'ans-x9.63 algorithms.
procedure
k : kdf-spec?
factories : (or/c crypto-factory? (listof crypto-factory?)) = (crypto-factories)
procedure
k : (or/c kdf-spec? kdf-impl?) input : bytes? salt : (or/c bytes? #f) params : (listof (list/c symbol? any/c)) = '() key-size : (or/c exact-nonnegative-integer? #f) = #f
The input argument may be a password or passphrase, or it may be input keying material from a key agreement operation.
The salt must be a bytestring (bytes?) except in the following cases: if the KDF is 'ans-x9.63, 'concat with a digest, 'sp800-108-counter, or 'sp800-108-double-pipeline, then salt must be #f; if the KDF is 'hkdf or 'concat with HMAC, then salt may be either #f or a bytestring. (Note that KDFs with optional salt do not treat #f as equivalent to #"", but to a digest-dependent string of zeros.)
The following parameters are recognized for (list 'pbkdf2 'hmac digest):
(list 'iterations iterations) —
number of iterations
The following parameters are recognized for 'scrypt:
(list 'N N) —
the CPU/memory cost (list 'p p) —
the parallelization factor (list 'r r) —
the block size
The following parameters are recognized for 'argon2d, 'argon2i, and 'argon2id:
The following parameters are recognized for the 'hkdf, 'concat, 'sp800-108-*, and 'asn-x9.63 families of KDFs:
(list 'info info-bytes) —
additional contextual information; see [HKDF, SP800-56A, SP800-108] for recommendations regarding the contents and format of this field
> (kdf '(pbkdf2 hmac sha256) #"I am the eggman" (crypto-random-bytes 16) '((iterations 100000)) #:key-size 32) #"J\373h\310mfmh\266\345JWde\227\225\313Ub\201\332\1O\367f\201{\232\256\363z\330"
> (kdf 'argon2id #"I am the walrus" #"googoogjoob" '((t 100) (m 2048) (p 1)) #:key-size 32) #"\30V\214|i\2072VE\242\345`+A\262\352Ni\230|6\365\227M\364\2\326y{\256\271\21"
> (define pre-key (.... do key agreement ....))
> (list (kdf '(hkdf sha256) pre-key #f '((info #"enc") (key-size 16))) (kdf '(hkdf sha256) pre-key #f '((info #"mac") (key-size 16))))
'(#"\36>\376\334H\331^\6\370\25\302gC2\362\26"
#"t\4\236=\200\225\334\344\375\262&\374\267\225?b")
Changed in version 2.0 of package crypto-lib: Added #:key-size argument. Previously, key-size was always passed in params.
The config parameters are nearly the same as for kdf, with the following exceptions:
The 'scrypt algorithm requires a parameter 'ln specifying the log (base 2) of the iteration count, instead of the 'N parameter expected by the kdf function.
The 'key-size parameter is not allowed. This library always generates password hashes with 32 bytes of raw output (before encoding).
See [OWASP] for parameter recommendations.
> (define pwcred (pwhash 'argon2id #"mypassword" '((m 4096) (t 10) (p 1)))) > pwcred "$argon2id$v=19$m=4096,t=10,p=1$dJPZCOuOJa3Foy6xCdYVLQ$1B/cNol5YfOkUKg3txiDzxR8gyyq9pyV4g6NP1x0krA"
Added in version 1.2 of package crypto-lib.
procedure
(pwhash-verify k password pwh) → boolean?
k : (or/c kdf-impl? #f) password : bytes? pwh : string?
If k is a KDF implementation (kdf-impl?), pwh must have been generated with the same KDF algorithm (but not necessarily the same implementation); otherwise an exception is raised. If k is #f, then the KDF algorithm is extracted from pwh and the (crypto-factories) list is searched for an implementation; if no implementation is found an exception is raised.
> (pwhash-verify #f #"mypassword" pwcred) #t
> (pwhash-verify #f #"wildguess" pwcred) #f
Added in version 1.2 of package crypto-lib.
procedure
(pbkdf2-hmac di pass salt #:iterations iterations [ #:key-size key-size]) → bytes? di : digest-spec? pass : bytes? salt : bytes? iterations : exact-positive-integer? key-size : exact-positive-integer? = (digest-size di)
> (pbkdf2-hmac 'sha256 #"I am the walrus" #"abcd" #:iterations 100000) #"\aR>\"^\241\301\253f\v\237\310\263\330T\321\301\307|\212`\370\rD\347\f`{>\226c\371"
procedure
(scrypt pass salt #:N N [ #:p p #:r r #:key-size key-size]) → bytes? pass : bytes? salt : bytes? N : exact-positive-integer? p : exact-positive-integer? = 1 r : exact-positive-integer? = 8 key-size : exact-positive-integer? = 32
Bibliography
| [OWASP] | “Password Storage Cheat Sheet.” https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet | |
| [HtSSaP] | Coda Hale, “How to Safely Store a Password: Use bcrypt.” http://codahale.com/how-to-safely-store-a-password/ | |
| [DUB] | Tony Arcieri, “Don’t Use bcrypt.” http://www.unlimitednovelty.com/2012/03/dont-use-bcrypt.html | |
| [PKCS5] | B. Kaliski, “PKCS #5: Password-Based Cryptography Specification.” https://tools.ietf.org/html/rfc2898 | |
| [bcrypt] | Niels Provos and David Mazières, “A Future-Adaptable Password Scheme.” https://www.usenix.org/legacy/events/usenix99/provos.html | |
| [scrypt] | Colin Percival, “The scrypt key derivation function.” http://www.tarsnap.com/scrypt.html | |
| [Argon2] | Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich, “Argon2: the memory-hard function for password hashing and other applications.” https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf | |
| [PHC] | “Password Hashing Competition.” https://password-hashing.net/ | |
| [HKDF] | “RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF).” https://tools.ietf.org/html/rfc5869 | |
| [SP800-56A] | “NIST Special Publication 800-56A Rev. 3: Recommendation for Pair-Wise Key-Establishment Schemes Using Discrete Logarithm Cryptography.” https://csrc.nist.gov/publications/detail/sp/800-56a/rev-3/final | |
| [SP800-56C] | “NIST Special Publication 800-56C Rev. 1: Recommendation for Key-Derivation Methods in Key-Establishment Schemes.” https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final | |
| [SP800-108] | “NIST Special Publication 800-108: Recommendation for Key Derivation Using Pseudorandom Functions (Revised).” https://csrc.nist.gov/publications/detail/sp/800-108/final | |
| [X963] | “Public Key Cryptography for the Financial Services Industry - Key Agreement and Key Transport Using Elliptic Curve Cryptography.” — |