Diffie–Hellman, step by step — and the man in the middle it cannot stop
Two parties who have never met agree on a secret by shouting in public. Step through the whole exchange — the prime and generator, the private exponents, and the modular exponentiation done the way a real implementation does it: square-and-multiply, one bit of the secret exponent at a time, with the square, the optional multiply and the running value shown for every bit. You will see exactly what a passive eavesdropper gets, then play the active eavesdropper the protocol has no way to notice, and finish with the two ways a badly chosen group leaks the secret: brute force, and small subgroups.
Notation
| p | The prime modulus. All arithmetic happens modulo p, in the multiplicative group of the integers 1 … p−1. |
| g | The generator (or "base"). Public. Its powers generate a subgroup of the group modulo p. |
| a, b | The two parties' private exponents — the actual secrets. Chosen at random, never sent. |
| A = ga mod p | Alice's public value. She sends it in the clear. |
| B = gb mod p | Bob's public value. Sent in the clear. |
| S = gab mod p | The shared secret: Alice computes Ba, Bob computes Ab, both land on the same number. |
| q = (p−1)/2 | The subgroup order, when p is a safe prime. The group has order 2q = p−1. |
| ord(g) | The order of g: the smallest d > 0 with gd ≡ 1 (mod p). The subgroup ⟨g⟩ has exactly ord(g) elements. |
| DLP | The discrete logarithm problem: given g, p and ga mod p, recover a. |
| KDF | Key derivation function (here HKDF, RFC 5869). Turns the raw group element S into actual keys. |
| MITM | Man in the middle: an active attacker who relays and rewrites the exchange. |
Glossary — the ideas this page is built on
The discrete logarithm problem is the whole hardness assumption
Diffie–Hellman is only as strong as the difficulty of one problem: given g, p and y = ga mod p, find a. Exponentiation mod p is one-way in practice — anyone can compute ga quickly with square-and-multiply, and nobody knows how to invert it quickly for a well-chosen group. This is the discrete logarithm problem (DLP). Note the asymmetry that makes the exchange possible: the legitimate party knows a, so it can exponentiate; the eavesdropper must invert, the hard direction. The best known algorithm for a prime field is the number field sieve — sub-exponential, but still infeasible at 2048 bits: RFC 7919 rates the ffdhe2048 group at about 103 bits of symmetric-equivalent strength. DH group sizes must therefore be far larger than AES key sizes: the sizes buy equal effort, not equal bit counts.
Diffie–Hellman is a key exchange, not authentication
The protocol answers one question: can two parties who share no secret agree on one, over a channel anybody can listen to? It answers nothing about who is on the other end. There is no identity in the maths — every value Alice receives (p, g, the peer's public value) is a number any party could have produced. That is not an oversight; authentication was a separate problem in 1976 and still is. A key exchange that also proves identity is called an authenticated key exchange, built by bolting DH together with signatures (TLS 1.2's DHE-RSA) or a pre-shared secret (a PAKE such as SPAKE2, a TLS 1.3 PSK mode, or the Noise family). The DH computation is unchanged — it just gains a proof of who it is talking to, which is exactly the thing the MITM panel below shows is missing.
Forward secrecy — why the exponents are ephemeral
Each run throws its exponents away: a fresh a per session, computed, used, forgotten. That gives forward secrecy (RFC 7919 §8.8). If an attacker records a session today and steals the long-term key next year, the recorded session stays unreadable, because the only thing that could open it — the ephemeral a and b — no longer exists anywhere. The variant where a party reuses a fixed exponent is static DH, and it is exactly the case where small-subgroup attacks matter: a partial recovery of that fixed exponent keeps paying off. Hence the modern insistence on DHE/ECDHE — the E is for ephemeral.
Why the generator's order matters
g generates a subgroup ⟨g⟩ of size ord(g), and the shared secret can only ever be one of those ord(g) values. If ord(g) is small, the secret lives in a small set — no cryptanalysis required, an attacker just enumerates it. If ord(g) contains small prime factors, the DLP splits by the Chinese Remainder Theorem into easy small problems (Pohlig–Hellman): a 2048-bit modulus is worthless if p−1 is 2·3·5·7·…, a smooth number. That is why a good group uses p = 2q+1 with q prime and insists that g and every received public value lie in the order-q subgroup. The order is the size of the space an attacker has to search.
What a "safe prime" is — p = 2q + 1
A prime p is a safe prime when q = (p−1)/2 is also prime. The group modulo p is then cyclic of order p−1 = 2q, so the only subgroups have size 1, 2, q and 2q — no room for small subgroups beyond the trivial pair {1, p−1}. That is why the FFDHE groups of RFC 7919 are all safe primes: a peer can enforce "stay in the order-q subgroup" with one cheap test instead of factoring p−1 and checking every factor. It also lets the standard's "nothing up my sleeve" derivation (from π in RFC 3526, from e in RFC 7919) be verified by a client in milliseconds.
Small-subgroup confinement
An attacker who cannot solve the real DLP can bend the group instead. Send a public value Y of small order r (say r = 2, so Y = p−1): the victim computes S = Yb mod p, which can only be 1 or p−1 — one bit of b, revealed. Repeat with elements of order 3, 5, 7, … and the residues b mod 2, b mod 3, b mod 5 assemble by CRT into b itself. With ephemeral exponents this is a dead end (b is thrown away), which is why RFC 2785 analyses precisely when protection is needed: static keys, and any protocol where the attacker can test whether an attempted key was correct. The defences are cheap: require 1 < Y < p−1 (RFC 7919 §5.1), use a safe-prime group, stay inside the order-q subgroup (cofactor exponentiation), or hash the shared value into the key so a wrong small-subgroup result cannot be observed.
Where this is used today, and what replaced it
Finite-field DH with the RFC 7919 groups is DHE in TLS 1.2/1.3 and SSH (RFC 4419). Its elliptic-curve cousin, ECDHE, gives the same security with far shorter keys and is now the default in TLS 1.3, WireGuard and the Signal X3DH handshake — the maths differs (points on a curve instead of integers mod p) but the shape of the exchange below is identical. In every case the raw DH output is fed into a KDF and never used directly as a key, and in every case the exchange must be authenticated — by a certificate signature, a pre-shared key, or a pinned public key — or an attacker simply does what the MITM panel lets you do.
Live man-in-the-middle: play Eve
Nothing here is broken. Run two separate Diffie–Hellman exchanges — Alice↔Eve and Eve↔Bob — and watch Eve end up holding two different secrets while Alice and Bob each believe they share one with the other. This is faithful to the protocol; every number below is computed live on this page. Change the inputs and press the button.
Brute force, and small subgroups
Two ways to break a group that are about the group, not about a clever attack on the mathematics. First: solve the discrete log by trying every exponent — trivial here, and hopeless on a real group. Second: make the victim compute a secret that lies in a tiny subgroup you chose.
1. Brute-force the discrete logarithm
2. Small-subgroup attack: force the secret into a tiny set
The attacker does not send a legitimate public value. It sends an element Y of small order r. The victim computes S = Yb mod p as usual — and S can now only be one of the r elements of that subgroup, so the attacker learns b mod r.
Try Y = 1 (order 1 — the secret is always 1), Y = 22 with p = 23 (order 2 — a single bit of b leaks), or Y = 2 with p = 23 (order 11 — only eleven possible secrets). The defences are the ones the FFDHE groups bake in: a safe prime, the range check 1 < Y < p−1, and confinement to the order-q subgroup.
3. Recover the whole secret on a smooth group (Pohlig–Hellman)
When p−1 is smooth — a product of small prime powers — the DLP breaks into one tiny problem per prime power, and the answers recombine by the Chinese Remainder Theorem. Below, every small subgroup is attacked by brute force and the bits of b fall out one chunk at a time. The default p = 65537 is a Fermat prime with p−1 = 216: maximally smooth, the worst possible group.