asymmetric · level 5

Diffie-Hellman

Two parties, one shared secret, zero transmissions of it.

170 XP

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:

  1. A bottle of identical public yellow paint (visible to Eve).
  2. 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 (a and b above are generated per-session, not per-server).
  • The session key depends on a and b, 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 KEXINIT flow.

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
  • OpenSSLfree tier

    `openssl pkey -text -in alice.pem` and friends. Inspect ECDH params with -text.

    cli
  • WireGuardfree tier

    Modern VPN built around X25519 ECDH + ChaCha20-Poly1305. Cleanest production DH usage.

    service
  • libsodiumfree tier

    `crypto_kx_*` for key exchange. X25519 internally, with safe defaults.

    library
  • agefree tier

    X25519 ECDH for file encryption. Tiny keys, modern primitives, good UX.

    cli