HKDF is the single tool underneath the entire TLS 1.3 key schedule. It has exactly two operations, each solving a distinct problem, both built on HMAC (a keyed hash producing fixed-length, pseudorandom, unforgeable output).

Why two operations? A raw Diffie-Hellman secret Z can’t be used directly as an AES key for two separate reasons:

  • It’s not shaped like a key. Z has mathematical structure (e.g. a curve point coordinate); its bits are biased and correlated, not uniformly random.
  • You need many keys, not one. Each direction, each phase, and each rotation needs its own independent key.

The two operations map exactly onto these:

Extract — the whitening step.

HMAC acts as a randomness extractor: feed it biased, structured input keying material (IKM), and the output PRK is statistically indistinguishable from uniform. Extract compresses and cleans — its input can be longer than its output. It never stretches.

Expand — the cloning step.

Iterated HMAC keyed by PRK, so it can produce output of any length L (this is the stretch step). The info label makes outputs cryptographically independent: knowing the output for info = "client key" tells you nothing about info = "server key".

So: Extract (messy secret → one clean key), then Expand (one clean key → many labeled keys). Everything in the TLS key schedule is one of these two calls.

See also

References

Questions

flashcards/software-engineering/tls

What are HKDF’s two operations and what does each do?::Extract whitens/compresses messy entropy into one clean fixed-length key (PRK); Expand stretches that key into many independent keys, one per label

Why can’t a raw Diffie-Hellman secret be used directly as an AES key?::Its bits are biased/structured (not uniformly random), AND one secret must become many independent keys — two separate problems

HKDF-Extract can take input longer than its output — it compresses and whitens, it does not stretch.

In HKDF-Expand, what makes two outputs cryptographically independent?::Using a different {{info label}} for each — same PRK, different label, independent output