← Back to the tool

Protocols · Under the hood

TLS 1.3 key schedule: from ECDH to a traffic key

What each button in the tool actually computes, step by step — with the real formulas.

The same ECDH exchange, applied to a specific purpose

"Run handshake" starts exactly like the ECDH tool: independent P-256 key pairs for Client and Server, each deriving the identical shared secret via scalar multiplication. What's different here is everything that happens to that shared secret afterward — TLS 1.3 never uses a raw ECDH output as an encryption key directly.

Binding the secret to this exact handshake, via HKDF

The shared secret is imported as HKDF input key material and run through crypto.subtle.deriveKey with the HKDF algorithm — real HKDF (RFC 5869), the same construction the key derivation functions module covers, not a simulation. Its info parameter carries a hash of this simulated handshake's own transcript (a running SHA-256 over the Hello messages both sides just exchanged), which is what cryptographically ties the resulting key to this specific connection — reusing the identical shared secret in a different handshake, with a different transcript, would derive a completely different key.

traffic key=HKDF(salt=∅, IKM=ECDH secret, info=H(transcript))\text{traffic key} = \mathrm{HKDF}(\text{salt} = \varnothing,\ \text{IKM} = \text{ECDH secret},\ \text{info} = H(\text{transcript}))

Extract, then Expand

HKDF is two stages folded into one Web Crypto call: Extract pools the shared secret's entropy into a fixed-size pseudorandom key, and Expand stretches that into an output of exactly the length requested — 256 bits here, sized for the AES-GCM key it becomes. Real TLS 1.3 runs this same two-stage process several times over the course of a handshake (early, handshake, and application traffic secrets, each further split into client-write and server-write keys); this tool collapses that down to one derivation to keep the core mechanism visible.

The derived key encrypts a real record

"Encrypt application data" takes the HKDF output directly as an AES-256-GCM key — no separate key-generation step — and encrypts your message exactly as the AES-GCM tool does: a random IV, ciphertext, and a 16-byte authentication tag.

Why the server lands on the identical key, independently

"Server derives & decrypts" runs the mirrored computation: the server's own ECDH scalar multiplication (landing on the same point, since ECDH is commutative), the same transcript hash (both sides hashed the identical exchanged messages), and the identical HKDF call — producing a bit-for-bit identical traffic key with no key ever having crossed the wire. That's the entire trick: everything two sides need to agree on a key is either public (the transcript) or independently derivable (the shared secret), never transmitted.

Now try it

Back to TLS 1.3 key schedule: from ECDH to a traffic key →

Run a real ECDH exchange, feed it through real HKDF bound to a transcript hash, and use the result to AES-GCM-encrypt a record — the exact chain TLS 1.3 runs on every connection.