← Back to Blog

HKDF (RFC 5869) Line by Line

HKDF (RFC 5869) Line by Line - QNSQY post-quantum encryption guide

When two computers agree on a shared secret over the internet, that secret is rarely usable as-is. It might be a chunk of bytes from a Diffie-Hellman exchange, the output of a key encapsulation mechanism, or a random number from a hardware source. Cryptographic algorithms expect keys of specific sizes, and they want those keys to look perfectly uniform. They also want different parts of a system to get different keys derived from the same source, so a leak in one part does not leak the rest.

HKDF, the HMAC-based Key Derivation Function specified in IETF RFC 5869, is the standard way to turn a raw shared secret into one or more uniform keys for downstream use. It was published in 2010 by Hugo Krawczyk, who also designed many of the surrounding protocols, including the Internet Key Exchange version 2 (IKEv2) and components of TLS. HKDF is now the key derivation step inside TLS 1.3, Signal, the Messaging Layer Security (MLS) protocol, the Noise Protocol Framework, and even ML-KEM, the post-quantum key encapsulation mechanism standardized in NIST FIPS 203.

This article walks through RFC 5869 line by line, in plain English, then covers when to use HKDF, common mistakes, and how it differs from related KDFs.

The Two-Step Idea: Extract Then Expand

Krawczyk's central design choice in HKDF was to split key derivation into two distinct steps with separate goals.

The first step is called Extract. Its job is to take an input that may have a lot of entropy but is not necessarily uniform (like a Diffie-Hellman shared point or an ML-KEM shared secret) and turn it into a single, fixed-size pseudorandom key. The Extract step does not need to produce a long output. It just needs to concentrate the available entropy into a clean uniform key, called the pseudorandom key (PRK).

The second step is called Expand. Its job is to take that PRK and stretch it into as many key bytes as you need. It also lets you bind the output to a context string so that the same PRK can produce different keys for different purposes without weakening any of them.

This separation matters because the two steps have different security goals. Extract has to handle messy real-world entropy. Expand has to be a well-behaved pseudorandom function, no more no less. By splitting them, HKDF can use the right primitive for each. Both steps use HMAC, which is well-studied and widely deployed. We explain HMAC itself in HMAC: How Keyed-Hash Authentication Works.

Inside Extract

The Extract function is defined like this:

PRK = HMAC-Hash(salt, IKM)

That is the entire definition. IKM is the input keying material, which is your raw secret. Salt is an optional non-secret value. Hash is a chosen hash function, usually SHA-256 or SHA-512.

The trick to understand is which input is the key and which is the message. HMAC takes a key and a message. In Extract, the salt is used as the HMAC key and the IKM is used as the HMAC message. This may feel backwards at first, but it is exactly right. The salt is meant to be public and varying; using it as the key gives HMAC's domain separation properties. The IKM is the actual secret material we are trying to compress.

If no salt is provided, RFC 5869 says to use a string of zero bytes the same length as the hash output. So with SHA-256 the default salt is 32 zero bytes.

The output PRK is one hash-length value. With SHA-256 that is 32 bytes. With SHA-512 it is 64 bytes.

A subtle point is that Extract is what makes HKDF safe to use with non-uniform secrets. If you used the IKM directly as a PRF key, parts of it might be predictable, which would weaken everything downstream. Extract assumes the IKM has enough entropy and produces a key that is computationally indistinguishable from random.

Inside Expand

Expand stretches the PRK to any length L up to 255 times the hash output size. With SHA-256 the maximum is 8160 bytes. The function is:

OKM = HKDF-Expand(PRK, info, L)

Internally, Expand builds the output by chaining HMAC calls. The first block T(1) is HMAC(PRK, info || 0x01). The next block T(2) is HMAC(PRK, T(1) || info || 0x02). In general:

T(i) = HMAC(PRK, T(i-1) || info || i)

The blocks are concatenated and the first L bytes are returned as OKM, the output keying material.

The info argument is where the magic of context binding happens. It is a non-secret string that lets the caller distinguish purposes. For example, in TLS 1.3 the info string for deriving the client write key is a structured value that includes "tls13 c hs traffic" plus the transcript hash. If two parties run TLS twice with the same shared secret but different transcripts, they get different keys because info differs. If a single connection needs both a write key and a MAC key, the info strings differ and the two keys are independent.

