When TLS derives the actual traffic secrets, it doesn’t just Expand with a plain label — it folds in the transcript hash, a hash of every handshake message seen so far, as the derivation context:

where Derive-Secret(secret, label, msgs) = HKDF-Expand-Label(secret, label, Hash(msgs), …) — so Hash(msgs) goes into HKDF’s info context.

This is what catches downgrade and tampering. Both sides independently hash the exact sequence of handshake bytes they saw and mix that hash into every key. So:

  • If a client saw one transcript and the server saw a tampered one, they compute different transcript hashes → different keys.
  • Different keys means the first encrypted message — the Finished message, a MAC over the transcript under these keys — fails to verify, and the handshake aborts.

The keys are cryptographically welded to the exact conversation that produced them. It’s this transcript-binding across the whole key schedule — not the certificate signature alone — that prevents downgrade and injection.

Precision point: transcript-binding applies to the secret-derivation steps (the traffic secrets). The final key/iv expansion uses an empty context — the binding already happened upstream.

See also

References

Questions

flashcards/software-engineering/tls

How does folding the transcript hash into key derivation catch a tampered or downgraded handshake?::A tampered handshake makes the two sides hash different transcripts → they derive different keys → the Finished MAC fails and the handshake aborts

What message actually fails when transcript-bound keys mismatch?::The Finished message — a MAC over the transcript computed under the derived keys

Transcript-binding is applied when deriving the traffic secrets, but the final key/iv expansion uses an empty context because the binding already happened upstream.