Skip to content
Lesson 8 of 14

Step 1 of 5 · Reading · ~2 min

Read

TLS 1.3 Handshake

The Key Schedule Tree

TLS 1.3 derives roughly a dozen secrets from two inputs: an optional PSK and a Diffie-Hellman shared secret. RFC 8446 §7.1 lays this out as a tree of HKDF-Extract and Derive-Secret calls. You will build the whole tree in this lesson.

The diagram

            0
            |
            v
  PSK ->  HKDF-Extract = Early Secret
            |
            +-> Derive-Secret(., "ext binder" | "res binder", "")
            +-> Derive-Secret(., "c e traffic", ClientHello)
            +-> Derive-Secret(., "e exp master", ClientHello)
            v
      Derive-Secret(., "derived", "")
            |
            v
   DHE -> HKDF-Extract = Handshake Secret
            |
            +-> Derive-Secret(., "c hs traffic", CH..SH)
            +-> Derive-Secret(., "s hs traffic", CH..SH)
            v
      Derive-Secret(., "derived", "")
            |
            v
     0 -> HKDF-Extract = Master Secret
            |
            +-> Derive-Secret(., "c ap traffic", CH..server Finished)
            +-> Derive-Secret(., "s ap traffic", CH..server Finished)
            +-> Derive-Secret(., "exp master",   CH..server Finished)
            +-> Derive-Secret(., "res master",   CH..client Finished)

Three "salt" extractions (Early, Handshake, Master) with the previous level's "derived" output as salt. Each level then sprouts traffic / exporter / resumption branches.

Why the tree shape

If two unrelated keys come from the same Extract, compromise of one cannot reveal the other — they go through independent Expand-Label calls with different labels and different transcript-hash contexts.

If a level changes (you mix in DHE, or you mix in a fresh 0 in front of the Master Secret), then ALL downstream secrets change, even though the upstream parent is unchanged. This is what gives application traffic forward secrecy relative to handshake traffic.

Reduction to code

python

Then each traffic secret is fanned out into a (key, iv, finished_key) triple via HKDF-Expand-Label with labels "key", "iv", "finished". From one DHE output you get the whole protocol's symmetric keying.

RFC 8448 §3 ground truth (SHA-256 / X25519 / no PSK)

Inputs:

  • DHE = 8bd4054fb55b9d63fdfbacf9f04b9f0d35e6d63f537563efd46272900f89492d
  • H(CH..SH) = 860c06edc07858ee8e78f0e7428c58edd6b43f2ca3e6e95f02ed063cf0e1cad8
  • H(CH..server Finished) = 9608102a0f1ccc6db6250b7b7e417b1a000eaada3daae4777a7686c9ff83df13

Expected:

  • Handshake Secret = 1dc826e93606aa6fdc0aadc12f741b01046aa6b99f691ed221a9f0ca043fbeac
  • c_hs_traffic = b3eddb126e067f35a780b3abf45e2d8f3b1a950738f52e9600746a0e27a55a21
  • s_hs_traffic = b67b7d690cc16c4e75e54213cb2d37b4e9c912bcded9105d42befd59d391ad38
  • Master Secret = 18df06843d13a08bf2a449844c5f8a478001bc4d4c627984d5a41da8d0402919
  • c_ap_traffic = 9e40646ce79a7f9dc05af8889bce6552875afa0b06df0087f792ebb7c17504a5
  • s_ap_traffic = a11af9f05531f856ad47116b45a950328204b4f44bfb6b3a4b4f1f3fcb631643

If your implementation matches those hex values byte-for-byte against the RFC, your key schedule is correct.

Up nextCertificateVerify SignatureTLS 1.3 Handshake

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…