HKDF (HMAC-based Extract-and-Expand Key Derivation Function) is a key derivation function used by many standard protocols.
It actually includes two operations:
- extract: this operation absorbs an arbitrary-long sequence of bytes and outputs a fixed-size master key (also known as
PRK
), suitable for use with the second function (expand). - expand: this operation generates an variable size subkey given a master key (also known as
PRK
) and a description of a key (or “context”) to derive from it. That operation can be repeated with different descriptions in order to derive as many keys as necessary.
The latter can be used without the former, if a randomly sampled key of the right size is already available.
Example:
unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES];
unsigned char subkey1[32];
unsigned char subkey2[32];
unsigned char subkey3[64];
crypto_kdf_hkdf_sha256_keygen(prk);
crypto_kdf_hkdf_sha256_expand(subkey1, sizeof subkey1,
"key for encryption",
(sizeof "key for encryption") - 1,
prk);
crypto_kdf_hkdf_sha256_expand(subkey2, sizeof subkey2,
"key for signatures",
(sizeof "key for signatures") - 1,
prk);
crypto_kdf_hkdf_sha256_expand(subkey3, sizeof subkey3,
"key for something else",
(sizeof "key for something else") - 1,
prk);
Usage:
int crypto_kdf_hkdf_sha256_expand(
unsigned char *out, size_t out_len,
const char *ctx, size_t ctx_len,
const unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES]);
The crypto_kdf_hkdf_sha256_expand()
function derives a subkey from a context/description ctx
of length ctx_len
bytes and a master key prk
of length crypto_kdf_hkdf_sha256_KEYBYTES
bytes.
The key is stored into out
whose length is out_len
bytes.
Up to crypto_kdf_hkdf_sha256_BYTES_MAX
bytes can be produced.
The generated keys satisfy the typical requirements of keys used for symmetric cryptography. In particular, they appear to be sampled from a uniform distribution over the entire range of possible keys.
Contexts don’t have to be secret. They just need to be distinct in order to produce distinct keys from the same master key.
Any crypto_kdf_hkdf_sha256_KEYBYTES
bytes key that appears to be sampled from a uniform distribution can be used for the prk
. For example, the output of a key exchange mechanism (such as crypto_kx_*
) can be used as a master key.
For convenience, the crypto_kdf_hkdf_sha256_keygen()
function creates a random prk
.
The master key should remain secret.
crypto_kdf_hkdf_sha256_expand()
is effectively a standard alternative to crypto_kdf_derive_from_key()
. It is slower, but the context can be of any size.
Example:
#define APPLICATION_UUID "6723D3AA-C6CA-4F3C-8F24-94B550C5F10A"
#define IKM "John Doe - 951a 6158 4fe0 8a0b ad7c b57b 7687 09b6"
crypto_kdf_hkdf_sha256_extract(prk,
(const unsigned char *) APPLICATION_UUID,
(sizeof APPLICATION_UUID) - 1,
(const unsigned char *) IKM,
(sizeof IKM) - 1);
Usage:
int crypto_kdf_hkdf_sha256_extract(unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES],
const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len);
The crypto_kdf_hkdf_sha256_extract()
function creates a master key (prk
) given an optional salt salt
(which can be NULL
, or salt_len
bytes), and input keying material ikm
of size ikm_len
bytes.
salt
is optional. It can be a public, unique identifier for a protocol or application. Its purpose is to ensure that distinct keys will be created even if the input keying material is accidentally reused across protocols.
A UUID is a decent example of a salt
. There is no minimum length.
If input keying material cannot be accidentally reused, using an empty (NULL
) salt is perfectly acceptable.
ikm
(Input Keying Material) is an arbitrary-long byte sequence. The bytes don’t have to be sampled from a uniform distribution. It can be any combination of text and binary data.
But the overall sequence needs to include some entropy.
The resulting PRK will roughly have the same entropy. The “extract” operation effectively extracts the entropy and packs it into a fixed-size key, but it doesn’t add any entropy.
Example:
#define IKM1 "John Doe - "
#define IKM2 "951a 6158 4fe0 8a0b ad7c b57b 7687 09b6"
crypto_kdf_hkdf_sha256_extract_init(&st, NULL, 0);
crypto_kdf_hkdf_sha256_extract_update(&st,
(const unsigned char *) IKM1,
(sizeof IKM1) - 1);
crypto_kdf_hkdf_sha256_extract_update(&st,
(const unsigned char *) IKM2,
(sizeof IKM2) - 1);
crypto_kdf_hkdf_sha256_extract_final(&st, prk);
Usage:
int crypto_kdf_hkdf_sha256_extract_init(
crypto_kdf_hkdf_sha256_state *state,
const unsigned char *salt, size_t salt_len);
int crypto_kdf_hkdf_sha256_extract_update(
crypto_kdf_hkdf_sha256_state *state,
const unsigned char *ikm, size_t ikm_len);
int crypto_kdf_hkdf_sha256_extract_final(
crypto_kdf_hkdf_sha256_state *state,
unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES]);
Instead of a one-shot call to crypto_kdf_hkdf_sha256_extract()
, it is possible to feed the input keying material incrementally.
In order to do so, initialize a state with crypto_kdf_hkdf_sha256_extract_init()
, then call crypto_kdf_hkdf_sha256_extract_update()
as many times as required, and finally generate the key with crypto_kdf_hkdf_sha256_extract_final()
.
Both the SHA256
and the SHA512
instantiations are supported.
The functions documented above use HKDF-SHA256
, but the HKDF-SHA512
can be used simply by replacing the crypto_kdf_hkdf_sha256
prefix with crypto_kdf_hkdf_sha512
.
HKDF-SHA512
is present for consistency and compatibility with existing protocols, but it doesn’t have practical security benefits over HKDF-SHA256
.
Therefore, the SHA256
instantiation is generally recommended.
crypto_kdf_hkdf_sha256_KEYBYTES
crypto_kdf_hkdf_sha256_BYTES_MIN
crypto_kdf_hkdf_sha256_BYTES_MAX
crypto_kdf_hkdf_sha512_KEYBYTES
crypto_kdf_hkdf_sha512_BYTES_MIN
crypto_kdf_hkdf_sha512_BYTES_MAX
HKDF was added in libsodium version 1.0.19.