Diffie-Hellman
Two parties, one shared secret, zero transmissions of it.
Diffie-Hellman
In 1976, Whitfield Diffie and Martin Hellman published a paper that started modern cryptography. The protocol they invented lets two parties — who have never met, who don't share any secret, communicating over an open channel monitored by an adversary — agree on a shared secret. The shared secret is never transmitted.
If you stop and think about that, it should feel impossible. It's not.
The paint-mixing analogy
Picture Alice and Bob sitting on opposite sides of a table. Eve sits in the middle and can see everything they do. They each have:
- A bottle of identical public yellow paint (visible to Eve).
- A bottle of their own private secret paint (Alice's red, Bob's blue), hidden in their pocket.
Step by step:
| Step | Alice does | Eve sees | Bob does |
|---|---|---|---|
| 1 | Mix yellow + red → orange | orange | (idle) |
| 2 | Hands the orange jar to Bob | orange in transit | receives orange |
| 3 | (idle) | green | Mix yellow + blue → green; hand to Alice |
| 4 | Receives green; mix in own red → "olive" | olive in their hands | Receives orange; mix in own blue → also "olive" |
After step 4, both Alice and Bob have the same colour: yellow + red + blue, mixed in the same ratios. They share a secret.
Eve has watched everything. She has yellow, orange (yellow+red), and green (yellow+blue). Can she compute olive (yellow+red+blue)? She'd need to un-mix orange to recover the pure red — and once paint is mixed, you can't separate it. The asymmetry between mixing (easy) and un-mixing (hard) is what makes the trick work.
The actual mathematics replaces "mix paints" with modular exponentiation in a finite group, where forwards is fast (a single pow_mod) and backwards is the Discrete Logarithm Problem, which has no known efficient algorithm.
The classical protocol
Public parameters (everyone agrees on these in advance, often baked into a standard):
- A large prime
p. - A generator
g, an element whose powers cycle through most of(Z/pZ)*.
Per session:
Alice picks secret a, sends A = g^a mod p.
Bob picks secret b, sends B = g^b mod p.
Both compute:
Alice: B^a mod p = (g^b)^a = g^(ab) mod p
Bob: A^b mod p = (g^a)^b = g^(ab) mod p
The shared secret is g^(ab) mod p. An eavesdropper saw A = g^a and B = g^b go past, but cannot compute g^(ab) without knowing a or b — and recovering a from g^a mod p is the discrete logarithm problem, the analogue of factoring for the multiplicative group.
ECDH — the elliptic-curve flavour
Replace "exponentiate in a multiplicative group mod p" with "scalar-multiply a base point on an elliptic curve". The protocol is structurally identical:
Alice picks secret a, sends A = a·G (curve point)
Bob picks secret b, sends B = b·G (curve point)
Both compute:
Alice: a · B = a · (b·G) = (ab)·G
Bob: b · A = b · (a·G) = (ab)·G
The shared secret is the curve point (ab)·G. The hard problem is the Elliptic Curve Discrete Logarithm Problem (ECDLP) — same idea, different group.
ECDH at 256 bits ≈ classical DH at 3072 bits in security. That's why TLS 1.3 hardcoded ECDHE with X25519 / P-256 and removed classical DH entirely.
What you do with the shared secret
You never use the raw output of DH directly as a symmetric key. The shared secret is a curve point or a group element — it has structure, not uniform randomness. Always run it through a KDF (key-derivation function) first:
key = HKDF(SHA256, secret = ECDH_output, info = "my-app v1 chacha20", length = 32)
HKDF gives you uniformly-distributed key bytes, plus context binding via the info parameter (so the same shared secret used for two purposes produces two different keys).
Forward secrecy
Now the killer feature. Imagine the long-term private key on a TLS server gets compromised. Without forward secrecy:
- The attacker pcap'd a session from a year ago.
- They use the leaked private key to decrypt the captured handshake.
- They recover the symmetric session key.
- They decrypt every byte of that session.
With forward secrecy:
- Each session uses a fresh, throwaway DH keypair (
aandbabove are generated per-session, not per-server). - The session key depends on
aandb, neither of which exists anywhere on disk. - Compromising the long-term key (used for signing the handshake — that's a separate operation) tells the attacker nothing about session keys.
This is ephemeral DH (DHE / ECDHE). TLS 1.3 only supports ephemeral key exchange — non-ephemeral was deemed too dangerous.
| Cipher suite | Forward secrecy? |
|---|---|
TLS_RSA_WITH_AES_128_GCM_SHA256 (TLS 1.2 RSA) |
No |
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 |
Yes |
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
Yes |
TLS_AES_128_GCM_SHA256 (TLS 1.3) |
Yes (mandatory) |
Authenticated key exchange
Plain DH is anonymous — both parties end up with the same secret, but neither knows who they're talking to. Eve can run a machine-in-the-middle attack: she does DH separately with Alice (pretending to be Bob) and with Bob (pretending to be Alice), and proxies messages between them, decrypting and re-encrypting as they pass.
To prevent this, real protocols authenticate the DH exchange with signatures:
- TLS: Server signs
(client_random, server_random, server_dh_pub)with its long-term cert key. Client verifies against the cert chain. - Signal: X3DH — a three-step DH that mixes long-term, signed pre-key, and ephemeral keys to give both authentication and forward secrecy.
- WireGuard: Noise IK pattern — pre-distributed static keys plus ephemeral DH per-session.
- SSH: Diffie-Hellman key exchange + host-key signature in the SSH
KEXINITflow.
Common gotchas
Use a strong group. Logjam (2015) showed that small or shared classical-DH primes were vulnerable to precomputation attacks. Either use the standard groups defined in RFC 3526 / RFC 7919 with at least 2048 bits, or — better — use ECDH with a modern curve.
Validate peer points. A naive ECDH implementation will accept any curve point as the peer's public key, including points on twist curves or low-order subgroups. Production libraries do this validation; if you're rolling your own, you must too. Bernstein's X25519 was designed so this validation is "automatic" — every 32-byte string is a valid X25519 input.
Don't reuse keys for non-ECDH purposes. Mixing ECDH and ECDSA on the same keypair is theoretically allowed but operationally fragile. Generate separate keypairs.
What this lesson asks of you
The playground walks you through paired DH exchanges (classic, ECDH, X25519), shows you the secret converging on both sides, and asks you to identify the conditions under which forward secrecy holds. The visualizer shows the paint-mixing analogy live — Alice's secret mixes in, Bob's secret mixes in, the eavesdropper has the public mixes but can't reach the shared shade.
Tools in the wild
4 tools- cliOpenSSLfree tier
`openssl pkey -text -in alice.pem` and friends. Inspect ECDH params with -text.
- serviceWireGuardfree tier
Modern VPN built around X25519 ECDH + ChaCha20-Poly1305. Cleanest production DH usage.
- librarylibsodiumfree tier
`crypto_kx_*` for key exchange. X25519 internally, with safe defaults.
- cliagefree tier
X25519 ECDH for file encryption. Tiny keys, modern primitives, good UX.