This is the right place to do domain separation. Hardcoding a unique info per call site means the same PRK can be reused safely across an entire protocol without weakening any individual key.

A Worked Example

Suppose you have run an X25519 Diffie-Hellman exchange and computed a 32-byte shared secret Z. You want to derive a 32-byte AES key for the client-to-server direction and another 32-byte key for the server-to-client direction.

You would compute:

PRK = HKDF-Extract(salt = transcript_hash, IKM = Z) client_key = HKDF-Expand(PRK, info = "c2s key v1", L = 32) server_key = HKDF-Expand(PRK, info = "s2c key v1", L = 32)

The two keys are independent. Knowing one does not help you compute the other. The transcript_hash binds the keys to the specific handshake. Reusing Z in another session with a different transcript yields totally different keys.

In the post-quantum setting, you replace the X25519 step with an ML-KEM call but keep HKDF for derivation. We cover this pattern in ML-KEM Explained and Hybrid Encryption.

Where HKDF Is Used

HKDF is everywhere in modern cryptography. A few headline deployments:

In TLS 1.3 (RFC 8446), every key in the handshake and traffic is derived through HKDF. The key schedule uses HKDF-Extract for each phase transition (early secret, handshake secret, master secret) and HKDF-Expand-Label, a small wrapper around HKDF-Expand, for individual keys.

In Signal, the double ratchet algorithm uses HKDF to derive new chain keys and message keys at every step. See the Signal protocol specifications for the exact key schedule.

In MLS (RFC 9420), HKDF is the only key derivation function. Group keys, sender keys, and welcome keys all come from HKDF chains rooted in the group's epoch secret.

In the Noise Protocol Framework, HKDF is the chosen KDF for nearly every Noise pattern.

In NIST FIPS 203 (ML-KEM), the K-PKE primitive uses SHAKE for some derivations but the wider envelope and standard hybrid constructions almost universally use HKDF-SHA-256 for the final session key. This is also true in the IETF post-quantum drafts. See IETF PQC Internet Drafts for an overview.

In Secure Shell, in the Wireguard VPN, and in the QUIC transport protocol, HKDF (or close variants) handles key derivation.

When to Use Salt and When Not To

The salt is the trickiest part of HKDF for newcomers. RFC 5869 lists three usage modes.

If the IKM is already uniform and high-entropy, like the output of a CSPRNG used directly, then the salt mostly does not matter and you can leave it as the default zero string.

If the IKM has high entropy but is not uniform, like a Diffie-Hellman shared secret, then a non-secret salt strengthens the analysis. Extract becomes a randomness extractor. The salt does not need to be secret but it should be unique per application or session if possible.

If the IKM is low-entropy, like a password, then HKDF is the wrong tool. You need a password-hashing function with deliberate slowness, like Argon2id. See Argon2id Explained. HKDF runs at full hash speed; an attacker can guess passwords through it about as fast as through plain HMAC.

A common pattern in protocols is to use the transcript hash as the salt, so different sessions get different salts even if the same long-term IKM is reused. This is what TLS 1.3 effectively does in its handshake key schedule.

Common Mistakes

Several mistakes show up regularly in audits.

Using HKDF for password hashing. As just noted, this is wrong because HKDF is fast. Use Argon2id, scrypt, or PBKDF2.

Skipping the info field. If you derive multiple keys from the same PRK with empty info or the same info, you get the same key. Always pick a unique info string per call site, ideally including a version tag and a description like "qnsqy v6 dek aad" so future versions can be distinguished.

Using non-uniform IKM directly as the PRK. Some implementations skip Extract and pass the raw shared secret as a PRF key. This is unsafe when the secret has structure, like a Diffie-Hellman element on a curve.

Reusing the same Extract output across unrelated protocols. If a long-term key is shared between two systems, derive a per-system PRK first by Extracting with a system-specific salt, then Expand within each system. This is sometimes called domain separation at the Extract layer.

Truncating the output incorrectly. HKDF's output is uniform, so any contiguous prefix is also uniform. Picking the first L bytes is fine. Picking arbitrary bytes from the middle is also fine but less common. What you should not do is mix bytes across multiple Expand calls without context separation.

HKDF vs Concat KDF vs SP 800-108

