HKDF

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.

Deriving keys from a master key

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.

Creating a master key from input keying material

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.

Incremental entropy extraction

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().

HKDF-SHA256 and HKDF-SHA512

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.

Constants

  • 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

Algorithm details

Notes

HKDF was added in libsodium version 1.0.19.

Last updated