Nobody Broke the Discrete Log: A Field Guide to Diffie-Hellman and Forward Secrecy
The discrete log held for fifty years; Logjam, small-subgroup, and invalid-curve attacks broke the deployment instead. A field guide to FFDHE, ECDH, and X25519.
Permalink1. They Precomputed the Prime Once, and Read Everyone
In 2015 a team of researchers pointed out something that should have been impossible: millions of HTTPS, SSH, and VPN servers were sharing the same handful of 1024-bit Diffie-Hellman primes. That sharing did not merely weaken those servers -- it changed the economics of breaking them. The most expensive step of the best known algorithm for computing discrete logarithms depends only on the prime, not on any particular connection, so an adversary who could afford the precomputation for one common prime could then decrypt, passively and forever, every connection that used it [1].
Breaking the single most common 1024-bit group would have exposed an estimated 18% of the top million HTTPS sites, and a second common prime an estimated 66% of VPNs [1]. Nobody had to break the math. They precomputed the prime once and read everyone.
Here is the part that should unsettle you. The people running those servers were not careless. They believed they were doing the one thing everyone agrees you must do: generate a brand-new key for every connection. The researchers put it plainly -- practitioners "believed this was safe as long as new key exchange messages were generated for every connection" -- and they were wrong, because a shared prime and a fresh per-connection secret are two different conditions, and only one of them was being met [1].
The attack, named Logjam, actually had a second half: an active attacker could force a browser and server to negotiate a deliberately breakable 512-bit "export-grade" group, so that even a modest adversary could solve the discrete log in real time [1]. Two coupled failures -- a downgrade and a precomputation -- and neither one touched the underlying hardness assumption. That active downgrade even had an exact RSA twin the same year, FREAK, which forced a browser down to breakable 512-bit "export" RSA instead of Diffie-Hellman [2]. The paper's title says the whole thing in two words: Imperfect Forward Secrecy.
The math held; the deployment handed the secret away.
This article is about that gap, and about closing it. The security a system actually gets from Diffie-Hellman is never simply "the discrete logarithm is hard." It is a stack of four conditions the caller must independently satisfy -- a genuinely fresh ephemeral secret, a strong and unshared group, a validated peer value, and an authenticated exchange -- plus one discipline underneath them: the raw output is a group element, not a key, so it must pass through a key derivation function.
Every world-scale Diffie-Hellman break you half-remember -- Logjam, small-subgroup key recovery, invalid-curve attacks on TLS, Raccoon, the anonymous man-in-the-middle -- is exactly one of those conditions quietly violated. The same unbroken math spawned a whole catalog of disasters, and the fifty-year history of the primitive is one sustained effort to pull those conditions back inside the primitive, the named group, and the protocol, so that a caller cannot forget them.
2. What Diffie-Hellman Actually Is, and the Four Things You Must Supply
Two strangers who have never met, standing on either side of a wire the whole world can read, end up sharing a secret that no eavesdropper can compute. That is one of the deepest ideas in computer science, and it hides four decisions the scheme refuses to make for you.
The mechanism, with just enough math
Fix a public group with a generator . Alice picks a secret number and sends ; Bob picks a secret and sends . Each raises what they received to their own secret, and because exponents commute, they land on the same value:
An eavesdropper sees , , and , but never or . A student can implement the whole thing in three lines of modular exponentiation.
A protocol in which two parties exchange the public values and over an open channel and each computes the shared secret , which an eavesdropper who sees only , , and cannot compute.
// Toy Diffie-Hellman over a tiny prime field. Illustrative sizes, NOT secure.
const p = 2147483647n; // a small 31-bit prime; a real DH prime is >= 3072 bits
const g = 7n; // a public generator element
function powmod(base, exp, m){
let r = 1n; base %= m;
while (exp > 0n){ if (exp & 1n) r = (r * base) % m; base = (base * base) % m; exp >>= 1n; }
return r;
}
const a = 1234567n; // Alice's fresh ephemeral secret (never sent)
const b = 7654321n; // Bob's fresh ephemeral secret (never sent)
const A = powmod(g, a, p); // Alice sends this over the open wire
const B = powmod(g, b, p); // Bob sends this over the open wire
const secretAlice = powmod(B, a, p); // Alice computes (g^b)^a
const secretBob = powmod(A, b, p); // Bob computes (g^a)^b
console.log('Alice g^ab =', secretAlice.toString());
console.log('Bob g^ab =', secretBob.toString());
console.log('do they agree? ->', secretAlice === secretBob);
// The raw shared value is a group element, NOT a key. Real code never uses it
// directly: it feeds g^ab into a KDF (HKDF-SHA256) to extract a uniform key.
const toyKey = (secretAlice * 2654435761n) % (2n ** 32n); // stand-in for HKDF only
console.log('derived key (toy stand-in) =', toyKey.toString()); Press Run to execute.
Why it is hard to break
The security rests on the discrete logarithm problem: given and , recover the exponent . In a well-chosen group this is believed intractable.
Given a generator and an element in a large group, the problem of recovering the exponent . It is believed hard in carefully chosen groups, and it is the foundation Diffie-Hellman rests on.
But hardness of the discrete log is not quite the property we need. Recovering is the discrete-log problem; computing the shared secret from and without recovering either exponent is the Computational Diffie-Hellman (CDH) assumption, a slightly weaker thing to assume hard. Sharper still: "the shared secret is indistinguishable from a random group element" is the Decisional Diffie-Hellman (DDH) assumption, and it is strictly stronger than CDH [3]. The distance between "hard to compute" and "looks random" is exactly why the raw output of the exchange is not yet a key.
CDH is the assumption that computing from and is hard. DDH is the stronger assumption that cannot even be distinguished from a random group element. DDH can fail in groups where CDH still holds, which is why the raw shared value is not treated as a uniform key.
There are real groups -- pairing-friendly elliptic curves -- where DDH is easy even though CDH is plausibly hard, so is computable-hard yet distinguishable from random. This is why "looks random" is the decisional assumption, not the computational one. The one-line consequence, paid off in the forward-secrecy and theory sections, is that you must extract a key from the shared value with a KDF rather than use it raw. Keep that fact in your pocket; it becomes a theorem later.
The thesis, stated as the primitive's interface
Now look at what the exchange quietly assumes but never enforces. Raw finite-field Diffie-Hellman outsources all of the following to the caller, at once:
- Freshness. The secret must be genuinely fresh per session, then destroyed. This is what buys forward secrecy.
- A strong, unshared group. The group must be large enough and not shared in a way that lets an attacker amortize work across everyone who uses it.
- A validated peer value. The incoming must actually be a well-formed element of the right group, not a hostile input chosen to leak your secret.
- Authentication. Something must bind and to identities, or an active attacker sits in the middle.
And beneath all four, a fifth discipline: the raw is a structured group element, not a key, so it must go through a key derivation function before anyone calls it a key [4]. The original 1976 construction supplies none of these; it hands you a shared secret and trusts you to remember the rest [5].
Diagram source
flowchart TD
A["Alice: fresh secret a, send g^a"] --> S["Shared value g^(ab)"]
B["Bob: fresh secret b, send g^b"] --> S
S --> K["KDF turns g^(ab) into a real key"]
F1["1. Freshness: is a truly ephemeral and destroyed"] -.-> A
F2["2. Strong and unshared group"] -.-> S
F3["3. Validate the peer value g^b"] -.-> B
F4["4. Authenticate the exchange"] -.-> S This table is the entire article compressed into a grid: each row a condition, each column showing who historically lifted that burden off the caller. It fills in from left (the caller does everything by hand) to right (the primitive, the named group, or the protocol does it).
| Condition | What the caller must supply | Where it eventually gets pulled inside |
|---|---|---|
| 1. Freshness | A per-session ephemeral secret, destroyed after use | The protocol: TLS 1.3 mandates ephemeral (EC)DHE |
| 2. Strong, unshared group | A large prime or curve nobody can precompute against | The named group: RFC 7919 FFDHE safe primes; strong curves |
| 3. Validated peer value | An on-curve, correct-subgroup public value | The primitive: X25519 twist security; or a hand-written check for classic ECDH |
| 4. Authentication | A signature or MAC binding the transcript to identities | The protocol: STS/SIGMA, then TLS 1.3 |
| 5. KDF discipline | Extract a uniform key from the raw shared value | Every modern design: HKDF over the shared secret |
The security a system gets from Diffie-Hellman is never "the discrete log is hard." It is four conditions the caller must supply -- fresh, strong-and-unshared, validated, authenticated -- and then a KDF beneath them. Every named break in this article is exactly one of those quietly missing.
Three of those four conditions were not obvious in 1976. Each was discovered the hard way, by a named break. To understand why a fifty-year-old idea still hands them to you, go back to the moment key agreement was only an idea -- an idea that, it turns out, had already been discovered in secret.
3. Where This Came From: 1976, and the Secret That Came First
In 1976 two Stanford researchers opened a paper with a sentence that has aged remarkably well: "We stand today on the brink of a revolution in cryptography" [5]. Whitfield Diffie and Martin Hellman were right about the revolution. They were also, unknown to them, second.
The problem before the answer
Before public key agreement, two parties who wanted to talk securely had to already share a secret key, delivered out of band -- by a trusted courier, a locked briefcase, a prior meeting. That does not scale. A network of parties who each want to talk privately needs on the order of pairwise keys, every one of them distributed and stored in advance. The cost of moving keys, not the cost of encryption, is what throttled commercial cryptography for decades.
The public origin
Diffie and Hellman's "New Directions in Cryptography" broke the deadlock. Their key exchange let two parties agree on a shared secret over a channel an adversary could fully observe: fix a public generator over a finite field, keep the exponents private, exchange and , and let both sides compute , with security resting on the discrete logarithm modulo a large prime [5]. The same paper credits Ralph Merkle's independent "puzzles" idea as a partial solution to the same problem, which is the seed of the argument -- pressed for years by Hellman -- that the scheme should be called Diffie-Hellman-Merkle [5], [6]. Merkle's own framing was that his construction "forces any enemy to expend an amount of work which increases as the square of the work required of the two communicants" [7].
Merkle conceived his puzzles around 1974 and saw a 1975 write-up rejected; the work was not published until 1978. Yet Diffie and Hellman's 1976 paper already cites it as "submitted to the CACM," which fixed the idea in the literature before its own publication -- part of why Hellman argued for the three-name credit [6].The classified precursor
Here is the part most textbooks skip. Inside the United Kingdom's signals-intelligence agency, GCHQ, and its information-security arm CESG, the same ideas had already arrived, and stayed locked in a safe. James Ellis conceived what he called "non-secret encryption" at the end of the 1960s -- the recipient, he realized, only needs to be in a different position from the interceptor, not a secret one [8]. Clifford Cocks worked out the trapdoor that we would now recognize as RSA in 1973, and Malcolm Williamson derived the discrete-exponentiation key exchange -- Diffie-Hellman in substance -- in 1974 [9]. All of it remained classified until December 1997 [9].
The counterfactual sharpens the whole thesis of this article. The idea of public key agreement existed inside a government agency and changed nothing about how the world built systems, because it was never published, never standardized, never turned into a named group or a protocol anyone could deploy. An idea that no caller can reach protects no one.
The GCHQ dates are genuinely disputed. Ellis's own account traces the origin only to "the 60's"; the widely repeated "1970" is the date of his internal report. The primary CESG and gchq.gov.uk pages that once hosted this history are gone, so the record here rests on the archived Ellis document plus corroborating biography, not on any live government URL [8], [9].Diagram source
timeline
title From a secret idea to a mandated protocol
1969 : Ellis conceives non-secret encryption inside GCHQ
1974 : Williamson derives DH-style key exchange, kept classified
1976 : Diffie and Hellman publish New Directions, finite-field DH
1985 : Miller proposes elliptic-curve cryptography (Koblitz independently, published 1987)
1992 : Station-to-Station signs the DH values for authentication
2006 : Bernstein publishes Curve25519 and the X25519 function
2015 : Logjam shows shared 1024-bit primes are a mass liability
2018 : TLS 1.3 mandates ephemeral key exchange, removes static DH
2024 : X25519MLKEM768 hybrid ships by default in OpenSSH 9.9 Diffie and Hellman had shown that two strangers could agree on a secret against an eavesdropper who only listens. They said almost nothing about an adversary who acts. That omission is not a flaw in the paper; it is the second of the four conditions, and closing it took another two decades.
4. The Anonymous-DH Gap: Why Raw DH Is Only Half a Protocol
Raw ephemeral Diffie-Hellman defeats a passive eavesdropper completely, and an active attacker not at all. The whole difference is one adversary who can inject packets, and it is the entire reason "just do a DH exchange" is never the whole answer.
The man-in-the-middle, mechanically
Suppose an attacker sits on the wire and can not only read but rewrite. When Alice sends toward Bob, the attacker keeps it and sends its own onward. When Bob replies with , the attacker keeps that too and sends back to Alice. Now the attacker shares one secret, , with Alice, and a different secret, , with Bob. It decrypts whatever Alice sends, reads or modifies it, re-encrypts under the Bob-side secret, and forwards. Neither party sees anything wrong: both completed a textbook-perfect exchange. The primitive simply never promised anything about who is on the other end.
Diagram source
sequenceDiagram
participant A as Alice
participant M as Attacker
participant B as Bob
A->>M: sends g^a intended for Bob
M->>B: substitutes g^m
B->>M: sends g^b intended for Alice
M->>A: substitutes g^m
Note over A,M: Alice agrees on secret g^am with the attacker
Note over M,B: Bob agrees on a different secret g^bm
Note over M: Attacker decrypts, reads, re-encrypts both directions This is a design gap, not an implementation bug. It falls to an active adversary specifically; a passive listener still learns nothing.
// Toy anonymous-DH MITM. Illustrative sizes, NOT secure.
const p = 2147483647n, g = 7n;
function powmod(base, exp, m){
let r = 1n; base %= m;
while (exp > 0n){ if (exp & 1n) r = (r*base) % m; base = (base*base) % m; exp >>= 1n; }
return r;
}
const a = 111111n, b = 999999n; // honest ephemeral secrets
const A = powmod(g, a, p); // Alice's message (intercepted)
const B = powmod(g, b, p); // Bob's message (intercepted)
const m = 424242n; // the attacker's own secret
const M = powmod(g, m, p); // the attacker forwards THIS to both sides
const aliceSide = powmod(M, a, p); // Alice computes (g^m)^a
const attackerA = powmod(A, m, p); // attacker computes (g^a)^m -> equal
const bobSide = powmod(M, b, p); // Bob computes (g^m)^b
const attackerB = powmod(B, m, p); // attacker computes (g^b)^m -> equal
console.log('Alice-attacker secret agrees?', aliceSide === attackerA);
console.log('Bob-attacker secret agrees? ', bobSide === attackerB);
console.log('Do Alice and Bob share a key?', aliceSide === bobSide); // false
// The attacker now holds both session secrets and relays, reading everything. Press Run to execute.
A key-agreement protocol that also binds the exchanged public values to identities, so that an active attacker cannot substitute its own values undetected. It is what raw Diffie-Hellman is missing.
The precision that carries the rest of the article
Authentication defeats the active adversary, and only the active adversary. It is orthogonal to the hardness of the group and to input validation. The sections ahead each violate a different condition, and it is tempting but wrong to think a signature would have saved them.
The fix, historically
Naming the goal came first. Christoph Guenther's 1989 identity-based key-exchange work is where the term perfect forward secrecy is generally said to first appear, attached to the goal that a compromised long-term key should not expose past sessions [10]. Diffie, van Oorschot, and Wiener turned it into a concrete protocol in 1992 with Station-to-Station, which authenticates the exchange by having each side sign the Diffie-Hellman values, and which gave the standard, most-cited formal treatment of the property [11]. A decade later Hugo Krawczyk distilled the pattern into SIGMA, the "SIGn-and-MAc" template that underlies IKE and, in spirit, the TLS 1.3 handshake; the SIGMA protocols "provide perfect forward secrecy" via a Diffie-Hellman exchange authenticated with digital signatures [12]. The authenticated-DH family is treated as standard material in the reference texts [3], [13].
The coinage is disputed and worth stating carefully: the term "perfect forward secrecy" traces to Guenther (1989/1990), while Diffie, van Oorschot, and Wiener (1992) gave the standard, most-cited formal treatment via Station-to-Station. Present both and pick neither silently [10], [11].Signing the DH values closes the man-in-the-middle. It does nothing for the group the exchange runs in -- and the original group, the multiplicative integers modulo a prime, turned out to hand the caller two more conditions at once, plus a nation-state-scale way to violate them.
5. Generation 1: Finite-Field DH and the Tyranny of the Shared Prime
Here is the mechanism underneath Logjam, the small-subgroup CVEs, and those 3072-bit key sizes you have wondered about -- and here is why the original finite-field group hands the caller more traps than any generation since.
Finite-field DH, mechanically
Pick a large prime , ideally a safe prime of the form with also prime, and a generator of the large prime-order subgroup. Alice and Bob exchange and and compute . The safe-prime shape is not cosmetic: it forces the cofactor down to 2, leaving only the trivial order-two subgroup (the values and ). So a small-subgroup attack can pin a key to at most one bit, which is exactly why an implementation must still reject , , and -- a point that becomes concrete below.
A prime of the form where is also prime. The multiplicative group modulo a safe prime has one large prime-order subgroup of order and only the trivial order-two subgroup (the values and ), a cofactor of 2. That confines any small-subgroup attack to at most one bit, which is why implementations still reject , , and .
The cost that shaped everything
Finite-field discrete logs have a sub-exponential attack. Index calculus, and its modern form the number field sieve (NFS), runs in heuristic complexity
far faster than brute force. The practical consequence: to reach roughly 128-bit security you need a 3072-bit prime, an order of magnitude larger than the 256-bit curve that buys the same margin [14]. That size penalty is the whole reason finite-field DH lost.
The best known sub-exponential algorithm for factoring and for finite-field discrete logarithms. Its most expensive stage depends only on the modulus (the prime), not on any individual target, which is why a shared prime is a shared weakness.
Logjam, in full
That "depends only on the prime" clause is the entire disaster. The NFS's expensive first step "is dependent only on this prime," so if millions of hosts share a handful of 1024-bit primes -- and in 2015 they did -- an adversary precomputes once and then decrypts every connection on that prime cheaply, passively, and indefinitely [1]. The Logjam researchers estimated that breaking the single most common 1024-bit prime would expose about 18% of the top million HTTPS sites, and a second common prime about 66% of VPNs and 26% of SSH servers [1]. And there was an active half: an attacker could downgrade a TLS handshake to 512-bit DHE_EXPORT parameters, which were breakable in near-real time, with 8.4% of the top million initially vulnerable [1].
The paper priced that precomputation directly, and the numbers are the point. Breaking a 512-bit export prime was demonstrated, not merely estimated: about 89,000 core-hours of work -- "slightly over one week" of wall-clock time spread across a couple of thousand cores -- after which each individual discrete log took a median of 70 seconds [1]. A 768-bit prime was an academic reach at roughly 36,500 core-years. And a 1024-bit prime was a nation-state one at roughly 45 million core-years of one-time precomputation, a figure the authors judged "not necessarily out of reach for a nation state" [1]. Every number there is a fixed, per-prime cost that reading one more connection barely adds to.
Diagram source
flowchart TD
P["One shared 1024-bit prime"] --> NFS["NFS first step: precompute per prime, about 45M core-years at 1024-bit, done once"]
NFS --> DB["Reusable precomputation for that prime"]
DB --> D["Per-connection descent: about 70 s at 512-bit"]
D --> R["Passively decrypt every session on that prime"]
P -.-> X["Active downgrade to 512-bit export group"]
X --> R Notice what never happens in that diagram: nobody solves a fresh discrete logarithm. The costly work is done once, against the prime, and amortized across everyone who shares it.
Practitioners "believed this was safe as long as new key exchange messages were generated for every connection" -- and were wrong.
They were wrong because a fresh per-connection secret satisfies the freshness condition while doing nothing about the unshared-group condition. Both had to hold; only one did [1].
The attacker never computed a fresh discrete logarithm. They precomputed the shared prime once and read everyone on it. The math held; the deployment handed the secret away.
The RSA twin, in one breath
Logjam did not travel alone. The same year, and the same relic of 1990s U.S. export-crypto policy, produced its RSA counterpart: FREAK, for "Factoring RSA Export Keys," publicly disclosed on 3 March 2015 by Karthikeyan Bhargavan and the miTLS team, with the client-side flaw tracked as CVE-2015-0204 [2]. A man-in-the-middle downgrades a client to 512-bit export RSA, factors that small modulus cheaply on rented hardware, and reads the session. At disclosure 36.7% of HTTPS servers with browser-trusted certificates still accepted export RSA -- falling to 6.5% as patches landed -- along with 8.5% of the Alexa Top-1M [2].
FREAK downgrades RSA key transport and is fundamentally an implementation bug, a client-side state-machine slip that accepts a key it never asked for; Logjam downgrades Diffie-Hellman parameters and is, in the weakdh.org authors' own words, "due to a flaw in the TLS protocol rather than an implementation vulnerability" [1]. RSA key transport is the RSA installment's territory, so FREAK stays a mention -- but it is proof that the export-downgrade wound had two matching edges, one for each key-exchange family.
Two precisions to keep straight. First, Logjam is two coupled failures, not one: the 512-bit export downgrade and the shared-prime precomputation. Second, FREAK is its RSA sibling, not the same attack -- both weaponize leftover 512-bit export keys, but FREAK is an implementation bug in RSA key transport while Logjam is a protocol flaw in Diffie-Hellman parameter negotiation [1], [2].Small-subgroup confinement
The safe-prime rule has teeth. If is not a safe prime, the group has small subgroups, and an attacker can weaponize them. The attacker sends a carefully chosen element of small order as its "public key." The victim raises it to its private exponent , producing an element that depends only on , and the attacker -- watching whether the handshake succeeds, or reading the resulting ciphertext -- recovers by trying the possibilities. Repeat across several small factors and combine with the Chinese Remainder Theorem, and the private exponent falls out.
This is the small-subgroup confinement attack of Lim and Lee (CRYPTO '97), and it works only when the victim reuses the private exponent long enough to be probed [15], [16]. It went live as CVE-2016-0701, where OpenSSL failed to run the subgroup check on X9.42-style (RFC 5114) non-safe-prime parameters while reusing a DH key, letting an attacker "discover a private DH exponent" [17], [18].
An attack that sends a small-order group element as a public key so that the victim's response depends only on the private exponent reduced modulo a small number. Repeating across several small factors and applying the Chinese Remainder Theorem recovers a reused private exponent.
// A NON-safe prime: p is prime but p-1 = 2^3 * 13 * 19 * 53. Insecure sizes.
const p = 104729n;
const g = 12n; // a generator of the full multiplicative group
function powmod(b, e, m){ let r = 1n; b %= m; while (e > 0n){ if (e & 1n) r = (r*b)%m; b = (b*b)%m; e >>= 1n; } return r; }
const x = 8675n; // Bob's REUSED private exponent -- the fatal choice
const smalls = [13n, 19n, 53n]; // small prime factors of p-1
const residues = [];
for (const r of smalls){
const h = powmod(g, (p - 1n) / r, p); // an element of small order r
const target = powmod(h, x, p); // the victim returns h^x = h^(x mod r)
let e = 0n;
for (let k = 0n; k < r; k++){ if (powmod(h, k, p) === target){ e = k; break; } } // tiny search
residues.push([r, e]);
console.log('recovered x mod ' + r + ' = ' + e);
}
// Combine the residues with the Chinese Remainder Theorem:
let Mtot = 1n; for (const r of smalls) Mtot *= r;
let X = 0n;
for (const [r, e] of residues){
const Mi = Mtot / r; let inv = 1n;
for (let k = 1n; k < r; k++){ if ((Mi * k) % r === 1n){ inv = k; break; } }
X = (X + e * Mi * inv) % Mtot;
}
console.log('reconstructed private exponent x = ' + X + ' (true value ' + x + ')'); Press Run to execute.
The fix: take "which prime" away from the caller
The response was to stop letting callers choose primes. RFC 7919 defines a fixed menu of named FFDHE groups (ffdhe2048 through ffdhe8192), every one a safe prime, so a custom, shared, weak, or backdoored modulus is simply not expressible on the wire [19]. That is condition #2 pulled inside a named group.
Why take the choice away so completely? Because a caller-chosen modulus can fail in a way no inspection reveals. In early 2016 the socat networking tool was found to have shipped a hard-coded 1024-bit Diffie-Hellman p parameter that was not prime at all, making a key exchange weaker than one over a genuine prime [20]. The advisory (MSVR-1499) is worth quoting for its candor: because "there is no indication of how these parameters were chosen, the existence of a trapdoor ... cannot be ruled out" [20].
The repair generated a fresh 2048-bit prime with OpenSSL's dhparam, and the flaw was reported by Santiago Zanella-Beguelin -- a Logjam co-author -- together with Microsoft Vulnerability Research [20]. A bring-your-own modulus can be weak or backdoored, and you cannot tell which by looking at it. That single degree of freedom is exactly what named safe-prime groups take away.
Two conditions leaked at once here: the group was neither strong-per-bit nor unshared, and a reused key turned the exchange into an oracle. The obvious escape is a group where the discrete log is harder per bit and there is no cheap shared-prime precomputation. That group is an elliptic curve -- and it introduced a sharper oracle of its own.
6. Generation 2: Elliptic-Curve DH, and the New Oracle
Move the same Diffie-Hellman protocol onto an elliptic curve and the number field sieve vanishes. A 256-bit key now buys what a 3072-bit prime bought. But the smaller, structure-free group sharpened one of the four conditions into a live, key-recovering oracle.
ECDH, mechanically
The curve internals belong to the elliptic-curves installment of this series; here we only need the shape. Replace "exponentiate " with "scalar-multiply the base point ": a private key is a scalar , the public key is the point , and the shared secret is the point , from which both sides take the x-coordinate [22]. The payoff is in the attack cost. On a strong curve the best known attack is the generic Pollard rho algorithm, running in about steps with no sub-exponential index calculus available, so a 256-bit curve delivers roughly 128-bit security -- concretely, rho against P-256 costs about operations [23]. Victor Miller and Neal Koblitz independently proposed elliptic-curve cryptography in the mid-1980s for exactly this reason [24], [25].
The new failure class
The smaller group came with a sharper edge. The peer's point is attacker-controlled, and the standard scalar-multiplication formulas "do not involve the constant coefficient " of the curve equation [16]. So if an attacker submits a point that satisfies everything except lying on the intended curve, the victim's arithmetic processes it without complaint -- effectively doing the computation on a different, weaker curve whose group has small subgroups. Scalar-multiplying such a point by a reused private key turns the operation into a key-extraction oracle, one small residue at a time, combined with the Chinese Remainder Theorem across many curves. This is the invalid-curve attack of Biehl, Meyer, and Mueller (CRYPTO 2000) [26], [16]. Jager, Schwenk, and Somorovsky made it practical against real TLS servers doing static or reused ECDH at ESORICS 2015, recovering the server's private key [27].
An attack that submits a point lying on a different, weaker curve than the intended one. Because scalar-multiplication formulas ignore the curve's constant term, the victim computes on the wrong curve and, if it reuses its private key, leaks that key one small residue at a time.
Diagram source
flowchart TD
A["Attacker sends a point not on the intended curve"] --> B["Scalar multiply ignores the curve constant b"]
B --> C["Computation lands in a weak subgroup of another curve"]
C --> D["Reused key leaks d modulo a small order"]
D --> E["Repeat across many curves, then CRT to the full key d"] The lesson, made mandatory
For classic cofactor ECDH the caller must validate every incoming point: it has to lie on the intended curve and in the correct prime-order subgroup. NIST SP 800-56A codifies this as full or partial public-key validation [22]. It is an explicit, easy-to-forget check the smaller group pushed back onto the implementer -- exactly the caller-facing condition this article tracks.
So ECDH shrank the key but still demanded a hand-written on-curve check and constant-time code that an implementer could botch. The next move makes both of those unnecessary by construction -- a curve where every 32-byte string is a valid input and the arithmetic is constant-time by design.
7. Generation 3: X25519, or "Make the Simple Implementation the Safe One"
Everything classic ECDH asked the caller to remember -- validate the point, write the arithmetic in constant time -- one curve was designed to make impossible to get wrong. That is the whole point of X25519.
What X25519 is
X25519 is the Diffie-Hellman function over Curve25519, a Montgomery curve over the prime field , computing x-coordinate-only DH with a uniform Montgomery ladder. Daniel Bernstein introduced it in 2006, and it was standardized as RFC 7748 in 2016 [29], [30]. Two design decisions are the entire construction, and each one pulls a caller-facing condition inside the primitive.
A scalar-multiplication algorithm that processes the secret scalar in a fixed sequence of steps, performing the same field operations at every bit regardless of the secret's value. It computes only the x-coordinate of the result and has no secret-dependent branches.
Constant-time by construction
The ladder is a fixed sequence of roughly 255 iterations, each doing the same field operations no matter what the secret bits are: no secret-dependent branch, no secret-indexed table lookup [30]. The timing side channel that a hand-written P-256 ladder can leak simply has no place to live. The primitive absorbed the constant-time requirement so the implementer cannot forget it.
Twist-security
The second decision is subtler and more beautiful. Curve25519 and its quadratic twist both have near-prime order, so an attacker who submits an off-curve x-coordinate does not land in a weak subgroup -- it lands in another strong group where there is nothing to confine a key into. The invalid-curve attack of Section 6 finds no purchase, and it needs no explicit on-curve check to stop it. That is why "the Curve25519 function was carefully designed to allow all 32-byte strings as Diffie-Hellman public keys," and why Bernstein's guidance on validating public keys is, bluntly, "Don't" [29], [16].
A property of a curve whose quadratic twist also has near-prime order. An attacker who supplies an off-curve x-coordinate is forced into another strong group instead of a weak subgroup, so invalid-curve attacks fail without any explicit point validation.
Diagram source
flowchart TD
In["Any 32-byte peer value, trusted or hostile"] --> L["Montgomery ladder: same steps for every secret bit"]
L --> Tw["An off-curve input lands on the twist, another strong group"]
Tw --> Out["No weak subgroup to confine a key: validation is structural"]
L --> Ct["No secret-dependent branch or lookup: the timing channel is closed"] The two rules that trip people up
Two details cause most X25519 confusion.
First, clamping. Before use, the secret scalar is masked with k[0] &= 248; k[31] &= 127; k[31] |= 64 [30]. Clamping constrains your own scalar so your side avoids cofactor and small-order pitfalls. It is emphatically not input validation, and it does nothing to a malicious peer value. X25519 is safe against hostile inputs because of twist security, not because of clamping.
The bit-masking applied to an X25519 secret scalar before use, which fixes certain high and low bits. It shapes your own scalar to sidestep cofactor issues, and it performs no check whatsoever on the peer's public value.
Second, the cofactor-8 all-zero check. Curve25519 has cofactor 8, so a low-order peer input can drive the shared output to the all-zero value. RFC 7748 says implementations "MAY check ... whether the resulting shared secret is the all-zero value and abort if so" -- a MAY, tied to the nuance of contributory behavior, not a MUST [30]. Most protocols do not need it because they mix identities and transcripts into the KDF anyway.
A precision that catches even careful readers: X25519 and its larger sibling X448 are the Montgomery, x-coordinate-only variant of elliptic-curve DH, not the SP 800-56A cofactor ECDH of Section 6, even though "ECDH" is the umbrella term for both. X448 targets a more conservative security level near 224 bits and is defined in the same RFC 7748 [30].Even here the KDF discipline is stated in the primitive's own voice: "Both of you can then hash this shared secret and use the result as a key," never the raw x-coordinate [29].
X25519 makes the primitive misuse-resistant: validated by construction, constant-time by design. It says nothing about the other two conditions -- freshness and authentication -- which remain the protocol's to enforce. And the first of those, freshness, is the exact hinge on which the property everyone wants from Diffie-Hellman either holds or silently evaporates.
8. The Breakthrough: Ephemeral Exchange and (Imperfect) Forward Secrecy
This is the property that makes people say "use DHE, not static," and the one most often stated as if it were automatic. It is not. It is a conjunction, and the failure catalog is its list of unmet conjuncts.
Forward secrecy, stated conditionally
If the Diffie-Hellman secret is freshly generated per session, never reused, destroyed after use, and run over a strong unshared group, then later compromise of the long-term authentication key cannot recover past session keys -- because the ephemeral secret those keys were derived from no longer exists. The long-term key's only job is to authenticate the exchange; the session's confidentiality dies with the ephemeral. Every word of that conditional is load-bearing. Drop "never reused" and you get a static key in disguise; drop "strong unshared group" and Logjam reads you anyway.
The guarantee that compromising a long-term key does not expose past session keys. It holds only when the per-session ephemeral secret was freshly generated, never reused, destroyed after use, and run over a strong, unshared group. It is a conjunction of those conditions, not an automatic consequence of using DH.
An ephemeral DH key is generated fresh for a single session and discarded immediately after. A static key is long-lived and reused across sessions, providing no forward secrecy. A cached or reused "ephemeral" is, for security purposes, a static key.
Diagram source
flowchart TD
E["Fresh ephemeral secret, used then destroyed"] --> H["Handshake, DH values signed by the long-term key"]
LT["Long-term key authenticates only"] --> H
H --> K["Session key derived from the ephemeral"]
K --> D["Ephemeral destroyed when the session ends"]
C["Attacker later steals the long-term key"] --> P["Past sessions stay secret: the ephemeral is gone"]
ST["Static DH: one reused key does everything"] --> O["Its compromise opens every past session at once"] | Mode | Fresh per session? | Forward secrecy |
|---|---|---|
| Static DH | No -- one long-lived key | None; a single key compromise opens all past sessions |
| Reused or cached "ephemeral" | No -- shared across sessions | Collapses for every session that shares it; also enables Raccoon |
| True ephemeral | Yes -- destroyed after use | Yes, conditional on a strong unshared group |
The honest other half: how the property fails
The conditional fails in three documented ways, and each is a row in the catalog.
Ephemeral-key reuse or caching. Sold as a performance optimization, reusing an ephemeral turns it into a static key and collapses forward secrecy for every session that shares it. Worse, reuse is the precondition for Raccoon (CVE-2020-1968): in TLS 1.2 and earlier, the premaster secret had its leading zero bytes stripped before the key derivation step, so the exact timing of the KDF leaked the most significant bits of the shared secret, recoverable as an instance of the Hidden Number Problem -- but only when the server reused its DH key long enough to gather timing samples [31].
Static DH. No ephemeral at all, so no forward secrecy at all -- the baseline failure that TLS 1.3 later abolished [32].
The Raccoon authors are blunt about the root condition: reusing a DH key "was already considered bad practice before the attack, as it does not provide forward secrecy" [31]. The timing oracle was a second penalty on top of the forward-secrecy loss.The discipline beneath it all
A Diffie-Hellman value "is NOT a uniformly random or pseudorandom string." -- RFC 5869, Section 3.3
Freshness, a strong group, validated inputs, authentication, and a KDF -- five things, and the caller was trusted to remember all of them. The last move in this story stops trusting the caller: it bakes the conditions into named objects and a protocol that refuses to run without them.
9. State of the Art (2024-2026): X25519 Everywhere, and the Quantum Clock
Here is what a practitioner actually deploys today -- and the single overlay that reframes all of it: every discrete-log DH in this article has an expiration date, and a well-funded adversary can start the clock by simply recording your traffic now.
The deployed reality
X25519 is the dominant key agreement across TLS 1.3, OpenSSH, WireGuard, and Signal, and the word "dominant" now rests on measured deployment rather than defaults alone. Cloudflare, which terminates a large fraction of the world's HTTPS and can therefore see it, reports that X25519 "now secures the vast majority of all Internet connections" [33]; OpenSSH has shipped Curve25519 key exchange as "the default when both the client and server support it" since version 6.5, back in 2014 [34], [29]; and WireGuard and Signal are built on the same primitive [35], [36]. Finite-field DH survives mainly as deprecated-but-present legacy in older TLS and IPsec [1], [32].
TLS 1.3 turned this whole article's argument into a requirement: RFC 8446 removed static RSA and static Diffie-Hellman key transport, requires ephemeral (EC)DHE, and restricts negotiation to named groups. The specification states the result directly: "Static RSA and Diffie-Hellman cipher suites have been removed; all public-key based key exchange mechanisms now provide forward secrecy" [32]. That is conditions #1, #2, and #4 -- freshness, a vetted group, and authentication -- pulled inside the protocol as non-optional. The fifty-year move is complete: the four conditions now live in the primitive, the named group, and the protocol, not in the caller's memory.
The quantum overlay
There is one problem no key size solves. All discrete-log Diffie-Hellman, finite-field and elliptic-curve alike, falls in polynomial time to Shor's algorithm on a sufficiently large quantum computer, and "harvest-now, decrypt-later" means an adversary can record your ECDH traffic today and decrypt it once such a machine exists [37]. So the field is wrapping ECDH rather than replacing it.
An attack strategy in which an adversary records encrypted traffic today and stores it, intending to decrypt it later once a cryptographically relevant quantum computer exists. It makes today's ECDH confidentiality a future liability.
The wrapper is X25519MLKEM768: it runs an X25519 exchange and an ML-KEM-768 key encapsulation in parallel and feeds both shared secrets into the KDF, so the session stays secure as long as either problem remains hard [38], [39]. Concretely, the client key share is 1216 bytes (1184 for ML-KEM plus 32 for X25519), the server share is 1120 bytes, the negotiated combined secret is 64 bytes, and the TLS codepoint is 0x11EC [38].
A public-key primitive in which a sender encapsulates a fresh random key to a recipient's public key, producing a ciphertext the recipient decapsulates. Unlike Diffie-Hellman, a KEM is inherently interactive and has no non-interactive agreement between two static public keys.
The transition was fast. OpenSSH 9.9, released on 2024-09-19, enabled mlkem768x25519-sha256 by default [40]. OpenSSL 3.5, a long-term-support release from April 2025, ships a DEFAULT TLS group list that "selects X25519MLKEM768 as one of the predicted keyshares" [41], [42]. Chrome and Cloudflare deployed post-quantum key agreement across their edges [33].
Two precisions that keep the story honest
First, a KEM is not a drop-in Diffie-Hellman. It has no non-interactive static-static agreement, so X25519MLKEM768 is a hybrid KEM handshake, not "post-quantum Diffie-Hellman." Second, in messaging, Signal's PQXDH (2023) is the analog: it adds an ML-KEM encapsulation to the older X3DH design but "still relies on the hardness of the discrete log problem for mutual authentication in this revision," so X25519 is retained, not retired [36], [43]. Signal described it as "an upgrade to the X3DH specification which we are calling PQXDH" [44].
X25519 is the default, TLS 1.3 made the discipline mandatory, and the hybrid buys time against Shor. So which primitive, with which parameters, in which case? That is a decision matrix -- and it is not a single winner.
10. Competing Approaches: The Decision Matrix and the AKE Families
There is no single best key-agreement primitive. The state of the art is a small set of deployed constructions, each dominant in a niche, wrapped by a small set of authentication families. The honest answer is "it depends."
The primitive decision matrix
| Primitive | Best classical attack | Classical security | Post-quantum | Public value size | Peer-input validation | Best suited for |
|---|---|---|---|---|---|---|
| FFDHE-3072 | Sub-exponential NFS | about 128-bit | No | about 384 bytes | Subgroup check on a safe prime | Legacy FIPS or interop only |
| ECDH P-256 | Generic rho, about | about 128-bit | No | 65 bytes | Hand-written on-curve and subgroup MUST | FIPS, HSM, existing deployments |
| X25519 | Generic rho, about | about 128-bit | No | 32 bytes | Structural, via twist security | New classical designs |
| X25519MLKEM768 | Generic rho plus lattice attacks on ML-KEM | about 128-bit and post-quantum | Yes, if either half holds | about 1.2 KB (client share) | X25519 structural plus KEM ciphertext check | The post-quantum default going forward |
Two rows carry the weight. "Best classical attack" separates FFDHE's sub-exponential tax from the curves' generic-only attack, which is why a curve needs a quarter of the bits [23], [14]. "Peer-input validation" separates a hand-written MUST for P-256 and a subgroup check for FFDHE from the structurally-unnecessary case of X25519 [22], [30]. The head-to-head rules fall out cleanly: X25519 for new designs; ECDHE with P-256 or FFDHE at 3072 bits and up only where FIPS or interop compels it; and ephemeral, always, for forward secrecy.
The authentication families
A primitive is only half a deployment; something must authenticate the exchange. Four families cover essentially all real usage.
| Family | How DH is authenticated | Deployed in | Trade-off |
|---|---|---|---|
| Signed-DH | Each side signs the transcript or the DH values | TLS 1.3, IKEv2, STS and SIGMA | Simple and well understood; needs a PKI or pinned keys |
| Implicitly-authenticated DH | Long-term and ephemeral keys combine inside the shared-secret formula | MQV and HMQV, parts of IKE | Compact, no signatures; subtle to get exactly right |
| DH-pattern frameworks | Compose several static and ephemeral DH operations | Noise, WireGuard's Noise_IK | Mutual auth plus identity hiding plus KCI resistance |
| Asynchronous AKE | Prekeys let a recipient be offline | Signal's X3DH and PQXDH | Deniable and offline-friendly; more moving parts |
Signed-DH dominates the open web through TLS 1.3, whose handshake is SIGMA in spirit [32], [12]. WireGuard composes several X25519 operations in the Noise_IK pattern to get mutual authentication, forward secrecy, key-compromise-impersonation resistance, and identity hiding at once [35]. Signal's X3DH and its post-quantum successor PQXDH deliver asynchronous, deniable key agreement to an offline recipient [43], [36].
The composition hazard
One more lesson hides in those families, and it is easy to miss: sub-protocols each proven secure in isolation can still compose into an insecure whole. The Triple Handshake attack (Bhargavan, Delignat-Lavaud, Fournet, Pironti, and Strub, IEEE Symposium on Security and Privacy 2014) chained a TLS key exchange with session resumption and renegotiation so that two distinct connections could be driven to share the same master secret, which broke client authentication and defeated the countermeasures of the day [45].
Paired with key-compromise impersonation -- where stealing your long-term key lets an attacker impersonate others to you -- it is the standing reminder that a secure handshake is not simply the sum of secure steps. It is also the concrete reason TLS 1.3 binds the entire transcript: the Finished MAC covers the whole handshake, so two runs that differ anywhere cannot be coerced into agreeing [32].
Signposts, not sections
Several approaches deserve a pointer rather than a treatment. MQV and HMQV are the implicitly-authenticated DH family, with narrow deployment in some IKE and Suite B profiles [22]. DH-based PAKEs and OPRFs -- CPace, OPAQUE, SPAKE2 -- use Diffie-Hellman for password-authenticated and oblivious key agreement and are the subject of the hash-to-curve and PAKE installment of this series. CSIDH is the isogeny-based candidate for a genuinely post-quantum Diffie-Hellman analog, and its cautionary neighbor SIDH/SIKE was broken outright in 2022 [46]. ElGamal is DH's public-key encryption cousin, not a key-agreement scheme. And X448 is the conservative higher-security sibling of X25519. Each is one sentence here by design.
Every row of those tables assumes the underlying hardness holds. That assumption has a precise shape on each track -- and one hard ceiling above both.
11. Theoretical Limits: Where the Hardness Lives, and Its Ceiling
Here is what a first course skips: the two Diffie-Hellman tracks do not have the same kind of hardness, the raw shared secret is provably not a good key, and every scheme in this article dies to the same quantum algorithm.
Finite-field DLP is sub-exponential
The general number field sieve solves finite-field discrete logs in , and its first step depends only on the prime -- the engine behind Logjam. Because the attack is sub-exponential rather than exponential, key sizes must grow quickly to stay ahead: 3072 bits for 128-bit security, 7680 for 192-bit [1], [14]. The Logjam paper's own field estimates trace that curve concretely: a 512-bit prime falls in near-real time after a one-week, 89,000-core-hour precomputation (a median of 70 seconds per subsequent log); a 768-bit prime is an academic reach at about 36,500 core-years; and a shared 1024-bit prime is a standing nation-state liability at about 45 million core-years of one-time precomputation [1]. As an independent measured anchor one size down, the RSA-250 factoring record fell to the number field sieve in February 2020 at about 2,700 core-years [21].
Elliptic-curve DLP is generic square-root, and no better
For a strong curve the best known attack is generic Pollard rho at about operations, and -- this is the deep part -- that matches Shoup's generic lower bound of [47]. For a well-chosen curve the gap between the best attack and the proven floor is closed: Curve25519 sits at about and P-256 at about [23]. The only escapes are specific bad families -- pairing (MOV and Frey-Ruck) reductions and anomalous curves -- which is why rigidity and vetted parameters matter, and which the elliptic-curves installment of this series treats in full.
| Target security | Finite-field DH prime | Elliptic curve (rho cost) | Best classical attack |
|---|---|---|---|
| 128-bit | 3072-bit | Curve25519 about , P-256 about | Sub-exponential NFS vs generic rho |
| 192-bit | 7680-bit | P-384 about | Sub-exponential NFS vs generic rho |
The CDH-DDH gap is why the KDF is a theorem
The assumptions form a ladder, but mind the direction: an algorithm that solves the left solves the right, so DLP is at least as hard as CDH, which is at least as hard as DDH (read instead as hardness assumptions, the implication runs the other way, with DDH-hardness implying CDH-hardness implying DLP-hardness). That last gap is real and not merely conservative. On pairing-friendly curves, DDH is easy while CDH is plausibly still hard, so the raw can be distinguished from uniform even when it cannot be computed by an attacker [3]. That is precisely why RFC 5869 forbids skipping the extract step and insists the shared value be run through a KDF before use [4]. Condition #5 is not a style preference; it is a consequence of a proven gap between two assumptions.
Forward secrecy's precise limits
Stated formally, forward secrecy protects the past, not the present or future, and only under the conjunction from Section 8: the ephemeral must have been ephemeral and the group strong. Its unmet conjuncts are, exactly, the failure catalog.
The ceiling: Shor
Above both tracks sits one algorithm that does not care which group you chose. Shor's algorithm solves discrete logarithms -- finite-field and elliptic-curve -- in polynomial time on a sufficiently large quantum computer, and no key size escapes it [37]. This is the one break in the entire article that is a break of the math rather than a leaked condition.
Every other failure in this article was a leaked condition -- a shared prime, an unvalidated point, a reused ephemeral, a missing signature. Shor is the only one that breaks the math itself. That is why the answer to Shor is a new problem, ML-KEM wrapped around X25519, and not simply a bigger curve.
If the theory is this settled -- a closed generic gap on curves, a sub-exponential tax on fields, a proven quantum cliff above both -- then what is left is genuinely open. And what is open is not the math. It is the interface, again.
12. Open Problems: What Remains Genuinely Unsettled
The discrete log is not the frontier. The frontier is what "post-quantum Diffie-Hellman" even means, whether constant-time can ever be a settled property, and how much broken cryptography is still in the field.
A practical post-quantum NIKE
Diffie-Hellman is a non-interactive key exchange: two parties who have each merely published a static public key can agree on a shared secret with no further interaction, and that single property is what makes asynchronous and static-static designs -- X3DH, Noise's static-static patterns -- possible. A KEM cannot do this. It is inherently interactive: one side encapsulates to the other's key and returns a ciphertext, so there is no agreement between two published static keys [38].
A scheme in which two parties agree on a shared secret purely from each other's published static public keys, with no live message exchange. Diffie-Hellman is a NIKE; a KEM is not, which is why KEMs cannot directly replace DH in static-static and asynchronous designs.
The only serious candidate for a genuinely post-quantum NIKE is the isogeny scheme CSIDH (Castryck, Lange, Martindale, Panny, and Renes, ASIACRYPT 2018), a commutative supersingular-isogeny group action that offers Diffie-Hellman's exact static-static, non-interactive interface. Its appeal is size: the paper advertises "public keys of only 64 bytes at a conjectured AES-128 security level, matching NIST's post-quantum security category I" [48]. But that quantum-security claim is contested. Bonnetain and Schrottenloher analyzed the underlying quantum abelian-hidden-shift attack and concluded that "the parameters proposed by the authors of CSIDH do not meet their expected quantum security" (EUROCRYPT 2020), a finding Peikert reached independently the same year [49], [50].
The consensus repair is to grow the parameters: the SQALE authors set out to "optimize large CSIDH parameters ... while still achieving the NIST security levels 1, 2, and 3," which pushes the prime to 1024 bits and beyond, with correspondingly slower group actions [51]. And CSIDH's close relative SIDH/SIKE was broken outright in 2022, when Castryck and Decru recovered keys against SIKEp434 "in about ten minutes on a single core" [46]. That is a reminder that young isogeny assumptions can fall catastrophically, and it is why today's hybrids sidestep the missing PQ-NIKE rather than solve it [36].
"Post-quantum Diffie-Hellman" is not a solved problem. A KEM is not a drop-in for DH's non-interactive interface, and the closest true analog -- 64-byte CSIDH -- buys much less quantum security than its key size advertises, so restoring the intended level pushes it to far larger, far slower parameters. The hybrid buys time; it does not resurrect the NIKE.
Deniability versus post-quantum authentication
Diffie-Hellman gives messaging protocols a property signatures cannot: deniability. Because a session secret in X3DH is derived from DH values rather than a signature, neither party can later prove to a third party who they talked to -- the transcript could have been produced by either side alone [43]. That is a deliberate feature, and it is in tension with the post-quantum migration. Signal's PQXDH ships post-quantum confidentiality first, but in its own words it "provides post-quantum forward secrecy ... but still relies on the hardness of the discrete log problem for mutual authentication in this revision" [36]. The reason it keeps a DH operation for the authentication step is precisely the open problem: the obvious post-quantum fix -- a post-quantum signature -- is inherently non-deniable, and a deniable post-quantum authentication scheme is not yet a settled, deployed design.
This is why PQXDH retains a discrete-log DH operation for mutual authentication even after adding ML-KEM for confidentiality: post-quantum confidentiality has shipped ahead of post-quantum authentication, and the deniability that DH provides has no drop-in post-quantum replacement yet [36].Constant-time as a moving target
X25519's ladder removes the timing channel at the primitive level, but Raccoon showed the same class of leak can live one layer up, in the protocol, when a premaster secret has its leading zeros stripped before hashing -- and it recurs specifically when a DH key is reused [31]. Formally-verified constant-time implementations are the structural answer, and they are not yet universal [30].
The legacy long tail
This is a deployment problem, not a math problem. 1024-bit FFDHE, shared and custom primes, static DH, and DHE on TLS 1.2 all persist in the field years after the fixes shipped, and Logjam's precomputation economics make those shared 1024-bit primes a standing mass-decryption risk [1]. RFC 7919's named groups, the TLS 1.3 ephemeral mandate, and browsers dropping DHE have shrunk the surface, but the migration is incomplete [19], [32].
Hybrid-combiner and downgrade robustness
The entire value of X25519MLKEM768 is "secure if either half holds," and that guarantee is only as good as the combiner and the negotiation around it: a flawed combiner, or an attacker who forces a downgrade to the weaker half, voids it. RFC 9794 standardizes the post-quantum/traditional vocabulary and OpenSSL's group-tuple syntax guards the ordering, but end-to-end downgrade-resistance proofs for the deployed handshakes are still being worked out [38], [37], [42].
If the open problems are mostly about the interface, then so is the guidance. Here is fifty years of failure compressed into rules you can apply in a design review on Monday.
13. The Practical Guide, and the One Sentence Underneath
Every rule below is one row of the failure catalog, inverted. If you remember nothing else, remember the diagnostic question, and the four conditions it audits.
Decision rules: use X, with parameters Y, in case Z
- New key agreement, general purpose: X25519, ephemeral [30], [29].
- Post-quantum-hardened (the recommended default going forward): X25519MLKEM768 [38], [41], [40].
- FIPS, regulated, or HSM-interop: ECDHE with P-256 or P-384, ephemeral, with full public-key validation; or the FIPS hybrid
SecP256r1MLKEM768[22], [38]. - Legacy finite-field interop only: FFDHE
ffdhe3072or larger, ephemeral, with the subgroup check -- never a custom prime and never one below 2048 bits [19], [1]. - Always, regardless of primitive: ephemeral and destroyed after use; validate the peer value appropriately (clamping is not validation); run the shared secret through HKDF; authenticate the exchange; never static DH; never reuse the ephemeral [4], [32].
| Situation | Use | Key parameters | Because (named break avoided) |
|---|---|---|---|
| New design, general | X25519, ephemeral | 32-byte keys, HKDF the secret | Validation and timing are structural |
| PQ-hardened default | X25519MLKEM768 | Hybrid, both secrets into the KDF | Shor and harvest-now-decrypt-later |
| FIPS / HSM / interop | ECDHE P-256 or P-384, ephemeral | Full public-key validation | Invalid-curve attacks |
| Legacy FF interop only | FFDHE ffdhe3072+, ephemeral | Subgroup check, never below 2048-bit | Logjam shared-prime precomputation |
| Always | Ephemeral, validate, KDF, authenticate | Destroy the ephemeral after use | Reuse, man-in-the-middle, CDH-DDH gap |
How to see which group a TLS or SSH server actually negotiates
For TLS, run openssl s_client -connect example.com:443 -tls1_3 and read the "Negotiated TLS1.3 group" line in the output; it names the group actually chosen, such as X25519 or X25519MLKEM768. To offer only the hybrid and confirm the server supports it, add -groups X25519MLKEM768 (OpenSSL 3.5 or later). For SSH, ssh -Q kex lists the key-exchange methods your client supports, and connecting with ssh -vv prints the method actually negotiated, for example mlkem768x25519-sha256.
Diagram source
flowchart TD
A{"New greenfield design?"} -- no --> F{"FIPS or HSM interop required?"}
A -- yes --> Q{"Post-quantum sensitive?"}
Q -- yes --> H["X25519MLKEM768, ephemeral"]
Q -- no --> X["X25519, ephemeral"]
F -- yes --> P["ECDHE P-256 or P-384, ephemeral, full validation"]
F -- no --> L["FFDHE ffdhe3072 or larger, ephemeral, subgroup check"] The misuse catalog
Each common mistake maps to exactly one named break. Static DH gives no forward secrecy. A reused or cached "ephemeral" collapses forward secrecy and enables Raccoon. A custom, shared, or sub-2048-bit prime invites Logjam, and a non-prime "prime" you cannot audit invites the socat failure. Skipping the on-curve and subgroup check for P-256 ECDH builds an invalid-curve oracle. Treating clamping as validation misreads why X25519 is safe. Using the raw x-coordinate or as a key ignores the CDH-DDH gap. And anonymous DH with no authentication is a standing man-in-the-middle.
The failure catalog
This is the thesis as evidence: every world-scale Diffie-Hellman break, in one grid, each a violated condition with the math intact underneath. The rows were derived across earlier sections -- Logjam [1] and its RSA twin FREAK [2], small-subgroup confinement [15] and CVE-2016-0701 [17], the non-prime socat modulus [20], the invalid-curve class [26] and its practical TLS demonstration [27], Raccoon [31], and the KDF requirement [4].
| Break | Year | Condition violated | Root cause | Foreclosed by |
|---|---|---|---|---|
| Anonymous-DH MITM | design gap | #4 Authentication | DH values bound to no identity | STS/SIGMA; TLS 1.3 signatures |
| Logjam shared-prime | 2015 | #2 Strong, unshared group | NFS first step precomputed per shared prime, about 45M core-years at 1024-bit | RFC 7919 named safe primes |
| Logjam 512-bit export | 2015 | #2 Strong group (active) | Downgrade to breakable export parameters | TLS 1.3 removes export and static DH |
| FREAK (Logjam's RSA twin) | 2015 | #2 Strong group (active) | Downgrade to 512-bit export RSA key transport, then factor it | TLS 1.3 removes export and static RSA |
| Small-subgroup (Lim-Lee); CVE-2016-0701 | 1997; 2016 | #3 Validation and #1 reuse | Low-order element vs a reused exponent, no subgroup check | Safe primes plus the subgroup check |
| Non-prime modulus (socat) | 2016 | #2 Strong, unshared group | A shipped custom 1024-bit "prime" that was composite, possibly backdoored | RFC 7919 named safe primes; never bring your own |
| Invalid-curve (BMM); JSS TLS-ECDH | 2000; 2015 | #3 Validation | Off-curve point, formulas ignore b, reused key | Mandatory on-curve and subgroup check; X25519 twist |
| Raccoon | 2020 | #1 Freshness (reuse) | Leading-zero stripping plus a reused DH key gives an MSB timing oracle | No key reuse; TLS 1.3 key schedule |
| Ephemeral reuse or caching | ongoing | #1 Freshness | A cached "ephemeral" is a static key; forward secrecy collapses | Fresh-per-session and destroyed; TLS 1.3 |
| Static DH | ongoing | #1 Freshness | No ephemeral, so no forward secrecy | TLS 1.3 ephemeral mandate |
| Raw x-coordinate as key | ongoing | #5 KDF discipline | CDH-DDH gap leaves the shared value biased | HKDF extract, per RFC 5869 |
Frequently asked questions
Does using DHE or ECDHE automatically give me forward secrecy?
No, and this is the field's most-corrected misconception. Forward secrecy is a conjunction: the ephemeral must be freshly generated per session, never reused, destroyed after use, and run over a strong unshared group. Reuse a performance-optimized ephemeral and you have a static key; run over a shared 1024-bit prime and a precomputation reads you anyway, which is why the foundational paper is titled Imperfect Forward Secrecy. "Ephemeral, therefore forward-secret" is false without those conditions.
Does clamping validate the peer's public key?
No. Clamping constrains your own secret scalar so your side of X25519 avoids cofactor and small-order pitfalls; it does nothing to check or reject a malicious peer value. X25519 is safe against off-curve inputs because of twist security -- the curve and its twist are both hard -- not because of clamping. That distinction trips up a lot of otherwise-correct code.
Can I use the raw shared secret, or the x-coordinate, directly as a key?
No. A Diffie-Hellman value is a structured group element, not a uniformly random string, and there are groups where it is even distinguishable from random -- the CDH-versus-DDH gap. Always run it through a KDF such as HKDF. Skipping the extract step is exactly what RFC 5869 warns against.
Is 1024-bit finite-field DH still fine if I generate a fresh key every connection?
No. Fresh per-connection keys do not help against Logjam's central result: the number-field-sieve's expensive step depends only on the prime, so a shared or common 1024-bit prime can be precomputed once and then used to passively decrypt every connection on it. Use ffdhe3072 or larger, or move to a curve. Below 2048 bits is indefensible.
X25519 or P-256 for a new project?
X25519 for anything greenfield: 32-byte keys, a single constant-time Montgomery ladder, and twist security that makes the two hardest ECDH mistakes -- timing leaks and missing point validation -- structurally unreachable. Use P-256 where FIPS, an HSM, or an existing deployment compels it, and then with full public-key validation and a constant-time library.
Is ML-KEM a drop-in replacement for Diffie-Hellman?
No. ML-KEM is a key encapsulation mechanism, which is inherently interactive and has no non-interactive static-static agreement, so it cannot express the asynchronous and static-static designs DH enables. That is why the post-quantum state of the art is a hybrid -- X25519MLKEM768, with X25519's secret combined with ML-KEM's -- rather than a like-for-like swap. The closest true post-quantum analog of Diffie-Hellman, the isogeny scheme CSIDH, does keep the non-interactive interface with 64-byte keys, but its quantum security is contested and reaching the intended level forces much larger, slower parameters.
Does signing the DH exchange fix weak primes or unvalidated points?
No. Authentication defeats the active man-in-the-middle and nothing else -- it is orthogonal to the group's hardness and to input validation. A signed handshake over a shared 1024-bit prime is still Logjam-exposed, and a signed exchange that skips the on-curve check is still an invalid-curve oracle. Authentication is one of four conditions, not a substitute for the others.
The one sentence underneath
Return to Logjam. Across fifty years, almost no deployed break ever solved a discrete logarithm. Attackers precomputed a shared prime, confined a reused key to a small subgroup, fed an off-curve point to a validation-free server, read the clock while a server stripped leading zeros, shipped a modulus that was not even prime, or simply sat in the middle of an unauthenticated exchange. "Using Diffie-Hellman safely" is not one choice but a stack: a strong unshared group, a validated input, a genuinely fresh-and-destroyed ephemeral, an authenticated exchange, and a KDF beneath them. The fifty-year story is the field pulling each of those off the caller's shoulders and into the primitive, the named group, and the protocol, so that the simple thing to do became the safe thing to do. Remove any layer and the matching failure class reopens.
The math held; the deployment handed the secret away.
There is one exception, and it is the horizon. Shor's algorithm is the single break in this whole story that touches the math itself, which is why today's ECDH is being wrapped, not replaced, and why the destination is a hybrid rather than a bigger prime. So audit the exchange the way the whole article has: when a public value you did not generate arrives, and you combine it with a secret you must have freshly generated and will immediately destroy -- is the group strong and unshared, is that incoming value validated, is the exchange authenticated, and does the raw result go through a KDF before anyone calls it a key? Using Diffie-Hellman safely is the discipline of never handing an attacker a condition your protocol forgot to own.
Study guide
Key terms
- Diffie-Hellman key agreement
- Two parties exchange g to the a and g to the b over an open channel and both compute g to the ab, a shared secret an eavesdropper who sees only the public values cannot compute.
- Discrete logarithm problem
- Given a generator g and the value g to the a, recover the exponent a. Believed hard in well-chosen groups, and the foundation Diffie-Hellman rests on.
- CDH vs DDH
- Computational Diffie-Hellman is computing the shared secret from the two public values; Decisional Diffie-Hellman is distinguishing it from random. DDH is strictly stronger, which is why the raw output is not yet a key.
- Forward secrecy
- Compromising a long-term key does not expose past sessions, but only if the per-session ephemeral was fresh, never reused, destroyed after use, and over a strong unshared group. It is a conjunction, not an automatic property.
- Safe prime
- A prime p equal to 2q plus 1 with q also prime. Its only small subgroup is the trivial order-two one (the values 1 and p minus 1, a cofactor of 2), so a small-subgroup attack can confine a key to at most one bit; implementations still reject 0, 1, and p minus 1.
- Number field sieve
- The best known sub-exponential attack on finite-field discrete logs. Its expensive first step depends only on the prime, so a shared prime is a shared weakness and the engine behind Logjam.
- Invalid-curve attack
- Sending a point on a different, weaker curve; because scalar-multiplication formulas ignore the curve constant, the victim computes on the wrong curve and leaks a reused private key.
- Twist security
- A curve whose quadratic twist also has near-prime order, so an off-curve input lands in another hard group. It makes X25519 safe against invalid inputs with no explicit validation.
- Clamping
- Bit-masking an X25519 secret scalar. It constrains your own scalar and is not validation of the peer value.
- Harvest-now decrypt-later
- Recording encrypted traffic today to decrypt once a quantum computer exists, which makes present-day ECDH confidentiality a future liability and motivates the post-quantum hybrids.
Comprehension questions
Why was Logjam possible even when servers generated a fresh key per connection?
Freshness and an unshared group are two different conditions. The number-field-sieve first step depends only on the prime, so a shared prime can be precomputed once and used to read every connection on it, no matter how fresh the per-connection secret is.
What are the four conditions plus the discipline beneath them?
A fresh ephemeral, a strong and unshared group, a validated peer input, and an authenticated exchange, with a KDF beneath all four. Every named break is exactly one of these violated.
Why is X25519 preferred over classic P-256 ECDH for new work?
Twist security makes input validation structural and the Montgomery ladder is constant-time by construction, so the two hardest ECDH mistakes, missing validation and timing leaks, are unreachable rather than merely avoided.
Why must the raw shared secret be run through a KDF?
A Diffie-Hellman value is a structured group element, not a uniform string, and the CDH-DDH gap means it can even be distinguishable from random. RFC 5869 requires extracting a key from it rather than using it raw.
Why is the post-quantum answer a hybrid rather than a bigger curve?
Shor's algorithm breaks all discrete-log DH in polynomial time regardless of key size, so the fix is to add a different hard problem, ML-KEM, wrapped around X25519, so the session is safe if either problem holds.
References
- (2015). Imperfect Forward Secrecy: How Diffie-Hellman Fails in Practice (weakdh.org). https://weakdh.org/ ↩
- (2015). FREAK: Factoring RSA Export Keys (Tracking the FREAK Attack). https://freakattack.com/ ↩
- (2023). A Graduate Course in Applied Cryptography. https://toc.cryptobook.us ↩
- (2010). RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF). IETF. https://www.rfc-editor.org/rfc/rfc5869.txt ↩
- (1976). New Directions in Cryptography. IEEE Transactions on Information Theory 22(6), pp.644-654. https://doi.org/10.1109/TIT.1976.1055638 ↩
- (1978). Secure Communications Over Insecure Channels. Communications of the ACM 21(4), pp.294-299. https://doi.org/10.1145/359460.359473 ↩
- (1978). Secure Communications Over Insecure Channels (CACM reproduction). Communications of the ACM. https://cacm.acm.org/research/secure-communications-over-insecure-channels/ ↩
- (1997). The History of Non-Secret Encryption (archived). CESG / archived copy. http://web.archive.org/web/20260306211506/https://cryptome.org/jya/ellisdoc.htm ↩
- (2026). Clifford Cocks. Wikipedia. https://en.wikipedia.org/wiki/Clifford_Cocks ↩
- (1989). An Identity-Based Key-Exchange Protocol. EUROCRYPT '89, LNCS 434, pp.29-37. https://doi.org/10.1007/3-540-46885-4_5 ↩
- (1992). Authentication and Authenticated Key Exchanges. Designs, Codes and Cryptography 2(2), pp.107-125. https://doi.org/10.1007/BF00124891 ↩
- (2003). SIGMA: The SIGn-and-MAc Approach to Authenticated Diffie-Hellman and Its Use in the IKE Protocols. CRYPTO 2003, LNCS 2729, pp.400-425. https://www.iacr.org/archive/crypto2003/27290399/27290399.pdf ↩
- (1996). Handbook of Applied Cryptography. https://cacr.uwaterloo.ca/hac/ ↩
- (2020). NIST SP 800-57 Part 1 Rev. 5: Recommendation for Key Management. NIST. https://csrc.nist.gov/pubs/sp/800/57/pt1/r5/final ↩
- (1997). A Key Recovery Attack on Discrete Log-based Schemes Using a Prime Order Subgroup. CRYPTO '97, LNCS 1294, pp.249-263. https://doi.org/10.1007/BFb0052240 ↩
- (2013). SafeCurves: twist security. https://safecurves.cr.yp.to/twist.html ↩
- (2016). OpenSSL Security Advisory (28 Jan 2016): DH small subgroups (CVE-2016-0701). OpenSSL. https://openssl-library.org/news/secadv/20160128.txt ↩
- (2016). CVE-2016-0701 Detail. NIST National Vulnerability Database. https://nvd.nist.gov/vuln/detail/CVE-2016-0701 ↩
- (2016). RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for TLS. IETF. https://www.rfc-editor.org/rfc/rfc7919.txt ↩
- (2016). Socat security advisory 7: Created new 2048bit DH modulus (MSVR-1499). dest-unreach.org. http://www.dest-unreach.org/socat/contrib/socat-secadv7.html ↩
- (2020). Factorization of RSA-250. https://caramba.loria.fr/rsa250.txt ↩
- (2018). NIST SP 800-56A Rev. 3: Recommendation for Pair-Wise Key-Establishment Schemes Using Discrete Logarithm Cryptography. NIST. https://csrc.nist.gov/pubs/sp/800/56/a/r3/final ↩
- (2013). SafeCurves: rho. https://safecurves.cr.yp.to/rho.html ↩
- (1985). Use of Elliptic Curves in Cryptography. CRYPTO '85, LNCS 218, pp.417-426. https://doi.org/10.1007/3-540-39799-X_31 ↩
- (1987). Elliptic Curve Cryptosystems. Mathematics of Computation 48(177), pp.203-209. https://doi.org/10.1090/S0025-5718-1987-0866109-5 ↩
- (2000). Differential Fault Attacks on Elliptic Curve Cryptosystems. CRYPTO 2000, LNCS 1880, pp.131-146. https://doi.org/10.1007/3-540-44598-6_8 ↩
- (2015). Practical Invalid Curve Attacks on TLS-ECDH. ESORICS 2015, LNCS 9326, pp.407-425. https://doi.org/10.1007/978-3-319-24174-6_21 ↩
- (2020). CVE-2020-0601 Detail (Curveball). NIST National Vulnerability Database. https://nvd.nist.gov/vuln/detail/CVE-2020-0601 ↩
- (2006). A state-of-the-art Diffie-Hellman function (Curve25519 / X25519). https://cr.yp.to/ecdh.html ↩
- (2016). RFC 7748: Elliptic Curves for Security. IETF. https://www.rfc-editor.org/rfc/rfc7748.txt ↩
- (2020). Raccoon Attack: Finding and Exploiting Most-Significant-Bit-Oracles in TLS-DH(E) (CVE-2020-1968). https://raccoon-attack.com/ ↩
- (2018). RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3. IETF. https://www.rfc-editor.org/rfc/rfc8446.html ↩
- (2024). The State of the Post-Quantum Internet. Cloudflare. https://blog.cloudflare.com/pq-2024/ ↩
- (2014). OpenSSH 6.5 Release Notes. OpenSSH. https://www.openssh.org/txt/release-6.5 ↩
- (2020). WireGuard: Protocol and Cryptography. WireGuard. https://www.wireguard.com/protocol/ ↩
- (2023). The PQXDH Key Agreement Protocol. Signal. https://signal.org/docs/specifications/pqxdh/ ↩
- (2025). RFC 9794: Terminology for Post-Quantum Traditional Hybrid Schemes. IETF. https://datatracker.ietf.org/doc/html/rfc9794 ↩
- (2025). Post-quantum hybrid ECDHE-MLKEM Key Agreement for TLSv1.3 (draft-ietf-tls-ecdhe-mlkem). IETF. https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/ ↩
- (2024). FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard (ML-KEM). NIST. https://csrc.nist.gov/pubs/fips/203/final ↩
- (2024). OpenSSH 9.9 Release Notes. OpenSSH. https://www.openssh.org/txt/release-9.9 ↩
- (2025). OpenSSL 3.5 Final Release. OpenSSL. https://openssl-library.org/post/2025-04-08-openssl-35-final-release/ ↩
- (2025). SSL_CTX_set1_groups and TLS group configuration (OpenSSL 3.5 manual). OpenSSL. https://docs.openssl.org/3.5/man3/SSL_CTX_set1_curves/ ↩
- (2016). The X3DH Key Agreement Protocol. Signal. https://signal.org/docs/specifications/x3dh/ ↩
- (2023). Quantum Resistance and the Signal Protocol (PQXDH). Signal. https://signal.org/blog/pqxdh/ ↩
- (2014). Triple Handshakes and Cookie Cutters: Breaking and Fixing Authentication over TLS. IEEE Symposium on Security and Privacy (S&P) 2014, pp.98-113. https://doi.org/10.1109/SP.2014.14 ↩
- (2022). An Efficient Key Recovery Attack on SIDH. IACR ePrint 2022/975 / EUROCRYPT 2023. https://eprint.iacr.org/2022/975 ↩
- (1997). Lower Bounds for Discrete Logarithms and Related Problems. EUROCRYPT '97, LNCS 1233, pp.256-266. https://doi.org/10.1007/3-540-69053-0_18 ↩
- (2018). CSIDH: An Efficient Post-Quantum Commutative Group Action. ASIACRYPT 2018, LNCS 11274, pp.395-427. https://doi.org/10.1007/978-3-030-03332-3_15 ↩
- (2020). Quantum Security Analysis of CSIDH. EUROCRYPT 2020, LNCS 12106, pp.493-522. https://doi.org/10.1007/978-3-030-45724-2_17 ↩
- (2020). He Gives C-Sieves on the CSIDH. EUROCRYPT 2020, LNCS 12106, pp.463-492. https://doi.org/10.1007/978-3-030-45724-2_16 ↩
- (2021). The SQALE of CSIDH: Sublinear Velu Quantum-resistant Isogeny Action with Low Exponents. Journal of Cryptographic Engineering 11, pp.349-368. https://doi.org/10.1007/s13389-021-00271-w ↩