NIST has its own family of KDFs documented in SP 800-108 and SP 800-56C. These overlap with HKDF in scope. We cover them in NIST SP 800-108 Counter Mode KDFs and Concat KDF (SP 800-56C).

In practice, NIST has recently started accepting HKDF in many of its standards, including FIPS 203 and NIST IR 8413 advisory documents. The two-step extract-then-expand pattern is now the dominant design across both IETF and NIST. See Two-Step Key Derivation for a discussion of why this convergence happened.

The Concat KDF defined in SP 800-56C is conceptually similar but does not have a separate Extract step in older versions; it directly hashes the IKM with concatenated context bytes. Newer revisions added an Extract step, mirroring HKDF.

The counter-mode KDFs in SP 800-108 are mostly Expand-style functions: they assume you already have a uniform key and want to derive more. They do not address the messy-secret case.

Hash Choice and Output Length

HKDF is parameterized by a hash function. The most common choice is SHA-256, giving 32-byte PRK and up to 8160-byte OKM. SHA-512 doubles the maximum output and may be slightly faster on 64-bit CPUs that lack SHA-NI hardware acceleration.

SHA-3 hashes (SHA3-256, SHA3-512) work fine in HKDF since HMAC is defined over any iterated hash. NIST SP 800-185 also defines KMAC, which can be used as an alternative; we discuss this in KMAC and cSHAKE Explained.

For post-quantum protocols, SHA-256 remains the de facto choice. ML-KEM uses SHA-3 internally for its own derivations, but the outer HKDF in hybrid constructions is almost always SHA-256.

The maximum OKM length, 255 times the hash output, is rarely binding. If you need more output, derive intermediate PRKs and Expand from each.

Frequently Asked Questions

Do I need to use Extract if my IKM is already random?

If the IKM is the output of a CSPRNG and is the right size for your hash, you can skip Extract and use the IKM as the PRK directly. RFC 5869 explicitly allows this. In practice, including Extract anyway is cheap and removes one source of bugs.

Is HKDF safe against quantum attackers?

HKDF's security depends on the underlying HMAC, which depends on the hash. SHA-256 has at most a square-root speedup against preimage finding under Grover's algorithm, so a 256-bit hash gives an effective 128-bit quantum security level. That is fine for symmetric-strength derivation in practice. The structural design of HKDF is unaffected by Shor's algorithm.

Can I use HKDF with my own hash function?

You can, but you lose interoperability and you take on the burden of analyzing the hash. Stick with NIST-approved hashes (SHA-256, SHA-384, SHA-512, SHA3-256, SHA3-512) for production work. For a deep dive on hash construction, see BLAKE3 Hashing.

How long should info be?

There is no fixed length. RFC 5869 caps the total inputs at the hash block size internally for performance, but practically info can be any short string. Include a protocol identifier, a version number, and a key purpose label.

Is HKDF FIPS-approved?

Yes. NIST has approved HKDF in several documents and in FIPS 203. SP 800-56C Rev. 2 explicitly endorses HKDF as an extraction-then-expansion KDF. See NIST FIPS Guide for the broader context.

Sources

  1. Krawczyk, H. and Eronen, P. "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)." IETF RFC 5869, May 2010. https://datatracker.ietf.org/doc/html/rfc5869
  2. Krawczyk, H. "Cryptographic Extraction and Key Derivation: The HKDF Scheme." Crypto 2010. IACR ePrint 2010/264. https://eprint.iacr.org/2010/264
  3. NIST Special Publication 800-56C Rev. 2. "Recommendation for Key-Derivation Methods in Key-Establishment Schemes." August 2020. https://csrc.nist.gov/pubs/sp/800/56/c/r2/final
  4. Rescorla, E. "The Transport Layer Security (TLS) Protocol Version 1.3." IETF RFC 8446, August 2018. https://datatracker.ietf.org/doc/html/rfc8446
  5. NIST FIPS 203. "Module-Lattice-Based Key-Encapsulation Mechanism Standard." August 2024. https://csrc.nist.gov/pubs/fips/203/final
  6. Barnes, R. et al. "The Messaging Layer Security (MLS) Protocol." IETF RFC 9420, July 2023. https://datatracker.ietf.org/doc/html/rfc9420

Related Articles

Protect Your Data Before Q-Day Arrives

QNSQY's NIST-standardized post-quantum encryption protects files against both current and quantum-era threats.

Try QNSQY