Key exchange and forward secrecy
Diffie-Hellman lets two strangers derive an identical shared secret over a channel an eavesdropper is watching, without ever transmitting it, but raw key exchange authenticates nobody — and using fresh, ephemeral keys per session adds forward secrecy, so a future key compromise can't unlock past traffic.
Key exchange and forward secrecy
Lesson 4 needs a shared symmetric key before it can do anything. Lesson 6's HMAC needs a shared key too. But "shared" has always been doing a lot of quiet work in those sentences — how do two parties who've never met, with no prior relationship, end up holding the same secret, when every message between them crosses a network an attacker can read in full? Mailing the key over doesn't work; the attacker reads the mail. This lesson is about the piece that makes every shared-key primitive in this module usable between strangers on the open internet.
The frame: Diffie-Hellman lets two parties combine public values with private secrets so that they land on an identical shared secret, while an eavesdropper who sees only the public exchange cannot compute it — and because raw key exchange proves nothing about identity, real systems bind it to authentication, and use fresh per-session keys to get forward secrecy.
The problem, stated precisely
Two parties, call them a browser and a server, have never communicated before. They need a symmetric key for fast AES-GCM encryption (lesson 4). Every byte they exchange to set that up is visible to anyone on the path — the coffee-shop wifi, the ISP, a backbone router. Whatever scheme they use has to work in the open, producing a secret that the two of them hold and nobody watching the exchange can derive, even having seen every message.
That sounds like it should be impossible — if the secret is derived entirely from public messages, why can't the eavesdropper run the same derivation? The answer is a function that's easy to compute forwards and infeasible to invert, and each side supplies a piece the other is missing.
Diffie-Hellman, briefly
The mechanism is Diffie-Hellman (DH), and the TLS lesson already works through it with real numbers and a hands-on playground where you can drag each side's private secret and watch the shared value fall out — that's the place to build the arithmetic intuition, so this lesson won't repeat the modular exponentiation in full. The shape, restated at the level this module needs:
Both sides combine the other side's public value with their own private secret, and — because of the underlying one-way function — both computations land on the identical result. The eavesdropper sees both public values cross the wire and still can't compute the shared secret, because doing so requires inverting a function (the discrete logarithm problem, in the classic formulation) that has no known efficient solution at the key sizes used in practice.
ECDH — elliptic-curve Diffie-Hellman — is the efficient modern form real systems actually use. It replaces "exponentiate modulo a prime" with point arithmetic on an elliptic curve, getting equivalent security from much smaller keys and faster computation. The shape is identical: public curve parameters, private scalars, exchanged public points, both sides derive the same shared point. If the modular version clicks, the curve version is the same idea on different math.
// Conceptual: two independent key pairs, one shared secret.
const clientKeys = await crypto.subtle.generateKey(
{ name: "ECDH", namedCurve: "P-256" },
false,
["deriveKey", "deriveBits"]
);
const serverKeys = await crypto.subtle.generateKey(
{ name: "ECDH", namedCurve: "P-256" },
false,
["deriveKey", "deriveBits"]
);
// Each side combines its OWN private key with the OTHER side's public key.
const sharedOnClient = await crypto.subtle.deriveBits(
{ name: "ECDH", public: serverKeys.publicKey },
clientKeys.privateKey,
256
);
const sharedOnServer = await crypto.subtle.deriveBits(
{ name: "ECDH", public: clientKeys.publicKey },
serverKeys.privateKey,
256
);
// sharedOnClient and sharedOnServer are byte-for-byte identical —
// and neither private key, nor the shared value itself, ever crossed a network.What DH doesn't give you: nobody's identity
Here's the gap, and it's the reason the rest of this lesson exists. DH produces a shared secret with whoever was on the other end of the exchange — it says nothing about who that was. A passive eavesdropper, one who only reads, is defeated. An active attacker, one who can intercept and rewrite messages, can do something worse: run two separate DH exchanges, one with the client (pretending to be the server) and one with the server (pretending to be the client). Both victims derive a perfectly valid shared secret — just not with each other. The attacker sits in the middle holding both secrets, decrypting, reading, and re-encrypting everything that flows through. Both ends see what looks like a secure connection. Both are wrong.
That's a man-in-the-middle (MITM) attack, and unauthenticated DH has no defense against it at all — the math works flawlessly for the attacker too; it just runs it twice.
The fix is authentication: binding the key exchange to a verified identity using the signatures and certificates from lesson 6. In TLS, this is exactly what the server's certificate and its CertificateVerify signature do — the server signs a hash of the handshake transcript, which includes both sides' DH public values, with its long-term private key. A certificate, ultimately signed by a CA the browser already trusts (the TLS lesson's chain-of-trust section), vouches that this specific public key belongs to this specific domain. An attacker running the MITM trick above would need to forge a signature over a transcript containing their own substituted DH value — and without the real server's private key, they can't. Key exchange gets you a secret channel; a signature over that exchange tells you who's actually holding the other end.
Forward secrecy: why the keys should be thrown away
There's a second, independent design choice layered on top: whether the private values a and b in the exchange are long-lived or ephemeral — generated fresh per session and discarded when it ends. TLS's ECDHE ("E" for ephemeral) does the latter, and the payoff is forward secrecy (also called perfect forward secrecy).
Here's the scenario forward secrecy defends against. An attacker records all of your encrypted traffic today and simply stores it, unreadable, for later. Years afterward, they steal the server's long-term private key — through a breach, a subpoena, a retired employee's laptop, whatever. Can they now go back and decrypt the years-old recordings?
With ephemeral DH, no. The server's long-term private key was never used to encrypt anything — only to sign each handshake and prove identity. The values that actually produced the session's symmetric key were the ephemeral a and b, generated in memory for that one connection and gone the moment it closed. Stealing the long-term key later gives the attacker nothing to derive those vanished per-session secrets from. Each session's key dies with the session.
Contrast this with old RSA key-transport TLS, which had no forward secrecy at all: the client picked the session key itself and encrypted it directly with the server's long-term RSA public key. That one long-term private key could decrypt every session ever recorded, forever, because it was the literal key used to protect each session key in transit. Steal it once — even years later — and every recorded conversation opens at once. TLS 1.3 removed this mode from the specification entirely; ephemeral key exchange is now mandatory, not a hardening option.
Where this goes next
Key exchange plus authentication gets two strangers to a shared, verified secret. But every step in this lesson quietly assumed the private values a and b were unpredictable — chosen so no one, including an attacker who knows the algorithm perfectly, could guess them. Where does that unpredictability actually come from, and what happens when it's weaker than it looks? That's randomness, the last piece, and the one this whole module has been building toward — because once you have secure randomness, key exchange, authentication, and symmetric encryption all in view at once, you're looking at the entire TLS handshake.
Go deeper
- MDN — SubtleCrypto.deriveBits() — The browser API for ECDH key derivation used in this lesson's code, including the exact parameter shape for combining a private key with a peer's public key.
- Cloudflare — TLS 1.3 overview — Explains why TLS 1.3 made ephemeral key exchange mandatory and dropped RSA key transport, with the forward-secrecy reasoning straight from the people who deployed it at scale.
- RFC 8446 — TLS 1.3 — Section 4.2.8 (key share) and Section 9.2 (mandatory forward secrecy) are the normative source for the claims this lesson makes about what TLS 1.3 requires.
Check yourself
Answer out loud, as if an interviewer asked. If you hand-wave, reread that section.
- State the problem key exchange solves in one sentence: what do two parties need, and what can an eavesdropper do to every message involved?
- In Diffie-Hellman, what does each side keep private, what crosses the wire, and why can't the eavesdropper compute the shared secret from what it saw?
- What's the practical advantage of ECDH over classic modular-exponentiation DH, and what stays exactly the same between the two?
- Describe the man-in-the-middle attack that raw, unauthenticated Diffie-Hellman permits, and explain precisely which mechanism from lesson 6 closes the gap and how.
- Define forward secrecy in terms of what an attacker can and cannot do after stealing a server's long-term private key years later.
- Why did TLS 1.2's RSA key-transport mode have no forward secrecy, and what specific design change in TLS 1.3 fixed it?
- Explain why forward secrecy is a statement about past sessions, not about the security of a connection compromised in real time.