<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Parag Mali - tag: nonce-reuse</title><description>Posts tagged nonce-reuse.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sun, 19 Jul 2026 05:08:41 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/nonce-reuse/rss.xml" rel="self" type="application/rss+xml"/><item><title>How Elliptic Curves and Diffie-Hellman Break in Real Life: The Discrete Log Never Fell</title><link>https://paragmali.com/blog/how-elliptic-curves-and-diffie-hellman-break-in-real-life-th/</link><guid isPermaLink="true">https://paragmali.com/blog/how-elliptic-curves-and-diffie-hellman-break-in-real-life-th/</guid><description>No one has solved the discrete log on a strong curve or a 2048-bit group -- yet PS3, Android wallets, TPMs, CurveBall, and Logjam all fell. Here is exactly how.</description><pubDate>Sat, 18 Jul 2026 00:00:00 GMT</pubDate><content:encoded>
On a strong curve or a 2048-bit group, no one has ever solved the discrete logarithm in the field -- yet PlayStation 3 consoles, Android Bitcoin wallets, tamper-resistant TPMs, Windows certificate validation, and live TLS sessions all fell. Every one of those breaks attacked a layer the discrete-log math depends on but does not control: the per-signature nonce (PS3, Android, Minerva, TPM-Fail, LadderLeak), the curve and certificate parameters (CurveBall), or the negotiated strength (Logjam). A *reused* nonce surrenders the key from just two signatures; a *biased* nonce needs a statistical attack over many. The only discrete log ever computed here was Logjam&apos;s, against a downgrade-forced, export-crippled, shared 512-bit prime -- never a strong deployed curve. The fix is never a bigger curve. It is hedged, constant-time signing, validated parameters, and retiring export and shared groups.
&lt;h2&gt;1. Seven Breaks, One Unbroken Problem&lt;/h2&gt;
&lt;p&gt;On a well-chosen elliptic curve, the best attack anyone has ever fielded against the discrete logarithm is a square-root-time random walk that has barely moved since the 1970s [@hac-ch11][@teske-2001]. No deployed P-256, Curve25519, or secp256k1 key, and no standard 2048-bit Diffie-Hellman group, has ever had its discrete log solved in public. By every measurement we can make, the mathematics is holding.&lt;/p&gt;
&lt;p&gt;And yet. PlayStation 3 consoles were jailbroken [@fail0verflow-ps3-2010]. Android Bitcoin wallets were emptied [@bitcoin-android-2013]. Tamper-resistant TPMs certified to government standards surrendered their signing keys [@tpmfail-2020]. Windows accepted forged certificates that chained to a counterfeit authority [@curveball-cve-2020-0601]. Live TLS sessions were downgraded and decrypted in transit [@logjam-weakdh-2015]. Every one of those systems ran the discrete-log math exactly as specified. The math was never where they broke.&lt;/p&gt;
&lt;p&gt;That is the paradox this article resolves, and the resolution fits in one sentence: none of these attacks solved a discrete logarithm. Each attacked a layer the math &lt;em&gt;depends on but does not control&lt;/em&gt;. There are exactly three such layers, and they organize everything that follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The per-signature nonce&lt;/strong&gt; -- the throwaway secret ECDSA needs for every signature. PS3, Android, Minerva, TPM-Fail, and LadderLeak all landed here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The curve and certificate parameters&lt;/strong&gt; -- which curve, and whose generator, you are actually talking to. CurveBall landed here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The negotiated strength&lt;/strong&gt; -- how strong a key exchange two parties agree to use before they exchange anything. Logjam landed here.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The discrete-logarithm assumption protects the private key against being &lt;em&gt;directly computed&lt;/em&gt; -- and nothing else. &quot;Nobody solved the discrete log&quot; and &quot;somebody recovered the private key&quot; are not contradictory statements, because the discrete-log &lt;em&gt;problem&lt;/em&gt; (genuinely hard) and a discrete-log &lt;em&gt;deployment&lt;/em&gt; (a nonce, some parameters, a negotiation) are different things. Every real break in this article lives in the gap between them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A note on scope. This piece treats the hardness of the elliptic-curve discrete logarithm as a black box: assume it holds, and watch everything around it fail. The structural mathematics of curve choice -- why one curve is weak and another is strong, how the number field sieve and Shor&apos;s algorithm actually work -- belongs to a companion article, &quot;How the Discrete Log Would Break: Elliptic Curves, Finite Fields, and Why Curve Choice Is the Weak Part.&quot; Here, we assume the strongbox is uncrackable and ask how the key kept getting out anyway.&lt;/p&gt;
&lt;p&gt;To see why a hard problem protects so little, you first have to see exactly what it promises -- and what it quietly leaves to the deployment.&lt;/p&gt;
&lt;h2&gt;2. What the Math Actually Promises&lt;/h2&gt;
&lt;p&gt;In 1976, Whitfield Diffie and Martin Hellman reduced the ancient problem of exchanging a secret over an open wire to a single piece of hard arithmetic: the discrete logarithm [@diffie-hellman-1976]. Two parties each pick a private exponent, exchange public powers of a shared generator, and combine them into a key that an eavesdropper cannot reconstruct without solving for one of those exponents.&lt;/p&gt;
&lt;p&gt;Nine years later, Victor Miller and, independently, Neal Koblitz moved the same hard problem onto elliptic curves [@miller-1985][@koblitz-1987]. Koblitz argued the curve version was &quot;likely to be harder than the classical discrete logarithm problem&quot; [@koblitz-1987] -- which is why a 256-bit curve buys security that a finite field needs thousands of bits to match. A clean, triumphant origin. So where did the promise leak?&lt;/p&gt;

Given a generator `G` and a public point `Q = dG` on an elliptic curve (or a base `g` and `g^d` in a finite field), recovering the secret integer `d` is the discrete logarithm problem. The security of ECDSA, ECDH, and Diffie-Hellman rests on the assumption that this recovery is computationally infeasible for well-chosen parameters. It is the one thing the math promises -- and, as we will see, the only thing.
&lt;p&gt;Signatures are where the story starts, because signatures introduce a second secret. The Elliptic Curve Digital Signature Algorithm (ECDSA) signs a message hash &lt;code&gt;z&lt;/code&gt; under a long-term private key &lt;code&gt;d&lt;/code&gt; by generating a fresh random value &lt;code&gt;k&lt;/code&gt;, computing a point &lt;code&gt;kG&lt;/code&gt;, taking its x-coordinate as &lt;code&gt;r&lt;/code&gt;, and producing:&lt;/p&gt;
&lt;p&gt;$$s = k^{-1},(z + r,d) \bmod n$$&lt;/p&gt;
&lt;p&gt;where &lt;code&gt;n&lt;/code&gt; is the order of the group. The verifier only ever sees &lt;code&gt;(r, s)&lt;/code&gt;. The private key &lt;code&gt;d&lt;/code&gt; is buried inside, protected by the discrete-log assumption -- nobody can read &lt;code&gt;d&lt;/code&gt; off the signature. But look at what else is in that equation.&lt;/p&gt;

The `k` in ECDSA is a *nonce*: a number used once. It must be secret, statistically uniform, and unique to each signature. It is not the private key, and it is thrown away the instant the signature is produced. It is also the single most load-bearing prerequisite in the whole scheme -- because `k` and `d` appear together in one linear equation, anything an attacker learns about `k` turns directly into knowledge of `d`.
&lt;p&gt;The discrete-log assumption guards &lt;code&gt;d&lt;/code&gt; against being &lt;em&gt;computed&lt;/em&gt; from the public key. It says nothing whatsoever about three things the assumption quietly depends on but does not itself enforce:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The nonce &lt;code&gt;k&lt;/code&gt; must be secret, uniform, and unique.&lt;/strong&gt; The math assumes a perfect &lt;code&gt;k&lt;/code&gt; and never checks that you supplied one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The parameters must be the standard, named ones.&lt;/strong&gt; The math assumes you are on the curve you think you are, with the generator you think you have.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The negotiated strength must not be crippled or shared.&lt;/strong&gt; The math assumes the group you agreed to use is actually strong.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each of those is the deployment&apos;s responsibility, not the primitive&apos;s. And each one became a pillar of failure.&lt;/p&gt;

flowchart TD
    DLP[&quot;(EC)DLP is hard: d cannot be computed from the public key dG&quot;]
    DLP --&amp;gt; N[&quot;Depends on the per-signature nonce k&quot;]
    DLP --&amp;gt; P[&quot;Depends on the curve and certificate parameters&quot;]
    DLP --&amp;gt; G[&quot;Depends on the negotiated group strength&quot;]
    N --&amp;gt; P1[&quot;Pillar 1 nonce: PS3, Android, Minerva, TPM-Fail, LadderLeak&quot;]
    P --&amp;gt; P2[&quot;Pillar 2 validation: CurveBall&quot;]
    G --&amp;gt; P3[&quot;Pillar 3 downgrade: Logjam&quot;]
&lt;p&gt;The curve migration itself is not our subject.Miller (1985) and Koblitz (1987) are mentioned here only to fix the primitive&apos;s birth. Whether a &lt;em&gt;particular&lt;/em&gt; curve is structurally weak -- anomalous curves, small embedding degrees, the number field sieve, Shor&apos;s algorithm -- is the structural mathematics deferred to the companion article. This article assumes the curve is strong and attacks everything around it. The math guards the key against being computed. It says nothing about the key being &lt;em&gt;handed over&lt;/em&gt;. Three contracts govern that handover, and each one broke.&lt;/p&gt;
&lt;h2&gt;3. Three Contracts the Deployment Signs and Never Reads&lt;/h2&gt;
&lt;p&gt;Every cryptographic deployment signs a contract it never reads. There are three clauses, one for each layer the math leans on, and the fine print on all three is where real products sank.&lt;/p&gt;
&lt;h3&gt;The nonce contract: k must be fresh&lt;/h3&gt;
&lt;p&gt;Take the ECDSA signing equation and ask the cruelest possible question: what if &lt;code&gt;k&lt;/code&gt; is not unique? Suppose two different messages, with hashes &lt;code&gt;z1&lt;/code&gt; and &lt;code&gt;z2&lt;/code&gt;, are signed under the &lt;em&gt;same&lt;/em&gt; nonce &lt;code&gt;k&lt;/code&gt;. Because &lt;code&gt;r&lt;/code&gt; is the x-coordinate of &lt;code&gt;kG&lt;/code&gt;, the same &lt;code&gt;k&lt;/code&gt; produces the same &lt;code&gt;r&lt;/code&gt; in both signatures. Now write them down side by side:&lt;/p&gt;
&lt;p&gt;$$s_1 = k^{-1}(z_1 + r,d), \qquad s_2 = k^{-1}(z_2 + r,d) \pmod n$$&lt;/p&gt;
&lt;p&gt;Subtract. The &lt;code&gt;rd&lt;/code&gt; term is identical in both, so it cancels, and &lt;code&gt;k&lt;/code&gt; falls straight out:&lt;/p&gt;
&lt;p&gt;$$s_1 - s_2 = k^{-1}(z_1 - z_2), \qquad k = \frac{z_1 - z_2}{s_1 - s_2} \bmod n$$&lt;/p&gt;
&lt;p&gt;Once you know &lt;code&gt;k&lt;/code&gt;, the first equation has exactly one unknown left, and you solve for the private key by hand:&lt;/p&gt;
&lt;p&gt;$$d = \frac{s_1,k - z_1}{r} \bmod n$$&lt;/p&gt;
&lt;p&gt;No lattice. No side channel. No discrete log. &lt;a href=&quot;https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/&quot; rel=&quot;noopener&quot;&gt;Two signatures that share a nonce&lt;/a&gt; are two linear equations in two unknowns, and a middle-schooler with modular arithmetic can finish the job.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The discrete-logarithm assumption protects &lt;code&gt;d&lt;/code&gt; against &lt;em&gt;direct computation from the public key&lt;/em&gt; -- and nothing else. It does not protect &lt;code&gt;d&lt;/code&gt; against being algebraically extracted when the nonce misbehaves. The moment two signatures reuse &lt;code&gt;k&lt;/code&gt;, the strongbox is irrelevant, because the deployment left a second door wide open.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The contract, then, is strict: &lt;code&gt;k&lt;/code&gt; must be secret, statistically uniform, and never reused or partially leaked. Break any word of that clause and the algebra above (or its statistical cousin, later) takes your key.&lt;/p&gt;
&lt;h3&gt;The parameter contract: talk to the curve you think you are&lt;/h3&gt;
&lt;p&gt;The second clause is about identity. A &lt;em&gt;named&lt;/em&gt; curve -- &quot;P-256,&quot; &quot;secp256k1&quot; -- is an agreement to use one specific set of domain parameters, and in particular one specific generator, the base point &lt;code&gt;G&lt;/code&gt; that every public key is measured against.&lt;/p&gt;

Every named elliptic curve fixes a single public point `G` from which all key material is derived: a private key `d` corresponds to the public point `dG`. The generator is not a secret -- it is a published constant. But it is load-bearing: &quot;the public key is `dG`&quot; is only meaningful if both sides agree on which `G`.

A certificate can specify its curve two ways. A *named curve* is an object identifier pointing at standardized domain parameters (fixed `a`, `b`, `p`, `G`, `n`). *Explicit parameters* spell out all of those numbers inline, which lets the certificate carry a *custom* generator. Accepting explicit parameters without checking them against a known named curve means trusting an attacker to tell you what `G` is.
&lt;p&gt;The contract: never trust attacker-supplied explicit parameters. A validator that checks a public &lt;em&gt;point&lt;/em&gt; but not the &lt;em&gt;generator&lt;/em&gt; it was measured against has, in effect, let the attacker choose the ruler and then declared the measurement correct. That is precisely the door CurveBall walked through [@curveball-cve-2020-0601].&lt;/p&gt;
&lt;h3&gt;The negotiation contract: agree to strength, not to a museum piece&lt;/h3&gt;
&lt;p&gt;The third clause governs what two parties agree to &lt;em&gt;before&lt;/em&gt; they exchange anything. In &lt;a href=&quot;https://paragmali.com/blog/nobody-broke-the-discrete-log-a-field-guide-to-diffie-hellma/&quot; rel=&quot;noopener&quot;&gt;ephemeral Diffie-Hellman&lt;/a&gt;, each side picks a fresh secret per connection, which is supposed to guarantee that compromising one session cannot retroactively expose others.&lt;/p&gt;

In ephemeral DH, the private exponents are generated fresh for each connection and discarded afterward, so a key recovered later cannot decrypt past sessions. This property is called forward secrecy. It is the reason &quot;just use a fresh ephemeral key&quot; feels like a complete defense -- and, as Logjam showed, the reason that feeling is dangerously incomplete.

In the 1990s, US export regulations capped the strength of cryptography shipped abroad, producing deliberately weakened cipher suites such as 512-bit `DHE_EXPORT` Diffie-Hellman [@logjam-weakdh-2015]. The policy was abandoned, but the code was not: two decades later, servers and libraries still spoke these crippled suites on request.
&lt;p&gt;The contract: the negotiated group must be strong, fresh, and unshared. The fine print that sank it: many servers ship the &lt;em&gt;same&lt;/em&gt; standardized 512-bit prime, so breaking one group breaks all of them at once [@logjam-weakdh-2015]. A fresh ephemeral exponent does not save you when the prime itself is weak and shared.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contract&lt;/th&gt;
&lt;th&gt;What it requires&lt;/th&gt;
&lt;th&gt;What violates it&lt;/th&gt;
&lt;th&gt;Field break&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Nonce&lt;/td&gt;
&lt;td&gt;&lt;code&gt;k&lt;/code&gt; secret, uniform, unique per signature&lt;/td&gt;
&lt;td&gt;a constant, repeated, or biased &lt;code&gt;k&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PS3, Android, Minerva, TPM-Fail, LadderLeak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameter&lt;/td&gt;
&lt;td&gt;named curve, standard generator &lt;code&gt;G&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;trusting attacker-supplied explicit parameters&lt;/td&gt;
&lt;td&gt;CurveBall&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negotiation&lt;/td&gt;
&lt;td&gt;strong, fresh, unshared group&lt;/td&gt;
&lt;td&gt;downgrade to a crippled, shared group&lt;/td&gt;
&lt;td&gt;Logjam&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Three contracts, three clauses of fine print. Over fifteen years, every clause was breached in the field -- and the breaches fall into exactly three piles.&lt;/p&gt;
&lt;h2&gt;4. The Failure Catalog: Seven Breaks, Three Layers, One Shape&lt;/h2&gt;
&lt;p&gt;None of the seven incidents below solved a discrete logarithm. Every one of them recovered a private key or forged a trusted one. Watch where each attack actually landed -- because the landing spot, not the marquee name, is the whole lesson.&lt;/p&gt;

timeline
    title EC and DH in the field, the assumption versus the breaks
    1976 : Diffie-Hellman, the assumption is born
    1985-1987 : Miller and Koblitz, the curve migration
    2010 : PS3 constant nonce, reuse
    2013 : Android SecureRandom, reuse
    2015 : Logjam, downgrade plus shared-prime NFS
    2019-2020 : Minerva and TPM-Fail, lattice bias
    2020 : LadderLeak, Bleichenbacher-Fourier sub-bit : CurveBall, unvalidated generator
&lt;p&gt;Here is the entire corpus on one page. The rightmost column is the thesis in a single word, repeated seven times.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incident&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Pillar&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Effort to break&lt;/th&gt;
&lt;th&gt;What fell&lt;/th&gt;
&lt;th&gt;Discrete log solved?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;PS3&lt;/td&gt;
&lt;td&gt;2010&lt;/td&gt;
&lt;td&gt;Nonce (reuse)&lt;/td&gt;
&lt;td&gt;a constant &lt;code&gt;k&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2 signatures&lt;/td&gt;
&lt;td&gt;ECDSA firmware-signing key&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android wallets&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;Nonce (reuse)&lt;/td&gt;
&lt;td&gt;repeated &lt;code&gt;k&lt;/code&gt; from a broken RNG&lt;/td&gt;
&lt;td&gt;2 reused signatures&lt;/td&gt;
&lt;td&gt;Bitcoin wallet keys&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logjam&lt;/td&gt;
&lt;td&gt;2015&lt;/td&gt;
&lt;td&gt;Downgrade&lt;/td&gt;
&lt;td&gt;forced 512-bit shared prime&lt;/td&gt;
&lt;td&gt;1 NFS precompute per prime&lt;/td&gt;
&lt;td&gt;TLS session secrecy&lt;/td&gt;
&lt;td&gt;Only a crippled, shared 512-bit export prime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TPM-Fail&lt;/td&gt;
&lt;td&gt;2019-2020&lt;/td&gt;
&lt;td&gt;Nonce (bias)&lt;/td&gt;
&lt;td&gt;timing leak of nonce bit-length&lt;/td&gt;
&lt;td&gt;many signatures + lattice&lt;/td&gt;
&lt;td&gt;256-bit TPM keys&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minerva&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Nonce (bias)&lt;/td&gt;
&lt;td&gt;timing leak of nonce bit-length&lt;/td&gt;
&lt;td&gt;about 2100 signatures + lattice&lt;/td&gt;
&lt;td&gt;ECDSA key&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LadderLeak&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Nonce (bias)&lt;/td&gt;
&lt;td&gt;sub-bit ladder leak&lt;/td&gt;
&lt;td&gt;many signatures + Bleichenbacher/Fourier&lt;/td&gt;
&lt;td&gt;ECDSA key&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CurveBall&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;td&gt;an unchecked generator&lt;/td&gt;
&lt;td&gt;1 forged certificate&lt;/td&gt;
&lt;td&gt;certificate trust itself&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Six &quot;No&quot;s and one heavily qualified &quot;Yes.&quot; Let us walk the three pillars in turn.&lt;/p&gt;
&lt;h3&gt;Pillar 1a: the nonce, reused (certain, from two signatures)&lt;/h3&gt;
&lt;p&gt;In December 2010, at the 27th Chaos Communication Congress, the group fail0verflow presented &quot;Console Hacking 2010&quot; and revealed that Sony had signed PlayStation 3 firmware using ECDSA with a &lt;em&gt;constant&lt;/em&gt; nonce [@fail0verflow-ps3-2010]. Not a weak random &lt;code&gt;k&lt;/code&gt;. The same &lt;code&gt;k&lt;/code&gt;, every time. Every signature Sony ever produced shared a nonce with every other, which means any two of them collapse to the algebra from Section 3.Keep two PS3 events distinct. fail0verflow&apos;s 27C3 talk exposed the constant-nonce &lt;em&gt;mechanism&lt;/em&gt;. George Hotz released a separate PS3 root key weeks later, in January 2011 [@hotz-ps3-2011]. The teaching case -- a hardcoded constant nonce -- is fail0verflow&apos;s. The console&apos;s chain of trust, the thing Sony spent years engineering, came apart because &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand/&quot; rel=&quot;noopener&quot;&gt;a random number generator&lt;/a&gt; was told to always return the same number.&lt;/p&gt;

flowchart LR
    A[&quot;Sig 1: s1 = k^-1 (z1 + r d)&quot;] --&amp;gt; C[&quot;Same k means same r&quot;]
    B[&quot;Sig 2: s2 = k^-1 (z2 + r d)&quot;] --&amp;gt; C
    C --&amp;gt; D[&quot;Subtract, k cancels: k = (z1 - z2) / (s1 - s2) mod n&quot;]
    D --&amp;gt; E[&quot;Solve for d = (s1 k - z1) / r mod n&quot;]
    E --&amp;gt; F[&quot;Private key recovered, no discrete log solved&quot;]
&lt;p&gt;This is not a metaphor. Here is the recovery, executable. Two signatures share a nonce, and a dozen lines of BigInt arithmetic reproduce the private key exactly:&lt;/p&gt;
&lt;p&gt;{`
// secp256k1 group order
const n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n;
const mod = (a, m) =&amp;gt; ((a % m) + m) % m;&lt;/p&gt;
&lt;p&gt;// Modular inverse via the extended Euclidean algorithm
function inv(a, m) {
  let [oldr, r] = [mod(a, m), m];
  let [olds, s] = [1n, 0n];
  while (r !== 0n) {
    const q = oldr / r;
    [oldr, r] = [r, oldr - q * r];
    [olds, s] = [s, olds - q * s];
  }
  return mod(olds, m);
}&lt;/p&gt;
&lt;p&gt;// A victim signs two different messages with the SAME nonce k
const d  = 0x00c0ffee1234567890abcdef00c0ffee1234567890abcdef00c0ffee12345678n; // secret
const k  = 0x0000deadbeef0000feed0000deadbeef0000feed0000deadbeef0000feed0001n; // reused nonce
const r  = mod(0x1122334455667788112233445566778811223344556677881122334455667788n, n);
const z1 = 0x1111111111111111111111111111111111111111111111111111111111111111n;
const z2 = 0x2222222222222222222222222222222222222222222222222222222222222222n;
const s1 = mod(inv(k, n) * mod(z1 + mod(r * d, n), n), n);
const s2 = mod(inv(k, n) * mod(z2 + mod(r * d, n), n), n);&lt;/p&gt;
&lt;p&gt;// The attacker sees only (r, s1, z1) and (r, s2, z2). Recover k, then d.
const kRec = mod((z1 - z2) * inv(mod(s1 - s2, n), n), n);
const dRec = mod((mod(s1 * kRec, n) - z1) * inv(r, n), n);
console.log(&apos;recovered nonce matches:  &apos; + (kRec === k));
console.log(&apos;recovered private key:    &apos; + (dRec === d));
console.log(&apos;d = 0x&apos; + dRec.toString(16));
`}&lt;/p&gt;
&lt;p&gt;Three years later, the same algebra emptied wallets. In August 2013, an advisory on bitcoin.org warned that &quot;a component of Android responsible for generating secure random numbers contains critical weaknesses&quot; that rendered &quot;all Android wallets&quot; vulnerable to theft [@bitcoin-android-2013]. The root cause, per Google&apos;s own postmortem, was that the platform&apos;s &lt;code&gt;SecureRandom&lt;/code&gt; &quot;may not receive cryptographically strong values&quot; because the underlying PRNG was improperly initialized [@android-securerandom-2013].&lt;/p&gt;
&lt;p&gt;The consequence was nonce &lt;em&gt;repetition&lt;/em&gt; across transactions. And because every Bitcoin signature is broadcast to a public ledger, anyone could scan the chain for two signatures sharing an &lt;code&gt;r&lt;/code&gt; and drain the wallet behind them.The bitcoin.org advisory names the flaw and the affected wallets but states no total loss figure [@bitcoin-android-2013]. Any &quot;coins stolen&quot; number you have seen is reported, not established -- treat it accordingly.&lt;/p&gt;
&lt;p&gt;The detector that would have caught both PS3 and Android is embarrassingly short. Group public signatures by &lt;code&gt;r&lt;/code&gt;; any collision is an exposed key:&lt;/p&gt;
&lt;p&gt;{`
const n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n;
const mod = (a, m) =&amp;gt; ((a % m) + m) % m;
function inv(a, m) {
  let [oldr, r] = [mod(a, m), m];
  let [olds, s] = [1n, 0n];
  while (r !== 0n) { const q = oldr / r; [oldr, r] = [r, oldr - q * r]; [olds, s] = [s, olds - q * s]; }
  return mod(olds, m);
}
// Build a public feed in which one signer reused a nonce, then forget the secret
const dSecret = 777n, kReused = 424242n, rReused = 987654321n;
const mk = (z) =&amp;gt; ({ r: rReused, s: mod(inv(kReused, n) * mod(z + mod(rReused * dSecret, n), n), n), z });
const feed = [ { r: 111n, s: 22n, z: 5n }, mk(1001n), { r: 222n, s: 44n, z: 9n }, mk(2002n), { r: 333n, s: 66n, z: 12n } ];&lt;/p&gt;
&lt;p&gt;const seen = new Map();
for (const g of feed) {
  const key = g.r.toString();
  if (seen.has(key)) {
    const a = seen.get(key), b = g;
    const k = mod((a.z - b.z) * inv(mod(a.s - b.s, n), n), n);
    const d = mod((mod(a.s * k, n) - a.z) * inv(a.r, n), n);
    console.log(&apos;NONCE REUSE at r = &apos; + key + &apos;  -&amp;gt;  private key d = &apos; + d.toString());
  } else {
    seen.set(key, g);
  }
}
`}&lt;/p&gt;
&lt;p&gt;Two incidents, two different root causes -- a hardcoded constant and a starved RNG -- and one identical, certain outcome. This is the crudest form of the whole thesis: the deployment lost the key while the discrete log stood untouched.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A &lt;em&gt;repeated&lt;/em&gt; nonce is certain key recovery from just two signatures, by elementary algebra. A &lt;em&gt;biased&lt;/em&gt; nonce -- one that merely leans, statistically, toward certain values -- leaks only a partial statistic and requires a statistical attack over many signatures. &quot;One biased signature leaks your key&quot; is false. The distinction is the difference between the next two subsections, and blurring it is the single most common error in this area.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Reused nonce&lt;/th&gt;
&lt;th&gt;Biased nonce&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;What leaks&lt;/td&gt;
&lt;td&gt;the whole nonce (it repeats)&lt;/td&gt;
&lt;td&gt;a partial statistic about the nonce&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signatures needed&lt;/td&gt;
&lt;td&gt;exactly 2&lt;/td&gt;
&lt;td&gt;many (Minerva about 2100; PuTTY about 60 [@putty-cve-2024-31497])&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attack&lt;/td&gt;
&lt;td&gt;elementary algebra&lt;/td&gt;
&lt;td&gt;Hidden Number Problem (lattice or Fourier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Certainty&lt;/td&gt;
&lt;td&gt;certain&lt;/td&gt;
&lt;td&gt;statistical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Examples&lt;/td&gt;
&lt;td&gt;PS3, Android&lt;/td&gt;
&lt;td&gt;Minerva, TPM-Fail, LadderLeak, PuTTY&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Pillar 1b: the nonce, biased (statistical, over many signatures)&lt;/h3&gt;
&lt;p&gt;Reuse is loud. The subtler failure is a nonce that never repeats but is not quite uniform -- one whose top bit is a little too often zero, say. It turns out that even this surrenders the key, and the theory predicting it predates the exploits by two decades.&lt;/p&gt;

Given many samples that each leak a few of the most-significant bits of `k`, together with the public signature data, recover the fixed secret `d`. Boneh and Venkatesan defined this problem in 1996 and proved it tractable [@boneh-venkatesan-1996]. Every biased-nonce attack in this article -- Minerva, TPM-Fail, LadderLeak, PuTTY -- is an instance of it.
&lt;p&gt;Nguyen and Shparlinski turned the abstract HNP into a concrete lattice attack on (EC)DSA with partially known nonces [@nguyen-shparlinski-2003], and Brumley and Tuveri showed in 2011 that the necessary bit-leak could be harvested &lt;em&gt;remotely&lt;/em&gt;, over a network, from an OpenSSL implementation whose scalar multiplication was not constant-time [@brumley-tuveri-2011]. Then the field split into two solver regimes, and telling them apart is essential.&lt;/p&gt;
&lt;p&gt;When the &lt;a href=&quot;https://paragmali.com/blog/correct-constant-time-and-still-owned-a-field-guide-to-side-/&quot; rel=&quot;noopener&quot;&gt;timing channel&lt;/a&gt; leaks a &lt;em&gt;few bits&lt;/em&gt; of the nonce -- typically its exact bit-length -- the HNP is solved by &lt;strong&gt;lattice reduction&lt;/strong&gt;. Minerva (CHES 2020) demonstrated this against smartcards and libraries whose scalar multiplication ran in time correlated with the nonce&apos;s length, reporting roughly &quot;2100 signatures to recover the private key on the standard secp256r1 curve&quot; [@minerva-2020].&lt;/p&gt;
&lt;p&gt;TPM-Fail (USENIX Security 2020) carried the same idea into hardware roots of trust. A timing leak in Intel firmware TPMs (CVE-2019-11090 [@cve-2019-11090]) and STMicroelectronics ST33 chips (CVE-2019-16863 [@cve-2019-16863]) recovered a 256-bit ECDSA key from an Intel fTPM in 4 to 20 minutes, and a VPN authentication key remotely in about five hours [@tpmfail-2020].Two honesty notes. Minerva&apos;s project page retracted its original EdDSA claim; the confirmed result is ECDSA-only [@minerva-2020]. And the TPM-Fail targets were not toys -- they held Common Criteria EAL4+ and FIPS 140-2 certification, and the leak survived that scrutiny [@tpmfail-2020].&lt;/p&gt;

flowchart TD
    L[&quot;A timing channel leaks partial information about the nonce k&quot;] --&amp;gt; Q{&quot;How much leaks per signature?&quot;}
    Q --&amp;gt;|&quot;at least 2 bits&quot;| LAT[&quot;Lattice reduction of the HNP: Minerva, TPM-Fail&quot;]
    Q --&amp;gt;|&quot;under 1 bit&quot;| FFT[&quot;Bleichenbacher and Fourier HNP: LadderLeak&quot;]
    LAT --&amp;gt; R[&quot;Private key d recovered over MANY signatures&quot;]
    FFT --&amp;gt; R
&lt;p&gt;The second regime is where people get it wrong. When the leak drops &lt;em&gt;below a single bit&lt;/em&gt; -- LadderLeak revealed the most-significant bit of the nonce &quot;but with probability less than one&quot; -- lattices become intractable, and the attack instead revives a Fourier-analysis method the paper credits to Daniel Bleichenbacher [@ladderleak-2020].&lt;/p&gt;
&lt;p&gt;The LadderLeak authors note that &quot;most of the attacks so far have relied on at least two bits of nonce bias,&quot; precisely the lattice regime. Their contribution was to break below it, over curves sect163r1 and NIST P-192, using the Fourier solver rather than a lattice [@ladderleak-2020]. Calling LadderLeak a lattice attack is the field&apos;s classic mistake.&lt;/p&gt;

&quot;Breaking ECDSA With Less Than One Bit Of Nonce Leakage&quot; -- the title of the LadderLeak paper, and the most compact statement of how little a nonce must betray before the key is gone.
&lt;p&gt;Notice what happened across this subsection. Every one of these attacks beat a signer that had already removed the reuse bug. The leak had moved from how the nonce was &lt;em&gt;generated&lt;/em&gt; to how it was &lt;em&gt;used&lt;/em&gt; -- a variable-time scalar multiplication -- and the cure had to move with it. Hold that thought; it is the pivot of the entire article. But reuse or bias, lattice or Fourier, two signatures or two thousand, the outcome is the same and the discrete logarithm is never touched: the deployment hands over &lt;code&gt;d&lt;/code&gt;, and the strongbox stays locked.&lt;/p&gt;
&lt;h3&gt;Pillar 2: the parameters, unvalidated (CurveBall)&lt;/h3&gt;
&lt;p&gt;The nonce pillar is about a secret the deployment mishandles. The second pillar is about an identity the deployment fails to check. On 14 January 2020, Microsoft patched CVE-2020-0601, reported by the NSA: &quot;a spoofing vulnerability exists in the way Windows CryptoAPI (Crypt32.dll) validates Elliptic Curve Cryptography (ECC) certificates&quot; [@curveball-cve-2020-0601]. It became known as CurveBall, and it is the cleanest example in the whole corpus of a break that has nothing to do with the mathematics of ECC.&lt;/p&gt;
&lt;p&gt;Recall the parameter contract. A public key is the point &lt;code&gt;dG&lt;/code&gt; -- meaningful only if everyone agrees on &lt;code&gt;G&lt;/code&gt;. CryptoAPI, when handed a certificate that specified its curve with &lt;em&gt;explicit&lt;/em&gt; parameters, checked that the certificate&apos;s public &lt;em&gt;point&lt;/em&gt; matched a trusted root&apos;s public key. What it did not check was the &lt;em&gt;generator&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So an attacker could craft a curve with a custom generator &lt;code&gt;G&apos;&lt;/code&gt;, pick any private key &lt;code&gt;d&apos;&lt;/code&gt; they liked, and compute &lt;code&gt;G&apos;&lt;/code&gt; such that &lt;code&gt;d&apos; * G&apos;&lt;/code&gt; equals the exact public point of a &lt;a href=&quot;https://paragmali.com/blog/a-perfect-signature-for-a-certificate-that-should-never-have/&quot; rel=&quot;noopener&quot;&gt;trusted root certificate authority&lt;/a&gt;. The forged certificate then presents the right public point on the wrong curve. Windows, comparing only the point, accepts it -- while a library that pins named curves rejects it instantly, because &lt;code&gt;G&apos;&lt;/code&gt; is not the standard base point.&lt;/p&gt;

flowchart TD
    A[&quot;Attacker picks explicit curve parameters with a custom generator G-prime&quot;] --&amp;gt; B[&quot;Chooses private key d-prime so that d-prime times G-prime equals a trusted root public point Q&quot;]
    B --&amp;gt; C[&quot;Forged certificate presents point Q on the attacker curve&quot;]
    C --&amp;gt; D{&quot;Windows CryptoAPI validation&quot;}
    D --&amp;gt;|&quot;Public point matches a trusted root? YES&quot;| E[&quot;Accepted, but the generator was never checked&quot;]
    D -.-&amp;gt;|&quot;A named-curve-pinning library also checks G&quot;| F[&quot;Rejected, G-prime is not the standard base point&quot;]
    E --&amp;gt; G[&quot;Forged code-signing or TLS certificate chains to a trusted root&quot;]
&lt;p&gt;The ECDLP was never in play. The attacker chose their own private key; they never had to recover anyone&apos;s. The failure is a single missing comparison -- generator against named curve -- buried in a certificate validation stack that a billion machines trusted. CurveBall is a validation bug, not a curve break, and the fix is exactly what the missing check would have been: reject explicit parameters, or fully re-validate them against the claimed named curve.&lt;/p&gt;
&lt;h3&gt;Pillar 3: the negotiation, downgraded (Logjam)&lt;/h3&gt;
&lt;p&gt;The third pillar is the one honest exception, and handled carelessly it looks like a refutation of the whole thesis. In 2015, the Logjam team (CVE-2015-4000) showed that a man-in-the-middle could rewrite a TLS handshake -- &quot;rewriting a ClientHello with DHE replaced by DHE_EXPORT&quot; -- to force the connection onto a 512-bit export-grade Diffie-Hellman group [@cve-2015-4000]. And here, unlike everywhere else, the attacker really does compute a discrete logarithm.&lt;/p&gt;
&lt;p&gt;The trick is that the expensive part of the number field sieve has to be done only once per prime, and servers share primes.&lt;/p&gt;

The fastest known classical algorithm for the finite-field discrete logarithm (and for integer factoring). Its decisive feature here: its costly first stage depends only on the *prime* being attacked, not on any particular exchanged key. Precompute once per prime, and every session under that prime becomes cheap to break. The internals belong to the companion article; what matters here is that one structural fact.
&lt;p&gt;The Logjam researchers found that &quot;8.4% of the Top 1 Million domains were initially vulnerable,&quot; and that because so many servers reused a handful of standardized 512-bit primes, &quot;the first step in the number field sieve ... is dependent only on this prime&quot; [@logjam-weakdh-2015]. One precomputation, amortized across every server that shipped that prime.&lt;/p&gt;

&quot;The first step in the number field sieve ... is dependent only on this prime.&quot; One precomputation breaks every server that shares the group.

sequenceDiagram
    participant C as Client
    participant M as MITM
    participant S as Server
    Note over M: One-time NFS precomputation per shared 512-bit prime
    C-&amp;gt;&amp;gt;M: ClientHello offering DHE
    M-&amp;gt;&amp;gt;S: ClientHello rewritten to DHE_EXPORT
    S-&amp;gt;&amp;gt;M: 512-bit export DH parameters, a shared standardized prime
    M-&amp;gt;&amp;gt;C: forwards the weak parameters
    Note over M: Precomputation done, so this connection solves fast
    M--&amp;gt;&amp;gt;C: session decrypted in near real time

The 512-bit `DHE_EXPORT` groups existed because 1990s US export regulations forbade shipping strong cryptography abroad, mandating deliberately breakable key exchange for foreign markets [@logjam-weakdh-2015]. The policy was abandoned by the early 2000s, but the cipher suites were never removed from server software. Logjam, in 2015, exploited a legal compromise that had been dead for over a decade and a codebase that never got the memo. The math did not decay -- the deployment simply kept a museum piece plugged in.
&lt;p&gt;Read carefully, Logjam is not a counterexample; it is the thesis in its most literal form. A discrete log &lt;em&gt;was&lt;/em&gt; computed, but only against a 512-bit prime that a downgrade forced the connection onto and that policy had left crippled and shared. The deployment set the weak, shared group on the table; the number field sieve merely finished the job. No strong, standard-sized group ever fell. The fix is the same shape as the break: remove export ciphers, negotiate vetted named groups (RFC 7919 [@rfc7919]), and adopt TLS 1.3, which removes static Diffie-Hellman entirely [@rfc8446].&lt;/p&gt;
&lt;p&gt;Three pillars, seven breaks, one shape: every attack hit a layer the discrete-log math depends on but does not control. Name the shape, and you have the whole discipline.&lt;/p&gt;
&lt;h2&gt;5. The Realization: Fix the Contracts, Not the Curve&lt;/h2&gt;
&lt;p&gt;There was no single eureka here. The insight accumulated across fifteen years of incidents and then snapped into one sentence: every field break attacked a layer the discrete-log math &lt;em&gt;depends on but does not control&lt;/em&gt; -- the nonce, then the parameters, then the negotiated strength -- and every fix changed &lt;em&gt;how the primitive is generated, used, validated, or negotiated&lt;/em&gt;, never the curve and never the log.&lt;/p&gt;
&lt;p&gt;The sharpest crystallization hides inside the word &quot;bias.&quot; For years it was treated as one problem with one fix: remove the random number generator, derive the nonce deterministically (RFC 6979 [@rfc6979], later blessed by FIPS 186-5 [@fips-186-5]), and the biased-nonce class is solved. But Minerva, TPM-Fail, and LadderLeak all recovered keys from signers that had &lt;em&gt;already&lt;/em&gt; done that. The reason is that &quot;bias&quot; was concealing two distinct root causes with two distinct cures.&lt;/p&gt;

flowchart TD
    B[&quot;One word: nonce bias&quot;] --&amp;gt; G[&quot;Generation defect: the RNG returns a constant, repeated, or predictable k&quot;]
    B --&amp;gt; U[&quot;Usage defect: variable-time scalar multiplication leaks the bit-length of k&quot;]
    G --&amp;gt; GC[&quot;Cure: deterministic and hedged nonces, RFC 6979 and FIPS 186-5&quot;]
    U --&amp;gt; UC[&quot;Cure: constant-time, exception-free scalar multiplication&quot;]
    GC --&amp;gt; BOTH[&quot;You need BOTH, they are orthogonal&quot;]
    UC --&amp;gt; BOTH
&lt;p&gt;A &lt;em&gt;generation&lt;/em&gt; defect is a bad nonce coming out of the sampler -- a constant (PS3), a repeat (Android), a predictable value. Determinism and hedging cure it. A &lt;em&gt;usage&lt;/em&gt; defect is a perfectly good nonce leaking through the &lt;em&gt;timing&lt;/em&gt; of the scalar multiplication that consumes it -- the bit-length falling out through a side channel (Minerva, TPM-Fail) or a sub-bit correlation (LadderLeak). Only constant-time code cures that. The two cures are orthogonal, and you need both.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Generalize the two-cures fork and you get the entire discipline of this field: four contracts, not one curve. &lt;em&gt;Generate&lt;/em&gt; the nonce so it cannot be predicted or repeated; &lt;em&gt;use&lt;/em&gt; it in constant time so it cannot leak; &lt;em&gt;validate&lt;/em&gt; your parameters so no attacker chooses your generator; &lt;em&gt;negotiate&lt;/em&gt; only strong, unshared groups so no downgrade can strand you. Every documented break is a breach of one of those four. No break in the corpus is a break of the discrete logarithm.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This also dissolves the apparent contradiction the article opened with. &quot;Nobody solved the discrete log&quot; and &quot;somebody recovered the key&quot; only sound incompatible if you conflate the discrete-log &lt;em&gt;problem&lt;/em&gt; with a discrete-log &lt;em&gt;deployment&lt;/em&gt;. The problem is a statement about computational hardness; the deployment is a nonce, a set of parameters, and a negotiation, wrapped around that hardness. The problem stayed hard. The wrapping tore.&lt;/p&gt;

If you remember one operational thing from this article, make it this: deterministic ECDSA and constant-time scalar multiplication are not two names for the same safeguard. A team can adopt RFC 6979, check the box marked &quot;nonce,&quot; and still ship a signer that a co-located attacker times to death. Ask both questions separately -- how is the nonce generated, and how is it used -- because they have separate answers.
&lt;p&gt;If the discipline is &quot;honor the four contracts, not the curve,&quot; what does a deployment that honors all four actually look like in 2026?&lt;/p&gt;
&lt;h2&gt;6. What Correct EC/DH Deployment Looks Like in 2026&lt;/h2&gt;
&lt;p&gt;The state of the art in this field is not a better discrete-log solver -- no such thing exists. It is a &lt;em&gt;deployment discipline&lt;/em&gt; at the three layers the math never controlled. Here it is, pillar by pillar.&lt;/p&gt;
&lt;h3&gt;Nonce: two orthogonal cures, and a primitive that folds in both&lt;/h3&gt;

Derive the nonce `k` deterministically from the private key and the message hash via an HMAC-based construction, so the signer &quot;do[es] not need access to a source of high-quality randomness&quot; [@rfc6979]. Identical key-and-message pairs always yield the same `k`; distinct messages yield unrelated nonces. This closes the *generation* defect -- the PS3 and Android reuse classes cannot occur.
&lt;p&gt;RFC 6979 also quietly records the deeper lesson. It insists on comparing each candidate nonce against the group order rather than reducing it modulo the order, warning that a &quot;simple modular reduction would induce biases that would be detrimental to signature security&quot; [@rfc6979]. That is a standards document, in 2013, acknowledging that &lt;em&gt;bias&lt;/em&gt;, not just reuse, is fatal. You can watch the effect yourself:&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// A raw sampler emits values in [0, 256). We need a nonce in [0, q). const q = 200n, B = 256n, trials = 400000; const raw = () =&amp;gt; BigInt(Math.floor(Number(B) * Math.random())); let biasedHigh = 0, fairHigh = 0; const half = q / 2n; for (let i = 0; i &amp;lt; trials; i++) {   const reduced = raw() % q;              // reduce mod q -&amp;gt; BIASED   if (reduced &amp;gt;= half) biasedHigh++;   let fair; do { fair = raw(); } while (fair &amp;gt;= q);  // compare to q, retry -&amp;gt; FAIR   if (fair &amp;gt;= half) fairHigh++; } console.log(&apos;reduce mod q -&amp;gt; P(nonce in top half): &apos; + (biasedHigh / trials).toFixed(4)); console.log(&apos;compare to q -&amp;gt; P(nonce in top half): &apos; + (fairHigh / trials).toFixed(4)); console.log(&apos;That gap from 0.5 is a free bit for a Hidden Number Problem attacker.&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;Determinism, though, fixes only generation. On hardware it also &lt;em&gt;adds&lt;/em&gt; a risk.&lt;/p&gt;

Compute the nonce from the private key, the message hash, *and* a few bytes of fresh randomness. A dead RNG falls back to the safe deterministic core; a differential fault, which needs two identical signings to differ, is defeated by the fresh input. FIPS 186-5 recommends this for hardware because bare determinism is &quot;of particular concern for ... deterministic signature schemes, as well as embedded or IoT devices and smartcards&quot; [@fips-186-5].

Compute `kG` with a data-independent trace of instructions, branches, and memory accesses, so the runtime never depends on the bit-length or value of `k`. This is the *usage* cure -- the only defense against Minerva, TPM-Fail, and LadderLeak, whose leaks all live in the multiplication, not the sampler. Its precursor is Brumley and Tuveri&apos;s 2011 remote attack on a non-constant-time OpenSSL ladder [@brumley-tuveri-2011].
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Hedged signing and constant-time multiplication are the two cures of the fork, and they do not substitute for each other. A hardware signer needs &lt;em&gt;both&lt;/em&gt;: hedge the generation, and make the usage constant-time. Checking one box leaves the other break wide open.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;New designs can sidestep the whole fork by choosing a misuse-resistant primitive. Ed25519 (RFC 8032) derives its nonce as a hash of the secret and message -- deterministic by construction, no per-signature RNG -- and signs with a single &lt;em&gt;complete&lt;/em&gt;, constant-time addition law that has no exceptional cases to mishandle [@rfc8032]. It folds both cures into the primitive and removes the dangerous choices from the caller&apos;s hands.Misuse-resistant is not misuse-proof. PuTTY derived P-521 ECDSA nonces such that &quot;the first 9 bits of each ECDSA nonce are zero,&quot; allowing &quot;full secret key recovery in roughly 60 signatures&quot; (CVE-2024-31497) [@putty-cve-2024-31497]. Determinism implemented carelessly re-introduced exactly the bias it was meant to prevent.&lt;/p&gt;
&lt;h3&gt;Validation: reject what you cannot vouch for&lt;/h3&gt;
&lt;p&gt;The CurveBall cure is direct: bind a certificate&apos;s curve to the standard parameters of its named curve, and reject explicit parameters or fully re-validate them. The January 2020 patch made &lt;code&gt;crypt32.dll&lt;/code&gt; check the generator, not merely the point [@curveball-cve-2020-0601].&lt;/p&gt;
&lt;p&gt;For key &lt;em&gt;exchange&lt;/em&gt; with static keys, the discipline is fuller. Before using a received point, confirm it is not the identity, lies on the curve, and sits in the correct prime-order subgroup -- the full public-key validation of NIST SP 800-56A, which blocks &lt;a href=&quot;https://paragmali.com/blog/the-curve-was-hard-the-gap-was-soft-a-field-guide-to-using-e/&quot; rel=&quot;noopener&quot;&gt;invalid-curve and small-subgroup attacks&lt;/a&gt; that would otherwise bleed a static key out one query at a time [@sp-800-56a-r3].&lt;/p&gt;
&lt;p&gt;The most durable answer is to make invalid inputs unrepresentable. X25519 works on a Montgomery curve chosen so that &lt;em&gt;every&lt;/em&gt; 32-byte string is an acceptable public key; the RFC even instructs implementations to &quot;accept non-canonical values,&quot; and to run the conditional swap &quot;in constant time&quot; [@rfc7748]. There is no explicit-parameter path to abuse and no on-curve check to forget.This is the &quot;make invalid states unrepresentable&quot; principle applied to cryptography: the safest validation code is the code you deleted because the type system already guarantees the property. But note the boundary carefully.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; X25519&apos;s scalar &quot;clamping&quot; fixes the cofactor and twist problems for ephemeral key exchange. It does &lt;em&gt;not&lt;/em&gt; replace named-curve pinning in the X.509 world, and some protocols still need a contributory-behavior check on the shared secret. Twist-secure curves solve the problems they were designed for -- not every parameter problem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Negotiation: remove the weak option entirely&lt;/h3&gt;
&lt;p&gt;The downgrade pillar&apos;s fix is structural. RFC 7919 replaced ad-hoc, shared Diffie-Hellman primes with a menu of vetted named groups negotiated like curves [@rfc7919]. TLS 1.3 went further and deleted the weak options outright, so there is nothing to downgrade &lt;em&gt;to&lt;/em&gt;.&lt;/p&gt;

&quot;Static RSA and Diffie-Hellman cipher suites have been removed; all public-key based key exchange mechanisms now provide forward secrecy.&quot; -- RFC 8446, the TLS 1.3 specification [@rfc8446].
&lt;p&gt;Because TLS 1.3 permits only strong ephemeral groups and binds the negotiation into the handshake transcript, the Logjam rewrite is not merely discouraged -- it is structurally impossible [@rfc8446]. The end state of the negotiation pillar is a protocol in which the crippled, shared 512-bit group Logjam relied on simply cannot be selected.&lt;/p&gt;
&lt;p&gt;Each of these is a choice, and where there is a choice, engineers argue. Which discipline actually wins in which threat model?&lt;/p&gt;
&lt;h2&gt;7. Competing Approaches: Which Defense Answers Which Adversary&lt;/h2&gt;
&lt;p&gt;These defenses are not rungs on one ladder. They are answers to different adversaries, and the honest way to compare them is one pillar at a time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A constant-time multiplier and a named-curve pin do not compete -- they defend different layers, and a real system needs one from each pillar. The comparisons below rank options &lt;em&gt;within&lt;/em&gt; a pillar. Picking the best nonce discipline does nothing for your parameter validation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Nonce: which signing discipline stops which adversary&lt;/h3&gt;
&lt;p&gt;The load-bearing insight is that no single column stops every row. Generation cures (deterministic [@rfc6979], hedged [@fips-186-5]) and the usage cure (constant-time) are orthogonal, which is why hedged-plus-constant-time became the practitioner default and why Ed25519 [@rfc8032] folds most of both into the primitive.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Adversary / failure&lt;/th&gt;
&lt;th&gt;Randomized (bare RNG)&lt;/th&gt;
&lt;th&gt;Deterministic (RFC 6979)&lt;/th&gt;
&lt;th&gt;Hedged (FIPS 186-5)&lt;/th&gt;
&lt;th&gt;Constant-time mult&lt;/th&gt;
&lt;th&gt;Ed25519&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;RNG returns constant or repeat (PS3, Android)&lt;/td&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;stops&lt;/td&gt;
&lt;td&gt;stops&lt;/td&gt;
&lt;td&gt;orthogonal&lt;/td&gt;
&lt;td&gt;stops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Differential fault on a deterministic signer&lt;/td&gt;
&lt;td&gt;randomness helps&lt;/td&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;stops&lt;/td&gt;
&lt;td&gt;orthogonal&lt;/td&gt;
&lt;td&gt;inherits the caveat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timing leak of nonce bit-length (Minerva, TPM-Fail)&lt;/td&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;stops&lt;/td&gt;
&lt;td&gt;stops (uniform ladder)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-bit ladder leak (LadderLeak)&lt;/td&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;stops&lt;/td&gt;
&lt;td&gt;stops (uniform ladder)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Read the rows, not the columns: reuse fails only the first row and is certain from two signatures; the timing leaks fail three of the five columns and are cured on a different axis entirely. That is the two-cures fork, rendered as a matrix.&lt;/p&gt;
&lt;h3&gt;Validation: three philosophies of not trusting your input&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Named-curve pinning&lt;/th&gt;
&lt;th&gt;Full validation (SP 800-56A)&lt;/th&gt;
&lt;th&gt;All-inputs-valid (X25519)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Philosophy&lt;/td&gt;
&lt;td&gt;reject non-standard parameters&lt;/td&gt;
&lt;td&gt;validate every received input&lt;/td&gt;
&lt;td&gt;make invalid inputs unrepresentable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stops CurveBall (unvalidated generator)&lt;/td&gt;
&lt;td&gt;yes, its exact fix&lt;/td&gt;
&lt;td&gt;yes, re-derives the generator&lt;/td&gt;
&lt;td&gt;yes, no explicit-parameter path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stops invalid-curve / small-subgroup&lt;/td&gt;
&lt;td&gt;partial&lt;/td&gt;
&lt;td&gt;yes, on-curve plus subgroup&lt;/td&gt;
&lt;td&gt;yes, twist-secure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-operation cost&lt;/td&gt;
&lt;td&gt;O(1) table compare&lt;/td&gt;
&lt;td&gt;O(1) up to one scalar mult&lt;/td&gt;
&lt;td&gt;one constant-time ladder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best suited for&lt;/td&gt;
&lt;td&gt;X.509, code-signing, PKI&lt;/td&gt;
&lt;td&gt;static-key ECDH, HSMs&lt;/td&gt;
&lt;td&gt;ephemeral ECDH&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Named-curve pinning is the precise CurveBall fix [@curveball-cve-2020-0601]; SP 800-56A validation is the belt-and-suspenders choice for static keys [@sp-800-56a-r3]; X25519 deletes the problem by construction [@rfc7748]. They converge on stopping CurveBall and diverge on everything else.&lt;/p&gt;
&lt;h3&gt;Downgrade: hardening, deletion, or hedging against tomorrow&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Config hardening + FFDHE&lt;/th&gt;
&lt;th&gt;TLS 1.3&lt;/th&gt;
&lt;th&gt;Hybrid post-quantum&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Removes the 512-bit export rung&lt;/td&gt;
&lt;td&gt;yes, within FFDHE&lt;/td&gt;
&lt;td&gt;yes, structurally&lt;/td&gt;
&lt;td&gt;yes, inherits 1.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Removes shared or unknown primes&lt;/td&gt;
&lt;td&gt;yes, named menu&lt;/td&gt;
&lt;td&gt;yes, fixed groups&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Downgrade protection in the handshake&lt;/td&gt;
&lt;td&gt;no, still in-band&lt;/td&gt;
&lt;td&gt;yes, transcript-bound&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defends against a future Shor adversary&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes, if ML-KEM holds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Residual weakness&lt;/td&gt;
&lt;td&gt;the legacy long tail&lt;/td&gt;
&lt;td&gt;non-TLS 1024-bit primes&lt;/td&gt;
&lt;td&gt;a KEM is not a NIKE, young PQC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;RFC 7919 named groups [@rfc7919] are the transitional hardening for TLS 1.2; TLS 1.3 is the structural end state [@rfc8446]; hybrid X25519 with &lt;a href=&quot;https://paragmali.com/blog/one-event-three-assumptions-five-answers-a-field-guide-to-th/&quot; rel=&quot;noopener&quot;&gt;ML-KEM&lt;/a&gt; (RFC 9954 [@rfc9954], ML-KEM standardized as FIPS 203 [@fips-203]) is the forward hedge against a quantum adversary. Only the last column defends against a break that would be genuine mathematics rather than a deployment slip.&lt;/p&gt;
&lt;p&gt;Every column in every table changed the deployment, not the curve. So is the discrete log itself simply unbreakable? Almost -- and the &quot;almost&quot; is the most honest paragraph in this article.&lt;/p&gt;
&lt;h2&gt;8. Theoretical Limits: What Is Provably True on Both Sides&lt;/h2&gt;
&lt;p&gt;This is the honest concession the thesis has been earning the right to make. There are two provable sides here, and the argument needs both.&lt;/p&gt;
&lt;h3&gt;The hardness side: no room for a strong curve to fall&lt;/h3&gt;
&lt;p&gt;On a well-chosen group, the discrete logarithm is genuinely hard, and we can say so with unusual precision. The best classical attack is Pollard&apos;s rho, a square-root-time random walk that needs negligible memory [@teske-2001]. In the strict generic model its expected cost is about $\sqrt{\pi n/2}$, roughly 1.25 times $\sqrt{n}$ group operations; on an elliptic curve, the negation map (where $P$ and $-P$ share an x-coordinate) trims that to about 0.886 times $\sqrt{n}$. Either way the exponent is the same, matching baby-step giant-step&apos;s $O(\sqrt{n})$ time [@hac-ch11].&lt;/p&gt;
&lt;p&gt;Against that upper bound, Shoup proved a generic &lt;em&gt;lower&lt;/em&gt; bound of $\Omega(\sqrt{p})$: no algorithm restricted to the group operation can do better [@shoup-1997]. The two meet at $\Theta(\sqrt{n})$. The generic gap is essentially closed -- which is exactly why a 256-bit curve delivers around 128-bit security, and why no strong deployed curve&apos;s discrete log has fallen.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Quantity&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Best classical DL attack&lt;/td&gt;
&lt;td&gt;Pollard&apos;s rho, about 1.25 times sqrt(n) generic; 0.886 times sqrt(n) on curves&lt;/td&gt;
&lt;td&gt;negligible space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Baby-step giant-step&lt;/td&gt;
&lt;td&gt;O(sqrt(n)) time and O(sqrt(n)) space&lt;/td&gt;
&lt;td&gt;the space-heavy twin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic lower bound&lt;/td&gt;
&lt;td&gt;Omega(sqrt(p)), provably no faster generic algorithm&lt;/td&gt;
&lt;td&gt;Shoup 1997&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finite-field DH (non-generic)&lt;/td&gt;
&lt;td&gt;sub-exponential via the number field sieve&lt;/td&gt;
&lt;td&gt;why DH needs about 3072-bit primes (2048-bit minimum)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strong deployed curve or standard group ever solved&lt;/td&gt;
&lt;td&gt;none, only Logjam&apos;s crippled shared 512-bit prime&lt;/td&gt;
&lt;td&gt;the thesis in one row&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The last two rows carry the concession. Finite-field Diffie-Hellman is &lt;em&gt;not&lt;/em&gt; generic: the number field sieve solves it in sub-exponential time, which is why finite-field DH needs primes of about 3072 bits to match the security a 256-bit curve gives (2048 bits is the absolute minimum), and why Logjam&apos;s downgrade to 512 bits was finishable at all [@logjam-weakdh-2015]. The number-field-sieve mathematics belongs to the companion article.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Logjam is the only break in this corpus that reached the mathematics, and the reason is structural, not accidental. A finite field carries extra arithmetic structure that a generic group lacks, which lets the number field sieve run in sub-exponential time, faster than the square-root barrier that protects a strong elliptic curve. That structural gap is the sole opening the discrete log ever offered, and even it was not enough alone: the computation became feasible only after the prime was cut to 512 export-grade bits. Raise the group back to a standard 2048-bit prime, or move to a curve where no such shortcut exists, and the opening closes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The leakage side: no room to hide a little bias&lt;/h3&gt;
&lt;p&gt;The other provable side indicts the deployment just as sharply. The Hidden Number Problem makes partial leakage fatal by construction: Boneh and Venkatesan proved that recovering a secret from many small most-significant-bit leaks is tractable [@boneh-venkatesan-1996], and Nguyen and Shparlinski turned that into a working lattice attack on (EC)DSA with partial nonces [@nguyen-shparlinski-2003].&lt;/p&gt;
&lt;p&gt;There is no &quot;a little bias is tolerable&quot; regime: any &lt;em&gt;consistent&lt;/em&gt; partial leak of &lt;code&gt;k&lt;/code&gt;, integrated over enough signatures, provably recovers &lt;code&gt;d&lt;/code&gt;. Two or more leaked bits fall to a lattice; a sub-bit leak falls to Bleichenbacher&apos;s Fourier method [@ladderleak-2020]. &quot;&lt;code&gt;k&lt;/code&gt; is secret, uniform, and unique&quot; is not a best practice -- it is a hard boundary.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The math is safe at the sizes we deploy (the generic gap is closed), and leakage is fatal at any size (the HNP admits no safe margin). Both proofs point the same way: the primitive is not the weak part. The nonce, the parameters, and the negotiation are.&lt;/p&gt;
&lt;/blockquote&gt;

Two clouds sit on the horizon. First, the shared-prime structure Logjam exploited at 512 bits revives at 1024. The Logjam team estimated that precomputing the single most common 1024-bit prime would expose roughly 18% of the top HTTPS sites; a second common prime would reach about 66% of IPsec VPNs and around 26% of SSH servers [@logjam-weakdh-2015] -- the same trick, resized, and a resourced adversary&apos;s project. Second, a large fault-tolerant quantum computer running Shor&apos;s algorithm would break the discrete logarithm outright, curve and finite field alike. That is the one future break that *is* the mathematics, and it is the subject of the companion article, not this one. The deployment answer, already shipping, is hybrid key exchange.
&lt;p&gt;So where, concretely, does EC and DH still break tomorrow?&lt;/p&gt;
&lt;h2&gt;9. Where EC and DH Still Break in the Field&lt;/h2&gt;
&lt;p&gt;The incidents are settled. The &lt;em&gt;classes&lt;/em&gt; are not. Every open problem below is a documented pattern waiting for its next instance, and each one maps to a pillar you have already met.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The next SecureRandom (nonce generation, Pillar 1).&lt;/strong&gt; A signer needs a unique, secret nonce at the exact moment its entropy is weakest: first boot, a headless IoT device with no entropy events, or a freshly cloned virtual machine carrying a duplicated seed. This is the mechanism behind Android 2013 [@android-securerandom-2013], and it recurs whenever hardware is provisioned faster than it can gather randomness.Cloned-VM state duplication is an especially nasty modern form: two hosts that never coordinated can emit the &lt;em&gt;same&lt;/em&gt; &quot;random&quot; nonce because they booted from one snapshot. The signing path can be hedged [@fips-186-5], but key generation and ephemeral key exchange still need a live, well-seeded source.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The next Minerva or LadderLeak (nonce usage, Pillar 1).&lt;/strong&gt; Constant-time scalar multiplication must be re-established against every new microarchitecture -- caches, execution ports, prefetchers, simultaneous multithreading -- and on every fragile incomplete-addition curve. The bit-length leak of Minerva [@minerva-2020] and the sub-bit leak of LadderLeak [@ladderleak-2020] both post-date deterministic nonces precisely because the leak had moved to usage. Formally verified primitives close the &lt;em&gt;known&lt;/em&gt; channels on the &lt;em&gt;chosen&lt;/em&gt; curve; FIPS-mandated P-curves and secp256k1 keep hand-written code in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The long tail of shared primes (downgrade, Pillar 3).&lt;/strong&gt; TLS 1.3 closes the web protocol layer, but export-grade and shared standardized primes outlived their policy elsewhere. IKEv1 VPNs and older SSH still ship shared 1024-bit Diffie-Hellman groups, and a single precomputation per shared prime amortizes across every server using it [@logjam-weakdh-2015]. The consensus recommendation is blunt: retire 1024-bit DH now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The next CurveBall (validation, Pillar 2).&lt;/strong&gt; CurveBall was one unchecked generator in one library [@curveball-cve-2020-0601]; the class is &quot;somewhere in a large validation stack, attacker-supplied parameters are trusted.&quot; Every new parser, certificate profile, or post-quantum key format reopens the class, and a single such bug in a widely embedded validator forges trust across everything that depends on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Post-quantum migration ahead of Shor (cross-pillar).&lt;/strong&gt; Hybrid key exchange -- running X25519 alongside ML-KEM so the session stays secure if either component holds -- is standardized (RFC 9954 [@rfc9954]; concrete groups such as X25519MLKEM768 in the ongoing ECDHE-MLKEM work [@draft-ietf-tls-ecdhe-mlkem]; ML-KEM standardized as FIPS 203 [@fips-203]) and is already deployed at scale: it protected a majority of the human web traffic Cloudflare measured by late 2025 [@cloudflare-pq-radar]. But the migration carries a trap that has nothing to do with the discrete log.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; ML-KEM is a key-&lt;em&gt;encapsulation&lt;/em&gt; mechanism, not a non-interactive key agreement. It does not reproduce X25519&apos;s static Diffie-Hellman semantics, where two long-term public keys combine without any live exchange. Systems that quietly assume &quot;post-quantum DH&quot; is a drop-in replacement for classical DH will find the shape of the primitive has changed underneath them. Migrate deliberately, not by search-and-replace.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;None of these needs new mathematics to break you. Each needs exactly one deployment mistake. Here is how not to make it.&lt;/p&gt;
&lt;h2&gt;10. What to Do on Monday&lt;/h2&gt;
&lt;p&gt;Every item below maps to a specific incident in this article. Do these, and you do not ship the next one.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Never put a bare runtime RNG in the signing path.&lt;/strong&gt; Use deterministic (RFC 6979 [@rfc6979]) or, on hardware, hedged (FIPS 186-5 [@fips-186-5]) nonces. &lt;em&gt;Prevents:&lt;/em&gt; PS3, Android.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Demand constant-time, exception-free scalar multiplication.&lt;/strong&gt; This is a separate control from the nonce discipline, not a duplicate of it. &lt;em&gt;Prevents:&lt;/em&gt; Minerva, TPM-Fail, LadderLeak.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prefer misuse-resistant primitives.&lt;/strong&gt; Ed25519 for new signing [@rfc8032], X25519 for ephemeral key exchange [@rfc7748]; for static-key ECDH, run full public-key validation per SP 800-56A [@sp-800-56a-r3]. &lt;em&gt;Prevents:&lt;/em&gt; whole classes of implementer error.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reject explicit curve parameters; require named curves.&lt;/strong&gt; Pin the curve, and the generator with it. &lt;em&gt;Prevents:&lt;/em&gt; CurveBall [@curveball-cve-2020-0601].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disable export ciphers and use vetted groups.&lt;/strong&gt; RFC 7919 named groups [@rfc7919] for residual TLS 1.2 DHE, or move to TLS 1.3 [@rfc8446]. &lt;em&gt;Prevents:&lt;/em&gt; Logjam.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Never assume a fresh ephemeral rescues a shared prime.&lt;/strong&gt; The number-field-sieve precomputation is per-prime, not per-connection [@logjam-weakdh-2015]. &lt;em&gt;Prevents:&lt;/em&gt; the subtle half of Logjam.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan the post-quantum migration as hybrid, not pure.&lt;/strong&gt; Run a classical curve alongside ML-KEM (RFC 9954 [@rfc9954]); remember a KEM is not a NIKE. &lt;em&gt;Prevents:&lt;/em&gt; harvest-now-decrypt-later exposure.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; New signing design, free to pick the curve: Ed25519. Must use ECDSA on a NIST or secp256k1 curve: hedged RFC 6979 &lt;em&gt;and&lt;/em&gt; a vetted constant-time multiply. Hardware or embedded signer: hedged, never bare-deterministic. Ephemeral key exchange: X25519. Static-key ECDH: full SP 800-56A validation. Certificate or PKI validation: pin named curves. Any TLS: version 1.3, and if you are stuck on 1.2 DHE, RFC 7919 groups with export ciphers off. Long-lived confidentiality: hybrid X25519 with ML-KEM.&lt;/p&gt;
&lt;/blockquote&gt;

1. **Nonce:** For every signature, can you show the nonce is both unpredictable (hedged or deterministic) *and* consumed in constant time? If you can only answer one half, you are exposed to the other.
2. **Parameters:** Does your certificate and key-exchange path reject attacker-supplied explicit parameters and validate every received point against the named curve?
3. **Negotiation:** Can a man-in-the-middle force any weaker group or version than you intend? If you cannot prove &quot;no,&quot; assume &quot;yes.&quot;
&lt;p&gt;Notice that not one item on this list is &quot;use a bigger curve.&quot; For every break except Logjam&apos;s downgrade, key size is irrelevant -- a 384-bit curve reuses a nonce exactly as fatally as a 256-bit one. The controls are about generation, usage, validation, and negotiation, because those are the four contracts, and the contracts are where reality broke.&lt;/p&gt;
&lt;p&gt;Two questions decide whether you got this right, and they happen to be the two most-repeated errors in the field.&lt;/p&gt;
&lt;h2&gt;11. FAQ: Misconceptions, Precisely Corrected&lt;/h2&gt;

Once, and only against a deliberately crippled target. Logjam computed discrete logarithms against a 512-bit export-grade Diffie-Hellman prime -- one that a man-in-the-middle downgrade could select and that many servers had in common [@logjam-weakdh-2015][@cve-2015-4000]. No strong deployed curve -- P-256, Curve25519, secp256k1 -- and no standard-sized group of 2048 bits or more has ever had its discrete log solved in public. The ceiling of the entire corpus is a 512-bit prime that policy left broken.

No, and this is the most common error. A *repeated* nonce recovers the key with certainty from exactly two signatures, by elementary algebra. A merely *biased* nonce emits only a faint statistical signal per signature, so recovery takes many of them plus a dedicated lattice or Fourier attack -- about 2100 for Minerva on secp256r1 [@minerva-2020], roughly 60 for PuTTY&apos;s nine-zero-bit bias [@putty-cve-2024-31497]. One biased signature leaks almost nothing.

No. LadderLeak&apos;s leak is under a single bit per signature, which lattice methods cannot exploit; it revives Bleichenbacher&apos;s Fourier-analysis solver instead [@ladderleak-2020]. The lattice regime is Minerva and TPM-Fail, whose leaks are two bits or more [@minerva-2020][@tpmfail-2020]. Calling all three &quot;lattice attacks&quot; collapses a distinction the attacks themselves are built on.

No. Padding oracles are an RSA and CBC-mode phenomenon; there is no elliptic-curve or Diffie-Hellman padding-oracle exemplar, and you should not go looking to manufacture one. The EC and DH attack surface is exactly the three pillars here -- the nonce, parameter validation, and downgrade. RSA&apos;s padding oracles, and the fact that RSA signing has no per-signature secret nonce to reuse, are covered in the companion piece &quot;How RSA Breaks in Real Life.&quot;

Only against half the problem. RFC 6979 removes the random number generator from the signing path and eliminates the reuse class [@rfc6979], but it does nothing about a timing leak during nonce *usage* -- which is exactly how Minerva, TPM-Fail, and LadderLeak recovered keys from deterministic signers. The complete answer is hedged signing plus constant-time scalar multiplication [@fips-186-5], the two orthogonal cures.

Almost never. The nonce, validation, and downgrade defects are all indifferent to key size: a forged CurveBall certificate is accepted no matter how large the curve is, because the flaw is a skipped generator check rather than a hard curve [@curveball-cve-2020-0601], and a reused or leaked nonce surrenders the private key whether that key is 256 or 384 bits. The one place size mattered was the export-group downgrade, and there the fix was to stop negotiating the 512-bit group at all [@logjam-weakdh-2015], not to enlarge it.

The primitive, no. The discrete logarithm on a strong group remains as hard as it was in the 1970s. What broke, repeatedly, was the deployment around it. Fix the nonce path, validate your parameters against named curves, and retire export and shared groups, and you have closed every break in this article. CurveBall was a validation bug, not a curve break [@curveball-cve-2020-0601] -- and that distinction is the whole point.
&lt;p&gt;Half a century after Diffie and Hellman reduced key exchange to one hard problem, the scoreboard is unambiguous: seven documented field breaks across three deployment layers, and a discrete-log ceiling that only ever reached a deliberately crippled, shared 512-bit export group -- never a strong deployed curve, never a standard-sized group. The mathematics kept its promise. The deployments did not.&lt;/p&gt;

The discrete log never fell. The nonce did, the parameters did, and the negotiation did -- and every one of those is the deployment&apos;s job, not the curve&apos;s.
&lt;p&gt;For the other half of the story -- how a curve itself could be structurally weak, how the number field sieve and Shor&apos;s algorithm actually break the math -- see the structural companion, &quot;How the Discrete Log Would Break.&quot; For the primitive that has no per-signature nonce to reuse and its own &lt;a href=&quot;https://paragmali.com/blog/rsa-is-a-trapdoor-not-a-cryptosystem-oaep-pss-and-the-25-yea/&quot; rel=&quot;noopener&quot;&gt;padding-oracle history&lt;/a&gt;, see the series sibling, &quot;How RSA Breaks in Real Life.&quot; This article was about the gap between a hard problem and a shipped product. That gap is where the keys keep getting out.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;how-ecc-and-dh-break-in-real-life&quot; keyTerms={[
  { term: &quot;Per-signature nonce (k)&quot;, definition: &quot;The once-used secret in ECDSA that must be secret, uniform, and unique per signature; the load-bearing prerequisite behind PS3, Android, Minerva, TPM-Fail, and LadderLeak.&quot; },
  { term: &quot;Nonce reuse vs. bias&quot;, definition: &quot;A repeated nonce recovers the key with certainty from two signatures; a biased nonce needs a statistical Hidden Number Problem attack over many signatures.&quot; },
  { term: &quot;Hidden Number Problem&quot;, definition: &quot;Recovering a fixed secret from many samples that each leak a few most-significant bits; the framework behind every biased-nonce attack.&quot; },
  { term: &quot;Two-cures fork&quot;, definition: &quot;Determinism and hedging fix nonce generation; constant-time scalar multiplication fixes nonce usage. The two are orthogonal and both are required.&quot; },
  { term: &quot;CurveBall (CVE-2020-0601)&quot;, definition: &quot;A Windows CryptoAPI bug that matched a certificate public point but never checked the generator against the named curve; a validation bug, not a curve break.&quot; },
  { term: &quot;Logjam (CVE-2015-4000)&quot;, definition: &quot;A TLS downgrade to a shared 512-bit export Diffie-Hellman prime, breakable because the number field sieve precomputes once per prime; the only discrete log in the corpus.&quot; },
  { term: &quot;Named curve vs. explicit parameters&quot;, definition: &quot;Referencing standardized domain parameters by identifier versus trusting attacker-supplied inline parameters, including a custom generator.&quot; }
]} questions={[
  { q: &quot;Why does a reused nonce recover the key from only two signatures?&quot;, a: &quot;Two signatures with the same nonce share r; subtracting the signing equations cancels the private-key term, yielding the nonce, and one equation then solves for the key.&quot; },
  { q: &quot;Why is LadderLeak not a lattice attack?&quot;, a: &quot;Its leak is under one bit per signature, which lattices cannot exploit, so it uses Bleichenbacher&apos;s Fourier method instead. Lattices are the Minerva and TPM-Fail regime.&quot; },
  { q: &quot;Is Logjam a counterexample to the thesis?&quot;, a: &quot;No. The only discrete log computed landed on a downgrade-forced, crippled, shared 512-bit prime; no strong or standard-sized group ever fell.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>elliptic-curve-cryptography</category><category>ecdsa</category><category>diffie-hellman</category><category>nonce-reuse</category><category>side-channel-attacks</category><category>tls-security</category><category>post-quantum</category><category>applied-cryptography</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>How AES Breaks in Real Life: The Attacks That Never Touched the Cipher</title><link>https://paragmali.com/blog/how-aes-breaks-in-real-life-the-attacks-that-never-touched-t/</link><guid isPermaLink="true">https://paragmali.com/blog/how-aes-breaks-in-real-life-the-attacks-that-never-touched-t/</guid><description>AES-the-cipher has never been broken in the field -- its deployments have. KRACK, repeated GCM nonces, and cache timing broke the wrapper, never the block.</description><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate><content:encoded>
**AES-the-cipher has never been broken in the field. Your AES traffic was decrypted anyway.** The best publicly known attack on the full cipher costs about $2^{126}$ operations for AES-128 -- its own authors say it does &quot;not threaten the practical use of AES in any way&quot; [@biclique-2011]. Yet real Wi-Fi sessions were decrypted, real HTTPS connections were forged, and real AES keys were lifted out of running servers. None of it touched the 128-bit block math. AES is a *permutation*, not a cryptosystem: to protect a message you wrap it in an *implementation*, a *mode*, and a *protocol*, and it was those three wrappers that failed -- a T-table lookup leaked the key through cache timing [@bernstein-2005; @osvik-shamir-tromer-2006]; 184 live HTTPS servers repeated an AES-GCM nonce and weaponized Joux&apos;s 2006 forbidden attack into forgery [@nonce-disrespecting-2016; @joux-2006]; and KRACK&apos;s replayed handshake rewound the AES-CCMP nonce and reused a keystream [@krack-2017]. Every fix changed *how AES is used*, never AES.
&lt;h2&gt;1. AES Is Unbroken. Your AES Traffic Was Decrypted Anyway.&lt;/h2&gt;
&lt;p&gt;The best publicly known attack on the full AES cipher costs roughly $2^{126}$ operations for AES-128 -- a number so far beyond feasible that its own discoverers wrote it does &quot;not threaten the practical use of AES in any way&quot; [@biclique-2011]. To put $2^{126}$ in scale: if every one of the billions of computers on Earth checked a billion keys a second, you would still wait longer than the age of the universe, many times over. By any honest measure, the cipher stands.&lt;/p&gt;
&lt;p&gt;And yet, in the same decade that number was published, attackers decrypted live Wi-Fi sessions, forged authenticated HTTPS connections, and lifted AES keys straight out of running servers -- none of them going anywhere near that $2^{126}$ wall. How does a cipher nobody can break keep producing decrypted traffic and stolen keys?&lt;/p&gt;
&lt;p&gt;The resolution is the thesis of this article, and you can hold it right now: &lt;strong&gt;none of these breaks touched the 128-bit block math.&lt;/strong&gt; AES is a keyed &lt;em&gt;permutation&lt;/em&gt; -- a function that scrambles one 16-byte block into another -- and nothing more. It is not a cryptosystem. To protect a real message you wrap that permutation in three things: an &lt;em&gt;implementation&lt;/em&gt; that computes it on real silicon, a &lt;em&gt;mode&lt;/em&gt; that chains it across a message, and a &lt;em&gt;protocol&lt;/em&gt; that establishes keys and drives the mode. Each wrapper is a new, independent way to fail. And it was the wrappers, every time, that failed.&lt;/p&gt;
&lt;p&gt;That gives you a diagnostic sentence to carry through the rest of this piece. When your encrypted traffic falls, the question is never &quot;was AES broken?&quot; It is: &lt;em&gt;which layer around AES failed -- the implementation, the mode, or the protocol?&lt;/em&gt; By the end you will be able to drop KRACK, the GCM nonce scandal, and tomorrow&apos;s not-yet-published incident into that one question on sight.&lt;/p&gt;

To break AES in the field, you never touch AES. The table lookup leaks, the nonce repeats, the handshake rewinds -- and the 128-bit block math never moves.
&lt;p&gt;This is Part 1 of &lt;em&gt;How It Breaks in Real Life&lt;/em&gt;, a series with a single recurring claim: the primitive&apos;s mathematics almost never caused the break; the deployment did -- the nonce, the padding, key generation, a downgrade, a validation bug, or a deprecated-but-still-live algorithm. AES is the cleanest case, which is why it goes first. Its companion piece, &lt;em&gt;How AES Would Break&lt;/em&gt;, asks what it would take to move the block itself: the key schedule, related-key cryptanalysis, the slow erosion of the security margin. That is the &lt;em&gt;would-break-in-theory&lt;/em&gt; story. This is the &lt;em&gt;did-break-in-the-field&lt;/em&gt; one.&lt;/p&gt;
&lt;p&gt;If the cipher was intact and the block math never moved, then everything that broke was built &lt;em&gt;around&lt;/em&gt; it. To see how &quot;unbroken cipher&quot; and &quot;decrypted traffic&quot; can both be true, you have to go back to what AES actually is -- and, more to the point, what it is not.&lt;/p&gt;
&lt;h2&gt;2. Why a Cipher Is Not a Cryptosystem&lt;/h2&gt;
&lt;p&gt;In October 2000, after a three-year open competition in which the world&apos;s cryptographers were &lt;em&gt;invited to attack&lt;/em&gt; the candidates, NIST selected Joan Daemen and Vincent Rijmen&apos;s Rijndael as the Advanced Encryption Standard [@nist-press-2000]. The choice followed a public evaluation of fifteen submissions narrowed to five finalists, judged on security, performance, and efficiency from servers to smart cards [@nist-aes-dev; @nist-jres-r1-1999; @nist-jres-r2-2001]. That adversarial process is why the core math has held for over two decades -- and why, when things break in the field, the fault lies elsewhere. Winning the competition made Rijndael a &lt;em&gt;cipher&lt;/em&gt;, not a cryptosystem.&lt;/p&gt;
&lt;p&gt;Here is the distinction the whole article turns on. AES, standardized as FIPS 197 in 2001 and editorially refreshed in 2023 with &lt;em&gt;no technical change&lt;/em&gt; to the algorithm, is a keyed &lt;strong&gt;128-bit permutation&lt;/strong&gt; [@fips-197]. It maps one 128-bit block to one 128-bit block under a 128-, 192-, or 256-bit key, and that is &lt;em&gt;all&lt;/em&gt; it does.AES applies 10, 12, or 14 rounds for the 128-, 192-, and 256-bit keys. The round function is identical across all three; key size changes only the round count and the key schedule that feeds it [@fips-197]. It has no notion of a message longer than 16 bytes, no integrity, no session or conversation. Feed it the same block and key twice and you get the same output twice. On its own it cannot safely encrypt a paragraph, let alone a Wi-Fi session.&lt;/p&gt;

A **block cipher** is a keyed permutation on fixed-size blocks -- AES maps one 16-byte block to another under a key. A **mode of operation** (CTR, CCM, GCM, and others) is the wrapper that chains that permutation across an arbitrary-length message and, in authenticated modes, adds integrity. The block cipher is the engine; the mode is the car. You do not drive an engine.
&lt;p&gt;To turn that engine into something that protects real data, you add three wrappers, and each one is a new, independent failure surface:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an &lt;strong&gt;implementation&lt;/strong&gt; that computes the permutation on a real CPU, and may leak through timing or cache behavior;&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;&lt;a href=&quot;https://paragmali.com/blog/the-ciphertext-was-unbreakable-the-attacker-rewrote-it-anyw/&quot; rel=&quot;noopener&quot;&gt;mode of operation&lt;/a&gt;&lt;/strong&gt; that chains the permutation across a whole message and, in AEAD modes, adds integrity -- and imposes a contract on how you feed it nonces;&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;protocol&lt;/strong&gt; that establishes keys and drives the mode, and can mismanage them.&lt;/li&gt;
&lt;/ul&gt;

flowchart TD
    B[&quot;AES 128-bit permutation -- never broken in the field&quot;]
    I[&quot;Implementation layer -- computes the block in code or silicon&quot;]
    M[&quot;Mode layer -- chains the block, adds integrity, imposes a nonce contract&quot;]
    P[&quot;Protocol layer -- establishes keys and drives the mode&quot;]
    B --&amp;gt; I --&amp;gt; M --&amp;gt; P
    I -. break .-&amp;gt; AtkI[&quot;Cache timing leaks the key&quot;]
    M -. break .-&amp;gt; AtkM[&quot;A repeated nonce reuses a keystream&quot;]
    P -. break .-&amp;gt; AtkP[&quot;A replayed handshake rewinds the nonce&quot;]
&lt;p&gt;This map is the lens for everything that follows. The block sits at the center, untouched. The implementation wraps it, the mode wraps that, the protocol wraps that -- and the three field breaks in this article land on the three outer rings, in order, working outward from the silicon.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; AES has a &lt;strong&gt;128-bit block size&lt;/strong&gt; for all three variants; only the &lt;em&gt;key&lt;/em&gt; is 128, 192, or 256 bits. When this article says &quot;the 128-bit block math never moved,&quot; it means the block permutation, not the key. Hold onto a foreshadow: key size will turn out to be irrelevant to every break here. Against nonce reuse and a reinstalled handshake key, AES-256 fails exactly as fast as AES-128; against cache timing it is just as vulnerable, though a longer key takes proportionally more leakage to extract.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One more piece of honesty before we watch the wrappers fail. The series this article opens says the primitive&apos;s math &lt;em&gt;almost&lt;/em&gt; never causes the break -- &quot;almost,&quot; not &quot;never&quot; -- and the hedge is load-bearing.Every break in this article is independent of key size. That is not a rhetorical flourish; it is a literal property of cache timing, nonce reuse, and handshake replay, none of which involve guessing key bits.&lt;/p&gt;

Some deployed primitives genuinely fell as *math*, not deployment. DES died to brute force because its 56-bit key was too short, which is *why* NIST ran the AES competition in the first place. RC4&apos;s keystream biases were turned into real plaintext recovery against TLS [@alfardan-2013]. MD5 and SHA-1 fell to collision attacks -- the SHAttered project produced the first SHA-1 collision at roughly $2^{63}$ hash computations [@shattered-2017]. Those are real breaks of the primitive&apos;s mathematics. AES is simply not one of them, which is what makes its deployment-versus-primitive split so clean. Never harden &quot;almost never&quot; into &quot;the math never breaks.&quot;
&lt;p&gt;Three wrappers, three contracts, three ways to fail -- none of them the cipher. Before watching each one break in the field, you need to see the contracts up close: what a mode actually promises, what a nonce is, and why the humble table lookup is a loaded gun.&lt;/p&gt;
&lt;h2&gt;3. The Layers Around the Block, and the Contracts They Impose&lt;/h2&gt;
&lt;p&gt;Every one of the three field breaks is the violation of a single, specific promise. If you see the promise clearly now, the break will look obvious later. So here are all three contracts, one per layer, in order.&lt;/p&gt;
&lt;h3&gt;The implementation contract: a lookup should not leak what it looked up&lt;/h3&gt;
&lt;p&gt;Computing AES the naive way -- byte by byte through its S-box and its field multiplications -- is slow in software. So fast implementations fold an entire round into precomputed lookup tables. Four tables of about 1 KB each, conventionally called &lt;code&gt;T0&lt;/code&gt; through &lt;code&gt;T3&lt;/code&gt;, turn each output column of a round into &lt;code&gt;T0[a] XOR T1[b] XOR T2[c] XOR T3[d]&lt;/code&gt;: four reads and three XORs, so a full round is four of those -- sixteen reads in all [@bernstein-2005].&lt;/p&gt;

A set of precomputed lookup tables (typically four tables of 256 four-byte entries, about 4 KB total) that fold AES&apos;s SubBytes, ShiftRows, and MixColumns steps into a handful of table reads per round. It is purely a speed optimization. The block permutation it computes is identical to the slow version -- but *how* it computes it now depends on secret-derived indices.
&lt;p&gt;The hidden assumption is that a table read takes the same time regardless of which entry you read. On a real CPU with a cache hierarchy, that is &lt;em&gt;false&lt;/em&gt;: the first access to a memory line is slow, a cached line is fast, and an attacker can measure the difference. And the indices &lt;code&gt;a, b, c, d&lt;/code&gt; are secret -- state bytes derived from the key XORed with the plaintext. So which table lines get touched, and therefore the timing, depends on the key. The implementation&apos;s contract is that a data-dependent lookup must not leak the data. The cache breaks it for free.&lt;/p&gt;
&lt;h3&gt;The mode contract: never repeat a (key, nonce) pair&lt;/h3&gt;
&lt;p&gt;A mode turns the one-block permutation into something that encrypts messages and detects tampering. Modern modes aim for AEAD.&lt;/p&gt;

Authenticated Encryption with Associated Data: a mode that provides confidentiality *and* integrity at once. It encrypts the plaintext and produces an authentication tag over both the ciphertext and some associated data (headers, sequence numbers) that is authenticated but not encrypted. GCM and CCM are AEAD modes; if the tag does not verify, the receiver rejects the message.
&lt;p&gt;The workhorse construction is counter mode. AES is run on a counter to produce a &lt;em&gt;keystream&lt;/em&gt;, and the keystream is XORed with the plaintext: keystream block $k_i = \mathrm{AES}_K(\text{nonce} \parallel i)$, and ciphertext $C_i = P_i \oplus k_i$. This is elegant and fast and fully parallel. It also carries one absolute obligation.&lt;/p&gt;

A **nonce** is a &quot;number used once.&quot; In counter-based modes the pair (key, nonce) must be **unique for every encryption under that key**. The nonce need not be secret or random -- a counter is fine -- but it must never repeat. This is the single most load-bearing rule in practical symmetric cryptography, and it is the rule every mode-and-protocol break in this article violates.
&lt;p&gt;Watch what happens if you break it. If two messages are encrypted under the same key &lt;em&gt;and the same nonce&lt;/em&gt;, they get the &lt;em&gt;same&lt;/em&gt; keystream. XOR the two ciphertexts and the keystream cancels:&lt;/p&gt;
&lt;p&gt;$$C_1 \oplus C_2 = (P_1 \oplus k) \oplus (P_2 \oplus k) = P_1 \oplus P_2$$&lt;/p&gt;
&lt;p&gt;The key never appears. The attacker who captures two same-nonce ciphertexts learns the XOR of the two plaintexts, and if they know or can guess one, they get the other -- with the cipher fully intact.&lt;/p&gt;

The failure that follows from a repeated (key, nonce) in a counter-based mode: identical keystream for two messages, so $C_1 \oplus C_2 = P_1 \oplus P_2$. The plaintext XOR leaks and the key is never touched. This one mechanic drives *both* the GCM break and the WPA2/CCMP break later in this article.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A block cipher in a counter-based mode is a keystream generator. Feed it the same (key, nonce) twice and you get the same keystream, so $C_1 \oplus C_2 = P_1 \oplus P_2$ -- the plaintext XOR leaks and the key is never touched. The nonce-uniqueness contract is the one promise that stops this. Break it and the strongest cipher in the world protects nothing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can watch the cancellation happen. Nothing below is real AES -- the toy keystream stands in for AES-CTR output -- but the XOR algebra is exactly the algebra of the real break.&lt;/p&gt;
&lt;p&gt;{`
// Same (key, nonce) =&amp;gt; same keystream for BOTH messages. That is the whole bug.
const keystream = [0x9e, 0x37, 0xb1, 0xa4, 0x55, 0x0c, 0xd2, 0x6f];&lt;/p&gt;
&lt;p&gt;function xorBytes(a, b) {
  const out = [];
  for (let i = 0; i &amp;lt; a.length; i++) out.push(a[i] ^ b[i]);
  return out;
}
const toBytes = (s) =&amp;gt; Array.from(s).map((c) =&amp;gt; c.charCodeAt(0));&lt;/p&gt;
&lt;p&gt;const p1 = toBytes(&quot;ATTACK 0&quot;);
const p2 = toBytes(&quot;defend!!&quot;);&lt;/p&gt;
&lt;p&gt;const c1 = xorBytes(p1, keystream);   // what the attacker sees
const c2 = xorBytes(p2, keystream);   // what the attacker sees&lt;/p&gt;
&lt;p&gt;const leaked = xorBytes(c1, c2);      // C1 ^ C2, computed with NO key
const truth  = xorBytes(p1, p2);      // P1 ^ P2, the secret relationship&lt;/p&gt;
&lt;p&gt;console.log(&quot;C1 ^ C2 =&quot;, leaked.join(&quot;,&quot;));
console.log(&quot;P1 ^ P2 =&quot;, truth.join(&quot;,&quot;));
console.log(&quot;keystream cancelled?&quot;, JSON.stringify(leaked) === JSON.stringify(truth));
// Know one plaintext, recover the other. The cipher stayed perfectly strong.
`}&lt;/p&gt;
&lt;p&gt;GCM adds one more thing to worry about. Its integrity tag is built from a secret authentication subkey derived from the key alone, and &lt;a href=&quot;https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/&quot; rel=&quot;noopener&quot;&gt;a repeated nonce&lt;/a&gt; exposes that subkey to attack as well -- which is how confidentiality loss becomes &lt;em&gt;forgery&lt;/em&gt;. We will pull that thread in the next section.&lt;/p&gt;
&lt;p&gt;Even with perfectly unique nonces, GCM has a budget. With random 96-bit nonces, NIST caps a single key at fewer than $2^{32}$ invocations, because random 96-bit values begin colliding around the birthday bound [@sp-800-38d]. Uniqueness is not just a coding rule; it is a counting problem.&lt;/p&gt;

When AES-GCM came to TLS 1.2, RFC 5288 let the implementation choose part of each record&apos;s nonce -- an &quot;explicit nonce&quot; -- and its own security-considerations text carried a &quot;Counter Reuse&quot; warning that a repeated counter is catastrophic [@rfc-5288]. The contract was not merely implied; it was written down, in the same document that shipped the feature. A decade later, a scan of the live Internet found servers breaking it anyway.
&lt;h3&gt;The protocol contract: install a key once, so its nonce only ever counts up&lt;/h3&gt;
&lt;p&gt;A mode still needs a protocol to establish keys and supply nonces. WPA2&apos;s 4-way handshake derives a fresh Pairwise Transient Key, and AES-CCMP then encrypts each frame with a nonce built from an incrementing packet number; CCM, like GCM, forbids repeating that counter under one key [@sp-800-38c; @ieee-80211i-2004]. The assumption is simple and, on its face, obviously true: a key is installed exactly once, so the packet number only ever counts &lt;em&gt;up&lt;/em&gt; and never rewinds.&lt;/p&gt;
&lt;p&gt;Here are the three contracts side by side. Keep the last column in view -- it is the bill each violation runs up.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;The contract it imposes&lt;/th&gt;
&lt;th&gt;What a violation costs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;Code or silicon that computes the AES round&lt;/td&gt;
&lt;td&gt;A data-dependent lookup must not leak the data&lt;/td&gt;
&lt;td&gt;Cache/timing side channel recovers the key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mode&lt;/td&gt;
&lt;td&gt;CTR, GCM, CCM chaining the block across a message&lt;/td&gt;
&lt;td&gt;The (key, nonce) pair must be unique per encryption&lt;/td&gt;
&lt;td&gt;Keystream reuse leaks $P_1 \oplus P_2$; in GCM, forgery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protocol&lt;/td&gt;
&lt;td&gt;Handshake and key management driving the mode&lt;/td&gt;
&lt;td&gt;A key is installed once, so its nonce only counts up&lt;/td&gt;
&lt;td&gt;A reinstalled key rewinds the nonce, forcing reuse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Three contracts, each reasonable, each &lt;em&gt;unenforced by the cipher&lt;/em&gt; -- the permutation cannot know whether you fed it a repeated nonce or read a leaky table. Which raises the only question that matters: in the real world, running on real servers and real routers, do these contracts actually hold? They do not. Here is where, when, and how each one broke.&lt;/p&gt;
&lt;h2&gt;4. Three Field Breaks, Three Layers, One Pattern&lt;/h2&gt;
&lt;p&gt;The comfortable belief is &quot;we standardized a strong cipher, so our encrypted traffic is safe.&quot; What follows are three independent refutations of that inference -- one at each layer around the block, ordered by when the field break landed. Read them as a catalog, not a lineage: these are not ciphers that replaced one another but three &lt;em&gt;simultaneous&lt;/em&gt; layers, every real deployment has all three at once, and the chronology is the attacker&apos;s frontier moving &lt;em&gt;outward&lt;/em&gt; as each inner layer hardened.&lt;/p&gt;

timeline
    title From the silicon outward -- one pattern, three layers
    2005-2006 : Implementation layer : Cache timing recovers AES keys (Bernstein, Osvik-Shamir-Tromer)
    2016 : Mode layer : 184 HTTPS servers repeat a GCM nonce (Böck et al., weaponizing Joux 2006)
    2017 : Protocol layer : KRACK rewinds the AES-CCMP nonce (Vanhoef and Piessens)
&lt;h3&gt;Generation 1: the implementation leaked (2005-2006)&lt;/h3&gt;
&lt;p&gt;In 2005, Daniel J. Bernstein did something the FIPS 197 math says is impossible: he recovered a full AES key &lt;em&gt;remotely&lt;/em&gt;, over a network -- without breaking AES at all [@bernstein-2005; @bernstein-index]. The target was a server doing nothing but AES under clean timing conditions, so the demonstration was remote in principle rather than turnkey -- and alarming precisely because it was not purely local. It ran ordinary T-table AES, whose secret-dependent indices left the encryption&apos;s timing faintly correlated with the key; enough samples pinned the key bytes. A year later, Dag Arne Osvik, Adi Shamir, and Eran Tromer formalized the idea into two reusable cache attacks -- &lt;strong&gt;Prime+Probe&lt;/strong&gt; and &lt;strong&gt;Evict+Time&lt;/strong&gt; -- recovering keys with a modest number of encryptions on a shared machine [@osvik-shamir-tromer-2006].&lt;/p&gt;

Recovering a secret from the physical *side effects* of a computation -- its timing, its cache footprint, its power draw -- rather than from the algorithm&apos;s inputs and outputs. A cache-timing attack on T-table AES watches which cache lines the lookups touch; because the touched lines depend on key-derived indices, the access pattern leaks the key.
&lt;p&gt;The mechanism is worth seeing as a chain, because every link is outside the cipher:&lt;/p&gt;

flowchart LR
    K[&quot;Secret index = key byte XOR plaintext byte&quot;] --&amp;gt; L[&quot;Which T-table cache line is touched&quot;]
    L --&amp;gt; M[&quot;Cache hit or miss changes measurable timing&quot;]
    M --&amp;gt; R[&quot;Attacker narrows and then pins the key byte&quot;]
&lt;p&gt;You can feel the leak in miniature. A real CPU caches memory in lines of 64 bytes, so a 256-entry table falls into a handful of cache lines. The attacker never sees the secret index -- only which &lt;em&gt;line&lt;/em&gt; was touched. Watch a single observation cut the keyspace:&lt;/p&gt;
&lt;p&gt;{`
// A CPU caches memory in 64-byte lines, so 256 one-byte table entries fall into
// a few &quot;cache lines.&quot; The attacker sees ONLY which line was touched -- not the index.
const LINE = 64;
const touchedLine = (index) =&amp;gt; Math.floor(index / LINE);&lt;/p&gt;
&lt;p&gt;const secretKeyByte = 0xB7;                   // unknown to the attacker
const plaintextByte = 0x2A;                   // attacker-chosen, known
const realIndex = secretKeyByte ^ plaintextByte;
const observed = touchedLine(realIndex);      // the only thing that leaks&lt;/p&gt;
&lt;p&gt;// Which key bytes are consistent with the observed cache line?
const candidates = [];
for (let k = 0; k &amp;lt; 256; k++) {
  if (touchedLine(k ^ plaintextByte) === observed) candidates.push(k);
}
console.log(&quot;observed cache line:&quot;, observed);
console.log(&quot;key-byte candidates remaining:&quot;, candidates.length, &quot;of 256&quot;);
console.log(&quot;true key byte still in the set?&quot;, candidates.includes(secretKeyByte));
// One measurement: 256 -&amp;gt; 64. Vary the known plaintext, intersect the sets, and the byte falls out.
`}&lt;/p&gt;
&lt;p&gt;The insight is the sharpest edge of the whole thesis, so state it plainly.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Cache timing recovers AES keys in the field -- that is real, and it is the reason &quot;AES is unbroken&quot; does not mean &quot;your key is safe.&quot; But the leak lives in the T-table&apos;s &lt;em&gt;memory-access pattern&lt;/em&gt;, not in the permutation. The table is a speed optimization; swap it for a leak-free implementation and the key stops leaking while the cipher stays byte-for-byte identical. Keys fall, the cipher stands. Keep that distinction sharp -- collapsing it is the reader&apos;s default error.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The fix pointed straight at the next era, and it changed how AES is &lt;em&gt;computed&lt;/em&gt;, never what AES computes. Two responses followed: &lt;strong&gt;constant-time bitsliced software&lt;/strong&gt;, which replaces the tables with data-independent boolean logic so no secret ever indexes memory [@kasper-schwabe-2009]; and, decisively, &lt;strong&gt;hardware AES-NI&lt;/strong&gt;, which executes each round in silicon with no lookup tables and data-independent latency [@intel-aes-ni]. Once AES-NI reached mainstream CPUs in the early 2010s, this surface largely closed -- and the attacker&apos;s frontier moved one ring outward, to the mode.The formalization is due to &lt;strong&gt;Osvik, Shamir, and Tromer&lt;/strong&gt; -- not Biham, a common misattribution [@osvik-shamir-tromer-2006].&lt;/p&gt;
&lt;h3&gt;Generation 2: the mode&apos;s contract was violated (2006, then 2016)&lt;/h3&gt;
&lt;p&gt;In June 2006, Antoine Joux submitted a public comment to NIST with a quietly devastating observation about GCM, now known as the &quot;forbidden attack&quot; [@joux-2006]. GCM, designed by David McGrew and John Viega, authenticates with a &lt;a href=&quot;https://paragmali.com/blog/the-tag-verified-the-cipher-held-the-forgery-went-through-a-/&quot; rel=&quot;noopener&quot;&gt;one-time polynomial MAC&lt;/a&gt; over the field $\mathrm{GF}(2^{128})$, keyed by a secret subkey derived from the encryption key alone [@mcgrew-viega-2004].&lt;/p&gt;

GCM&apos;s authentication secret, computed as $H = \mathrm{AES}_K(0^{128})$ -- the encryption of an all-zero block under the key. Every authentication tag is a polynomial in $H$ evaluated over the ciphertext, masked by $\mathrm{AES}_K(J_0)$ where $J_0$ comes from the nonce. Security depends on $H$ staying secret, which in turn depends on the nonce never repeating.
&lt;p&gt;Joux&apos;s point: if a (key, nonce) pair repeats, the mask $\mathrm{AES}_K(J_0)$ is &lt;em&gt;identical&lt;/em&gt; across the two messages, so the difference of their tags becomes a polynomial equation over $\mathrm{GF}(2^{128})$ whose unknown is $H$. Solve for the roots and you recover $H$; with $H$ in hand, you can forge a valid authentication tag for a message you chose -- universal forgery [@joux-2006]. The confidentiality loss from keystream reuse was already bad; this makes &lt;em&gt;integrity&lt;/em&gt; fall too.&lt;/p&gt;

flowchart TD
    N[&quot;Same (key, nonce) used for two messages&quot;] --&amp;gt; KS[&quot;Identical CTR keystream&quot;]
    KS --&amp;gt; C[&quot;C1 XOR C2 equals P1 XOR P2, confidentiality lost&quot;]
    N --&amp;gt; J[&quot;Identical tag mask AES_K of J0&quot;]
    J --&amp;gt; EQ[&quot;Tag difference becomes a polynomial equation in H over GF(2 to the 128)&quot;]
    EQ --&amp;gt; F[&quot;Roots reveal the subkey H, enabling forgery&quot;]
&lt;p&gt;For ten years this was a footnote -- a warning about a contract nobody, surely, would break. Then, in 2016, Hanno Böck, Aaron Zauner, Sean Devlin, Juraj Somorovsky, and Philipp Jovanovic scanned the Internet and found it broken in the wild [@nonce-disrespecting-2016]. Their paper, &quot;Nonce-Disrespecting Adversaries,&quot; reported &lt;strong&gt;184 live HTTPS servers actually repeating GCM nonces&lt;/strong&gt; -- &quot;which fully breaks the authenticity of the connections&quot; -- among them large corporations, financial institutions, and a credit-card company, plus more than 70,000 servers using random nonces at volume risk.&lt;/p&gt;
&lt;p&gt;They then did the thing Joux only described: they weaponized the repeats into working forgeries and injected content into live sessions. The root cause was mundane and entirely operational -- buggy hardware and firmware nonce generators, and counters that reset. The mode&apos;s assumption failed; AES did exactly what it was told.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; GCM will not stop you from repeating a nonce. Never generate a nonce you might repeat across process restarts, threads, forks, or virtual-machine clones and snapshots. A saved counter that resets to zero on restart is worse than useless -- it guarantees the reuse. AES-256 offers exactly zero protection here: key size is irrelevant to nonce reuse.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Two precisions keep this honest. First, the mechanism is layer-agnostic in the worst way: this is the &lt;em&gt;same keystream-reuse atom&lt;/em&gt; from Section 3, now carrying a forgery payload because GCM&apos;s integrity also leans on the nonce. Second, resist the tempting overstatement.&lt;strong&gt;Precision lock.&lt;/strong&gt; One nonce reuse is &lt;em&gt;already&lt;/em&gt; a break: it immediately leaks $C_1 \oplus C_2 = P_1 \oplus P_2$ and gives one root-finding equation for $H$. But &lt;em&gt;uniquely pinning&lt;/em&gt; $H$ for reliable universal forgery generally needs at least two collisions. Say &quot;one reuse is already catastrophic, and more reuse pins the subkey&quot; -- never &quot;one reuse recovers the key&quot; [@joux-2006]. The evidence for live reuse is the Böck et al. scan, five authors, 184 servers -- not the frequently miscited IBM Domino CVE [@nonce-disrespecting-2016].&lt;/p&gt;
&lt;p&gt;The fixes, once again, changed the &lt;em&gt;usage&lt;/em&gt; and left the cipher alone: nonce-misuse-resistant AES-GCM-SIV, where a repeat leaks only whether two messages were equal [@rfc-8452], and TLS 1.3&apos;s deterministic per-record nonce, which deletes the implementation-chosen &quot;explicit nonce&quot; that RFC 5288 had exposed [@rfc-8446]. As the mode hardened, the frontier moved one final ring outward -- to the protocol.&lt;/p&gt;
&lt;h3&gt;Generation 3: the protocol reused the nonce (2017)&lt;/h3&gt;
&lt;p&gt;WPA2&apos;s 4-way handshake exists to install a fresh key on both sides. The client installs its Pairwise Transient Key after message 3, and AES-CCMP then encrypts every frame with a nonce built from a packet number that only counts up. For reliability, the standard lets message 3 be retransmitted -- and there the trap was set. In 2017, Mathy Vanhoef and Frank Piessens showed that an attacker who captures and &lt;em&gt;replays message 3&lt;/em&gt; forces the client to reinstall a key it is already using, resetting the CCMP packet number and replay counter to their starting values [@krack-2017]. The nonce rewinds. The keystream repeats. This is KRACK: Key Reinstallation Attack.&lt;/p&gt;

Installing a key that is already in use. Because installing a key also initializes its associated nonce or packet-number counter, reinstalling an in-use key *rewinds* that counter to its starting value -- forcing the same (key, nonce) pairs, and therefore the same keystream, to be used again. KRACK triggers this by replaying a handshake message the protocol was willing to accept twice.

sequenceDiagram
    participant C as Client
    participant A as Access Point
    participant M as Attacker
    A-&amp;gt;&amp;gt;C: Message 1 (ANonce)
    C-&amp;gt;&amp;gt;A: Message 2 (SNonce)
    A-&amp;gt;&amp;gt;C: Message 3 (install key)
    Note over C: Installs PTK, packet number starts counting up
    C-&amp;gt;&amp;gt;A: Message 4 (acknowledge)
    M-&amp;gt;&amp;gt;C: Replay Message 3
    Note over C: Reinstalls the same key, packet number resets to start
    Note over C,A: Nonce reused, keystream repeats, frames become decryptable
&lt;p&gt;The insight is the same shape as before, delivered one layer further out: AES-CCMP did &lt;em&gt;exactly what it was told&lt;/em&gt;. The permutation was flawless; the state machine told it to reuse a nonce, and it obeyed. The consequence is keystream reuse -- identical (key, nonce) yields $C_1 \oplus C_2 = P_1 \oplus P_2$ -- so a known-plaintext frame yields the keystream and decrypts the colliding frame.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scope lock.&lt;/strong&gt; Against AES-CCMP specifically, KRACK forces nonce-reuse &lt;em&gt;decryption and replay&lt;/em&gt; -- not forgery and not AES key recovery. Forgery arises for the TKIP and GCMP cases, not CCMP. The especially devastating all-zero-key variant was an Android and Linux &lt;code&gt;wpa_supplicant&lt;/code&gt; implementation bug that reinstalled an all-zero key, still not a break of AES [@krack-2017].&lt;/p&gt;
&lt;p&gt;The fix was a backwards-compatible patch to the handshake state machine -- refuse to reinstall an in-use key -- shipped across Android, Linux, Apple, Windows, and OpenBSD in 2017 [@krack-2017]. Structurally, the Wi-Fi Alliance announced WPA3 in 2018 [@wifi-wpa3-2018]; its Personal mode swaps WPA2&apos;s pre-shared-key authentication for the SAE (Dragonfly) key exchange and mandates anti-reinstallation checks plus management-frame protection. The 4-way handshake still installs the pairwise key, so KRACK immunity comes from that mandatory hardening -- the same defense WPA2 received as a patch -- not from SAE removing the handshake [@dragonblood-2019]. The protocol changed. AES did not.&lt;/p&gt;
&lt;h3&gt;The pattern, seen all at once&lt;/h3&gt;
&lt;p&gt;Put the three side by side and the shape is unmistakable. Same violated contract in three costumes; same untouched block every time.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Incident&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;Fix (usage, not cipher)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Bernstein; Osvik-Shamir-Tromer cache timing [@bernstein-2005; @osvik-shamir-tromer-2006]&lt;/td&gt;
&lt;td&gt;2005-2006&lt;/td&gt;
&lt;td&gt;Implementation&lt;/td&gt;
&lt;td&gt;Secret-dependent T-table indices leak via cache/timing&lt;/td&gt;
&lt;td&gt;A data-dependent lookup leaks the data&lt;/td&gt;
&lt;td&gt;Constant-time bitslicing; AES-NI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Joux forbidden attack; Böck et al. scan [@joux-2006; @nonce-disrespecting-2016]&lt;/td&gt;
&lt;td&gt;2006, 2016&lt;/td&gt;
&lt;td&gt;Mode&lt;/td&gt;
&lt;td&gt;Repeated GCM nonce reuses keystream and exposes $H$&lt;/td&gt;
&lt;td&gt;Buggy nonce generators break uniqueness&lt;/td&gt;
&lt;td&gt;AES-GCM-SIV; TLS 1.3 derived nonces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KRACK [@krack-2017]&lt;/td&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;td&gt;Protocol&lt;/td&gt;
&lt;td&gt;Replayed message 3 rewinds the CCMP nonce&lt;/td&gt;
&lt;td&gt;State machine reinstalls an in-use key&lt;/td&gt;
&lt;td&gt;Handshake patch; WPA3-SAE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Biclique baseline [@biclique-2011]&lt;/td&gt;
&lt;td&gt;2011&lt;/td&gt;
&lt;td&gt;The cipher itself&lt;/td&gt;
&lt;td&gt;Best single-key attack: $2^{126.1}$ for AES-128&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;td&gt;None needed -- &quot;no practical impact&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Seen one at a time, these look like three unrelated bugs -- a cache thing, a TLS thing, a Wi-Fi thing. Seen together, they are a single pattern: every time, a contract &lt;em&gt;around&lt;/em&gt; the permutation was violated, real traffic or keys fell, and the 128-bit block math did not move. That pattern is the whole point, and it is worth naming out loud.&lt;/p&gt;
&lt;h2&gt;5. The Fixes Changed How AES Is Used, Never AES&lt;/h2&gt;
&lt;p&gt;Stop treating the three incidents as separate stories. Line them up and one realization collapses the subject: &lt;em&gt;every&lt;/em&gt; field break attacked a layer around the permutation, and &lt;em&gt;every&lt;/em&gt; fix changed how AES is &lt;em&gt;used&lt;/em&gt; -- never AES itself.&lt;/p&gt;
&lt;p&gt;This is not a eureka discovery. It is an engineering discipline, and it is visible only because the same shape repeats three times across 2005, 2016, and 2017. Look at what each fix actually touched:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Implementation.&lt;/strong&gt; Constant-time bitsliced code and hardware AES-NI compute the round with no data-dependent memory access [@kasper-schwabe-2009; @intel-aes-ni]. The tables are gone; the permutation they computed is unchanged.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mode.&lt;/strong&gt; AES-GCM-SIV makes a repeated nonce merely detectable rather than catastrophic -- a repeat leaks only whether two messages were equal, never the subkey $H$ [@rfc-8452]. TLS 1.3 derives each nonce deterministically from the record sequence number, deleting the footgun RFC 5288 had exposed [@rfc-8446]. The nonce plumbing changed; AES did not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protocol.&lt;/strong&gt; The KRACK patch forbids reinstalling an in-use key, and WPA3 makes that anti-reinstallation defense mandatory (alongside management-frame protection) while swapping WPA2&apos;s pre-shared-key authentication for the SAE key exchange; the 4-way handshake persists, hardened rather than removed [@krack-2017; @wifi-wpa3-2018; @dragonblood-2019]. The state machine changed; AES did not.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not one of these altered the block permutation. They hardened wrappers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The cipher is the fixed point around which everything else evolved. Every field break attacked a layer around the permutation, and every remedy -- constant-time and AES-NI, misuse-resistant AEAD and derived nonces, patched handshakes and WPA3 -- hardened a &lt;em&gt;wrapper&lt;/em&gt;. Soundness requires the weakest of the three layers to hold, and hardening any one of them is an exercise in &lt;em&gt;usage&lt;/em&gt;, not cryptanalysis.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is a dynamic hiding in the chronology, and it is no coincidence. As AES-NI closed the implementation gap around 2010, the weakest remaining link -- the attacker&apos;s frontier -- moved to the mode, where the 2016 scan found it. As misuse-resistant modes and derived nonces closed that, the frontier moved to the protocol, where KRACK found it in 2017. The layers did not take turns being weak; the attacker simply always works the &lt;em&gt;current&lt;/em&gt; weakest one. That is why &quot;we use a strong cipher&quot; was never the right unit of analysis. The right unit is the weakest wrapper you are still running.&lt;/p&gt;

This article is the *did-break-in-the-field* story. Its companion, *How AES Would Break*, is the *would-break-in-theory* one: the key schedule, related-key attacks such as the Biryukov-Khovratovich results on AES-256 that only exist in a related-key model correct deployments never create [@biryukov-khovratovich-2009], and the slow erosion of the security margin. Different question, different article. And the general craft of constant-time code, fault resistance, and key custody belongs to a third sibling on secure implementation. This piece links out to both rather than re-teaching them, because the point here is narrow and sharp: the wrappers broke, and the wrappers were fixed.
&lt;p&gt;If every fix is a species of &quot;use AES correctly,&quot; then the state of the art is just the catalog of what &quot;correctly&quot; means at each layer in 2026 -- and, tellingly, where even correct-by-the-book still is not quite enough.&lt;/p&gt;
&lt;h2&gt;6. What Correct AES Deployment Looks Like in 2026&lt;/h2&gt;
&lt;p&gt;The modern answer is unglamorous, and that is exactly the point: be sound at all three layers at once, because a hardened mode does nothing for a leaky implementation, and a constant-time implementation does nothing for a protocol that rewinds its nonce. Here is the correct-deployment endpoint, keyed to the three failure loci.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implementation.&lt;/strong&gt; Use hardware AES -- Intel and AMD AES-NI, or ARMv8&apos;s cryptographic extension -- which runs each round in silicon with no lookup tables and data-independent latency [@intel-aes-ni]. Where the CPU lacks AES instructions, fall back to a &lt;strong&gt;constant-time bitsliced&lt;/strong&gt; software implementation that never lets a secret index memory [@kasper-schwabe-2009]. Mainstream libraries typically make this selection at runtime, preferring hardware AES with a constant-time fallback.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mode.&lt;/strong&gt; Use a &lt;a href=&quot;https://paragmali.com/blog/the-aead-decision-matrix-seven-ciphers-three-edges-one-choic/&quot; rel=&quot;noopener&quot;&gt;vetted AEAD&lt;/a&gt;, never a hand-rolled chain of primitives. AES-GCM is the performance leader when you can &lt;em&gt;guarantee&lt;/em&gt; nonce uniqueness -- either random 96-bit nonces kept under the NIST budget of fewer than $2^{32}$ invocations per key [@sp-800-38d], or TLS 1.3&apos;s deterministic per-record nonce derived from the sequence number [@rfc-8446]. Where uniqueness cannot be guaranteed, use &lt;strong&gt;AES-GCM-SIV&lt;/strong&gt;, which survives a repeat gracefully [@rfc-8452].&lt;/p&gt;

An authenticated-encryption scheme that does not fail catastrophically when a nonce repeats. In an MRAE scheme such as AES-GCM-SIV, a repeated (nonce, message) pair leaks only the fact that the two plaintexts were identical -- never the keystream across distinct messages, never the subkey $H$. It is the provably strongest guarantee achievable once you admit that nonces sometimes repeat.
&lt;p&gt;&lt;strong&gt;Protocol.&lt;/strong&gt; At the link layer, patch WPA2 against key reinstallation or move to WPA3, which mandates anti-reinstallation checks and management-frame protection to close the defect while still running a 4-way handshake to install the pairwise key [@krack-2017; @wifi-wpa3-2018; @dragonblood-2019]. At the transport layer, prefer TLS 1.3 over 1.2: it deletes the explicit-nonce footgun and the downgrade and renegotiation hazards that made 1.2 fragile [@rfc-8446].&lt;/p&gt;
&lt;p&gt;There is one caveat that keeps this from being a victory lap, and it is a good illustration of how deep the &quot;usage&quot; story goes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &quot;AES-NI is constant-time by construction&quot; is true by &lt;em&gt;design intent&lt;/em&gt;, but on Ice Lake and later Intel cores, and Armv8.4-A and later, data-operand-independent timing -- including for AES instructions -- is only &lt;em&gt;guaranteed&lt;/em&gt; when the processor&apos;s data-independent-timing mode is explicitly enabled [@intel-doit; @biggers-2023]. A constant-time algorithm is necessary but not sufficient for constant-time execution; part of the guarantee lives below your software, in a mode you now have to request.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The practical upshot is that &quot;constant-time&quot; has quietly become a setting rather than an assumption.On recent Intel cores the mode is called DOITM (Data Operand Independent Timing Mode); on Arm it is the DIT (Data-Independent Timing) processor-state bit. Eric Biggers raised the cross-vendor issue publicly in 2023, and it is the reason security-sensitive code on the newest silicon must ask for timing guarantees rather than inherit them [@biggers-2023; @intel-doit].&lt;/p&gt;
&lt;p&gt;&quot;Use a vetted AEAD, use hardware AES, patch your handshake&quot; is the whole answer for greenfield code. But engineers inherit constraints -- a fixed nonce source, a distributed system that cannot coordinate a counter, a CPU with no AES instructions. So the real question is rarely &quot;what is best.&quot; It is &quot;what are my options, ranked, and exactly when does each one apply?&quot;&lt;/p&gt;
&lt;h2&gt;7. Closing Each Gap: The Competing Defenses&lt;/h2&gt;
&lt;p&gt;At two of the three layers a practitioner actually has a &lt;em&gt;choice&lt;/em&gt;, and the honest framing is a set of trade-offs, not a single winner. Take the two design spaces in turn.&lt;/p&gt;
&lt;h3&gt;The side channel: hardware AES-NI versus software bitslicing&lt;/h3&gt;
&lt;p&gt;These two coexist because they optimize different constraints. Where the CPU has AES instructions, AES-NI is the fastest option and constant-time by construction (subject to the DOIT/DIT caveat from the previous section) [@intel-aes-ni]. Where it does not -- older cores, small embedded parts -- constant-time bitsliced software gives a verifiable timing guarantee at the cost of speed and implementation effort [@kasper-schwabe-2009]. The one option that is never acceptable in security code is the original T-table implementation.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;T-table AES (broken)&lt;/th&gt;
&lt;th&gt;Bitsliced constant-time software&lt;/th&gt;
&lt;th&gt;Hardware AES-NI / ARMv8&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Secret-dependent memory access&lt;/td&gt;
&lt;td&gt;Yes (about 4 KB of tables)&lt;/td&gt;
&lt;td&gt;None (boolean logic)&lt;/td&gt;
&lt;td&gt;None (silicon datapath)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timing side-channel resistance&lt;/td&gt;
&lt;td&gt;Fails [@bernstein-2005; @osvik-shamir-tromer-2006]&lt;/td&gt;
&lt;td&gt;Constant-time by construction&lt;/td&gt;
&lt;td&gt;Data-independent by design; DOIT/DIT mode needed on Ice Lake+/Armv8.4+ [@intel-doit]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput&lt;/td&gt;
&lt;td&gt;Fast (pre-attack)&lt;/td&gt;
&lt;td&gt;Good when blocks are batched&lt;/td&gt;
&lt;td&gt;Fastest; line-rate AEAD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block-parallelism needed&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (weak for a single block)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardware requirement&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;CPU AES instructions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best suited for&lt;/td&gt;
&lt;td&gt;Nothing security-sensitive&lt;/td&gt;
&lt;td&gt;No-AES-NI or verifiable software&lt;/td&gt;
&lt;td&gt;Everything with the instruction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;The nonce: four live strategies&lt;/h3&gt;
&lt;p&gt;This is the design space where the 2016 scan drew blood, so it deserves the careful table. The four options trade nonce size, streaming ability, and misuse tolerance against each other.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;AES-GCM, random 96-bit nonce&lt;/th&gt;
&lt;th&gt;AES-GCM, TLS 1.3 counter nonce&lt;/th&gt;
&lt;th&gt;AES-GCM-SIV (MRAE)&lt;/th&gt;
&lt;th&gt;XChaCha20-Poly1305&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Nonce size&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;96-bit (derived)&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;192-bit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Passes&lt;/td&gt;
&lt;td&gt;1 (streaming)&lt;/td&gt;
&lt;td&gt;1 (streaming)&lt;/td&gt;
&lt;td&gt;2 (buffered)&lt;/td&gt;
&lt;td&gt;1 (streaming)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On accidental repeat&lt;/td&gt;
&lt;td&gt;Catastrophic (leaks $P_1 \oplus P_2$; two collisions give $H$ and forgery)&lt;/td&gt;
&lt;td&gt;Structurally prevented per connection&lt;/td&gt;
&lt;td&gt;Graceful (leaks only message equality)&lt;/td&gt;
&lt;td&gt;Catastrophic on a true repeat, but essentially never collides at random&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safe budget per key&lt;/td&gt;
&lt;td&gt;Under $2^{32}$ messages [@sp-800-38d]&lt;/td&gt;
&lt;td&gt;Per-connection sequence, no reuse if the counter is sound&lt;/td&gt;
&lt;td&gt;Effectively unbounded against misuse&lt;/td&gt;
&lt;td&gt;About $2^{96}$ random nonces before collision risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relative speed&lt;/td&gt;
&lt;td&gt;Fastest (line rate)&lt;/td&gt;
&lt;td&gt;Fastest&lt;/td&gt;
&lt;td&gt;About 0.92 cpb on Broadwell, 14-19% slower than OpenSSL GCM [@gueron-lindell-2015]&lt;/td&gt;
&lt;td&gt;Fast in constant-time software, no AES-NI needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primitive&lt;/td&gt;
&lt;td&gt;AES&lt;/td&gt;
&lt;td&gt;AES&lt;/td&gt;
&lt;td&gt;AES&lt;/td&gt;
&lt;td&gt;ChaCha20 (not AES)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Read the table as a decision, not a ranking. If you own a reliable per-connection counter, deterministic-nonce AES-GCM is both fastest and safe -- that is what TLS 1.3 does [@rfc-8446]. If you &lt;em&gt;cannot&lt;/em&gt; guarantee uniqueness -- distributed writers, stateless functions, restart- or clone-prone systems -- AES-GCM-SIV is the provably strongest answer under misuse, at the price of a second pass and a buffered message [@rfc-8452; @rogaway-shrimpton-2006]. And if AES is not mandated and you just want &quot;a random nonce is always safe,&quot; XChaCha20-Poly1305&apos;s 192-bit nonce makes accidental collision astronomically unlikely [@xchacha-draft] -- a different primitive, included as the most common answer to the nonce footgun rather than as an AES deployment.&lt;/p&gt;
&lt;p&gt;Every option here changes &lt;em&gt;how AES is used&lt;/em&gt; -- or swaps AES out entirely -- and each buys its safety with a specific cost: a pass, a throughput hit, a hardware dependency, a different primitive. Which makes the honest way to close the technical arc a question about limits: how little does the attacker actually need, and how much can the defender actually guarantee?&lt;/p&gt;
&lt;h2&gt;8. Theoretical Limits: What Is Provably True on Both Sides&lt;/h2&gt;
&lt;p&gt;The thesis has two sides, and each has its own provable frontier. The surprise is the asymmetry: the cipher side is essentially closed, and all the hard, unavoidable limits live in the deployment.&lt;/p&gt;
&lt;h3&gt;The cipher side: the math that did not break&lt;/h3&gt;
&lt;p&gt;Biclique cryptanalysis is the &lt;em&gt;entire&lt;/em&gt; published erosion of the full-cipher security margin. Bogdanov, Khovratovich, and Rechberger reported the first single-key attacks on the full cipher at $2^{126.1}$, $2^{189.7}$, and $2^{254.4}$ for AES-128, AES-192, and AES-256 -- with no related-key assumption [@biclique-2011]. Against brute force at $2^{128}$, $2^{192}$, and $2^{256}$, that is a gain of at most roughly 1.6 to 2.3 bits: a factor of a few, not a factor that matters.A &quot;bit&quot; of security is a doubling of attacker work, so shaving 2 bits makes the attack about four times faster than brute force -- still astronomically far from feasible.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;AES-128&lt;/th&gt;
&lt;th&gt;AES-192&lt;/th&gt;
&lt;th&gt;AES-256&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Brute-force cost&lt;/td&gt;
&lt;td&gt;$2^{128}$&lt;/td&gt;
&lt;td&gt;$2^{192}$&lt;/td&gt;
&lt;td&gt;$2^{256}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best known single-key attack (biclique)&lt;/td&gt;
&lt;td&gt;$2^{126.1}$&lt;/td&gt;
&lt;td&gt;$2^{189.7}$&lt;/td&gt;
&lt;td&gt;$2^{254.4}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Margin removed&lt;/td&gt;
&lt;td&gt;about 1.9 bits&lt;/td&gt;
&lt;td&gt;about 2.3 bits&lt;/td&gt;
&lt;td&gt;about 1.6 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practical threat&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relevance to the field breaks above&lt;/td&gt;
&lt;td&gt;Zero&lt;/td&gt;
&lt;td&gt;Zero&lt;/td&gt;
&lt;td&gt;Zero&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;There is no proof that any attack on AES &lt;em&gt;must&lt;/em&gt; cost as much as exhaustive search -- concrete block ciphers essentially never come with such a theorem -- so confidence rests on more than two decades of open cryptanalysis since the competition, not on an impossibility result [@fips-197]. But the reading is unambiguous, and the authors said it themselves.&lt;/p&gt;

The best known attacks on the full AES &quot;do not threaten the practical use of AES in any way.&quot; -- Bogdanov, Khovratovich, and Rechberger, 2011 [@biclique-2011]
&lt;p&gt;Notice the defender&apos;s column in the table never moves, and key size is irrelevant to every operational break in the sections above. The cipher side is, for practical purposes, a closed question.&lt;/p&gt;
&lt;h3&gt;The deployment side: where the real limits live&lt;/h3&gt;
&lt;p&gt;Now the asymmetry. Three &lt;em&gt;provable&lt;/em&gt; boundaries constrain real systems, and none of them is about AES&apos;s strength:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The GCM birthday bound.&lt;/strong&gt; With random 96-bit nonces, collision risk grows by the birthday bound, so NIST caps a single key at fewer than $2^{32}$ invocations [@sp-800-38d]. This is a structural limit of random-nonce GCM, independent of the cipher -- the mathematical reason &quot;just use random nonces at massive scale&quot; eventually fails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The AEAD trilemma.&lt;/strong&gt; No single scheme is simultaneously single-pass and line-rate, fully nonce-misuse-resistant, &lt;em&gt;and&lt;/em&gt; large-nonce. GCM gives you the first, GCM-SIV the second, XChaCha the third-and-a-half; you cannot have all three at once [@rfc-8452]. And misuse-resistant AE is provably the &lt;em&gt;strongest possible&lt;/em&gt; guarantee once nonces may repeat -- an attacker can always at least detect that the same message was encrypted, and a well-designed SIV scheme leaks nothing more [@rogaway-shrimpton-2006].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ISA-guaranteed whole-machine constant time is impossible in general.&lt;/strong&gt; The instruction set does not, in general, promise data-independent instruction timing, so even AES and XOR instructions may be data-dependent on recent cores unless the timing mode is enabled [@intel-doit; @biggers-2023]. A constant-time &lt;em&gt;algorithm&lt;/em&gt; cannot by itself guarantee constant-time &lt;em&gt;execution&lt;/em&gt;; part of the guarantee lives below the software, and the general craft of getting it right is the subject of the &lt;a href=&quot;https://paragmali.com/blog/correct-constant-time-and-still-owned-a-field-guide-to-side-/&quot; rel=&quot;noopener&quot;&gt;secure-implementation sibling&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The cipher-secure and system-secure claims are different, and the gap between them is &lt;em&gt;inherent&lt;/em&gt;, not accidental. The cipher side has a negligible margin nibble -- under two bits. The deployment side has the real, provable limits: the GCM birthday bound, the single-pass / misuse-resistant / large-nonce trilemma, and the structural impossibility of ISA-guaranteed constant time. Key size buys nothing against any of them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the cipher side is closed and the deployment side has hard limits, then the live frontier is wherever those deployment limits are still being hit in the wild. That is not a solved problem. It is an active one.&lt;/p&gt;
&lt;h2&gt;9. Open Problems: Where AES Still Breaks in the Field&lt;/h2&gt;
&lt;p&gt;The cipher side is closed; the deployment side is not. Here are the places the same three-layer pattern is still live -- each an &lt;em&gt;operational&lt;/em&gt; frontier, consistent with the thesis that the weakest link is the wrapper, not the block.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Guaranteeing nonce uniqueness in distributed, multi-writer, restart-prone systems.&lt;/strong&gt; This is the exact gap that produced Generation 2: buggy counters and generators on 184 live servers repeated GCM nonces [@nonce-disrespecting-2016]. The best partial answer, MRAE via AES-GCM-SIV, makes a repeat &lt;em&gt;detectable&lt;/em&gt; rather than catastrophic [@rfc-8452] -- but the single-pass, fully misuse-resistant, large-nonce scheme the trilemma forbids remains unrealized. In a world of stateless functions, cloned VMs, and shared keys across a fleet, &quot;just keep a counter&quot; is still an unsolved systems problem, not a solved one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-VM cache side channels in multi-tenant clouds.&lt;/strong&gt; The 2005-2006 threat model assumed a shared physical machine; virtualization brought it back at scale. Irazoqui, Inci, Eisenbarth, and Sunar recovered AES keys &lt;em&gt;across virtual-machine boundaries&lt;/em&gt; using Flush+Reload with memory deduplication [@irazoqui-2014]. Hardware AES removes the &lt;em&gt;table&lt;/em&gt; channel, which is why AES-NI and constant-time code remain load-bearing in 2026.Even instruction timing can be data-dependent on recent cores unless the timing mode is enabled, so cloud tenancy keeps the Generation-1 question open even for hardware AES [@biggers-2023].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fault and differential-fault attacks.&lt;/strong&gt; Inducing a computation fault -- through voltage glitching, a laser, or rowhammer-style effects -- can recover an AES key from a handful of faulty ciphertexts [@piret-quisquater-2003]. This is again an implementation and hardware failure, not a cipher failure; the depth belongs to the secure-implementation sibling, but it is a live operational locus wherever an attacker has physical or near-physical access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The long tail of devices without hardware AES.&lt;/strong&gt; Constant-time software is slower than AES-NI, and the performance gap quietly tempts developers back toward leaky tables on the smallest parts [@kasper-schwabe-2009]. On many of those devices the pragmatic answer is to ship a constant-time stream cipher such as ChaCha20 instead of fighting AES&apos;s software side channels [@xchacha-draft].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Formal verification of protocol state machines,&lt;/strong&gt; so the next KRACK is caught before it ships. TLS 1.3 was co-designed with formal analysis and has verified component implementations, such as those in the HACL* library [@hacl-2017]. Wi-Fi&apos;s SAE was &lt;em&gt;not&lt;/em&gt; fully immunized: Dragonblood found downgrade, denial-of-service, and side-channel leaks the authors argue are &quot;inherent to Dragonfly,&quot; and even patched software remained affected by a novel leak [@dragonblood-2019]. The state of the art at the protocol layer is &lt;em&gt;better&lt;/em&gt;, not &lt;em&gt;finished&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Every open problem here is the same sentence in new clothes: a contract around the permutation is hard to keep in the real world. Which means the practical guide almost writes itself -- it is the thesis made operational, each rule routed to the incident it prevents.&lt;/p&gt;
&lt;h2&gt;10. What to Do on Monday&lt;/h2&gt;
&lt;p&gt;Everything above collapses into a short decision procedure and a shorter list of nevers, each rule tied to the incident it prevents.&lt;/p&gt;

flowchart TD
    S[&quot;Deploying AES&quot;] --&amp;gt; Q1{&quot;CPU has AES instructions?&quot;}
    Q1 --&amp;gt;|Yes| HW[&quot;Use AES-NI or ARMv8 crypto, enable timing mode on newest cores&quot;]
    Q1 --&amp;gt;|No| CT[&quot;Use constant-time bitsliced software&quot;]
    HW --&amp;gt; Q2{&quot;Can you guarantee nonce uniqueness?&quot;}
    CT --&amp;gt; Q2
    Q2 --&amp;gt;|Yes| GCM[&quot;AES-GCM with deterministic nonces, TLS 1.3 style&quot;]
    Q2 --&amp;gt;|No| Q3{&quot;Is AES mandated?&quot;}
    Q3 --&amp;gt;|Yes| SIV[&quot;AES-GCM-SIV, misuse-resistant&quot;]
    Q3 --&amp;gt;|No| XC[&quot;XChaCha20-Poly1305, 192-bit nonce&quot;]
&lt;p&gt;The rules behind the tree:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Implementation.&lt;/strong&gt; Use a library that selects AES-NI or ARMv8 crypto at runtime and falls back to constant-time software; never table-based AES in security code; on the newest cores, be aware the data-independent-timing mode may need to be requested [@intel-aes-ni; @intel-doit]. &lt;em&gt;Prevents Generation 1: cache-timing key recovery.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mode and nonce.&lt;/strong&gt; Reliable per-connection counter, as in TLS or QUIC? Use AES-GCM with deterministic nonces [@rfc-8446]. Cannot &lt;em&gt;guarantee&lt;/em&gt; uniqueness across restarts, threads, or clones? Use AES-GCM-SIV [@rfc-8452]. Want &quot;a random nonce is always safe&quot; and AES is not mandated? Use XChaCha20-Poly1305 [@xchacha-draft]. Never hand-roll a mode, and never choose your own explicit GCM nonce. &lt;em&gt;Prevents Generation 2: nonce reuse and forgery.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protocol.&lt;/strong&gt; Patch WPA2 against key reinstallation or move to WPA3, keep clients updated, and prefer TLS 1.3 over 1.2 [@krack-2017; @rfc-8446]. &lt;em&gt;Prevents Generation 3: handshake-driven nonce rewind.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The single highest-value check you can add to code review is a nonce-reuse detector. It is the exact contract the 184 servers violated, and it fits in a few lines.&lt;/p&gt;
&lt;p&gt;{`
// Scan a stream of (key, nonce) pairs and flag the first repeat.
function findNonceReuse(records) {
  const seen = new Set();
  for (const r of records) {
    const tag = r.key + &quot;|&quot; + r.nonce;
    if (seen.has(tag)) return { reused: true, key: r.key, nonce: r.nonce };
    seen.add(tag);
  }
  return { reused: false };
}&lt;/p&gt;
&lt;p&gt;const stream = [
  { key: &quot;k1&quot;, nonce: &quot;00000001&quot; },
  { key: &quot;k1&quot;, nonce: &quot;00000002&quot; },
  { key: &quot;k1&quot;, nonce: &quot;00000001&quot; },   // counter reset after a restart -- the bug
];&lt;/p&gt;
&lt;p&gt;console.log(findNonceReuse(stream));
// { reused: true, key: &apos;k1&apos;, nonce: &apos;00000001&apos; } -- catch it in review, not in an incident.
`}&lt;/p&gt;
&lt;p&gt;Now hold each pitfall up to the mirror. Every one of these confident sentences reproduces a specific, named incident.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The confident mistake&lt;/th&gt;
&lt;th&gt;The incident it reproduces&lt;/th&gt;
&lt;th&gt;The fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Table-based AES is fine in security code&lt;/td&gt;
&lt;td&gt;Bernstein; Osvik-Shamir-Tromer cache timing [@bernstein-2005; @osvik-shamir-tromer-2006]&lt;/td&gt;
&lt;td&gt;AES-NI or constant-time software&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repeating a nonce across restarts, threads, or clones is unlikely to matter&lt;/td&gt;
&lt;td&gt;The 184-server GCM forgery scan [@nonce-disrespecting-2016]&lt;/td&gt;
&lt;td&gt;AES-GCM-SIV or a disciplined counter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-256 is safer against these attacks&lt;/td&gt;
&lt;td&gt;Every break here -- key size is irrelevant [@nonce-disrespecting-2016]&lt;/td&gt;
&lt;td&gt;Fix the wrapper, not the key size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constant-time source means constant-time execution&lt;/td&gt;
&lt;td&gt;The DOIT/DIT timing caveat [@intel-doit; @biggers-2023]&lt;/td&gt;
&lt;td&gt;Enable the timing mode where required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An unpatched WPA2 handshake is good enough&lt;/td&gt;
&lt;td&gt;KRACK [@krack-2017]&lt;/td&gt;
&lt;td&gt;Patch WPA2 or deploy WPA3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

When you review encryption code, find where the nonce comes from and ask exactly one question: can this value repeat across a restart, a fork, a thread, or a clone? If you cannot prove it never repeats, you are reading a latent Generation-2 incident. Reach for AES-GCM-SIV or a 192-bit-nonce AEAD instead of arguing about how improbable a collision seems.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; 1. AES-NI or ARMv8 crypto, with a constant-time software fallback. 2. A vetted AEAD -- deterministic-nonce AES-GCM where a per-connection counter is reliable, AES-GCM-SIV where uniqueness is hard, XChaCha20-Poly1305 where AES is not mandated. 3. Patched WPA2 or WPA3, and TLS 1.3 over 1.2.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The checklist is short because the lesson is one sentence. Before restating it, clear the handful of confident, wrong sentences that keep this bug alive in design meetings.&lt;/p&gt;
&lt;h2&gt;11. Frequently Asked Questions&lt;/h2&gt;


No. The best publicly known attack on the full cipher, biclique cryptanalysis, costs about $2^{126}$ operations for AES-128 -- a fraction-of-a-bit improvement over brute force with zero practical impact, and its authors say it does &quot;not threaten the practical use of AES in any way&quot; [@biclique-2011]. Every break in this article happened in a layer around the cipher, not in the cipher.


Yes -- via cache-timing side channels [@bernstein-2005; @osvik-shamir-tromer-2006]. But that is *not* a break of the cipher. The T-table implementation&apos;s memory-access pattern leaked the key; the block permutation did not. Swap the leaky tables for a constant-time implementation and the key stops leaking while AES stays byte-for-byte identical. &quot;Unbroken cipher&quot; and &quot;stolen key&quot; are both true at once.


Be precise here. One reuse immediately leaks $P_1 \oplus P_2$ through keystream reuse, and it gives one equation for the GHASH subkey $H$. But *pinning* $H$ for reliable universal forgery generally needs at least two collisions [@joux-2006]. One reuse is already catastrophic -- do not understate it -- but do not say &quot;one reuse recovers the key&quot; either.


No. Every break here is implementation, mode, or protocol; key size provides no protection against cache timing, nonce reuse, or a reinstalled handshake key [@nonce-disrespecting-2016]. For the nonce-reuse and handshake breaks AES-256 fails exactly as fast as AES-128; for cache timing it is just as vulnerable, though a longer key takes proportionally more leakage to extract. If someone proposes AES-256 as the fix for any incident in this article, they have misdiagnosed the layer.


No. Against AES-CCMP, KRACK forces nonce-reuse decryption and replay, not AES key recovery [@krack-2017]. The especially damaging all-zero-key case was an Android and Linux `wpa_supplicant` implementation bug, not a weakness in AES. No block-math weakness is involved, and no AES key is recovered by the attack itself.


No. A padding oracle is a mode-and-validation failure in CBC deployments -- the receiver leaks whether decrypted padding was valid -- and it never touches the AES permutation. It is the same moral as this article (a wrapper broke), in a different deployment, and it is covered in a dedicated padding-oracle sibling. AES itself is not the weak link there either.

&lt;p&gt;Every correction points at the same root: the cipher was never the weak link. Time to say the sentence the whole article was built to earn.&lt;/p&gt;
&lt;h2&gt;12. To Break AES in the Field, You Never Touch AES&lt;/h2&gt;
&lt;p&gt;Return to the paradox we opened with, now resolved. The best attack on the full cipher is a fraction-of-a-bit shave with no practical impact [@biclique-2011] -- and yet Wi-Fi sessions were decrypted, HTTPS connections forged, and keys lifted from running servers, because every one of those breaks happened in a wrapper the cipher knows nothing about. The implementation leaked through cache timing [@bernstein-2005; @osvik-shamir-tromer-2006]. The mode&apos;s nonce contract was violated on 184 live servers, weaponizing Joux&apos;s decade-old forbidden attack into forgery [@nonce-disrespecting-2016; @joux-2006]. The protocol rewound its nonce when a replayed handshake reinstalled a key [@krack-2017]. Three layers, three field breaks, and the 128-bit block math untouched in all three.&lt;/p&gt;
&lt;p&gt;And every fix changed how AES is &lt;em&gt;used&lt;/em&gt;, never AES: constant-time code and AES-NI at the implementation, misuse-resistant AEAD and derived nonces at the mode, patched handshakes and WPA3 at the protocol [@kasper-schwabe-2009; @rfc-8452; @rfc-8446; @wifi-wpa3-2018]. The cipher is the fixed point; the engineering happened all around it.&lt;/p&gt;

A cipher is not a cryptosystem. When your traffic falls, do not ask whether AES broke -- ask which wrapper did: the implementation, the mode, or the protocol.
&lt;p&gt;One last honesty, because the series depends on it. The claim is &quot;almost never,&quot; not &quot;never.&quot; Scoped to AES, the split is pristine -- the block math never fell in deployment. But some deployed primitives genuinely broke as &lt;em&gt;math&lt;/em&gt;: DES&apos;s 56-bit key, RC4&apos;s keystream biases [@alfardan-2013], and the &lt;a href=&quot;https://paragmali.com/blog/the-fingerprint-two-files-shared-a-field-guide-to-cryptograp/&quot; rel=&quot;noopener&quot;&gt;MD5 and SHA-1 collisions&lt;/a&gt; [@shattered-2017]. AES has not joined them, and naming the cases where cryptanalysis won is what keeps the thesis honest rather than triumphant.&lt;/p&gt;
&lt;p&gt;The cipher was never the weak link -- and no bigger key would have saved a single one of these systems. That is why this is Part 1 of a series about how things break &lt;em&gt;in real life&lt;/em&gt;, not a chapter on block-cipher cryptanalysis. The companion piece, &lt;em&gt;How AES Would Break&lt;/em&gt;, takes up the other question: what it would take to move the block itself. This one answered the question that actually decrypts traffic. It was never the cipher. It was the wrapper, every time.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;how-aes-breaks-in-real-life&quot; keyTerms={[
  { term: &quot;Block cipher vs. mode of operation&quot;, definition: &quot;AES is a keyed permutation on one 16-byte block; a mode (CTR, GCM, CCM) chains it across a whole message and turns it into a cryptosystem.&quot; },
  { term: &quot;Nonce / IV&quot;, definition: &quot;A number used once. Counter-based modes require the (key, nonce) pair to be unique per encryption, or the keystream repeats.&quot; },
  { term: &quot;AEAD&quot;, definition: &quot;Authenticated Encryption with Associated Data: confidentiality and integrity together, with headers authenticated but not encrypted. GCM and CCM are AEAD modes.&quot; },
  { term: &quot;Keystream reuse&quot;, definition: &quot;Identical (key, nonce) yields identical keystream, so the XOR of two ciphertexts equals the XOR of their plaintexts. The key is never touched.&quot; },
  { term: &quot;T-table&quot;, definition: &quot;Precomputed lookup tables that fold an AES round into a few reads. A speed optimization whose secret-dependent indices leak through the cache.&quot; },
  { term: &quot;Cache-timing attack&quot;, definition: &quot;Recovering a secret from the timing or cache footprint of data-dependent memory access, not from the algorithm output.&quot; },
  { term: &quot;GHASH subkey H&quot;, definition: &quot;GCM&apos;s authentication secret, the encryption of an all-zero block under the key. A repeated nonce turns tag differences into equations that reveal it.&quot; },
  { term: &quot;Key reinstallation&quot;, definition: &quot;Installing an already-in-use key, which rewinds its nonce or packet-number counter and forces keystream reuse. The mechanism KRACK exploits.&quot; },
  { term: &quot;MRAE&quot;, definition: &quot;Nonce-misuse-resistant authenticated encryption, such as AES-GCM-SIV, where a repeated nonce leaks only message equality, never the subkey.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>cryptography</category><category>aes</category><category>aes-gcm</category><category>nonce-reuse</category><category>krack</category><category>side-channel-attack</category><category>authenticated-encryption</category><category>tls</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>The Math Held; The Interface Leaked: A Field Guide to Digital Signatures</title><link>https://paragmali.com/blog/the-math-held-the-interface-leaked-a-field-guide-to-digital-/</link><guid isPermaLink="true">https://paragmali.com/blog/the-math-held-the-interface-leaked-a-field-guide-to-digital-/</guid><description>ECDLP and RSA held; the nonce, the canonical form, and the verifier broke instead. A field guide to ECDSA, EdDSA, RSA-PSS, determinism, and malleability.</description><pubDate>Sun, 12 Jul 2026 17:41:17 GMT</pubDate><content:encoded>
**A signature scheme&apos;s hardness assumption almost never breaks in the field.** The elliptic-curve discrete logarithm and RSA have held for decades. What breaks instead are the three things the scheme hands the caller: the per-signature **nonce**, the signature&apos;s **canonical form**, and the **verifier&apos;s acceptance rule**.&lt;p&gt;ECDSA outsources all three, and the same untouched-math scheme produced four categorically different disasters: a reused nonce that gave up the PlayStation 3 master key from two signatures [@ps3-27c3-2010], a biased nonce that recovered a PuTTY key in about sixty (a &lt;em&gt;statistical&lt;/em&gt; attack: one few-bit-biased signature does &lt;strong&gt;not&lt;/strong&gt; leak your key) [@cve-2024-31497], a malleable &lt;code&gt;(r, n-s)&lt;/code&gt; that let anyone alter a Bitcoin transaction id [@bip-340], and a verifier that accepted &lt;code&gt;(0, 0)&lt;/code&gt; for any message (&quot;Psychic Signatures&quot;) [@cve-2022-21449].&lt;/p&gt;
&lt;p&gt;The history since ECDSA is one sustained move to pull each surface back &lt;em&gt;inside&lt;/em&gt; the scheme: RFC 6979 derives the nonce deterministically, hedged signing mixes a little randomness back in to survive fault attacks, and Ed25519 and BIP-340 Schnorr make deterministic nonces, a fixed encoding, and a byte-exact verifier native by design -- the clean scheme that sat patent-frozen from 1989 until 2008.&lt;/p&gt;
&lt;p&gt;RSA-PSS answers the same &quot;form&quot; question on the RSA track with the tightest proof of any deployed signature. In 2026 these coexist by niche, and every one of them falls to Shor. Signing safely is a stack: the right scheme, a nonce you need not trust the RNG for, a canonical form, and a strict verifier. The math held; the interface leaked.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;1. Two Signatures, One Nonce, and the Master Key Fell Out&lt;/h2&gt;
&lt;p&gt;In December 2010, at the 27th Chaos Communication Congress, a group called fail0verflow put a single slide on the screen that ended the security of the PlayStation 3 [@ps3-27c3-2010]. Sony had signed every piece of PS3 firmware with ECDSA, a scheme whose security rests on a discrete logarithm no one has ever solved for a 256-bit curve.&lt;/p&gt;
&lt;p&gt;Sony&apos;s engineers did not need to fail. They reused the same secret random number -- the per-signature nonce -- for every signature, and any two signatures that share a nonce give up the private key by grade-school algebra. The curve was pristine. The master key fell out of two subtractions.&lt;/p&gt;
&lt;p&gt;Twelve years later, the failure jumped one layer up the stack. Java shipped an ECDSA &lt;em&gt;verifier&lt;/em&gt; that accepted the signature &lt;code&gt;(r, s) = (0, 0)&lt;/code&gt; for any message and any public key -- &quot;Psychic Signatures,&quot; CVE-2022-21449, a CVSS 7.5 flaw that forged TLS handshakes, signed JWTs, SAML assertions, and WebAuthn responses alike [@cve-2022-21449], [@neil-madden-2022]. One break leaked a private key through the &lt;em&gt;nonce&lt;/em&gt;; the other waved a forgery through at the &lt;em&gt;verifier&lt;/em&gt;. Neither touched the underlying hardness assumption. Nobody solved a discrete log. Nobody factored a modulus.&lt;/p&gt;
&lt;p&gt;That is the pattern this article is about, worth stating as a question you can carry through every section: &lt;strong&gt;when a signer or verifier meets a value it did not create, what secret does it leak, or what forgery does it wave through?&lt;/strong&gt; The answer is almost never &quot;the math was weak.&quot; A signature scheme is only as safe as three things it hands the caller and refuses to decide for you: the per-signature &lt;strong&gt;nonce&lt;/strong&gt;, the signature&apos;s &lt;strong&gt;canonical form&lt;/strong&gt;, and the &lt;strong&gt;verifier&apos;s acceptance rule&lt;/strong&gt;. ECDSA outsources all three at once.&lt;/p&gt;
&lt;p&gt;That is why one scheme -- its elliptic-curve discrete logarithm fully intact -- spawned four categorically different disasters: the reused nonce of the PS3, the drained Android Bitcoin wallets, the malleable transactions that unsettled Bitcoin, and the psychic verifier in Java. You half-know these headlines already. This is the one sentence underneath all of them.&lt;/p&gt;

The math held; the interface leaked.
&lt;p&gt;The rest of signature history since ECDSA is one effort to pull those three surfaces back &lt;em&gt;inside&lt;/em&gt; the scheme, so the caller can no longer get them wrong. To see why a shared random number hands over a private key -- and why &quot;just use Ed25519&quot; is a real answer but not the whole one -- you first have to be precise about what a signature promises, and the three jobs ECDSA quietly delegates to you.&lt;/p&gt;
&lt;h2&gt;2. What a Signature Actually Is (and the Three Things ECDSA Hands You)&lt;/h2&gt;
&lt;p&gt;Everyone knows the one-liner: sign with the private key, verify with the public key. That one-liner hides three decisions the scheme refuses to make for you. Start with the part that is simple.&lt;/p&gt;
&lt;p&gt;A digital signature scheme is three algorithms. &lt;code&gt;KeyGen -&amp;gt; (pk, sk)&lt;/code&gt; produces a public verification key and a private signing key. &lt;code&gt;Sign(sk, m) -&amp;gt; sigma&lt;/code&gt; turns a message and the private key into a signature. &lt;code&gt;Verify(pk, m, sigma) -&amp;gt; {accept, reject}&lt;/code&gt; lets anyone holding the public key check the pair. The word &lt;em&gt;anyone&lt;/em&gt; is the whole point.&lt;/p&gt;
&lt;p&gt;A message authentication code also proves a message is authentic, but it verifies with the same secret used to produce it, so only insiders can check it. A signature is &lt;strong&gt;publicly verifiable&lt;/strong&gt; and therefore &lt;strong&gt;non-repudiable&lt;/strong&gt;: the entire world can confirm it, and only the holder of &lt;code&gt;sk&lt;/code&gt; could have produced it.&lt;/p&gt;

A triple of algorithms: KeyGen produces a public key and a private key; Sign maps a private key and a message to a signature; Verify maps a public key, a message, and a signature to accept or reject. Unlike a message authentication code, it is publicly verifiable and non-repudiable, since verification needs only the public key.
&lt;p&gt;Public verifiability is what makes a signature strictly stronger than a MAC, and strictly slower. A MAC can rest on a single shared secret and a fast keyed hash. A signature cannot rest on any shared secret, because the verifier must be the whole world, so it has to be built on a public-key hardness assumption -- and pays for it in both compute and signature size.&lt;/p&gt;
&lt;h3&gt;The bar every scheme is measured against&lt;/h3&gt;
&lt;p&gt;&quot;Secure&quot; needs a precise meaning, and cryptographers settled on one in 1988. Goldwasser, Micali, and Rivest defined &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-/&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;existential unforgeability under adaptive chosen-message attack&lt;/strong&gt;&lt;/a&gt;, or EUF-CMA: an adversary who may ask you to sign any messages it likes, adaptively, still cannot produce a valid signature on a single &lt;em&gt;new&lt;/em&gt; message it never queried [@gmr-1988]. That is a demanding bar, and every scheme in this article is designed to clear it.&lt;/p&gt;
&lt;p&gt;But notice the exact word: a new &lt;em&gt;message&lt;/em&gt;. EUF-CMA says nothing about whether there might be more than one valid signature on a message you &lt;em&gt;did&lt;/em&gt; sign. Tighten the game by one word and you get &lt;strong&gt;strong unforgeability&lt;/strong&gt;, SUF-CMA: the adversary cannot produce any new valid &lt;em&gt;signature&lt;/em&gt;, even on an already-signed message. The gap between &quot;new message&quot; and &quot;new signature&quot; looks pedantic. It is the entire malleability story that unravels a Bitcoin transaction in a later section, so it is worth planting the seed now.&lt;/p&gt;

The standard security goal for signatures: after obtaining signatures on messages of its choice, an adversary still cannot produce a valid signature on any message it did not query. It forbids forging a new message but says nothing about producing a second signature on an old one.

A stronger goal that forbids even a new signature on an already-signed message. The difference from EUF-CMA is one word -- new message versus new signature -- and it is exactly the property that signature malleability violates.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; EUF-CMA does not promise there is only one valid signature per message. A scheme can be perfectly unforgeable in the textbook sense and still let anyone manufacture a second, different signature on a message you already signed. Whether that matters depends on whether your protocol ever treats the signature itself as an identifier.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The thesis, stated as the scheme&apos;s interface&lt;/h3&gt;
&lt;p&gt;Here is the organizing idea of the field guide. A signature scheme hands the caller three things and quietly makes each one the caller&apos;s responsibility. ECDSA is the worked example that outsources all three.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Surface 1, the nonce.&lt;/strong&gt; To sign, ECDSA needs a fresh secret random number for every signature. Supply a bad one and you leak the private key. &lt;strong&gt;Surface 2, the canonical form.&lt;/strong&gt; A valid ECDSA signature &lt;code&gt;(r, s)&lt;/code&gt; has an evil twin &lt;code&gt;(r, n-s)&lt;/code&gt; that also verifies, so the caller must decide whether those are &quot;the same&quot; signature. &lt;strong&gt;Surface 3, the verifier.&lt;/strong&gt; ECDSA&apos;s acceptance rule involves modular inversions and range checks, and the specification left enough slack that two conformant-looking verifiers can disagree about which signatures are valid. Every deployed break in this article lives in one of these three surfaces.&lt;/p&gt;

flowchart TD
    KG[KeyGen] --&amp;gt; SK[Private key sk]
    KG --&amp;gt; PK[Public key pk]
    M[Message] --&amp;gt; SG[Sign]
    SK --&amp;gt; SG
    N[&quot;Surface 1: the per-signature nonce you must supply&quot;] --&amp;gt; SG
    SG --&amp;gt; SIG[&quot;Signature r and s&quot;]
    SIG --&amp;gt; CF[&quot;Surface 2: is r and s the same as r and n minus s?&quot;]
    CF --&amp;gt; VF[Verify]
    PK --&amp;gt; VF
    M --&amp;gt; VF
    VR[&quot;Surface 3: the byte-exact acceptance rule&quot;] --&amp;gt; VF
    VF --&amp;gt; OUT{&quot;Accept or reject&quot;}
&lt;p&gt;The academic description of ECDSA that stands in for the paywalled ANSI standard spells out this division of labor: the scheme defines the arithmetic and leaves nonce generation, encoding choices, and verifier strictness to the implementer [@johnson-menezes-vanstone-2001], [@ansi-x962-1999]. The cleaner schemes we reach later pull these surfaces back inside the definition. BIP-340 Schnorr, for instance, specifies its verifier down to the byte precisely so the caller cannot get surface 3 wrong [@bip-340].&lt;/p&gt;
&lt;p&gt;The table below is the entire article compressed into one grid: read across each row to watch a surface move from &quot;the caller&apos;s problem&quot; to &quot;the scheme&apos;s problem.&quot;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Surface&lt;/th&gt;
&lt;th&gt;Plain ECDSA&lt;/th&gt;
&lt;th&gt;RFC 6979 / hedged ECDSA&lt;/th&gt;
&lt;th&gt;Ed25519&lt;/th&gt;
&lt;th&gt;BIP-340 Schnorr&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Caller supplies from an RNG&lt;/td&gt;
&lt;td&gt;Pulled inside: derived from key and message&lt;/td&gt;
&lt;td&gt;Pulled inside: native deterministic&lt;/td&gt;
&lt;td&gt;Pulled inside: synthetic and hedged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Canonical form&lt;/td&gt;
&lt;td&gt;Caller must enforce low-&lt;code&gt;s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Caller must enforce low-&lt;code&gt;s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pulled inside: strict &lt;code&gt;S &amp;lt; L&lt;/code&gt; on decode&lt;/td&gt;
&lt;td&gt;Pulled inside: fixed 64-byte form&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Verifier rule&lt;/td&gt;
&lt;td&gt;Caller implements, easy to botch&lt;/td&gt;
&lt;td&gt;Caller implements, easy to botch&lt;/td&gt;
&lt;td&gt;Mostly specified (RFC 8032)&lt;/td&gt;
&lt;td&gt;Pulled inside: specified at the byte level&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Two of those three surfaces were invented before ECDSA existed: the nonce in 1985, the &quot;form&quot; problem in 1978. To understand why a modern scheme still hands them to you, we have to go back to the moment signatures were only an idea.&lt;/p&gt;
&lt;h2&gt;3. Where Signatures Came From (1976 to 1996)&lt;/h2&gt;
&lt;p&gt;In 1976, two Stanford researchers could describe exactly what a signature must do and had no way to build one. Whitfield Diffie and Martin Hellman&apos;s &quot;New Directions in Cryptography&quot; named the requirement -- a private operation that anyone can verify with public information, giving the world a way to hold a signer to their message -- but it offered no algorithm to realize it [@diffie-hellman-1976]. The problem was posed before it was solved, and the two ways it eventually got solved are the two tracks this whole article runs along.&lt;/p&gt;
&lt;h3&gt;Track A: the trapdoor line&lt;/h3&gt;
&lt;p&gt;Two years later, RSA gave the first concrete construction. Signing is the private-key operation, verifying is the public-key operation, and &lt;a href=&quot;https://paragmali.com/blog/rsa-is-a-trapdoor-not-a-cryptosystem-oaep-pss-and-the-25-yea/&quot; rel=&quot;noopener&quot;&gt;the trapdoor&lt;/a&gt; -- factoring is hard, so inverting the public operation without the private exponent is infeasible -- makes the asymmetry real [@rsa-1978].&lt;/p&gt;
&lt;p&gt;It worked, and it &lt;em&gt;immediately&lt;/em&gt; exhibited surface 2. Textbook RSA, which signs the bare message as $\sigma = m^d \bmod N$, is existentially forgeable: pick any $\sigma$, compute $m = \sigma^e \bmod N$, and you have a valid pair for a message you did not choose but that is still &quot;signed.&quot;&lt;/p&gt;
&lt;p&gt;It is also multiplicatively malleable, because $\sigma_1 \cdot \sigma_2 \bmod N$ is a valid signature on $m_1 \cdot m_2 \bmod N$. The raw trapdoor has no notion of which messages are legitimately structured, so the caller must impose that structure. Getting that structure right, with a proof, took fifteen years and is the subject of a later section.&lt;/p&gt;
&lt;h3&gt;Track B: the discrete-log line&lt;/h3&gt;
&lt;p&gt;In 1985, Taher ElGamal built the first signature whose hardness rests on the discrete logarithm problem rather than factoring [@elgamal-1985]. It also introduced the load-bearing footgun that defines the rest of this article: a &lt;strong&gt;fresh secret nonce&lt;/strong&gt; drawn for every single signature. Learn that nonce, or reuse it, and the private key follows. This is where surface 1 enters the lineage, and DSA and ECDSA inherit it unchanged.&lt;/p&gt;
&lt;h3&gt;The engine and the yardstick&lt;/h3&gt;
&lt;p&gt;Two ideas from the late 1980s frame everything after. In 1986, Fiat and Shamir showed how to turn an interactive identification protocol -- prove you know a secret without revealing it -- into a non-interactive signature, by replacing the verifier&apos;s random challenge with a hash of the commitment and the message [@fiat-shamir-1986]. That transform is the engine underneath Schnorr, DSA, and EdDSA, and it comes with a catch we return to in the limits section: its security is argued in the random-oracle model, treating the hash as an ideal random function.&lt;/p&gt;
&lt;p&gt;Then, in 1988, GMR gave the field its yardstick, EUF-CMA, the definition every later scheme is measured against [@gmr-1988].&lt;/p&gt;

A method that converts an interactive public-coin identification protocol into a non-interactive signature by replacing the verifier&apos;s random challenge with a hash of the commitment and the message. Its security is argued in the random-oracle model, which treats the hash function as an ideal random function.
&lt;h3&gt;The clean template, and the patent that froze it&lt;/h3&gt;
&lt;p&gt;In 1989, Claus-Peter Schnorr published the scheme that, in hindsight, got everything right. The signer commits to a random point $R = kG$, computes a challenge $e = H(R \mathbin{|} m)$ by hashing the commitment and message, and answers with $s = k + ex$, where $x$ is the private key. Verification is a single linear check. That linearity is not a detail; it is the reason the scheme is provable, batchable, aggregatable, and non-malleable, and it is the direct ancestor of EdDSA and BIP-340 [@schnorr-1989-1991].&lt;/p&gt;
&lt;p&gt;There was one problem, and it bent the entire history. Schnorr patented the scheme (US 4,995,082, invented 1989, granted 1991, expired 2008) [@patent-us4995082]. NIST, which needed a royalty-free national standard, could not use it. So the best-behaved discrete-log signature ever designed sat frozen for two decades while the government standardized around something else. The clean scheme was invented first and deployed last.&lt;/p&gt;
&lt;p&gt;Schnorr&apos;s work appears twice in the literature: at CRYPTO &apos;89 as &quot;Efficient Identification and Signatures for Smart Cards,&quot; and in the Journal of Cryptology in 1991 as &quot;Efficient Signature Generation by Smart Cards.&quot; They are the same line of work at two venues; cite either, and do not treat one date as a correction of the other.&lt;/p&gt;

timeline
    title From an idea to a standard, 1976 to 2020
    1976 : Diffie and Hellman name the requirement, no algorithm
    1978 : RSA, the first construction, Track A
    1985 : ElGamal introduces the per-signature nonce, Track B
    1986 : Fiat-Shamir turns identification into signatures
    1989 : Schnorr, clean and linear, but patented until 2008
    1994 : DSA standardized as FIPS 186
    1999 : ECDSA moves DSA onto elliptic curves
    2011 : Ed25519 bakes determinism in
    2013 : RFC 6979 retrofits deterministic nonces
    2020 : BIP-340 Schnorr, byte-exact
&lt;p&gt;With the clean scheme locked behind a patent, the U.S. government needed a signature standard of its own. The choices it made under patent pressure are the reason the worse-behaved scheme shipped to the entire internet first.&lt;/p&gt;
&lt;h2&gt;4. Padding, DSA, and the Fateful Nonce&lt;/h2&gt;
&lt;p&gt;Two tracks, two ways to get it wrong first. RSA had to learn not to sign the bare message. The discrete-log camp had to standardize around a secret it could not afford to leak.&lt;/p&gt;
&lt;h3&gt;Track A cleans up its form&lt;/h3&gt;
&lt;p&gt;The fix for textbook RSA&apos;s forgeability is a rule you will hear again: never sign the bare message. Hash it first, then pad the hash into a full-width, unpredictable representative that fills the modulus. Done right, this kills existential forgery -- an attacker can no longer start from a random $\sigma$ and back out a validly structured message -- and it destroys the multiplicative structure that let signatures be multiplied together.&lt;/p&gt;
&lt;p&gt;The workhorse encoding is PKCS#1 v1.5, a deterministic padding standardized in the early 1990s and still ubiquitous [@rfc-8017]. Full-Domain Hash was the conceptual bridge: hash the message across the entire width of the modulus. And in 1996, Bellare and Rogaway&apos;s Probabilistic Signature Scheme (PSS) added a random salt and, with it, the tightest security proof any deployed signature would ever get [@bellare-rogaway-pss-1996], [@rsa-1978].&lt;/p&gt;
&lt;p&gt;We give PSS its own section later; for now, note only that Track A&apos;s first footgun was the &lt;em&gt;form&lt;/em&gt; of what it signed, and Track A fixed it by structuring that form.&lt;/p&gt;
&lt;h3&gt;Track B standardizes, under patent pressure&lt;/h3&gt;
&lt;p&gt;The discrete-log camp went a different way, and politics shaped the outcome. In 1991 NIST proposed the Digital Signature Algorithm, and in 1994 it became FIPS 186 [@wikipedia-dsa]. DSA is an ElGamal and Schnorr relative, deliberately structured to sidestep both the RSA patent and Schnorr&apos;s patent, credited to David Kravitz and assigned to the NSA [@patent-us5231668]. In dodging the clean patented scheme, DSA inherited ElGamal&apos;s design decision without modification: a fresh secret nonce for every signature.&lt;/p&gt;
&lt;p&gt;Keep the DSA dates distinct: proposed in 1991, standardized as FIPS 186 in 1994. The DSA patent (Kravitz, US 5,231,668) was filed in 1991 and granted in 1993. These four dates get conflated constantly.&lt;/p&gt;
&lt;h3&gt;Track B goes to curves&lt;/h3&gt;
&lt;p&gt;Through the late 1990s, Certicom and the ANSI X9F1 committee moved DSA onto elliptic curves. The &lt;a href=&quot;https://paragmali.com/blog/the-curve-was-hard-the-gap-was-soft-a-field-guide-to-using-e/&quot; rel=&quot;noopener&quot;&gt;elliptic-curve discrete logarithm&lt;/a&gt; is harder per bit than the finite-field version, so ECDSA gets equivalent security from much smaller keys -- 256-bit curves for 128-bit security, against 3072-bit fields for RSA and finite-field DSA. The template was otherwise unchanged, and so was the fateful decision: the &lt;strong&gt;same fresh secret nonce, once per signature&lt;/strong&gt; [@johnson-menezes-vanstone-2001].&lt;/p&gt;
&lt;p&gt;The normative ECDSA standard, ANSI X9.62-1999, is paywalled, so this article cites the open Johnson-Menezes-Vanstone description as its stand-in and never quotes X9.62 text directly. One more scoping note that matters for later decision rules: ECDSA lives on two different families of curves in practice -- the NIST P-curves used across web PKI, and secp256k1, the curve Bitcoin and Ethereum chose. The nonce discipline is identical on both.&lt;/p&gt;
&lt;p&gt;By the end of the 1990s, an enormous share of the world&apos;s authentication rested on Track B: DSA and ECDSA, both carrying a per-signature secret that had to be unique, unpredictable, and never leaked -- and that the &lt;em&gt;caller&lt;/em&gt;, not the scheme, was responsible for generating perfectly, every single time. Surface 1 was loaded and handed over to millions of implementers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; RSA&apos;s first footgun was the &lt;em&gt;form&lt;/em&gt; of what it signed -- the bare message was forgeable and malleable, and the cure was to structure it. The discrete-log camp&apos;s first footgun was the &lt;em&gt;secret&lt;/em&gt; it signed with -- a per-signature nonce the scheme trusted the caller to get right. Hold both in mind: the rest of the story is these two footguns being pulled, one at a time, back inside the scheme.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The discrete-log camp had built its entire deployed base on a secret the caller had to generate perfectly, every single time. It took exactly one game console, one wallet app, and one SSH client to prove how badly that goes.&lt;/p&gt;
&lt;h2&gt;5. The Nonce Catastrophe: ECDSA in Full&lt;/h2&gt;
&lt;p&gt;Here is the mechanism underneath the PS3 break, the drained Bitcoin wallets, and the 2024 PuTTY disclosure. And here is the precision almost every retelling gets wrong.&lt;/p&gt;
&lt;h3&gt;ECDSA, mechanically&lt;/h3&gt;
&lt;p&gt;Key generation is a private scalar $d$ and its public point $Q = [d]G$, where $G$ generates a group of prime order $n$. To sign a message $m$, ECDSA draws a nonce $k$, forms $R = [k]G$, sets $r = x(R) \bmod n$, and computes&lt;/p&gt;
&lt;p&gt;$$s = k^{-1}\bigl(H(m) + r,d\bigr) \bmod n.$$&lt;/p&gt;
&lt;p&gt;Verification recovers $u_1 = H(m),s^{-1}$ and $u_2 = r,s^{-1}$ and accepts exactly when $x\bigl([u_1]G + [u_2]Q\bigr) \bmod n = r$ [@johnson-menezes-vanstone-2001].Throughout, $H(m)$ is the hash truncated to the leftmost $\text{bitlen}(n)$ bits, not the full digest -- which matters whenever the hash is wider than the order $n$, as with the P-521 curve behind the PuTTY story below. Stare at the signing equation and the danger is obvious.&lt;/p&gt;
&lt;p&gt;The nonce $k$ is the load-bearing secret, and $s$ mixes $k$, the message hash, and the private key $d$ into one linear relation. Anything you learn about $k$ becomes a linear equation in $d$.&lt;/p&gt;

The fresh secret integer that a discrete-log signature draws for each signature, used to form the commitment point. It is the load-bearing secret: because the signature value mixes the nonce with the message hash and the private key, anything an attacker learns about the nonce turns into a linear equation in the private key.
&lt;h3&gt;The repeat case: certain from two signatures&lt;/h3&gt;
&lt;p&gt;Sign two different messages with the &lt;em&gt;same&lt;/em&gt; nonce and the two signatures share the same $r$ (because $r$ depends only on $k$). Subtract the two signing equations and $k$ cancels, leaving a single linear equation you can solve directly for the private key:&lt;/p&gt;
&lt;p&gt;$$d = \frac{s_2,H(m_1) - s_1,H(m_2)}{r,(s_1 - s_2)} \bmod n.$$&lt;/p&gt;
&lt;p&gt;This is not statistical. It is exact, from &lt;em&gt;two&lt;/em&gt; signatures. It is the PlayStation 3: Sony used a constant $k$, so every firmware signature shared an $r$, and two of them were enough [@ps3-27c3-2010]. And it is the 2013 Android Bitcoin thefts: a flaw in the Java &lt;code&gt;SecureRandom&lt;/code&gt; implementation caused the nonce to repeat across signatures generated on affected devices, and real coins drained from wallets whose signing keys were exposed by the collision [@bitcoin-android-2013], [@klyubin-android-2013].&lt;/p&gt;
&lt;p&gt;Keep the PS3 disclosures distinct: fail0verflow presented the constant-nonce ECDSA break at 27C3 in December 2010; George Hotz released a separate PS3 root key in January 2011 [@ps3-jailbreak-wikipedia]. They are related events, not the same one.&lt;/p&gt;
&lt;p&gt;The widely cited figure of roughly 55 BTC lost in the 2013 Android incident comes from secondary reporting; the bitcoin.org primary alert states no loss total. Treat the number as reported, not official.&lt;/p&gt;
&lt;p&gt;The recovery really is grade-school algebra. The demonstration below signs two messages under &lt;a href=&quot;https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/&quot; rel=&quot;noopener&quot;&gt;a reused nonce&lt;/a&gt; on toy-sized parameters, then plays the attacker: given only the public signature values, it recovers the nonce and then the private key.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Illustrative toy sizes, NOT secure. Here r stands in for x([k]G) mod n; // a real attacker reads r straight off the two signatures. const n = 1000003n;                       // small prime &quot;group order&quot; const mod = (a, m) =&amp;gt; ((a % m) + m) % m; function inv(a, m) {                       // modular inverse, extended Euclid   let [or, r] = [mod(a, m), m], [os, s] = [1n, 0n];   while (r !== 0n) { const q = or / r; [or, r] = [r, or - q * r]; [os, s] = [s, os - q * s]; }   return mod(os, m); } // Signer&apos;s secrets (the attacker never sees these): const d = 123456n;                        // private key const k = 654321n;                        // per-signature nonce -- REUSED: the whole bug const r = 42n;                            // r = x([k]G) mod n; same k gives the same r const h1 = 111111n, h2 = 222222n;         // two message hashes // ECDSA signing: s = k^{-1} (h + r*d) mod n const s1 = mod(inv(k, n) * mod(h1 + r * d, n), n); const s2 = mod(inv(k, n) * mod(h2 + r * d, n), n); // Attacker sees only (r, s1, s2, h1, h2). Recover k, then d: const kRec = mod(mod(h1 - h2, n) * inv(mod(s1 - s2, n), n), n); const dRec = mod(mod(s1 * kRec - h1, n) * inv(r, n), n); console.log(&apos;recovered nonce k = &apos; + kRec + &apos;  (true &apos; + k + &apos;)&apos;); console.log(&apos;recovered key   d = &apos; + dRec + &apos;  (true &apos; + d + &apos;)&apos;); console.log(dRec === d ? &apos;PRIVATE KEY RECOVERED from two signatures&apos; : &apos;mismatch&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;Two subtractions and two inversions. No discrete logarithm was harmed.&lt;/p&gt;
&lt;h3&gt;The bias case: statistical, and it needs many&lt;/h3&gt;
&lt;p&gt;Now change the failure. Instead of repeating the nonce, suppose the nonce generator is subtly &lt;em&gt;biased&lt;/em&gt; -- say the top few bits are always zero. No two signatures share an $r$, so the two-subtraction trick does not apply, and no single signature hands you the key.&lt;/p&gt;
&lt;p&gt;What you have instead is a stream of signatures that each leak a little information about a secret, which is precisely the &lt;strong&gt;Hidden Number Problem&lt;/strong&gt;: recover a hidden value from many samples that each reveal a few high-order bits. Reformulate the biased signatures as an HNP instance, run lattice reduction, and the private key falls out -- but only after you collect &lt;em&gt;many&lt;/em&gt; signatures.&lt;/p&gt;

The problem of recovering a hidden integer from many samples that each leak only a few of its high-order bits. A stream of biased-nonce signatures is an HNP instance, and lattice reduction solves it. This is why a biased nonce is a statistical attack that needs many signatures rather than a single one.
&lt;p&gt;The canonical modern proof is PuTTY. In April 2024, CVE-2024-31497 disclosed that PuTTY&apos;s ECDSA nonce generator for the NIST P-521 curve was biased so that the first 9 bits of each nonce are zero, enabling full secret key recovery in roughly 60 signatures [@cve-2024-31497]. Sixty. Not two.&lt;/p&gt;
&lt;p&gt;The rigor behind that number is a quarter-century of work: Boneh and Venkatesan introduced the Hidden Number Problem in 1996 [@boneh-venkatesan-1996]; Howgrave-Graham and Smart turned it into the first practical lattice recovery of DSA keys from partial nonces in 2001 [@howgrave-graham-smart-2001]; Nguyen and Shparlinski made ECDSA key recovery from a few leaked nonce bits rigorous in 2003 [@nguyen-shparlinski-2003]; and by 2020, LadderLeak recovered keys with under one bit of nonce leakage per signature [@ladderleak-2020].&lt;/p&gt;
&lt;p&gt;Bleichenbacher&apos;s much-cited biased-nonce DSA attack has no standalone peer-reviewed paper; it circulated through talks and other authors&apos; write-ups. Cite the mechanism through the Nguyen-Shparlinski and LadderLeak lattice literature, not a fabricated Bleichenbacher reference.&lt;/p&gt;
&lt;h3&gt;The precision that almost every retelling gets wrong&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A repeated nonce leaks the private key with certainty from just two signatures. A few-bit-biased nonce is different: a statistical lattice attack that needs many signatures, not one. Conflating repeat with bias is the single most corrected misconception in this corner of cryptography.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attack&lt;/th&gt;
&lt;th&gt;Signatures needed&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Certainty&lt;/th&gt;
&lt;th&gt;Canonical example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Repeated nonce&lt;/td&gt;
&lt;td&gt;Two&lt;/td&gt;
&lt;td&gt;Elementary algebra, two linear equations&lt;/td&gt;
&lt;td&gt;Exact, deterministic&lt;/td&gt;
&lt;td&gt;PS3 (2010); Android wallets (2013)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Biased nonce&lt;/td&gt;
&lt;td&gt;Many (tens to thousands)&lt;/td&gt;
&lt;td&gt;Hidden Number Problem, lattice reduction&lt;/td&gt;
&lt;td&gt;Statistical&lt;/td&gt;
&lt;td&gt;PuTTY (roughly 60); LadderLeak (under one bit)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A reused nonce is certain from two signatures, and the fix is to never reuse it. A biased nonce is a statistical HNP or lattice attack that needs many signatures, and the fix is unbiased generation or a constant-time implementation. They have different signature counts, different math, and different cures. Do not conflate them, and do not claim that a single few-bit-biased signature leaks the key.&lt;/p&gt;
&lt;/blockquote&gt;

Each biased signature gives a linear relation of the form $k_i \equiv a_i + b_i\,d \pmod n$ where the unknown nonce $k_i$ is known to be small (its top bits are zero). Stack these relations as rows of a lattice whose short vectors correspond to the small nonces. Running LLL or BKZ finds a short vector, which reveals the nonces and hence $d$. The fewer bits each signature leaks, the more signatures and the more lattice-reduction effort you need -- hence &quot;statistical, and it needs many.&quot;
&lt;h3&gt;Two root causes hide under the word &quot;bias&quot;&lt;/h3&gt;
&lt;p&gt;There is a second fork that decides which cure you need. A &lt;em&gt;generation&lt;/em&gt; failure -- a bad RNG that repeats or biases the nonce -- is cured by not depending on the RNG at all, which is the deterministic signing of the next section. But a &lt;em&gt;usage&lt;/em&gt; failure is different: even a perfectly generated nonce leaks if the code that consumes it, the scalar multiplication $[k]G$, runs in time or draws power that depends on the nonce&apos;s bit-length. That is a side channel, and no amount of clever nonce &lt;em&gt;generation&lt;/em&gt; closes it.&lt;/p&gt;
&lt;p&gt;In 2019, both Minerva and TPM-Fail recovered ECDSA keys from exactly this timing leak, then finished the job with the same lattice machinery as the bias case [@minerva-2019], [@tpm-fail-2019]. The cure there is constant-time scalar multiplication.&lt;/p&gt;

flowchart TD
    K[&quot;The per-signature nonce k&quot;] --&amp;gt; G[&quot;Generation failure: how k is produced&quot;]
    K --&amp;gt; U[&quot;Usage failure: how k is consumed&quot;]
    G --&amp;gt; R[&quot;Repeat: same k twice, key from two signatures&quot;]
    G --&amp;gt; B[&quot;Bias: predictable bits, key from many signatures&quot;]
    U --&amp;gt; T[&quot;Timing or EM leak of k during scalar multiplication&quot;]
    R --&amp;gt; C1[&quot;Cure: deterministic nonce, RFC 6979&quot;]
    B --&amp;gt; C1
    T --&amp;gt; C2[&quot;Cure: constant-time scalar multiplication&quot;]
&lt;p&gt;So the random-number generator was the enemy in three of these four stories. The obvious fix is a better RNG. The actual fix, the one the whole field converged on, was to use no randomness at all.&lt;/p&gt;
&lt;h2&gt;6. The Fix for a Randomness Disaster Was Less Randomness&lt;/h2&gt;
&lt;p&gt;The counterintuitive move: if a bad random nonce is what kills you, stop rolling the dice. Derive the nonce deterministically from the one thing that is already secret and the one thing that is already unique.&lt;/p&gt;
&lt;h3&gt;RFC 6979, the retrofit&lt;/h3&gt;
&lt;p&gt;Thomas Pornin&apos;s RFC 6979 does exactly that. It derives the nonce as a deterministic function of the private key and the message hash, driving an HMAC-based deterministic random bit generator so that signing consumes &lt;em&gt;no&lt;/em&gt; runtime entropy. &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand/&quot; rel=&quot;noopener&quot;&gt;A broken system RNG&lt;/a&gt; can no longer cause a repeat or a generation bias, because there is no RNG in the signing path [@rfc-6979].&lt;/p&gt;
&lt;p&gt;The elegance is that nothing downstream changes: a deterministic signature verifies under the &lt;em&gt;same&lt;/em&gt; verifier as a randomized one, so this is a drop-in with zero protocol change. The RFC states the goal plainly -- signers &quot;do not need access to a source of high-quality randomness&quot; -- and warns that even slight biases in nonce generation may be turned into attacks, exactly the HNP lineage from the previous section [@rfc-6979].&lt;/p&gt;

Deriving the per-signature nonce as a deterministic function of the private key and the message, via an HMAC-based deterministic random bit generator, so that signing needs no runtime entropy and a failing RNG can neither repeat nor bias the nonce. The resulting signature verifies under the ordinary verifier.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The whole class of PS3 and Android failures came from trusting a random-number generator to produce a perfect nonce. Deterministic signing removes the generator from the signing path entirely. Then the field adds a measured dose of randomness back for a different reason.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;But determinism is not unconditionally safer&lt;/h3&gt;
&lt;p&gt;Determinism trades one threat model for another. A fully deterministic signer is a &lt;em&gt;fixed, replayable target&lt;/em&gt;. Feed it the same key and message twice and it does exactly the same computation, which is the ideal setup for a &lt;strong&gt;differential fault attack&lt;/strong&gt;: run the signer twice, glitch one of the two runs with a voltage or clock disturbance, and diff the correct output against the faulted one to recover the key.&lt;/p&gt;
&lt;p&gt;Poddebniak and colleagues demonstrated this against deterministic signature schemes in 2018 [@poddebniak-fault-2018]. NIST took the point: FIPS 186-5 approves deterministic ECDSA yet explicitly flags deterministic schemes as being of particular concern for fault attacks [@fips-186-5].&lt;/p&gt;

Randomized signing is vulnerable to RNG failure but resists differential fault attacks, because two signings of the same message differ. Deterministic signing is immune to RNG failure but hands an attacker a replayable target. Neither extreme dominates. Poddebniak et al. showed the fault channel is real, and the FIPS 186-5 warning is the standards body saying so out loud. The resolution is not to pick a side but to combine them.
&lt;h3&gt;The resolution: hedged signing&lt;/h3&gt;
&lt;p&gt;Hedged signing mixes a little fresh randomness &lt;em&gt;back in&lt;/em&gt; on top of the deterministic derivation. RFC 6979&apos;s own section on variants sanctions adding extra input to the nonce derivation, noting that the result is then no longer deterministic (EC)DSA [@rfc-6979]. The payoff is that a hedged signer survives &lt;em&gt;both&lt;/em&gt; failure modes: if the RNG dies, the deterministic core still produces a safe, distinct nonce; if the RNG works, the fresh entropy makes each signing unrepeatable and defeats the differential fault attack.&lt;/p&gt;
&lt;p&gt;This is where modern libraries land by default. Go&apos;s &lt;code&gt;crypto/ecdsa&lt;/code&gt; hedges: its documentation says signatures &quot;are not deterministic, but entropy is mixed with the private key and the message,&quot; and that &quot;if rand is nil, Sign will produce a deterministic signature according to RFC 6979&quot; [@go-crypto-ecdsa]. BoringSSL mixes in additional entropy the same way [@boringssl-ecdsa], and the security analysis of hedged Fiat-Shamir signatures under faults gives this posture a formal footing [@aranha-hedged-fs-2019].&lt;/p&gt;

Mixing a small amount of fresh randomness into the deterministic nonce derivation, so the signer survives both RNG failure and single fault attacks. It is explicitly not deterministic (EC)DSA, and it is the default in libraries such as Go crypto/ecdsa and BoringSSL.
&lt;p&gt;One caution: it fixes only the &lt;em&gt;generation&lt;/em&gt; surface. The &lt;em&gt;usage&lt;/em&gt; leak from the previous section -- the timing side channel during scalar multiplication -- is untouched by how you derive the nonce, and its cure remains constant-time code. Three cures, three distinct threats.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nonce strategy&lt;/th&gt;
&lt;th&gt;Nonce source&lt;/th&gt;
&lt;th&gt;Survives RNG failure?&lt;/th&gt;
&lt;th&gt;Resists the fault attack?&lt;/th&gt;
&lt;th&gt;Where it lands&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Randomized (classic ECDSA)&lt;/td&gt;
&lt;td&gt;Fresh RNG each signature&lt;/td&gt;
&lt;td&gt;No, the RNG is the whole risk&lt;/td&gt;
&lt;td&gt;Yes, runs differ&lt;/td&gt;
&lt;td&gt;The PS3 and Android failure mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deterministic (RFC 6979)&lt;/td&gt;
&lt;td&gt;HMAC-DRBG of key and message&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No, a replayable target&lt;/td&gt;
&lt;td&gt;Verifies under the ordinary verifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hedged (RFC 6979 variants)&lt;/td&gt;
&lt;td&gt;Deterministic core plus fresh entropy&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Go and BoringSSL default&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;RFC 6979 pulled surface 1 back inside the scheme, but only surface 1, and only as a patch bolted onto a design that still leaks the other two. A cleaner scheme would make deterministic nonces, a fixed encoding, and a byte-exact verifier all native. That scheme already existed. It had been waiting on a patent to expire for two decades.&lt;/p&gt;
&lt;h2&gt;7. The Breakthrough: Schnorr Done Right&lt;/h2&gt;
&lt;p&gt;Everything RFC 6979 retrofitted, one scheme had baked in from birth. It was invented in 1989 and could not be deployed until 2008, because it was patented.&lt;/p&gt;
&lt;h3&gt;Why Schnorr is clean&lt;/h3&gt;
&lt;p&gt;Recall Schnorr&apos;s response, $s = k + ex$. That single linear equation is what makes the scheme provable, batchable, and aggregatable. Provable, because the linear structure is what the Pointcheval-Stern forking lemma needs to reduce forgery to the discrete-log problem in the random-oracle model [@pointcheval-stern-1996-2000]. Batchable, because a linear verification equation lets you check many signatures as one combined equation, the subject of a later section. Aggregatable, because linear things add, which is why threshold and multi-signature schemes like FROST and MuSig2 build directly on Schnorr.&lt;/p&gt;
&lt;p&gt;The Fiat-Shamir transform is still the engine turning the underlying identification protocol into a signature; Schnorr is simply the cleanest identification protocol to feed it.&lt;/p&gt;
&lt;h3&gt;EdDSA and Ed25519 pull all three surfaces inside&lt;/h3&gt;
&lt;p&gt;In 2011, Bernstein, Duif, Lange, Schwabe, and Yang published EdDSA, and its edwards25519 instantiation, Ed25519, is the scheme that pulls all three surfaces inside the definition by design [@bernstein-eddsa-2011].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Surface 1, the nonce, is native and deterministic.&lt;/strong&gt; Ed25519 computes the nonce as $r = H(\text{prefix} \mathbin{|} M) \bmod L$, hashing a secret key-derived prefix together with the message. There is no runtime RNG in the signing path, so the entire PS3 and Android failure class is structurally impossible rather than merely discouraged. The designers said so directly, and named the incident:&lt;/p&gt;

Signatures are generated deterministically ... directly relevant to the recent collapse of the Sony PlayStation 3 security system.
&lt;p&gt;&lt;strong&gt;Surface 2, the encoding, is fixed.&lt;/strong&gt; An Ed25519 signature is a fixed 64-byte string, a point encoding followed by a scalar, and RFC 8032 mandates a strict range check that the scalar satisfy $0 \le S &amp;lt; L$ on decode. Enforcing that check makes a strict Ed25519 implementation strongly unforgeable, closing the malleability surface at the encoding level [@rfc-8032].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Surface 3, the arithmetic, is safe by construction.&lt;/strong&gt; edwards25519 uses a complete twisted-Edwards addition law with no exceptional cases, so the constant-time implementation is the natural one, and there are no special points where a naive verifier misbehaves [@bernstein-eddsa-2011]. Determinism here is &lt;em&gt;structural&lt;/em&gt;, not a patch bolted on afterward, which is the real reason &quot;why determinism helps&quot; is a property of the design rather than a library option.&lt;/p&gt;

A Schnorr signature instantiated on a complete twisted-Edwards curve with a key-prefixed deterministic nonce and a fixed encoding. Ed25519 is its edwards25519 instantiation: constant-time by construction, misuse-resistant by design, and strongly unforgeable under a strict verifier.

A nonce derived by hashing a secret key-derived prefix together with the message, so it is deterministic, unique per message, and never drawn from a runtime random-number generator. Hashing the public key into the challenge additionally hardens the scheme in multi-user and related-key settings.
&lt;p&gt;Key-prefixing -- folding the public key into the hash that produces the challenge -- is a small design choice with outsized value: it hardens the scheme against multi-user and related-key attacks that a bare Schnorr challenge would leave open.&lt;/p&gt;

flowchart TD
    M[Message] --&amp;gt; SCH
    SK[Private key] --&amp;gt; SCH
    subgraph SCH[&quot;Inside EdDSA and BIP-340&quot;]
        N[&quot;Surface 1: deterministic key-prefixed nonce&quot;]
        F[&quot;Surface 2: fixed canonical encoding&quot;]
        V[&quot;Surface 3: byte-exact verifier&quot;]
    end
    SCH --&amp;gt; SIG[&quot;One valid signature, no caller choices&quot;]
    SIG --&amp;gt; OK{&quot;Accept or reject, deterministically&quot;}
&lt;h3&gt;BIP-340: Schnorr over secp256k1, with the verifier pulled inside&lt;/h3&gt;
&lt;p&gt;Bitcoin needed Schnorr on its own curve, secp256k1, and in 2020 BIP-340 delivered it: 64-byte signatures, 32-byte x-only public keys, and, most importantly for surface 3, a verifier specified down to the byte. The specification states that the verification algorithm is &quot;completely specified ... at the byte level,&quot; the guarantee whose full payoff we reach when we return to verifier strictness later [@bip-340].&lt;/p&gt;
&lt;p&gt;BIP-340 also adopts the hedged idea natively: it derives a &lt;em&gt;synthetic&lt;/em&gt; nonce and notes that the added randomness is only supplemental to security, so a faulty RNG cannot break it. Bitcoin&apos;s Taproot upgrade deployed it in November 2021 [@optech-taproot-2021].&lt;/p&gt;
&lt;p&gt;Now the punchline. The scheme that makes deterministic nonces, a fixed encoding, and a byte-exact verifier native is Schnorr, invented in 1989. It could not be deployed until its patent expired in 2008, which is exactly why the worse-behaved ECDSA shipped to the entire internet two decades before its clean successor. Ed25519 arrived in 2011, BIP-340 in 2020. The best-behaved design was first on paper and last in production.&lt;/p&gt;
&lt;p&gt;The two-decade gap between the clean scheme&apos;s invention (US 4,995,082, 1989) and its deployment -- Ed25519 in 2011, BIP-340 in 2020 -- is a patent artifact, not a technical one.&lt;/p&gt;
&lt;p&gt;One honest caveat keeps this from being a silver bullet. Plain Ed25519 has no hedging knob: it is purely deterministic, so on hardware where an attacker can inject faults it is the &lt;em&gt;harder-hit&lt;/em&gt; fault target, and hardened embedded implementations re-inject randomness exactly as the hedged-signing analysis recommends [@poddebniak-fault-2018], [@aranha-hedged-fs-2019]. This is coexistence, not a coronation. Ed25519 and BIP-340 close all three surfaces on the discrete-log track. But the other 1976 track, RSA, had its own reckoning with the &lt;em&gt;form&lt;/em&gt; of a signature, and its answer came with something the discrete-log schemes still lack: a tight proof.&lt;/p&gt;
&lt;h2&gt;8. RSA-PSS: Probabilistic Signing with a Tight Proof&lt;/h2&gt;
&lt;p&gt;On the discrete-log track, determinism was the virtue. On the RSA track, the opposite move -- adding randomness -- bought the best security proof any deployed signature has.&lt;/p&gt;
&lt;h3&gt;PSS, mechanically&lt;/h3&gt;
&lt;p&gt;Never sign the bare message. The EMSA-PSS encoding hashes the message, mixes in a fresh random salt, and builds a full-width encoded message before the RSA private-key operation. Concretely, it forms $H = \mathrm{Hash}(\text{padding} \mathbin{|} \mathrm{Hash}(M) \mathbin{|} \text{salt})$, masks the salt-bearing data block against a mask generated from $H$, and assembles the encoded message as maskedDB followed by $H$ followed by the trailer byte &lt;code&gt;0xbc&lt;/code&gt;. The signature is that encoded message raised to the private exponent modulo $n$ [@rfc-8017], [@bellare-rogaway-pss-1996]. Verification recomputes the whole structure and checks it exactly, rejecting anything that does not fit the padding shape.&lt;/p&gt;

flowchart LR
    M[Message] --&amp;gt; MH[&quot;mHash = Hash of M&quot;]
    SALT[&quot;Random salt&quot;] --&amp;gt; H2[&quot;H = Hash of padding, mHash, salt&quot;]
    MH --&amp;gt; H2
    H2 --&amp;gt; DB[&quot;maskedDB = data block xor mask from H&quot;]
    DB --&amp;gt; EM[&quot;EM = maskedDB then H then 0xbc&quot;]
    H2 --&amp;gt; EM
    EM --&amp;gt; SIG[&quot;Signature = EM to the d mod n&quot;]
&lt;h3&gt;Why probabilistic buys a tight proof&lt;/h3&gt;
&lt;p&gt;Bellare and Rogaway proved PSS existentially unforgeable with a &lt;em&gt;tight&lt;/em&gt; reduction to the RSA problem in the random-oracle model: a forger against PSS can be turned into an algorithm that inverts RSA with essentially the same success probability and running time [@bellare-rogaway-pss-1996].&lt;/p&gt;
&lt;p&gt;That word &lt;em&gt;tight&lt;/em&gt; is the whole prize. A &lt;strong&gt;tight&lt;/strong&gt; reduction loses almost nothing between the scheme&apos;s security and the underlying hard problem, so a concrete key size gives you concrete confidence; a &lt;strong&gt;loose&lt;/strong&gt; reduction loses a large factor, so you either accept a weaker guarantee or inflate parameters to compensate. By contrast, PKCS#1 v1.5&apos;s signature padding has only a heuristic argument, not a tight proof. Among deployed signatures, PSS&apos;s guarantee is the gold standard, and it sets up the proof-quality spectrum we return to in the limits section.&lt;/p&gt;

The Probabilistic Signature Scheme encoding for RSA, which injects a fresh random salt into the padding before the RSA private-key operation and has the verifier recompute and check that structure exactly. The salt is what makes its security reduction to the RSA problem tight, unlike the deterministic PKCS#1 v1.5 padding.
&lt;p&gt;Full-Domain Hash was PSS&apos;s provable but loose predecessor: hash the message across the full width of the modulus, then apply the private operation. PSS keeps that idea and adds the random salt, and the salt is precisely what upgrades the reduction from loose to tight.&lt;/p&gt;
&lt;h3&gt;The deployment reality, stated exactly&lt;/h3&gt;
&lt;p&gt;&quot;Is RSA-PSS widely deployed?&quot; has a split answer, and blurring the split is a common error. TLS 1.3 makes RSA-PSS mandatory to implement for the online handshake signature: the &lt;code&gt;CertificateVerify&lt;/code&gt; message uses the &lt;code&gt;rsa_pss_rsae_*&lt;/code&gt; schemes, and a compliant implementation must support them [@rfc-8446]. That is a specification requirement.&lt;/p&gt;
&lt;p&gt;Separately, as a matter of deployment, most Web-PKI &lt;em&gt;certificates&lt;/em&gt; -- the long-lived signatures a certificate authority puts on the certs in the chain -- are still PKCS#1 v1.5. So the handshake signature your browser makes live and the &lt;a href=&quot;https://paragmali.com/blog/a-perfect-signature-for-a-certificate-that-should-never-have/&quot; rel=&quot;noopener&quot;&gt;certificate-chain signatures&lt;/a&gt; it validates are &lt;em&gt;different&lt;/em&gt; signatures with &lt;em&gt;different&lt;/em&gt; padding, and the honest answer separates them.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The handshake CertificateVerify signature is RSA-PSS by TLS 1.3 requirement. The certificate-chain signatures a certificate authority produces are, in practice, still mostly PKCS#1 v1.5. Same connection, two padding schemes, two different signatures. Do not answer &quot;does the web use PSS?&quot; without saying which of the two you mean.&lt;/p&gt;
&lt;/blockquote&gt;

This is the boundary never to blur. PKCS#1 v1.5 *encryption* padding is the classic Bleichenbacher decryption oracle, a genuine break of the encryption scheme, and a companion part in this series on RSA owns that story. PKCS#1 v1.5 *signatures* are a different animal: the scheme is not broken.&lt;p&gt;Its real-world failures were &lt;em&gt;implementation&lt;/em&gt; bugs in lax verifiers -- most famously the low-exponent forgery that a sloppy parser allows when the public exponent is small, and its 2014 re-occurrence known as BERserk. Those are parsing bugs, not a break of the signature math. That low-exponent forgery has no standalone peer-reviewed paper, so cite the mechanism and the secondary write-ups, never a fabricated primary. The practical rule stands regardless: use RSA-PSS for new designs [@rfc-8017].
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;The cost profile that decides where PSS fits&lt;/h3&gt;
&lt;p&gt;RSA&apos;s economics are lopsided and they determine where it belongs. Verification uses a tiny public exponent, usually 65537, so it is very fast. Signing uses the full-width private exponent, so it is slow, and the signatures are large: 256 bytes at RSA-2048, 384 bytes at RSA-3072, an order of magnitude bigger than a 64-byte elliptic-curve signature. That makes RSA-PSS excellent where you verify far more often than you sign, such as certificates and firmware images, and painful where you sign constantly. We put real numbers on this in the state-of-the-art section.&lt;/p&gt;
&lt;p&gt;PSS pinned RSA&apos;s &lt;em&gt;form&lt;/em&gt; with a proof. But the discrete-log schemes still had an unresolved question about form, one that let anyone alter a signature without the key and briefly threatened to break Bitcoin.&lt;/p&gt;
&lt;h2&gt;9. Malleability, Canonicalization, and Strong Unforgeability&lt;/h2&gt;
&lt;p&gt;Recall the promise EUF-CMA does &lt;em&gt;not&lt;/em&gt; make: that there is only one valid signature per message. For ECDSA there are always at least two, and the caller was left to notice.&lt;/p&gt;
&lt;h3&gt;Surface 2: ECDSA malleability, mechanically&lt;/h3&gt;
&lt;p&gt;Take any valid ECDSA signature &lt;code&gt;(r, s)&lt;/code&gt;. Now negate the scalar: &lt;code&gt;(r, n - s)&lt;/code&gt; also verifies, and it is a different signature on the &lt;em&gt;same&lt;/em&gt; message, producible by anyone with no private key at all. The reason is visible in the verification equation. Replacing $s$ with $n - s$ is replacing it with $-s \bmod n$, which flips the signs of both $u_1$ and $u_2$, negating the recovered point to $-R$. On these curves a point and its negation share an $x$-coordinate, so $x(-R) \bmod n = x(R) \bmod n = r$, and the check still passes.&lt;/p&gt;
&lt;p&gt;BIP-340 states the fact plainly: &quot;If (r,s) is a valid ECDSA signature then (r,n-s) is also valid&quot; [@bip-340]. So standard ECDSA meets EUF-CMA but not SUF-CMA -- exactly why that one-word distinction was worth planting seven sections ago.&lt;/p&gt;

The existence of a second valid signature on an already-signed message that anyone can derive without the private key. For ECDSA, whenever (r, s) is valid so is (r, n - s). It is the reason plain ECDSA satisfies EUF-CMA but fails SUF-CMA.
&lt;h3&gt;The protocol fallout: Bitcoin transaction malleability&lt;/h3&gt;
&lt;p&gt;Malleability is harmless until a protocol treats the signature as an identifier. Bitcoin did. A transaction id is a hash computed over the whole transaction &lt;em&gt;including its signatures&lt;/em&gt;, so flipping &lt;code&gt;s&lt;/code&gt; to &lt;code&gt;n - s&lt;/code&gt; changes the txid without changing what the transaction does. Anything that referenced an unconfirmed transaction by its id -- a chain of dependent spends, a naive payment tracker -- could be broken by a third party rewriting the signature in flight [@decker-wattenhofer-2014].&lt;/p&gt;
&lt;p&gt;The honest Mt. Gox nuance: transaction malleability was real and was publicly blamed for the 2014 collapse, but Decker and Wattenhofer measured the malleability activity on the network and found it insufficient to account for the losses, and later analysis attributes most of the missing coins to a wallet compromise around 2011. Real and blamed, but not the sole cause.&lt;/p&gt;
&lt;h3&gt;The fix: low-s canonicalization restores SUF-CMA&lt;/h3&gt;
&lt;p&gt;If two signatures differ only by $s$ versus $n - s$, pick one. The &lt;strong&gt;low-s&lt;/strong&gt; rule requires $s \le n/2$, so exactly one of the twins is standard and the other is rejected on sight. Bitcoin moved this from proposal to policy across BIP-62 and BIP-146, and it restores strong unforgeability in the system that needed it most [@bip-62], [@bip-146]. Ed25519&apos;s analogue is the strict &lt;code&gt;S &amp;lt; L&lt;/code&gt; range check that RFC 8032 mandates on decode, which is what makes a strict Ed25519 implementation SUF-CMA [@rfc-8032].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If any part of your protocol hashes over or deduplicates by the signature itself -- transaction ids, caches, idempotency keys, replay checks -- plain EUF-CMA lets an attacker manufacture a second valid signature and break your assumption. Enforce low-s or a strict &lt;code&gt;S &amp;lt; L&lt;/code&gt; range check, or choose a SUF-CMA-by-design scheme such as BIP-340.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Surface 3: the verifier, which closed last and most embarrassingly&lt;/h3&gt;
&lt;p&gt;The third surface failed in two distinct shapes. The first is a &lt;em&gt;permissive&lt;/em&gt; verifier. In 2022, Java&apos;s ECDSA verification accepted the all-zero signature &lt;code&gt;(r, s) = (0, 0)&lt;/code&gt; for any message and any key, because it skipped the range check that forbids zero. &quot;Psychic Signatures,&quot; CVE-2022-21449, scored CVSS 7.5 and forged anything built on Java&apos;s signatures: TLS, signed JWTs, SAML assertions, OIDC id tokens, and WebAuthn responses [@cve-2022-21449], [@neil-madden-2022].&lt;/p&gt;
&lt;p&gt;The second shape is more subtle: &lt;em&gt;honest disagreement&lt;/em&gt;. &quot;Taming the Many EdDSAs&quot; showed that verifiers all conforming to RFC 8032 nonetheless split on adversarial inputs -- cofactored versus cofactorless verification equations, &lt;a href=&quot;https://paragmali.com/blog/the-signature-was-valid-the-message-was-forged-a-field-guide/&quot; rel=&quot;noopener&quot;&gt;non-canonical point encodings&lt;/a&gt;, small-order points -- so the same signature could be valid to one library and invalid to another [@chalkias-many-eddsas-2020]. For a consensus system that is a disaster, and ZIP-215 answers it by pinning a byte-exact validation profile on which single and batch verification always agree [@zip-215].&lt;/p&gt;
&lt;p&gt;Edwards curves have a cofactor, so the verification equation can be written to multiply through by it (cofactored) or not (cofactorless). RFC 8032 notes the cofactorless check is sufficient but not required, and that latitude is exactly what let conformant verifiers disagree until ZIP-215 fixed a single byte-exact rule.&lt;/p&gt;
&lt;p&gt;BIP-340 treats this surface as an engineering requirement rather than an afterthought, which is why its verifier is specified to the byte:&lt;/p&gt;

The verification algorithm must be completely specified at the byte level. This guarantees that nobody can construct a signature that is valid to some verifiers but not all.
&lt;h3&gt;The evidence, in one table&lt;/h3&gt;
&lt;p&gt;Every named break in this article splits cleanly across the three surfaces. Read the &quot;Surface&quot; column and the thesis stops being a slogan and becomes a body count.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Break&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Surface&lt;/th&gt;
&lt;th&gt;Sub-type&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;The rule it teaches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;PlayStation 3&lt;/td&gt;
&lt;td&gt;2010&lt;/td&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Repeat&lt;/td&gt;
&lt;td&gt;One constant nonce across all signatures&lt;/td&gt;
&lt;td&gt;Never reuse a nonce; two signatures leak the key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android Bitcoin wallets&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Generation&lt;/td&gt;
&lt;td&gt;Broken SecureRandom repeated the nonce&lt;/td&gt;
&lt;td&gt;Do not trust the RNG; derive the nonce&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitcoin txid, Mt. Gox era&lt;/td&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;2. Canonical form&lt;/td&gt;
&lt;td&gt;Malleability&lt;/td&gt;
&lt;td&gt;(r, n - s) is a free second signature&lt;/td&gt;
&lt;td&gt;Enforce low-s; treat SUF-CMA as the bar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minerva, TPM-Fail&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Usage, timing&lt;/td&gt;
&lt;td&gt;Nonce bit-length leaked during scalar mult&lt;/td&gt;
&lt;td&gt;Constant-time scalar multiplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Many EdDSAs, ZIP-215&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;3. Verifier&lt;/td&gt;
&lt;td&gt;Disagreement&lt;/td&gt;
&lt;td&gt;Conformant verifiers split on edge cases&lt;/td&gt;
&lt;td&gt;Specify the verifier at the byte level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Psychic Signatures&lt;/td&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;3. Verifier&lt;/td&gt;
&lt;td&gt;Permissive&lt;/td&gt;
&lt;td&gt;Verifier accepted (0, 0) for any message&lt;/td&gt;
&lt;td&gt;Range-check the signature; reject zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PuTTY&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;1. Nonce&lt;/td&gt;
&lt;td&gt;Bias&lt;/td&gt;
&lt;td&gt;Top 9 bits of each P-521 nonce were zero&lt;/td&gt;
&lt;td&gt;Unbiased or deterministic nonce generation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Notice what is missing from the root-cause column: not once did anyone solve an elliptic-curve discrete logarithm or factor a modulus. That is Aha 1, complete. Fix the nonce, fix the form, fix the verifier, and you have a signature you can trust one at a time. But the modern world verifies millions at once, and the obvious way to batch them is quietly forgeable.&lt;/p&gt;
&lt;h2&gt;10. Batch Verification, and the One Condition That Makes It Safe&lt;/h2&gt;
&lt;p&gt;Schnorr&apos;s linearity offers a tempting shortcut: instead of checking a thousand signatures one by one, check their sum once. Done naively, that shortcut lets an attacker slip forgeries past you.&lt;/p&gt;
&lt;h3&gt;The technique&lt;/h3&gt;
&lt;p&gt;Because Schnorr, EdDSA, and BIP-340 verification is a single linear equation, a batch of $u$ signatures can be checked as one random linear combination of those equations. That collapses $u$ separate double-scalar multiplications into a single large multi-scalar multiplication, whose cost is sublinear in the number of distinct bases, which is where the throughput win comes from. The original Ed25519 paper reports a batch of 64 signatures verifying at under 134,000 cycles per signature, against 273,364 cycles for a single verification, roughly a twofold speedup [@bernstein-eddsa-2011].&lt;/p&gt;

Checking many signatures with one combined equation instead of one equation each, by exploiting the linearity of Schnorr-type verification. It is safe only when each signature&apos;s contribution is weighted by an independent random coefficient; the naive equal-weight sum is forgeable.
&lt;h3&gt;The non-negotiable safety condition&lt;/h3&gt;
&lt;p&gt;The coefficients must be &lt;strong&gt;randomized&lt;/strong&gt;. Here is why the naive all-ones sum fails. A signature verifies exactly when its verification residual is zero. If you add the residuals with equal weight, an attacker can submit two invalid signatures whose residuals are equal and opposite, so they cancel in the sum: the batch equation holds even though neither signature is valid.&lt;/p&gt;
&lt;p&gt;Weight each residual by an independent random coefficient and the errors no longer cancel except with negligible probability. BIP-340 derives its batch coefficients from a CSPRNG seeded over &lt;em&gt;all&lt;/em&gt; of the inputs, so an attacker cannot predict the weights and engineer a set of residuals that sums to zero without each signature being individually valid [@bip-340].&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Illustrative model. A signature is VALID iff its verification residual is 0. const n = 1000003n; const mod = (a, m) =&amp;gt; ((a % m) + m) % m; // Attacker forges two invalid signatures whose residuals are equal and opposite. const D = 777n;                 // the cancelling error the attacker chooses const e1 = mod(D, n);           // residual of forged signature 1 (nonzero: invalid) const e2 = mod(-D, n);          // residual of forged signature 2 (nonzero: invalid) console.log(&apos;signature 1 valid on its own? &apos; + (e1 === 0n));   // false console.log(&apos;signature 2 valid on its own? &apos; + (e2 === 0n));   // false // Naive all-ones batch: accept iff the plain sum of residuals is 0 const allOnes = mod(e1 + e2, n); console.log(&apos;all-ones batch residual = &apos; + allOnes + (allOnes === 0n ? &apos;  -&amp;gt; ACCEPTED, forgery slips through&apos; : &apos;  -&amp;gt; rejected&apos;)); // Randomized batch: weight each residual by a per-input random coefficient const a1 = 31337n, a2 = 424242n;   // seeded by a CSPRNG over ALL inputs const rand = mod(a1 * e1 + a2 * e2, n); console.log(&apos;randomized batch residual = &apos; + rand + (rand === 0n ? &apos;  -&amp;gt; accepted&apos; : &apos;  -&amp;gt; REJECTED, forgery caught&apos;));&lt;/code&gt;}&lt;/p&gt;

flowchart TD
    IN[&quot;u signatures to verify&quot;] --&amp;gt; SEED[&quot;Seed a CSPRNG over all u inputs&quot;]
    SEED --&amp;gt; COEF[&quot;Random coefficients a1 through au&quot;]
    COEF --&amp;gt; MSM[&quot;One multi-scalar multiplication&quot;]
    MSM --&amp;gt; OK{&quot;Batch equation holds?&quot;}
    OK --&amp;gt;|yes| ACC[&quot;Accept all&quot;]
    OK --&amp;gt;|no| FALL[&quot;Fall back to individual verification&quot;]
    IN --&amp;gt; BAD[&quot;Naive all-ones coefficients&quot;]
    BAD --&amp;gt; CANCEL[&quot;Attacker makes two residuals cancel&quot;]
    CANCEL --&amp;gt; FORGE[&quot;Invalid batch accepted, a forgery&quot;]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Naive all-ones batch verification is forgeable, because an attacker can craft invalid signatures whose verification errors cancel in the sum. Randomize the coefficients, seeded by a CSPRNG over every input, or do not batch at all.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;What batching does not give you, and where it is not used&lt;/h3&gt;
&lt;p&gt;A failed batch tells you only that &lt;em&gt;some&lt;/em&gt; signature in it is bad, not which one, so you must fall back to individual verification to find the culprit -- a real cost when batches are large. Batching is also a Schnorr-family privilege: BIP-340 notes that standardized ECDSA &quot;cannot be verified more efficiently in batch,&quot; so the ECDSA deployments that dominate the web get no speedup here [@bip-340]. And adoption is narrower than the technique&apos;s fame suggests.&lt;/p&gt;
&lt;p&gt;Bitcoin Core does not batch-verify signatures in consensus validation as of 2024 through 2026: the determinism and simplicity of validating each signature independently outweigh the throughput gain. Batch verification is a real optimization for specific high-volume verifiers, not a universal default.&lt;/p&gt;
&lt;p&gt;That is the deployed toolkit: ECDSA, Ed25519, BIP-340 Schnorr, and RSA-PSS, each with its surfaces pulled in to a different degree. Which one should you reach for in 2026? The honest answer is not a single winner.&lt;/p&gt;
&lt;h2&gt;11. State of the Art and Competing Approaches (2024 to 2026)&lt;/h2&gt;
&lt;p&gt;There is no single best signature scheme to crown. The state of the art is four deployed constructions, each dominant in a niche, plus a set of signposts pointing at the frontier. This is coexistence with selective supersession, not a ladder where each rung replaces the last.&lt;/p&gt;
&lt;h3&gt;The four, per niche&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Deterministic or hedged ECDSA&lt;/strong&gt; leads by raw deployment. It is what FIPS, TLS, and the secp256k1 chains run, and modern libraries hedge it by default [@rfc-6979], [@go-crypto-ecdsa]. &lt;strong&gt;Ed25519&lt;/strong&gt; is the misuse-resistant greenfield default: deterministic nonce, fixed encoding, constant-time by construction [@rfc-8032], [@bernstein-eddsa-2011]. &lt;strong&gt;BIP-340 Schnorr&lt;/strong&gt; leads for consensus and threshold work, being byte-exact, SUF-CMA by design, and linear enough to aggregate [@bip-340]. &lt;strong&gt;RSA-PSS&lt;/strong&gt; leads verify-heavy and interoperability-bound settings with its tight proof and cheap verification [@rfc-8017], [@rfc-8446].&lt;/p&gt;
&lt;p&gt;Of the classical schemes, only finite-field DSA is withdrawn for new signatures by FIPS 186-5; deterministic ECDSA is &lt;em&gt;approved&lt;/em&gt;, which is not the same as &lt;em&gt;mandated&lt;/em&gt; [@fips-186-5].&lt;/p&gt;

FIPS 186-5 (2023) approves ECDSA, EdDSA, and deterministic ECDSA, and withdraws finite-field DSA for new signatures. Two practical consequences follow. First, &quot;approved&quot; is not &quot;mandated&quot;: you may hedge, and the default posture in compliant libraries is randomized or hedged signing rather than bare determinism. Second, EdDSA is now a first-class FIPS citizen, so choosing Ed25519 no longer forces you outside the compliance boundary the way it once did [@fips-186-5].
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;ECDSA (hedged)&lt;/th&gt;
&lt;th&gt;Ed25519&lt;/th&gt;
&lt;th&gt;BIP-340 Schnorr&lt;/th&gt;
&lt;th&gt;RSA-PSS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Hardness&lt;/td&gt;
&lt;td&gt;ECDLP (P-256, secp256k1)&lt;/td&gt;
&lt;td&gt;ECDLP (edwards25519)&lt;/td&gt;
&lt;td&gt;ECDLP (secp256k1)&lt;/td&gt;
&lt;td&gt;RSA, related to factoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature size&lt;/td&gt;
&lt;td&gt;64 to 72 bytes (DER)&lt;/td&gt;
&lt;td&gt;64 bytes&lt;/td&gt;
&lt;td&gt;64 bytes&lt;/td&gt;
&lt;td&gt;256 bytes at 2048, 384 at 3072&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public key size&lt;/td&gt;
&lt;td&gt;33 bytes compressed&lt;/td&gt;
&lt;td&gt;32 bytes&lt;/td&gt;
&lt;td&gt;32 bytes, x-only&lt;/td&gt;
&lt;td&gt;256 to 384 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nonce or randomness&lt;/td&gt;
&lt;td&gt;Hedged or RFC 6979&lt;/td&gt;
&lt;td&gt;Native deterministic&lt;/td&gt;
&lt;td&gt;Synthetic, hedged&lt;/td&gt;
&lt;td&gt;Fresh random salt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Malleable by default?&lt;/td&gt;
&lt;td&gt;Yes, enforce low-s&lt;/td&gt;
&lt;td&gt;No under strict S range&lt;/td&gt;
&lt;td&gt;No, byte-exact&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch verification&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proof quality&lt;/td&gt;
&lt;td&gt;Idealized only&lt;/td&gt;
&lt;td&gt;Loose, forking lemma&lt;/td&gt;
&lt;td&gt;Loose, forking lemma&lt;/td&gt;
&lt;td&gt;Tight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sign versus verify&lt;/td&gt;
&lt;td&gt;Balanced&lt;/td&gt;
&lt;td&gt;Balanced&lt;/td&gt;
&lt;td&gt;Balanced&lt;/td&gt;
&lt;td&gt;Slow sign, fast verify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;FIPS, secp256k1 chains&lt;/td&gt;
&lt;td&gt;Greenfield default&lt;/td&gt;
&lt;td&gt;Consensus, threshold&lt;/td&gt;
&lt;td&gt;RSA interop, verify-heavy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Benchmarks, read correctly&lt;/h3&gt;
&lt;p&gt;The reference measurements everyone quotes for Ed25519 come from the eBATS and eBACS public benchmarking effort [@bernstein-eddsa-2011], [@ebacs-bench].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ed25519 operation&lt;/th&gt;
&lt;th&gt;Cost, eBATS reference measurement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Sign one message&lt;/td&gt;
&lt;td&gt;87,548 cycles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verify one signature&lt;/td&gt;
&lt;td&gt;273,364 cycles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verify in a batch of 64&lt;/td&gt;
&lt;td&gt;under 134,000 cycles per signature&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Two facts most first courses skip. RSA verifies far faster than it signs, often by more than an order of magnitude, because the public exponent is tiny and the private exponent is full width, whereas elliptic-curve schemes are balanced between the two operations. And rankings flip with the implementation: a build with hand-tuned P-256 assembly can make ECDSA out-run Ed25519, the reverse of the eBATS ordering. Never quote a cross-scheme benchmark as if it were a property of the mathematics. Measure the library you ship, on the CPU you run.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Signposts at the frontier&lt;/h3&gt;
&lt;p&gt;Four constructions sit just outside the four-scheme core and deserve a pointer rather than a section. &lt;strong&gt;XEdDSA&lt;/strong&gt; lets you sign with an X25519 Diffie-Hellman key, a specialized key-reuse trick that is deployed at scale in the Signal protocol and the applications built on it, so it is peripheral by role but anything but academic [@xeddsa-spec].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MuSig2&lt;/strong&gt; and &lt;strong&gt;FROST&lt;/strong&gt; are two-round multi-signature and threshold-Schnorr protocols that produce an ordinary 64-byte signature indistinguishable from a single-signer one [@musig2-2020], [@rfc-9591]. &lt;strong&gt;BLS&lt;/strong&gt; offers pairing-based aggregation and anchors Ethereum consensus; a companion part in this series on hashing to curves covers it.&lt;/p&gt;
&lt;p&gt;And the horizon is &lt;strong&gt;post-quantum&lt;/strong&gt;: NIST finalized &lt;a href=&quot;https://paragmali.com/blog/the-thirty-year-migration-ships-in-a-pip-install-how-post-qu/&quot; rel=&quot;noopener&quot;&gt;ML-DSA&lt;/a&gt; in FIPS 204 and SLH-DSA in FIPS 205 in August 2024, both with signatures ranging from kilobytes to tens of kilobytes, an order of magnitude or more larger than anything in the table above [@fips-204], [@fips-205].&lt;/p&gt;
&lt;p&gt;XEdDSA is de-randomized like plain EdDSA, so absent hedging it inherits the same differential-fault exposure, which the hedged Fiat-Shamir analysis addresses [@aranha-hedged-fs-2019].&lt;/p&gt;
&lt;p&gt;Every row of those tables assumes the hardness holds. That assumption has a precise shape, and a known expiration date.&lt;/p&gt;
&lt;h2&gt;12. Theoretical Limits&lt;/h2&gt;
&lt;p&gt;Here is what a first course skips: none of these schemes has an unconditional proof, their proofs differ enormously in quality, and every one of them dies to the same quantum algorithm.&lt;/p&gt;
&lt;h3&gt;Conditional security, three assumptions&lt;/h3&gt;
&lt;p&gt;Every guarantee in this article is conditional. The elliptic-curve schemes rest on the hardness of the elliptic-curve discrete logarithm; RSA-PSS rests on the RSA problem, which is no harder than factoring; and all of the Fiat-Shamir schemes rest additionally on the &lt;strong&gt;random-oracle model&lt;/strong&gt;, a heuristic that proves security while treating the hash function as an ideal random function that no real hash exactly is. &quot;Provably secure&quot; here always means &quot;secure if these assumptions hold.&quot; That is not a weakness peculiar to signatures; it is the honest status of essentially all deployed public-key cryptography.&lt;/p&gt;
&lt;h3&gt;The tightness spectrum&lt;/h3&gt;
&lt;p&gt;The interesting frontier is not whether the proofs exist but how much they lose. A reduction converts an attacker on the scheme into an algorithm against the hard problem, and the &lt;em&gt;tightness&lt;/em&gt; of that conversion is how much security leaks in the translation.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scheme&lt;/th&gt;
&lt;th&gt;Reduction (random-oracle model)&lt;/th&gt;
&lt;th&gt;Tightness&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;RSA-PSS&lt;/td&gt;
&lt;td&gt;To the RSA problem, Bellare-Rogaway&lt;/td&gt;
&lt;td&gt;Tight&lt;/td&gt;
&lt;td&gt;The best guarantee of any deployed signature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schnorr, EdDSA&lt;/td&gt;
&lt;td&gt;To discrete log via the forking lemma, Pointcheval-Stern&lt;/td&gt;
&lt;td&gt;Loose, quadratic loss&lt;/td&gt;
&lt;td&gt;Seurin showed the loss is essentially optimal, a barrier not a gap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(EC)DSA&lt;/td&gt;
&lt;td&gt;Only with an idealized conversion function, Fersch-Kiltz-Poettering&lt;/td&gt;
&lt;td&gt;Idealized&lt;/td&gt;
&lt;td&gt;Secure because ECDLP holds, not because the scheme is clean&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;RSA-PSS has a tight reduction to RSA [@bellare-rogaway-pss-1996]. Schnorr and EdDSA have only a loose, quadratic-loss reduction through the forking lemma [@pointcheval-stern-1996-2000], and Seurin proved that this loss is essentially the best any algebraic reduction can do, so it is a genuine barrier rather than a proof waiting to be improved [@seurin-2012]. Later work made the multi-user Schnorr reduction explicit, with a loss on the order of the number of hash queries [@kiltz-masny-pan-2016]. And (EC)DSA has the weakest guarantee of all: the only known proofs must model its conversion function as an idealized, bijective random oracle [@fersch-kiltz-poettering-2016].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Deployment and proof quality are inversely correlated here. The most-deployed signature in the world, ECDSA, has the least satisfying proof: it is secure because the elliptic-curve discrete logarithm is hard, not because the scheme is clean. And tight proof or loose, every scheme in this article falls to Shor&apos;s algorithm. The destination is post-quantum, not a better curve.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The classical hardness bounds are near-optimal but unproven. Elliptic-curve discrete log costs about 2^128 operations for a 256-bit curve by Pollard&apos;s rho, which is optimal in the generic-group model, while factoring costs sub-exponential effort by the general number field sieve. Neither hardness is proven, and RSA inversion is not even proven equivalent to factoring.&lt;/p&gt;
&lt;h3&gt;The one bound that is not conjectural&lt;/h3&gt;
&lt;p&gt;There is exactly one limit in this section that is not an open question. Shor&apos;s algorithm solves both factoring and the discrete logarithm in quantum polynomial time, so a cryptographically relevant quantum computer breaks &lt;em&gt;every&lt;/em&gt; scheme in this article at once [@shor-1997]. No curve size and no modulus length helps, because Shor&apos;s cost is polynomial in the key size. The elliptic-curve schemes and RSA-PSS fall together.&lt;/p&gt;

For encryption, an adversary can record ciphertext today and decrypt it after building a quantum computer, so the harvest-now-decrypt-later threat is urgent. Signatures authenticate in real time, so a signature forged after a quantum computer exists cannot retroactively forge yesterday&apos;s authenticated session. The exception is long-lived keys: code-signing roots, firmware, and certificate roots that must stay valid for decades have to outlive Shor, which is exactly where post-quantum signatures are being adopted first [@shor-1997].
&lt;p&gt;If the theory is this settled -- conditional proofs of differing quality and a known quantum cliff -- then everything still up to you lives in the code. Here is that part, compressed into rules you can apply on Monday.&lt;/p&gt;
&lt;h2&gt;13. The Practical Guide: Decision Rules&lt;/h2&gt;
&lt;p&gt;Every rule below is one row of the failure catalog, inverted. If you remember nothing else, remember the decision tree.&lt;/p&gt;

flowchart TD
    START{&quot;What constrains you?&quot;} --&amp;gt; G[&quot;Greenfield, no constraint&quot;]
    START --&amp;gt; F[&quot;FIPS or compliance&quot;]
    START --&amp;gt; R[&quot;Must interoperate with RSA&quot;]
    START --&amp;gt; B[&quot;Bitcoin or byte-exact consensus&quot;]
    START --&amp;gt; T[&quot;Threshold or multisig&quot;]
    G --&amp;gt; GE[&quot;Ed25519 with a strict verifier&quot;]
    F --&amp;gt; FE[&quot;Hedged ECDSA P-256 or FIPS-approved Ed25519, enforce low-s&quot;]
    R --&amp;gt; RE[&quot;RSA-PSS as PS256, at least RSA-3072&quot;]
    B --&amp;gt; BE[&quot;BIP-340 Schnorr, or low-s strict-DER ECDSA if legacy&quot;]
    T --&amp;gt; TE[&quot;FROST or MuSig2, never deterministic nonces&quot;]
&lt;h3&gt;Use X, with these parameters, in this case&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Greenfield, no FIPS constraint.&lt;/strong&gt; Reach for &lt;strong&gt;Ed25519&lt;/strong&gt;, verified with a strict profile that enforces the canonical &lt;code&gt;S &amp;lt; L&lt;/code&gt; range and a ZIP-215-style byte-exact rule [@rfc-8032], [@zip-215]. You get deterministic nonces, a fixed encoding, and constant-time arithmetic for free.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FIPS-constrained.&lt;/strong&gt; Use &lt;strong&gt;deterministic or hedged ECDSA on P-256&lt;/strong&gt; following RFC 6979 and its hedged variant, or FIPS 186-5-approved &lt;strong&gt;Ed25519&lt;/strong&gt;. Enforce low-s, and insist on a constant-time library so you do not reopen the Minerva timing surface [@rfc-6979], [@fips-186-5].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RSA interoperability for a new design.&lt;/strong&gt; Use &lt;strong&gt;RSA-PSS&lt;/strong&gt; (the &lt;code&gt;PS256&lt;/code&gt; scheme), never PKCS#1 v1.5 for new work. Set the salt length equal to the hash length, use a matching MGF hash, and choose at least RSA-3072 for 128-bit security [@rfc-8017].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bitcoin or byte-exact consensus.&lt;/strong&gt; Use &lt;strong&gt;BIP-340 Schnorr&lt;/strong&gt;, as deployed in Taproot. If you are stuck with legacy ECDSA, enforce low-s and strict DER encoding so transactions are not malleable [@bip-340], [@bip-146].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Threshold or multisig.&lt;/strong&gt; Use &lt;strong&gt;FROST&lt;/strong&gt; or &lt;strong&gt;MuSig2&lt;/strong&gt; over BIP-340, and &lt;em&gt;never&lt;/em&gt; use deterministic nonces in multi-party signing -- a deterministic nonce in an interactive protocol can be replayed across differing co-signer inputs and leak the share [@rfc-9591], [@musig2-2020], [@bip-340].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Always.&lt;/strong&gt; Hedge the nonce whenever the platform can face fault injection. Domain-separate your keys per scheme: BIP-340 warns that reusing one key across RFC-6979 ECDSA and BIP-340 can leak it through cross-scheme nonce coincidences [@bip-340]. Verify strictly, and when any part of your protocol hashes over signatures, treat SUF-CMA, not merely EUF-CMA, as the bar.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Key parameters&lt;/th&gt;
&lt;th&gt;Because of (named break)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Greenfield, no FIPS&lt;/td&gt;
&lt;td&gt;Ed25519&lt;/td&gt;
&lt;td&gt;Strict &lt;code&gt;S &amp;lt; L&lt;/code&gt;, ZIP-215 verifier&lt;/td&gt;
&lt;td&gt;Psychic Signatures, Many EdDSAs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FIPS-constrained&lt;/td&gt;
&lt;td&gt;Hedged ECDSA P-256, or FIPS Ed25519&lt;/td&gt;
&lt;td&gt;RFC 6979 or hedged, low-s, constant-time&lt;/td&gt;
&lt;td&gt;PS3, PuTTY, Minerva&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSA interop, new design&lt;/td&gt;
&lt;td&gt;RSA-PSS as PS256&lt;/td&gt;
&lt;td&gt;Salt length equals hash length, RSA-3072 or larger&lt;/td&gt;
&lt;td&gt;v1.5 low-exponent forgeries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitcoin or consensus&lt;/td&gt;
&lt;td&gt;BIP-340 Schnorr (Taproot)&lt;/td&gt;
&lt;td&gt;Or low-s and strict DER if legacy ECDSA&lt;/td&gt;
&lt;td&gt;Bitcoin txid malleability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threshold or multisig&lt;/td&gt;
&lt;td&gt;FROST or MuSig2&lt;/td&gt;
&lt;td&gt;Never deterministic nonces in multi-party&lt;/td&gt;
&lt;td&gt;Reused-nonce key recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Ed25519 for anything greenfield. Hedged ECDSA on P-256 for FIPS and secp256k1 chains. RSA-PSS for RSA interoperability. BIP-340 Schnorr for consensus and threshold. Whatever you pick, verify strictly and hedge the nonce wherever faults are possible.&lt;/p&gt;
&lt;/blockquote&gt;

Generate an Ed25519 keypair, then sign and verify a message, with OpenSSL 3.x:&lt;ul&gt;
&lt;li&gt;Create the private key: &lt;code&gt;openssl genpkey -algorithm ed25519 -out sk.pem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Extract the public key: &lt;code&gt;openssl pkey -in sk.pem -pubout -out pk.pem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Sign: &lt;code&gt;openssl pkeyutl -sign -inkey sk.pem -rawin -in message.txt -out sig.bin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Verify: &lt;code&gt;openssl pkeyutl -verify -pubin -inkey pk.pem -rawin -in message.txt -sigfile sig.bin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To force RSA-PSS rather than PKCS#1 v1.5 when signing with an RSA key: &lt;code&gt;openssl dgst -sha256 -sign rsa.pem -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -out sig.bin message.txt&lt;/code&gt;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Turn each of those rules around and you get the antipatterns that keep showing up in real code review: the misuse catalog.&lt;/p&gt;
&lt;h2&gt;14. Common Misuse, and the One Sentence Underneath&lt;/h2&gt;
&lt;p&gt;Every break in this article was a violated rule from the last section. Here they are as the antipatterns a reviewer actually finds in real code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A caller-supplied nonce from a weak or predictable RNG.&lt;/strong&gt; This is the Android wallets and, in its biased form, PuTTY [@bitcoin-android-2013], [@cve-2024-31497].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A nonce reused across messages or keys.&lt;/strong&gt; This is the PS3, and it is certain from two signatures [@ps3-27c3-2010].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non-constant-time scalar multiplication.&lt;/strong&gt; This is Minerva and TPM-Fail: even a perfect nonce leaks through timing [@minerva-2019], [@tpm-fail-2019].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skipping the range, subgroup, or canonical-s checks.&lt;/strong&gt; This reopens malleability, and for a permissive verifier it reopens forgery [@bip-340].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treating EUF-CMA as if it meant non-malleability.&lt;/strong&gt; This is Bitcoin transaction-id malleability: the scheme was unforgeable and the protocol still broke [@bip-340].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Naive all-ones batch coefficients.&lt;/strong&gt; A forgeable batch, defeated by errors that cancel in the sum [@bip-340].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A lax or permissive verifier.&lt;/strong&gt; This is Psychic Signatures, which accepted the all-zero signature for any message [@cve-2022-21449].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PKCS#1 v1.5 for new designs, or blurring the encryption-versus-signature split.&lt;/strong&gt; Use RSA-PSS, and never confuse the v1.5 decryption oracle with the unbroken v1.5 signature scheme [@rfc-8017].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signing without domain separation.&lt;/strong&gt; One key across two schemes can leak through cross-scheme nonce coincidences [@bip-340].&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; First, an ECDSA nonce drawn from a dubious or caller-supplied RNG instead of a derived or hedged one. Second, a missing low-s or strict &lt;code&gt;S &amp;lt; L&lt;/code&gt; check, so signatures are malleable. Third, a verifier that does not range-check the signature values and will therefore accept degenerate inputs. Those three account for most of the named breaks in this article.&lt;/p&gt;
&lt;/blockquote&gt;


No, and this is the most repeated mistake in the area. A *repeated* nonce leaks the key with certainty from just two signatures -- that is the PS3 break. A *biased* nonce, where the top few bits are always zero, is a statistical attack: it takes many signatures and lattice reduction, framed as the Hidden Number Problem, to recover the key, as PuTTY&apos;s roughly-sixty-signature break showed. &quot;One few-bit-biased signature leaks your key&quot; is false.


No. Deriving the nonce deterministically removes the RNG-failure disaster, but it turns the signer into a fixed, replayable target for fault attacks: glitch one of two identical signings and diff the outputs. The resolution is hedged signing -- mix a little fresh randomness back in -- which most modern libraries do by default.


No, but it is nuanced. Standard Ed25519 can be malleable at the group level unless the verifier enforces the canonical `S &amp;lt; L` range check that RFC 8032 mandates on decode. Enforce it, as strict libraries do, and Ed25519 is strongly unforgeable; skip it and you inherit a malleability surface.


Encryption. The v1.5 *encryption* padding is the classic Bleichenbacher decryption oracle. The v1.5 *signature* scheme is not broken; its real failures were implementation bugs in lax verifiers, such as low-exponent forgeries, not a break of the signature math. Even so, use RSA-PSS for new designs.


Ed25519 for anything greenfield: deterministic nonces, a fixed 64-byte encoding, constant-time arithmetic, and strong unforgeability under strict verification. Use ECDSA only where a standard or platform compels it, such as FIPS environments or secp256k1 chains, and then with RFC 6979 or hedged nonces, low-s enforcement, and a constant-time library.


If any part of your protocol hashes over or deduplicates by the signature itself -- transaction ids, caches, replay checks -- you need SUF-CMA, because plain EUF-CMA lets an attacker produce a different valid signature on the same message. Enforce low-s or a strict `S &amp;lt; L` range, or choose a SUF-CMA-by-design scheme like BIP-340.


No. Shor&apos;s algorithm breaks both discrete log and factoring in polynomial time, so ECDSA, EdDSA, BIP-340, and RSA-PSS all fall to a cryptographically relevant quantum computer, and no curve or modulus size helps. The destination is post-quantum signatures such as ML-DSA and SLH-DSA, finalized in August 2024 and being adopted first for long-lived keys.

&lt;h3&gt;The one sentence underneath&lt;/h3&gt;
&lt;p&gt;Return to that slide at 27C3. Sony&apos;s curve was pristine; two signatures gave up the master key [@ps3-27c3-2010]. Now line up the rest of the body count. Across three decades, almost no deployed break ever solved an elliptic-curve discrete logarithm or factored a modulus. Attackers reused a nonce, biased one [@cve-2024-31497], leaked one through timing [@minerva-2019], flipped a signature&apos;s &lt;code&gt;s&lt;/code&gt; to &lt;code&gt;n - s&lt;/code&gt;, or accepted the all-zero signature for any message [@cve-2022-21449]. The hardness assumption sat untouched through all of it.&lt;/p&gt;
&lt;p&gt;That is why &quot;signing done right&quot; is not a single choice but a stack: the right scheme for the job, a nonce you do not have to trust the RNG for, a canonical form, and a byte-exact verifier -- and, the deepest lesson the evolution kept teaching, a preference for schemes where the dangerous case simply cannot be reached, which is what Ed25519 and BIP-340 buy you. Remove any one layer and a surface reopens.&lt;/p&gt;
&lt;p&gt;The horizon does not change the shape of the problem, only its arithmetic: post-quantum signatures like ML-DSA and SLH-DSA will hand the same three surfaces to the same callers, at ten to a hundred times the signature size [@fips-204]. The nonce, the form, and the verifier will still be where the security lives.&lt;/p&gt;

A signature is only as safe as the nonce, the form, and the verifier the scheme hands you. The math held; the interface leaked.
&lt;p&gt;Using a signature safely, in the end, is the discipline of never handing an attacker a value your scheme forgot to own.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;digital-signatures-ecdsa-eddsa-rsa-pss&quot; keyTerms={[
  { term: &quot;Per-signature nonce&quot;, definition: &quot;The fresh secret a discrete-log signature draws for each signature; reusing or biasing it leaks the private key.&quot; },
  { term: &quot;EUF-CMA&quot;, definition: &quot;Unforgeability against any new message, even after an adversary obtains signatures on messages of its choice.&quot; },
  { term: &quot;SUF-CMA&quot;, definition: &quot;A stronger goal that also forbids a new signature on an already-signed message; the property malleability violates.&quot; },
  { term: &quot;Signature malleability&quot;, definition: &quot;A second valid signature anyone can derive without the private key, such as the pair with n minus s for ECDSA.&quot; },
  { term: &quot;Hidden Number Problem&quot;, definition: &quot;The lattice problem behind biased-nonce key recovery, and the reason a bias is statistical rather than certain.&quot; },
  { term: &quot;Deterministic signing (RFC 6979)&quot;, definition: &quot;Deriving the nonce from the private key and the message so signing needs no runtime randomness.&quot; },
  { term: &quot;Hedged signing&quot;, definition: &quot;Mixing fresh randomness into a deterministic nonce so the signer survives both RNG failure and fault attacks.&quot; },
  { term: &quot;Low-s canonicalization&quot;, definition: &quot;Requiring the s value to lie in the lower half of its range so only one of the malleable twins is standard.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>digital-signatures</category><category>ecdsa</category><category>eddsa</category><category>rsa-pss</category><category>schnorr</category><category>nonce-reuse</category><category>applied-cryptography</category><category>cryptography</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>One Number, Used Twice: How a Repeated Nonce Hands Over Your Private Key -- and How Determinism Takes It Back</title><link>https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/</link><guid isPermaLink="true">https://paragmali.com/blog/one-number-used-twice-how-a-repeated-nonce-hands-over-your-p/</guid><description>A repeated ECDSA nonce leaks your private key with grade-school algebra; a reused AES-GCM IV forges ciphertext. Why -- and how determinism fixes both.</description><pubDate>Thu, 09 Jul 2026 10:13:36 GMT</pubDate><content:encoded>
Reuse one number and you can lose everything. A **repeated** ECDSA signing nonce leaks your private key with grade-school algebra: two signatures under the same nonce, one subtraction, one modular inverse, and the key falls out. That is how the Sony PlayStation 3 master key was recovered [@f0f]. A **repeated** AES-GCM initialization vector is just as fatal: it instantly leaks $P_1 \oplus P_2$ and hands an attacker the material to forge authenticated ciphertext [@joux] [@nda]. The fix for both is one counter-intuitive move: stop trusting runtime randomness for the once-per-operation number. Derive it (RFC 6979, EdDSA, hedged signatures) [@rfc6979] [@rfc8032], or make a repeat non-catastrophic (AES-GCM-SIV) [@rfc8452]. One distinction separates understanding from folklore: a *biased* nonce is a *different*, statistical attack needing many signatures, not one [@ns] [@ladderleak].
&lt;h2&gt;1. One Number, Used Twice&lt;/h2&gt;
&lt;p&gt;In December 2010, on stage at the 27th Chaos Communication Congress in Berlin, three researchers working under the name fail0verflow explained how they had recovered the master private key that anchors the entire PlayStation 3 chain of trust [@f0f]. There was no brute force, no exotic side channel, no supercomputer grinding for months. There were two signatures and the kind of algebra you learned before you were old enough to drive. Sony had signed its code with ECDSA, the elliptic-curve digital signature algorithm, and Sony had signed &lt;em&gt;every&lt;/em&gt; firmware image with the &lt;em&gt;same&lt;/em&gt; secret per-signature number.Two events that are often merged into one: fail0verflow &lt;em&gt;disclosed&lt;/em&gt; the constant-nonce flaw at 27C3 in December 2010; George Hotz, known as geohot, &lt;em&gt;separately&lt;/em&gt; published an extracted PS3 root key in January 2011 [@geohot]. The talk revealed the mechanism; the key release came afterward. Keep them distinct.&lt;/p&gt;
&lt;p&gt;Sit with how strange that is. The console&apos;s security rested on a signature scheme that most cryptographers would call sound. AES was not broken. The elliptic curve was not broken. The signature math was not broken. One input was reused, and the entire trust anchor fell out of a schoolbook manipulation that a motivated teenager could reproduce on paper.&lt;/p&gt;
&lt;p&gt;Now the mirror image, in a completely different primitive. In 2016, a team scanning the live internet found 184 public HTTPS servers repeating a different one-time number -- the AES-GCM initialization vector -- and reported that this &quot;fully breaks the authenticity of the connections&quot; [@nda]. Not the confidentiality of a lab toy: the authenticity of real TLS sessions on the open web, letting an attacker forge messages the server would accept as genuine.&lt;/p&gt;
&lt;p&gt;Same disease, entirely different organ. One is a signature that proves who you are; the other is the cipher that guards most of the web&apos;s traffic. Both staked everything on a number that was supposed to be used correctly exactly once.&lt;/p&gt;
&lt;p&gt;That shared bet is the subject of this article. Both primitives depend, silently, on a value that must be unique and unpredictable -- once, per key. The precise question: &lt;em&gt;what, exactly, does each stake on that number, and what is the exact cost of getting it wrong?&lt;/em&gt; For the repeat case, the answer is total and immediate -- and the algebra is short enough to print in full.&lt;/p&gt;

In cryptography, reusing one number can cost you everything.
&lt;p&gt;That is the punchline, and by the end you will have earned it rather than taken it on faith -- the full key-recovery algebra, Antoine Joux&apos;s cipher attack, the statistical cousin that needs only a &lt;em&gt;fraction&lt;/em&gt; of a leaked bit, and the deterministic designs that dissolve the whole failure family. But how can &lt;em&gt;two&lt;/em&gt; signatures undo an entire console&apos;s chain of trust? To see it, we first have to meet the number both of these primitives are quietly betting everything on.&lt;/p&gt;
&lt;h2&gt;2. The Secret Number Both Primitives Depend On&lt;/h2&gt;
&lt;p&gt;Two algorithms, designed decades and a world apart -- a discrete-logarithm signature and a &lt;a href=&quot;https://paragmali.com/blog/the-ciphertext-was-unbreakable-the-attacker-rewrote-it-anyw&quot; rel=&quot;noopener&quot;&gt;block-cipher mode of authenticated encryption&lt;/a&gt; -- turn out to share a single point of failure. Before we can watch it break, we need to know where the number comes from and why it has to exist at all.&lt;/p&gt;
&lt;p&gt;The signature side is the older lineage. ElGamal&apos;s 1985 signature scheme introduced a per-signature secret exponent [@elgamal], and when the United States standardized the Digital Signature Algorithm as FIPS 186 in 1994 [@fips186], that per-signature secret came along for the ride. Its elliptic-curve descendant, ECDSA, keeps it. The value is called the signing nonce, written $k$, and it is regenerated fresh for every single signature.&lt;/p&gt;

The per-signature secret scalar in (EC)DSA. For each signature the signer draws a new $k$ in the range $[1, n-1]$, where $n$ is the order of the curve&apos;s base point. It must be unique, secret, and uniformly distributed -- exactly once per key. It is never transmitted, yet every signature is computed from it.
&lt;p&gt;The cipher side is younger. When NIST standardized Galois/Counter Mode in SP 800-38D in 2007, the specification required that the initialization vector -- the $\text{IV}$, GCM&apos;s nonce -- be distinct for every message encrypted under a given key [@sp80038d]. Same structural demand, opposite secrecy requirement: the signing nonce must be secret, while the GCM nonce may be completely public. What both must be is &lt;em&gt;fresh&lt;/em&gt;.&lt;/p&gt;

The per-message value GCM mixes in so that encrypting under a fixed key never repeats its keystream. Uniqueness under a given key -- not secrecy -- is the entire contract. A public counter is a perfectly good GCM nonce; a *repeated* one is a catastrophe.
&lt;p&gt;Now put the two governing equations side by side. An ECDSA signature on a message whose hash (as an integer) is $z$, under private key $d$, is the pair $(r, s)$:&lt;/p&gt;
&lt;p&gt;$$r = (k \cdot G)_x \bmod n, \qquad s = k^{-1},(z + r,d) \bmod n$$&lt;/p&gt;
&lt;p&gt;Here $G$ is the fixed base point, $n$ its prime order, $d$ the private key, and $r$ is the x-coordinate of the curve point $k \cdot G$ reduced modulo $n$. The AES-GCM authentication tag on ciphertext $C$ with additional data $A$ is:&lt;/p&gt;
&lt;p&gt;$$T = E_K(J_0) \oplus \mathrm{GHASH}_H(A, C), \qquad H = E_K(0^{128})$$&lt;/p&gt;
&lt;p&gt;$E_K$ is AES under key $K$; $J_0$ is a pre-counter block derived from the IV; $\mathrm{GHASH}_H$ is a polynomial hash over the field $\mathrm{GF}(2^{128})$ keyed by the secret subkey $H$, which is simply AES applied to the all-zero block.&lt;/p&gt;
&lt;p&gt;Look at what both equations hide inside them. The signing equation multiplies the private key $d$ by the public $r$ and masks it behind $k$. The tag equation masks the keyed hash behind the one-time pad $E_K(J_0)$, which is a deterministic function of the nonce. In each case, one freshly chosen value is the only thing standing between a routine operation and a full disclosure. That gives us the single shared contract, short enough to fit on one line: &lt;strong&gt;the number must be unique and unpredictable, exactly once, per key.&lt;/strong&gt;&lt;/p&gt;

This is the in-depth sequel to [Part 2](/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand), which surveyed the two ways cryptographic randomness betrays you -- predictability and repetition -- across many primitives at breadth. Part 2 owns the general taxonomy; this post goes deep on two specific victims, the ECDSA nonce and the GCM IV, and derives the exact algebra of each collapse. We will not re-teach the two-knobs framework here; we will spend it.
&lt;p&gt;None of this was a secret to the designers. Bellare, Goldwasser, and Micciancio analyzed the danger of a poorly generated DSA nonce as early as their 1997 CRYPTO paper, &quot;The DSS case&quot; [@bgm], showing that a linear-congruential nonce generator breaks the scheme.Terminology drifts across modes, so fix it now: a &lt;em&gt;nonce&lt;/em&gt; need only be unique (CTR, GCM); an &lt;em&gt;IV&lt;/em&gt; for CBC must additionally be unpredictable; a &lt;em&gt;salt&lt;/em&gt; is unique and public and feeds a KDF. The mode dictates which property is load-bearing. Part 2 and the block-cipher-modes installment work through the distinctions; here we only need &quot;fresh, once, per key.&quot; The contract was understood from the beginning; nobody misunderstood the rule. They simply could not always keep it.&lt;/p&gt;
&lt;p&gt;The contract says &quot;never repeat.&quot; So begin with the crudest possible violation -- the &lt;em&gt;same&lt;/em&gt; number, twice -- and watch precisely how much it costs.&lt;/p&gt;
&lt;h2&gt;3. Two Signatures, One k, and Your Private Key&lt;/h2&gt;
&lt;p&gt;This is the entire thesis compressed into one page of algebra -- no lattice, no side channel, no probability, just subtraction and one division. How little work does &lt;em&gt;complete&lt;/em&gt; key recovery actually take?&lt;/p&gt;
&lt;p&gt;Start from the fact that makes it possible. The first signature component, $r = (k \cdot G)_x \bmod n$, depends only on the nonce $k$ and the fixed public base point $G$. It does not depend on the message. So if you sign two &lt;em&gt;different&lt;/em&gt; messages under the &lt;em&gt;same&lt;/em&gt; $k$, both signatures carry the &lt;em&gt;identical&lt;/em&gt; $r$. That repeated $r$ is the visible tell -- a fingerprint an attacker can spot by scanning a signature log, with no secret knowledge at all.This is exactly what fail0verflow read off the PlayStation 3: every firmware signature carried the same $r$, the public fingerprint of a constant $k$. You do not need to break anything to notice it; you just look for two signatures with a matching first half.&lt;/p&gt;
&lt;p&gt;Now write the two signing equations under that shared $k$:&lt;/p&gt;
&lt;p&gt;$$s_1 = k^{-1}(z_1 + r,d) \pmod n, \qquad s_2 = k^{-1}(z_2 + r,d) \pmod n$$&lt;/p&gt;
&lt;p&gt;Subtract the second from the first. The private key appears only inside the identical $r,d$ term, so it cancels completely:&lt;/p&gt;
&lt;p&gt;$$s_1 - s_2 = k^{-1}\big((z_1 + r,d) - (z_2 + r,d)\big) = k^{-1}(z_1 - z_2) \pmod n$$&lt;/p&gt;
&lt;p&gt;Every quantity on the right except $k$ is public: the two message hashes $z_1, z_2$ and the two signature halves $s_1, s_2$. So solve for the nonce directly:&lt;/p&gt;
&lt;p&gt;$$k = (z_1 - z_2),(s_1 - s_2)^{-1} \bmod n$$&lt;/p&gt;
&lt;p&gt;One subtraction on top, one modular inverse on the bottom. With $k$ in hand, substitute back into the first equation -- multiply through by $k$, subtract $z_1$, and divide by the public $r$:&lt;/p&gt;
&lt;p&gt;$$s_1 k = z_1 + r,d \implies d = (s_1 k - z_1),r^{-1} \bmod n$$&lt;/p&gt;
&lt;p&gt;That is the private key. Count the cost to the attacker: &lt;strong&gt;two&lt;/strong&gt; signatures, a handful of modular operations,Constant work here means a fixed number of field operations, independent of the key size. A 256-bit curve is no harder to break this way than a 160-bit one, because the attack never touches the key&apos;s length. and the recovery is &lt;em&gt;certain&lt;/em&gt; -- not probable, not &quot;on average,&quot; but exact, every time, the moment the repeat occurs. It solves a two-by-two linear system that the reuse handed over for free.&lt;/p&gt;

flowchart TD
    A[&quot;Two signatures made under the same nonce k&quot;] --&amp;gt; B[&quot;They share the same r, because r depends only on k and G&quot;]
    B --&amp;gt; C[&quot;Subtract the two s equations&quot;]
    C --&amp;gt; D[&quot;The r times d term cancels, leaving k as the only unknown&quot;]
    D --&amp;gt; E[&quot;One modular inverse recovers k&quot;]
    E --&amp;gt; F[&quot;Put k back into either s equation&quot;]
    F --&amp;gt; G[&quot;The private key d falls out, with certainty&quot;]
&lt;p&gt;You do not have to take the algebra on faith -- here is the whole attack as runnable code over a toy curve order, recovering the private key from public values alone:&lt;/p&gt;
&lt;p&gt;{`
// Recover the ECDSA private key d from two signatures sharing nonce k.
function egcd(a, b) {
  if (b === 0n) return [a, 1n, 0n];
  const [g, x, y] = egcd(b, a % b);
  return [g, y, x - (a / b) * y];
}
function modInv(a, n) {
  a = ((a % n) + n) % n;
  const [g, x] = egcd(a, n);
  if (g !== 1n) throw new Error(&apos;no inverse&apos;);
  return ((x % n) + n) % n;
}&lt;/p&gt;
&lt;p&gt;const n = 1103n;   // toy prime &quot;curve order&quot; (real curves use ~256-bit n)
const d = 937n;    // the SECRET key the attacker must not know
const k = 421n;    // the reused nonce (identical for both signatures)
const r = 668n;    // r = (k*G).x mod n -- SHARED because k is shared
const z1 = 123n, z2 = 456n;  // two different message hashes&lt;/p&gt;
&lt;p&gt;// Victim signs both messages with the SAME k:
const s1 = (modInv(k, n) * (z1 + r * d)) % n;
const s2 = (modInv(k, n) * (z2 + r * d)) % n;&lt;/p&gt;
&lt;p&gt;// Attacker sees only public (r, s1, z1, s2, z2). Recover k, then d:
const kRec = ((((z1 - z2) % n) + n) % n) * modInv((((s1 - s2) % n) + n) % n, n) % n;
const dRec = ((((s1 * kRec - z1) % n) + n) % n) * modInv(r, n) % n;&lt;/p&gt;
&lt;p&gt;console.log(&apos;shared r    :&apos;, r.toString());
console.log(&apos;recovered k :&apos;, kRec.toString(), &apos;(true k =&apos;, k.toString() + &apos;)&apos;);
console.log(&apos;recovered d :&apos;, dRec.toString(), &apos;(true d =&apos;, d.toString() + &apos;)&apos;);
console.log(dRec === d ? &apos;PRIVATE KEY RECOVERED from two signatures.&apos; : &apos;mismatch&apos;);
`}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Two signatures sharing one nonce equal certain, instant, total key recovery -- by hand, with pure algebra. Reuse is not a slope that gradually weakens security; it is a cliff. &quot;Reuse one number equals everything&quot; is a literal statement about ECDSA, not a rhetorical flourish.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is not a museum piece. The PlayStation 3 fell to exactly this -- a &lt;em&gt;constant&lt;/em&gt; $k$ across every firmware signature, two images sharing one $r$, the master key recovered [@f0f]. Section 5 catalogs the rest of the real-world chain, from that constant-nonce break down to biases of less than a single bit.&lt;/p&gt;
&lt;p&gt;Certainty from a literal repeat is the easy case -- easy for an attacker to run, and, as we will see, easy for a defender to eliminate. So attackers asked a sharper and far more dangerous question. What if the nonces are &lt;em&gt;never&lt;/em&gt; equal, only slightly, invisibly &lt;em&gt;skewed&lt;/em&gt;?&lt;/p&gt;
&lt;h2&gt;4. From Repeat to Bias: The Hidden Number Problem&lt;/h2&gt;
&lt;p&gt;You do not need the nonces to be &lt;em&gt;equal&lt;/em&gt;. You only need them to be a little bit &lt;em&gt;predictable&lt;/em&gt; -- and then to collect enough of them. This is where the story turns, because it dismantles the comforting fix you probably just thought of. If a literal repeat is the danger, surely &quot;make sure the nonces are always different&quot; closes it? It does not come close.&lt;/p&gt;
&lt;p&gt;Here is the shift in viewpoint. In Section 3, a repeat gave us an exact equation and we solved it. Now suppose each nonce is merely &lt;em&gt;skewed&lt;/em&gt; -- say the top few bits are always zero, or the generator leans slightly toward small values. No two nonces are equal, so the certainty attack never triggers. But each signature is now a &lt;em&gt;noisy linear equation&lt;/em&gt; relating the public data to the secret key $d$ modulo $n$, with the unknown nonce constrained to a smaller range than it should occupy. One such equation tells you almost nothing.&lt;/p&gt;
&lt;p&gt;Stack hundreds or thousands of them as the rows of a lattice, though, and the secret $d$ shows up as one unusually short vector hiding in that grid. A reduction algorithm hunts it down, and the key reads straight out.&lt;/p&gt;

The problem of recovering a hidden integer -- here, the private key -- from many samples that each reveal only a few most-significant bits of a related product modulo $n$. Dan Boneh and Ramarathnam Venkatesan introduced it in 1996, and it is the framework underneath every biased-nonce attack on (EC)DSA [@hnp].

An integer grid of points formed by adding and subtracting a fixed set of basis vectors. Biased-nonce recovery constructs a lattice whose unusually short vector encodes the secret key; a reduction algorithm finds that vector. We stay conceptual here -- the internals of LLL and the closest-vector problem belong to their own discussion.
&lt;p&gt;The genealogy is worth knowing, because each step needed &lt;em&gt;less&lt;/em&gt; leakage than the last. Boneh and Venkatesan framed the Hidden Number Problem in 1996 [@hnp]. Around 2000, Daniel Bleichenbacher showed that even a tiny statistical bias in DSA nonces is exploitable through a Fourier-analytic version of the same idea.Bleichenbacher&apos;s seminal biased-nonce result was never published as a standalone paper. It survives as a 2000 IEEE P1363 presentation and a 2005 CRYPTO rump-session talk, &quot;Experiments with DSA,&quot; and is reconstructed through the reference lists of later work such as Nguyen-Shparlinski and LadderLeak. A standalone Bleichenbacher paper on this would be a fabrication; the history genuinely has this shape.&lt;/p&gt;
&lt;p&gt;Nick Howgrave-Graham and Nigel Smart made the lattice attack practical in 2001, recovering keys from partially known nonces [@hgs]. Phong Nguyen and Igor Shparlinski then proved rigorous polynomial-time ECDSA key recovery from just a few leaked nonce bits per signature in 2003 [@ns]. Two decades on, LadderLeak pushed the frontier to its logical extreme, breaking ECDSA with &lt;em&gt;less than one bit&lt;/em&gt; of nonce leakage per signature [@ladderleak].&lt;/p&gt;
&lt;p&gt;Now the distinction that everything downstream depends on. The biased case is &lt;em&gt;not&lt;/em&gt; the repeat case with weaker numbers. It is a different kind of attack with a different cost profile, and conflating them is the single most common way this topic gets mistold.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A &lt;em&gt;repeated&lt;/em&gt; nonce leaks the key from &lt;em&gt;two&lt;/em&gt; signatures, with certainty (Section 3). A &lt;em&gt;biased&lt;/em&gt; nonce leaks nothing usable from any single signature; it takes &lt;em&gt;many&lt;/em&gt; signatures -- hundreds, thousands, or more -- aggregated by a lattice or Fourier reduction before the key emerges. &quot;One biased signature reveals your key&quot; is false. Keep the two cases apart or you will misquote the entire failure family.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Laid out side by side, the contrast is stark:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Repeated nonce&lt;/th&gt;
&lt;th&gt;Biased nonce&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Leak per signature&lt;/td&gt;
&lt;td&gt;The entire nonce (matches another)&lt;/td&gt;
&lt;td&gt;A few bits, or a fraction of one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signatures needed&lt;/td&gt;
&lt;td&gt;Two&lt;/td&gt;
&lt;td&gt;Many (hundreds to millions, per bias)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;Subtraction and one modular inverse&lt;/td&gt;
&lt;td&gt;Lattice reduction or Fourier analysis (HNP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result&lt;/td&gt;
&lt;td&gt;Certain and exact&lt;/td&gt;
&lt;td&gt;Statistical, high-probability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Work&lt;/td&gt;
&lt;td&gt;Schoolbook algebra by hand&lt;/td&gt;
&lt;td&gt;Serious computation on aggregated data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defeated only by&lt;/td&gt;
&lt;td&gt;Distinct nonces&lt;/td&gt;
&lt;td&gt;Removing the bias, not just repeats&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

flowchart TD
    A[&quot;Many signatures, each nonce slightly biased&quot;] --&amp;gt; B[&quot;Each is a noisy linear equation in the secret d&quot;]
    B --&amp;gt; C[&quot;Stack the equations as rows of a lattice&quot;]
    C --&amp;gt; D[&quot;The secret d hides in one unusually short vector&quot;]
    D --&amp;gt; E[&quot;Lattice reduction finds that short vector&quot;]
    F[&quot;Read the private key d straight out of it&quot;]
    E --&amp;gt; F
&lt;p&gt;The lesson lands hard. If even a &lt;em&gt;fraction of one bit&lt;/em&gt; of bias, harvested across enough signatures, surrenders the key, then &quot;never literally repeat $k$&quot; is nowhere near enough of a rule, and &quot;use a better random number generator&quot; is a treadmill, not a fix. The only question left is how bad this got in real, shipped hardware -- devices built by careful engineers who knew all of this.&lt;/p&gt;
&lt;h2&gt;5. The Catastrophes in the Wild: ECDSA&lt;/h2&gt;
&lt;p&gt;The theory is alarming; the field results are worse. What follows is the evolution told as a body count, and the number to watch is the &lt;em&gt;leak the attacker needed&lt;/em&gt;. It shrinks with almost every entry, from an entire repeated nonce down to a fraction of a single bit, and every generation still won.&lt;/p&gt;
&lt;p&gt;The 2010 PlayStation 3 break sits at one end: a &lt;em&gt;constant&lt;/em&gt; nonce across all firmware signatures, the maximum possible leak, two signatures sufficient [@f0f]. In 2013 the Android &lt;code&gt;SecureRandom&lt;/code&gt; defect produced repeated and predictable signing nonces on mobile devices; because Bitcoin wallets sign transactions with ECDSA, attackers drained the vulnerable ones [@bitcoin]. Google&apos;s own writeup traced the root cause to improper initialization of the underlying PRNG in the Java Cryptography Architecture [@securerandom].The often-repeated figure of roughly 55 BTC stolen in the 2013 Android incident comes from secondary news reporting [@hackernews]. The primary bitcoin.org advisory [@bitcoin] names the flaw and the affected wallet apps but states no loss total. Treat the number as reported, not official.&lt;/p&gt;
&lt;p&gt;Then the leak starts shrinking. Minerva (2020) attacked real smart cards and cryptographic libraries that leaked the &lt;em&gt;bit-length&lt;/em&gt; of the nonce through timing -- a whisper of bias, not a repeat -- and recovered keys from &quot;just 500 signatures for simulated leakage data, 1200 for real cryptographic library data, and 2100 for smartcard data&quot; [@minerva].&lt;/p&gt;
&lt;p&gt;TPM-Fail (disclosed 2019, published at USENIX Security 2020) turned a timing side channel in Intel&apos;s firmware TPM (CVE-2019-11090) and an STMicroelectronics TPM (CVE-2019-16863) into nonce-bit leakage. It recovered an ECDSA key from the Intel fTPM in 4 to 20 minutes and, remotely, &quot;the authentication key of a virtual private network (VPN) server in 5 hours&quot; [@tpmfail] [@moghimi].&lt;/p&gt;
&lt;p&gt;LadderLeak (2020) then broke ECDSA with &lt;em&gt;less than one bit&lt;/em&gt; of nonce leakage per signature, exploiting a Montgomery-ladder side channel in OpenSSL [@ladderleak], a result published at ACM CCS 2020 [@ladderleak_doi]. And in 2024, PuTTY&apos;s use of P-521 exposed a structural bias so clean it needs almost no data at all (CVE-2024-31497) [@putty].&lt;/p&gt;

&quot;The first 9 bits of each ECDSA nonce are zero. This allows for full secret key recovery in roughly 60 signatures.&quot; -- the PuTTY P-521 disclosure [@putty]
&lt;p&gt;Sixty signatures. An attacker who had watched roughly sixty SSH handshakes, or collected sixty signed Git commits, could reconstruct the user&apos;s private key. The affected PuTTY versions ran from 0.68 through 0.80 (fixed in 0.81), and the same biased generator shipped in FileZilla, WinSCP, TortoiseGit, and TortoiseSVN [@putty]. Collect them into one table and the trend is impossible to miss:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;th&gt;Repeat or bias&lt;/th&gt;
&lt;th&gt;Leak per signature&lt;/th&gt;
&lt;th&gt;Signatures needed&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;2010&lt;/td&gt;
&lt;td&gt;Sony PS3 firmware&lt;/td&gt;
&lt;td&gt;Repeat (constant k)&lt;/td&gt;
&lt;td&gt;The entire nonce&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;[@f0f]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;Android Bitcoin wallets&lt;/td&gt;
&lt;td&gt;Repeat / predictable k&lt;/td&gt;
&lt;td&gt;Nonce reused or guessable&lt;/td&gt;
&lt;td&gt;A few&lt;/td&gt;
&lt;td&gt;[@bitcoin] [@securerandom]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Minerva (cards, libraries)&lt;/td&gt;
&lt;td&gt;Bias (nonce bit-length)&lt;/td&gt;
&lt;td&gt;Timing reveals length&lt;/td&gt;
&lt;td&gt;500 to 2100&lt;/td&gt;
&lt;td&gt;[@minerva]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019-2020&lt;/td&gt;
&lt;td&gt;TPM-Fail (Intel, STMicro TPMs)&lt;/td&gt;
&lt;td&gt;Bias (timing)&lt;/td&gt;
&lt;td&gt;Nonce high bits via timing&lt;/td&gt;
&lt;td&gt;Many&lt;/td&gt;
&lt;td&gt;[@tpmfail] [@moghimi]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;LadderLeak (OpenSSL ladder)&lt;/td&gt;
&lt;td&gt;Bias (sub-bit)&lt;/td&gt;
&lt;td&gt;Less than one bit&lt;/td&gt;
&lt;td&gt;Many&lt;/td&gt;
&lt;td&gt;[@ladderleak]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;PuTTY P-521 (CVE-2024-31497)&lt;/td&gt;
&lt;td&gt;Bias (structural)&lt;/td&gt;
&lt;td&gt;First 9 bits fixed at zero&lt;/td&gt;
&lt;td&gt;~60&lt;/td&gt;
&lt;td&gt;[@putty]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

flowchart LR
    A[&quot;1996 HNP framework: bias is exploitable in principle&quot;] --&amp;gt; B[&quot;2010 PS3: a constant nonce, the whole value repeats&quot;]
    B --&amp;gt; C[&quot;2013 Android: predictable and repeated nonces&quot;]
    C --&amp;gt; D[&quot;2020 Minerva and TPM-Fail: a few biased bits via timing&quot;]
    D --&amp;gt; E[&quot;2020 LadderLeak: less than one bit per signature&quot;]
    E --&amp;gt; F[&quot;2024 PuTTY P-521: nine fixed bits, about 60 signatures&quot;]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Read the arc left to right and one lesson dominates: the leakage an attacker needs keeps &lt;em&gt;shrinking&lt;/em&gt;, from an entire repeated nonce to less than a single bit. No generator is perfectly unbiased, and -- as Part 2 argued -- you can never prove one is. Any residual bias, given enough signatures, eventually surrenders the key. &quot;Harden the random number generator&quot; buys time; it does not close the hole. That conclusion is the synthesis of the whole table, and it is what forces the search for a structural answer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every entry above is a &lt;em&gt;signature&lt;/em&gt; scheme bleeding out its private key through the nonce. You would be forgiven for thinking this is an ECDSA problem. It is not. The exact same structural flaw -- one number that must be used once and is fatal on reuse -- was hiding inside a completely different primitive, the cipher that protects most of the traffic on the modern web.&lt;/p&gt;
&lt;h2&gt;6. The Other Primitive: AES-GCM&apos;s Forbidden Attack&lt;/h2&gt;
&lt;p&gt;Different math, a different decade, a different threat model -- and the exact same fatal dependency on one number used once. The puzzle here is sharper: AES-GCM&apos;s nonce is &lt;em&gt;public&lt;/em&gt;, so how can reusing a value the attacker already knows leak a forgery key? The answer is Antoine Joux&apos;s forbidden attack, and it mirrors the signature story beat for beat.&lt;/p&gt;
&lt;p&gt;Recall the tag from Section 2: $T = E_K(J_0) \oplus \mathrm{GHASH}_H(A, C)$, where $H = E_K(0^{128})$. Two facts about that formula are load-bearing. First, $H$ depends only on the key, so it is fixed for the whole life of that key. Second, $E_K(J_0)$ depends only on the key and the nonce, through the pre-counter block $J_0$. Reuse the nonce and $E_K(J_0)$ comes back &lt;em&gt;identical&lt;/em&gt;.&lt;/p&gt;

The secret 128-bit value $H = E_K(0^{128})$ -- AES applied to the all-zero block -- that keys GCM&apos;s polynomial authenticator over $\mathrm{GF}(2^{128})$. It never changes for a given key, and recovering it lets an attacker forge authentication tags at will.
&lt;p&gt;Now reuse one $(\text{key}, \text{IV})$ pair on two messages. Two things break, and they break separately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The confidentiality break, from a single collision.&lt;/strong&gt; GCM encrypts with counter mode: the ciphertext is the plaintext XORed with a keystream generated from $J_0$. Same key, same IV means the same $J_0$, means the &lt;em&gt;same keystream&lt;/em&gt;. So for two messages encrypted under the reused pair, $C \oplus C&apos; = P \oplus P&apos;$. The keystream cancels and the XOR of the two plaintexts falls out immediately, from just one collision, with no further work. If the attacker knows or guesses one plaintext, the other is fully revealed. Here is that leak as runnable code:&lt;/p&gt;
&lt;p&gt;{`
// Reuse one (key, IV) in a keystream mode -&amp;gt; C XOR C&apos; leaks P XOR P&apos;.
// GCM encrypts with AES-CTR: same (key, IV) reproduces the SAME keystream.
function xor(a, b) { return a.map((x, i) =&amp;gt; x ^ b[i]); }
const bytes = s =&amp;gt; Array.from(new TextEncoder().encode(s));
const hex = a =&amp;gt; a.map(x =&amp;gt; x.toString(16).padStart(2, &apos;0&apos;)).join(&apos;&apos;);&lt;/p&gt;
&lt;p&gt;// A fixed keystream stands in for AES-CTR(key, IV): identical for both messages.
const keystream = [0x9e,0x37,0x5a,0xc1,0x02,0xbb,0x44,0xd0,0x71,0x6f,0x88,0x2a];&lt;/p&gt;
&lt;p&gt;const P1 = bytes(&apos;transfer=10 &apos;).slice(0, keystream.length);
const P2 = bytes(&apos;transfer=9999&apos;).slice(0, keystream.length);&lt;/p&gt;
&lt;p&gt;const C1 = xor(P1, keystream);   // ciphertext 1
const C2 = xor(P2, keystream);   // ciphertext 2, SAME keystream&lt;/p&gt;
&lt;p&gt;// Attacker never sees the key or keystream -- only C1 and C2:
const leak = xor(C1, C2);        // == P1 XOR P2, the keystream cancels
console.log(&apos;C1 XOR C2 :&apos;, hex(leak));
console.log(&apos;P1 XOR P2 :&apos;, hex(xor(P1, P2)), &apos;(identical -&amp;gt; keystream is gone)&apos;);&lt;/p&gt;
&lt;p&gt;// If the attacker also knows P1, then P2 falls out immediately:
console.log(&apos;recover P2:&apos;, new TextDecoder().decode(new Uint8Array(xor(leak, P1))));
`}&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The authenticity break, from the tag difference.&lt;/strong&gt; This is Joux&apos;s contribution. Write the two tags under the reused pair and XOR them. The identical blind $E_K(J_0)$ cancels exactly:&lt;/p&gt;
&lt;p&gt;$$T \oplus T&apos; = \mathrm{GHASH}_H(A, C) \oplus \mathrm{GHASH}_H(A&apos;, C&apos;)$$&lt;/p&gt;
&lt;p&gt;Every block of $A, C, A&apos;, C&apos;$ is observable on the wire, and $\mathrm{GHASH}_H$ is a polynomial in $H$ over $\mathrm{GF}(2^{128})$ with those blocks as coefficients. So the right-hand side is a &lt;em&gt;known polynomial in the one unknown $H$&lt;/em&gt;, and it equals the observed left-hand side. Move everything to one side and you have a polynomial equation whose roots are candidate values for $H$; factoring it over $\mathrm{GF}(2^{128})$ yields that candidate set [@joux].&lt;/p&gt;
&lt;p&gt;Recover $H$ and, since $E_K(J_0) = T \oplus \mathrm{GHASH}_H(A, C)$ falls out too, the attacker can compute a valid tag for &lt;em&gt;any&lt;/em&gt; ciphertext they like under that nonce. That is a &lt;a href=&quot;https://paragmali.com/blog/secure-against-whom-the-security-definitions-every-protocol-&quot; rel=&quot;noopener&quot;&gt;universal forgery&lt;/a&gt;.&lt;/p&gt;

Antoine Joux&apos;s 2006 attack on GCM: reusing a $(\text{key}, \text{IV})$ pair cancels the tag&apos;s $E_K(J_0)$ blind, turning the difference of two tags into a known polynomial over $\mathrm{GF}(2^{128})$ whose roots are candidates for the subkey $H$. The name reflects that GCM&apos;s unique-IV rule is not a suggestion -- breaking it is forbidden [@joux].

flowchart TD
    A[&quot;Tag T equals E_K(J0) XOR GHASH_H of the message&quot;] --&amp;gt; B[&quot;J0 comes from the IV, H comes only from the key&quot;]
    B --&amp;gt; C[&quot;Reuse the same key and IV on a second message&quot;]
    C --&amp;gt; D[&quot;E_K(J0) is identical in both tags, so it cancels under XOR&quot;]
    D --&amp;gt; E[&quot;T XOR T-prime is a known polynomial in H over GF(2 to the 128)&quot;]
    E --&amp;gt; F[&quot;Its roots are the candidate values for the subkey H&quot;]
    F --&amp;gt; G[&quot;Two or more collisions pin H, enabling universal forgery&quot;]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A &lt;em&gt;single&lt;/em&gt; $(\text{key}, \text{IV})$ collision leaks $P_1 \oplus P_2$ immediately and unconditionally -- confidentiality is gone at once. Recovering the subkey $H$ is a &lt;em&gt;separate&lt;/em&gt; step: one collision yields a &lt;em&gt;set of candidate roots&lt;/em&gt;, and pinning $H$ uniquely generally needs &lt;em&gt;two or more&lt;/em&gt; collisions. &quot;One reuse instantly recovers the authentication key&quot; overstates it. &quot;One reuse instantly destroys confidentiality and opens the road to forgery&quot; is exact.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is not confined to whiteboards. In 2016 the Nonce-Disrespecting Adversaries team scanned live TLS and &quot;identified 184 HTTPS servers repeating nonces, which fully breaks the authenticity of the connections&quot; [@nda].Joux&apos;s result is his 2006 public comment to NIST, whose embedded document title reads &quot;Authentication Failures in NIST version of GCM&quot; [@joux]. It is sometimes miscited as IACR ePrint 2006/187 -- but that number is a different paper, on HMAC and NMAC, by Kim, Biryukov, Preneel, and Hong [@eprint187]. Cite the NIST comment, not the ePrint. These were production HTTPS endpoints, not lab toys, and their nonce reuse let anyone forge authenticated records against them.The canonical live-TLS evidence for GCM nonce reuse is that paper [@nda], not CVE-2016-0270, which concerns IBM Domino rather than OpenSSL. If you see that CVE cited as the OpenSSL GCM-reuse bug, it is a misattribution.&lt;/p&gt;
&lt;p&gt;Two primitives, one disease: a once-per-operation number that is death on reuse. If the flaw is structural rather than a coding slip, then &quot;be more careful with your randomness&quot; cannot be the cure -- Section 5 already showed that treadmill has no end. The fix has to be architectural. What would that even look like?&lt;/p&gt;
&lt;h2&gt;7. Make the Number Deterministic&lt;/h2&gt;
&lt;p&gt;The cure for a randomness disaster turned out to be &lt;em&gt;less&lt;/em&gt; randomness. That sentence should feel wrong -- we have spent five sections watching bad randomness destroy keys, so surely the answer is &lt;em&gt;better&lt;/em&gt; randomness. It is not, and seeing why is the moment the whole subject clicks into place.&lt;/p&gt;
&lt;p&gt;Go back to what actually failed. The signing equation needs a $k$ that is unique and unpredictable. Every real break came from the &lt;em&gt;runtime source&lt;/em&gt; of that $k$: a constant, a repeated PRNG output, a biased generator, a timing leak. So Thomas Pornin&apos;s RFC 6979 asks a disarming question -- what if we never draw $k$ from a runtime source at all? Instead, &lt;em&gt;derive&lt;/em&gt; it, deterministically, from two things the signer already holds: the private key and the message [@rfc6979].&lt;/p&gt;

A signature whose per-message nonce is computed as a pseudorandom function of the private key and the message, using no runtime randomness at all. The same $(\text{key}, \text{message})$ pair always yields the same nonce, and therefore the same signature. RFC 6979 specifies this for (EC)DSA; EdDSA builds it into the scheme [@rfc6979] [@rfc8032].
&lt;p&gt;Concretely, RFC 6979 runs an HMAC-based deterministic random bit generator, seeded from the private key $x$ and the message hash, and rejection-samples its output into the valid range $[1, n-1]$. The two helper encodings simply turn the key and the hash into fixed-width byte strings so the HMAC has well-defined inputs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;h  = H(message)                              # hash the message
V  = 0x01 0x01 ... 0x01                       # hlen bytes of 0x01
K  = 0x00 0x00 ... 0x00                       # hlen bytes of 0x00
K  = HMAC(K, V || 0x00 || int2octets(x) || bits2octets(h))
V  = HMAC(K, V)
K  = HMAC(K, V || 0x01 || int2octets(x) || bits2octets(h))
V  = HMAC(K, V)
loop:
    T = &quot;&quot;                                     # accumulate qlen bits of output
    while bitlen(T) &amp;lt; qlen:                    # one HMAC block if qlen &amp;lt;= hlen; more if longer
        V = HMAC(K, V);  T = T || V
    k = bits2int(T)
    if 1 &amp;lt;= k &amp;lt;= n-1:  return k               # in range -&amp;gt; use it
    K = HMAC(K, V || 0x00);  V = HMAC(K, V)    # else reseed and retry
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at what this buys. The generator is seeded by the private key and the message, and nothing else, and two consequences close the &lt;em&gt;generation&lt;/em&gt; side of the Part-A attack surface.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;repeat&lt;/strong&gt; case dies because two &lt;em&gt;different&lt;/em&gt; messages produce two &lt;em&gt;unrelated&lt;/em&gt; nonces, while the same message reproduces the same signature harmlessly -- there is no runtime generator whose collision could hand you a shared $k$. The &lt;strong&gt;bias&lt;/strong&gt; case dies wherever the bias was &lt;em&gt;born in a generator&lt;/em&gt;: with no runtime generator in the loop, there is nothing to skew and no entropy pool to under-initialize. Every Section-5 break that lived in the generator -- the repeated $k$ on the PS3, the predictable $k$ from a weak &lt;code&gt;SecureRandom&lt;/code&gt; -- has simply been removed from the design, because there is no generator left to fail.&lt;/p&gt;

Deterministic signers &quot;do not need access to a source of high-quality randomness.&quot; -- RFC 6979 [@rfc6979]
&lt;p&gt;And the compatibility story is almost too good: a verifier cannot even tell. The verification equation is unchanged, because a signature is still just a valid $(r, s)$ pair; only the signer&apos;s method of choosing $k$ changed. Deployments can switch to deterministic signing unilaterally. You do not have to take the &quot;same input, same output&quot; claim on trust either:&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Deterministic nonce k = PRF(private key, message). // Real RFC 6979 uses HMAC-DRBG; this toy uses a small deterministic mixer // only to show the SHAPE: same (key,msg) =&amp;gt; same k; different msg =&amp;gt; unrelated k. const MASK = (1n &amp;lt;&amp;lt; 64n) - 1n; const n = (1n &amp;lt;&amp;lt; 61n) - 1n;              // toy prime modulus, k in [1, n-1] function rotl(x, r) { return ((x &amp;lt;&amp;lt; r) | (x &amp;gt;&amp;gt; (64n - r))) &amp;amp; MASK; } function prf(key, msg) {                  // deterministic, consults no RNG   let h = 1469598103934665603n;           // 64-bit offset basis   for (const ch of (key + &apos;|&apos; + msg)) {     h = (h ^ BigInt(ch.charCodeAt(0))) &amp;amp; MASK;     h = (h * 1099511628211n) &amp;amp; MASK;       // FNV-style multiply     h = rotl(h, 13n) ^ (h &amp;gt;&amp;gt; 7n);          // extra diffusion so 1 char avalanches   }   return (h % (n - 1n)) + 1n;              // map into [1, n-1] } const sk = &apos;private-key-0xA1B2&apos;; console.log(&apos;k(pay Bob 10) :&apos;, prf(sk, &apos;pay Bob 10&apos;).toString()); console.log(&apos;k(pay Bob 10) :&apos;, prf(sk, &apos;pay Bob 10&apos;).toString(), &apos;&amp;lt;- identical, no RNG consulted&apos;); console.log(&apos;k(pay Bob 11) :&apos;, prf(sk, &apos;pay Bob 11&apos;).toString(), &apos;&amp;lt;- unrelated&apos;); console.log(&apos;No source of runtime randomness was consulted.&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;The same idea has a sibling that was born deterministic. EdDSA, and its popular instantiation Ed25519, does not bolt determinism on -- it designs the runtime randomness out from the start, computing its per-signature secret as a hash of a key-derived prefix and the message [@rfc8032].That is why Ed25519 has no separate &quot;use RFC 6979&quot; mode: there is no runtime nonce anywhere in the scheme to bias or repeat. Determinism is not an option you enable; it is the definition.&lt;/p&gt;
&lt;p&gt;Standards bodies followed the evidence: FIPS 186-5, the current US signature standard, now approves deterministic ECDSA &quot;as specified in IETF RFC 6979&quot; [@fips1865] [@fips1865pdf]. There is even a quiet operational bonus. A deterministic signer is &lt;em&gt;testable&lt;/em&gt;: you can ship known-answer test vectors and check byte-for-byte that an implementation is correct, which a randomized signer can never offer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The fix for a randomness failure was to remove the randomness. Derive the nonce from the private key and the message, and there is no runtime generator left to repeat or bias -- both failure modes vanish together.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Determinism looks like a clean, total victory. It is narrower than that, and the boundary matters. What it closes is the &lt;em&gt;generation&lt;/em&gt; side of Part A. It does nothing for the &lt;em&gt;usage&lt;/em&gt; side: recall that Minerva, TPM-Fail, and LadderLeak never touched a generator -- they read the nonce&apos;s leading bits out of the &lt;em&gt;timing&lt;/em&gt; of the scalar multiplication $k \cdot G$, and that leak is present no matter how $k$ was chosen [@minerva] [@tpmfail] [@ladderleak]. Determinism fixes &lt;em&gt;generation&lt;/em&gt;, not &lt;em&gt;usage&lt;/em&gt;; the defense against that class is a &lt;em&gt;constant-time&lt;/em&gt; scalar multiplication, a separate discipline that RFC 6979 neither provides nor promises. Worse, determinism can even &lt;em&gt;add&lt;/em&gt; exposure: by making signing perfectly repeatable, we hand a fault-injection attacker a perfectly repeatable target, a hazard RFC 6979&apos;s own security considerations acknowledge [@rfc6979]. If Go&apos;s standard library knows this well enough to refuse pure determinism on purpose, we had better find out what it is afraid of.&lt;/p&gt;
&lt;h2&gt;8. The Catch, and the Hedge&lt;/h2&gt;
&lt;p&gt;Here is a fact that should stop you: Go&apos;s standard library signs ECDSA &lt;em&gt;non-deterministically on purpose&lt;/em&gt;, and its source code says so in a comment. After seven sections arguing that runtime randomness is the enemy, one of the most widely used standard-library signers refuses to remove it. That is not carelessness. It is a second threat model.&lt;/p&gt;
&lt;p&gt;Determinism closed the randomness door, but it opened a different one. A deterministic signer computes the &lt;em&gt;same&lt;/em&gt; signature every time for a given input, which means an attacker who can run it twice on the same message gets two identical computations to compare -- and comparison is exactly the lever a fault attack pulls.&lt;/p&gt;

An attack that induces a hardware fault -- a voltage glitch, a clock glitch, a laser pulse -- during a computation, then compares the faulty output against a correct one to solve for the secret. Against a *deterministic* signer the attacker can obtain a correct signature and a faulted signature for the identical input, which is precisely what makes the difference exploitable.
&lt;p&gt;This is not hypothetical. Aranha, Orlandi, Takahashi, and Zaverucha proved that de-randomized Fiat-Shamir schemes -- EdDSA among them -- are vulnerable to differential fault attacks [@hedged]. And the standard itself agrees: FIPS 186-5 warns that fault attacks are &quot;of particular concern for ... deterministic signature schemes, as well as embedded or IoT devices and smartcards&quot; [@fips1865pdf]. The very property that made determinism testable and repeatable -- same input, same output -- is what hands a fault attacker a fixed target to hammer.&lt;/p&gt;
&lt;p&gt;The resolution is not to retreat to randomized signing, which reintroduces every repeat and bias failure. It is to take a small step back toward randomness, on purpose and in a controlled way: &lt;strong&gt;hedged signatures.&lt;/strong&gt;&lt;/p&gt;

A signature whose nonce is derived from the private key, the message, *and* a fresh random value $r$: $k = H(sk, m, r)$. If the random source fails, the construction degrades to the deterministic -- still safe -- case. If a fault is injected, the fresh $r$ means the signer is no longer a fixed, replayable target.
&lt;p&gt;Read that definition twice, because it is the whole trick. Hedging is not &quot;randomized signing with extra steps.&quot; It is a design that is safe &lt;em&gt;whichever&lt;/em&gt; thing goes wrong. Lose your entropy source entirely and you fall back to RFC 6979&apos;s determinism, which we already proved kills repeat and bias. Keep your entropy and you also get a moving target that defeats the fault attack determinism exposed. The randomness is present but no longer load-bearing.&lt;/p&gt;

&quot;Signatures generated by this package are not deterministic, but entropy is mixed with the private key and the message, achieving the same level of security in case of randomness source failure.&quot; -- Go `crypto/ecdsa` [@goecdsa]
&lt;p&gt;That is why Go hedges by default, and it is not alone. BoringSSL&apos;s FIPS module passes &quot;a SHA512 hash of the private key and digest as additional data into the RBG ... a hardening measure against entropy failure&quot; [@boringssl]. RFC 6979 itself sanctions hedged variants in its Section 3.6 [@rfc6979], and FIPS 186-5 specifies an &quot;Extra Random Bits&quot; method that draws a nonce at least 64 bits longer than needed so any statistical bias becomes negligible [@fips1865pdf].&lt;/p&gt;
&lt;p&gt;The honest caveats: hedging still consumes &lt;em&gt;some&lt;/em&gt; entropy, and its security proofs cover &lt;em&gt;specific&lt;/em&gt; fault classes rather than all conceivable faults. It is a well-chosen trade, not a free lunch.&lt;/p&gt;
&lt;p&gt;The right mental model is three parallel choices selected by threat model, not a podium with one winner:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Randomized&lt;/th&gt;
&lt;th&gt;Deterministic (RFC 6979 / EdDSA)&lt;/th&gt;
&lt;th&gt;Hedged&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;RNG needed at signing time&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Some (not load-bearing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Immune to repeats&lt;/td&gt;
&lt;td&gt;No (RNG can collide)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Immune to bias&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resists fault injection&lt;/td&gt;
&lt;td&gt;Yes (moving target)&lt;/td&gt;
&lt;td&gt;No (fixed target)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Known-answer test vectors&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standardized&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (RFC 6979, FIPS 186-5)&lt;/td&gt;
&lt;td&gt;Yes (RFC 6979 3.6; FIPS 186-5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shipped by default in&lt;/td&gt;
&lt;td&gt;Legacy code&lt;/td&gt;
&lt;td&gt;Many libraries&lt;/td&gt;
&lt;td&gt;Go &lt;code&gt;crypto/ecdsa&lt;/code&gt;, BoringSSL FIPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best when&lt;/td&gt;
&lt;td&gt;Rarely the right pick&lt;/td&gt;
&lt;td&gt;No fault threat in scope&lt;/td&gt;
&lt;td&gt;Hardware, embedded, smartcard, HSM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For any new (EC)DSA signer -- especially on hardware, embedded, smartcard, or HSM targets where fault injection is realistic -- hedge it: derive the nonce from the key, the message, and a little fresh entropy. You keep repeat and bias immunity even if the random source fails, and you deny the fixed target a fault attack needs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So the signature side does not settle on a single champion. It settles on a &lt;em&gt;matched set&lt;/em&gt; of choices, each correct for a different adversary. That is a satisfying place to land -- but it is only half the patient. What is the symmetric-encryption analogue for the cipher we left bleeding in Section 6?&lt;/p&gt;
&lt;h2&gt;9. Fixing the Cipher: Nonce-Misuse-Resistant AEAD&lt;/h2&gt;
&lt;p&gt;The signature fix was &quot;derive the number.&quot; The cipher&apos;s fix rhymes: &lt;em&gt;derive the IV from the message itself&lt;/em&gt;, so that the worst a repeat can leak is the fact that two messages were byte-for-byte identical. Same instinct, symmetric setting.&lt;/p&gt;
&lt;p&gt;The idea predates AES-GCM-SIV. Phillip Rogaway and Thomas Shrimpton gave it a provable-security treatment in 2006, defining deterministic authenticated encryption and the Synthetic IV construction that realizes it [@rs_eprint] [@rs_doi]. It was standardized as AES-SIV in RFC 5297 [@rfc5297].&lt;/p&gt;

An initialization vector computed *from the message* -- together with the key and associated data -- rather than drawn at random. Because the IV is a function of the plaintext, two different plaintexts get different IVs automatically, and only byte-identical messages ever collide.

An authenticated cipher that, if a nonce is ever repeated, leaks at most whether two identical $(\text{key}, \text{nonce}, \text{plaintext}, \text{associated data})$ inputs were the same -- never the key, never arbitrary plaintext. Reuse degrades it gracefully rather than catastrophically.
&lt;p&gt;The modern instantiation is AES-GCM-SIV (RFC 8452) [@rfc8452]. It computes a keyed hash of the &lt;em&gt;entire&lt;/em&gt; plaintext and associated data using POLYVAL, and folds that into a synthetic IV that doubles as the authentication tag; the synthetic IV then seeds ordinary AES-CTR encryption.POLYVAL is a little-endian relative of GHASH, chosen deliberately so AES-GCM-SIV can reuse the same carry-less multiplication hardware that already accelerates AES-GCM. The parallel to GCM is by design, not coincidence.&lt;/p&gt;
&lt;p&gt;Two more refinements matter: fresh message-authentication and message-encryption keys are &lt;em&gt;derived per nonce&lt;/em&gt; from the master key, which also pushes back the birthday bound that limits plain GCM and raises the safe message limit to roughly $2^{50}$ per key [@gcmsiv_spec]. The price is structural and unavoidable: because the tag depends on the whole plaintext, you cannot emit the first ciphertext byte until you have read the last plaintext byte. AES-GCM-SIV is inherently &lt;em&gt;two-pass and not online&lt;/em&gt;.&lt;/p&gt;

flowchart TD
    A[&quot;Master key and nonce&quot;] --&amp;gt; B[&quot;Derive per-message auth and encryption keys&quot;]
    P[&quot;Plaintext and associated data&quot;] --&amp;gt; C[&quot;POLYVAL hash under the derived auth key&quot;]
    B --&amp;gt; C
    C --&amp;gt; D[&quot;Synthetic IV, which doubles as the authentication tag&quot;]
    D --&amp;gt; E[&quot;Seed AES-CTR encryption with the synthetic IV&quot;]
    B --&amp;gt; E
    E --&amp;gt; F[&quot;Output ciphertext and tag&quot;]
&lt;p&gt;Now state precisely what &quot;misuse-resistant&quot; earns you, because the phrase invites overclaiming. On a nonce repeat, AES-GCM-SIV discloses only whether the two messages &quot;were equal or not,&quot; which RFC 8452 identifies as &quot;the minimum amount of information that a deterministic algorithm can leak&quot; [@rfc8452]. Compare that to Section 6, where a single GCM repeat surrendered $P_1 \oplus P_2$ and set up universal forgery. The SIV construction converts a catastrophe into a whisper.&lt;/p&gt;

On nonce reuse, a misuse-resistant AEAD discloses only &quot;whether the messages were equal or not ... the minimum amount of information that a deterministic algorithm can leak.&quot; -- RFC 8452 [@rfc8452]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &quot;Misuse-resistant&quot; does not mean reuse is harmless. On a repeat, AES-GCM-SIV still leaks that two identical messages were sent under the same nonce -- the provable minimum, but not nothing. You get &lt;em&gt;graceful&lt;/em&gt; degradation, not immunity. It is a safety net, not a license to reuse nonces deliberately.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is a parallel path worth knowing, though it solves a slightly different problem. XChaCha20-Poly1305 extends the nonce to 192 bits, which makes &lt;em&gt;random&lt;/em&gt; nonce selection collision-safe and lets systems stay stateless without a counter [@xchacha]. That is genuinely useful, but it is not true misuse resistance: an &lt;em&gt;exact&lt;/em&gt; nonce repeat still breaks Poly1305 the way any keystream reuse does. A big nonce makes accidental collisions astronomically unlikely; it does not make a deliberate repeat safe. Collect the options:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scheme&lt;/th&gt;
&lt;th&gt;Passes / online&lt;/th&gt;
&lt;th&gt;On nonce reuse&lt;/th&gt;
&lt;th&gt;Misuse-resistant?&lt;/th&gt;
&lt;th&gt;Nonce size&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;AES-GCM&lt;/td&gt;
&lt;td&gt;1 pass, online&lt;/td&gt;
&lt;td&gt;Catastrophic (Section 6)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;Fastest with a reliable unique counter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-GCM-SIV&lt;/td&gt;
&lt;td&gt;2 pass, not online&lt;/td&gt;
&lt;td&gt;Leaks only message equality&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;96-bit&lt;/td&gt;
&lt;td&gt;POLYVAL and per-nonce keys [@rfc8452]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES-SIV&lt;/td&gt;
&lt;td&gt;2 pass, not online&lt;/td&gt;
&lt;td&gt;Leaks only message equality&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Vector input&lt;/td&gt;
&lt;td&gt;Deterministic key-wrap [@rfc5297]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;1 pass, online&lt;/td&gt;
&lt;td&gt;Random reuse unlikely; exact reuse breaks&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;192-bit&lt;/td&gt;
&lt;td&gt;Stateless random nonces [@xchacha]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Two families, one move. On the signature side we stopped trusting runtime randomness by &lt;em&gt;deriving&lt;/em&gt; the number; on the AEAD side we stop trusting it by making a repeat &lt;em&gt;non-catastrophic&lt;/em&gt;. It feels, at last, like the problem is solved. It is not -- and the reasons are not missing features anyone can build. Some of them are walls that no amount of engineering can move.&lt;/p&gt;
&lt;h2&gt;10. Theoretical Limits and the Open Frontier&lt;/h2&gt;
&lt;p&gt;Some of the walls we keep hitting are not missing features that a cleverer engineer will one day supply. They are proofs that you cannot have everything at once.&lt;/p&gt;

First: an online cipher cannot be misuse-resistant. To leak only equality on a repeat, the first bit of ciphertext must depend on the *entire* plaintext, which forces a second pass -- so &quot;streaming&quot; and &quot;misuse-resistant&quot; cannot both hold [@rfc8452]. Second: pure determinism cannot resist faults. The very repeatability that kills nonce reuse is the property a fault attack exploits, so resisting faults means reintroducing some randomness to hedge [@hedged]. Neither wall is a missing feature. Both are theorems. There is no single perfect primitive -- only a matched pair chosen per threat model.
&lt;p&gt;Those two impossibilities explain the shape of everything in Sections 7 through 9. AES-GCM-SIV is two-pass &lt;em&gt;because it has to be&lt;/em&gt;. Hedged signatures exist &lt;em&gt;because&lt;/em&gt; determinism alone cannot cover the fault case. We did not fail to find the one perfect design; the one perfect design is provably not there.&lt;/p&gt;
&lt;p&gt;A third limit is epistemic, and it is the quiet engine under this entire article.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can never prove a generator is unpredictable; you can only fail to prove it predictable. That asymmetry -- argued in &lt;a href=&quot;https://paragmali.com/blog/predictable-or-repeated-the-only-two-ways-cryptographic-rand&quot; rel=&quot;noopener&quot;&gt;Part 2&lt;/a&gt; -- is why &quot;harden the random number generator&quot; was never a structural fix. LadderLeak made the point unforgettable by winning with less than one bit of bias [@ladderleak]. Deriving the number sidesteps the unprovable question entirely: there is nothing left to certify.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And the biased-nonce frontier is still &lt;em&gt;descending&lt;/em&gt;: LadderLeak&apos;s sub-one-bit result is the current floor, not a proven bottom [@ladderleak]. The theoretical minimum bias that remains exploitable is an open question, and history says to bet on &quot;smaller than you think.&quot;&lt;/p&gt;
&lt;p&gt;Then there is the genuinely open ground, where the same disease is re-emerging one abstraction level up. In threshold and multi-party ECDSA, several parties must &lt;em&gt;jointly&lt;/em&gt; produce a nonce that is unbiased and secret from all of them, even when some participants misbehave. That is the Hidden Number Problem wearing a new coat: get the joint nonce generation subtly wrong and the biased-nonce attacks come straight back [@threshold]. On the AEAD side, none of the four ciphers we surveyed is &lt;em&gt;key-committing&lt;/em&gt;, and that gap is exploitable.&lt;/p&gt;

An authenticated cipher whose ciphertext binds to exactly one key, so it cannot be decrypted to a different valid plaintext under a different key. None of AES-GCM, AES-GCM-SIV, AES-SIV, or XChaCha20-Poly1305 is key-committing, which is precisely what enables partitioning-oracle attacks.
&lt;p&gt;Julia Len, Paul Grubbs, and Thomas Ristenpart showed that this non-committing property turns certain systems into partitioning oracles that recover passwords and keys [@len]. There is no drop-in standard for committing AEAD yet -- an active research front. It is worth being precise about what is &lt;em&gt;not&lt;/em&gt; open, though: AES-GCM-SIV and AES-SIV already sit exactly at the minimum-leakage lower bound for the misuse case, because &quot;leaks only equality&quot; is provably optimal for any deterministic scheme [@rfc8452]. That specific gap is closed; you cannot do better, and you do not need to.&lt;/p&gt;
&lt;p&gt;Step back and the emotional arc completes. We began thinking nonce reuse was sloppy hygiene, discovered it was a cliff, thought determinism sealed the cliff, learned determinism opened a fault door, and now find that some of the remaining doors are welded shut by theorems while others open onto genuinely unexplored rooms. None of that theory, however, changes what an engineer should actually do on Monday morning. After all of it, what do you type?&lt;/p&gt;
&lt;h2&gt;11. What a Practitioner Does Today&lt;/h2&gt;
&lt;p&gt;Everything above collapses into a decision tree that fits on an index card. You do not need to re-derive Joux&apos;s polynomial in a code review; you need two questions for signing and two for encryption.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;signing&lt;/strong&gt;: for any (EC)DSA or DSA signer, derive the nonce with RFC 6979 or a hedged variant rather than drawing it at runtime [@rfc6979]. For greenfield systems, prefer Ed25519, which is deterministic by construction [@rfc8032]. If fault injection is a realistic threat -- smart cards, HSMs, IoT silicon an attacker can hold -- hedge, so an entropy failure degrades to safe determinism and a glitch still faces a moving target [@hedged] [@boringssl] [@goecdsa]. And never compute a nonce as &lt;code&gt;rand() mod n&lt;/code&gt; from a raw PRNG without the anti-bias margin FIPS 186-5 specifies as Extra Random Bits [@fips1865pdf]. On hardware whose timing an attacker can measure -- smart cards, TPMs, co-located VMs -- pair all of this with a &lt;em&gt;constant-time&lt;/em&gt; scalar multiplication: RFC 6979 fixes how $k$ is generated, not how it is used, so the Minerva, TPM-Fail, and LadderLeak timing leaks close only when $k \cdot G$ itself runs in constant time [@minerva] [@tpmfail] [@ladderleak].&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;AEAD&lt;/strong&gt;: if you can guarantee a unique nonce -- a reliable, non-resetting counter -- AES-GCM is the fastest choice, provided you cap invocations per key against the birthday bound and rekey before you approach it [@sp80038d] [@rfc8452]. If nonce uniqueness is even slightly uncertain -- multiple writers sharing a key, VM snapshots that replay state, forked or stateless workers -- reach for AES-GCM-SIV [@rfc8452] or the 192-bit-nonce XChaCha20-Poly1305 [@xchacha]. For deterministic key-wrapping, AES-SIV is purpose-built [@rfc5297].&lt;/p&gt;

flowchart TD
    S[&quot;Signing (EC)DSA or DSA&quot;] --&amp;gt; Q1{&quot;Fault injection in scope, such as smartcard or HSM or IoT&quot;}
    Q1 --&amp;gt;|Yes| H[&quot;Hedge: nonce from key, message, and fresh entropy&quot;]
    Q1 --&amp;gt;|No| G{&quot;Greenfield design&quot;}
    G --&amp;gt;|Yes| ED[&quot;Prefer Ed25519&quot;]
    G --&amp;gt;|No| R6[&quot;Deterministic RFC 6979&quot;]
    A[&quot;Choosing an AEAD&quot;] --&amp;gt; Q2{&quot;Reliable unique nonce counter&quot;}
    Q2 --&amp;gt;|Yes| GCM[&quot;AES-GCM, cap invocations and rekey&quot;]
    Q2 --&amp;gt;|No| MR[&quot;AES-GCM-SIV or XChaCha20-Poly1305&quot;]
&lt;p&gt;The same choices read cleanly as a do-and-do-not table:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Call this&lt;/th&gt;
&lt;th&gt;Never this&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Signing, general&lt;/td&gt;
&lt;td&gt;RFC 6979 deterministic or hedged&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rand() mod n&lt;/code&gt; from a raw PRNG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signing, greenfield&lt;/td&gt;
&lt;td&gt;Ed25519&lt;/td&gt;
&lt;td&gt;Home-rolled DSA nonce logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signing, hardware or embedded&lt;/td&gt;
&lt;td&gt;Hedged (key, message, entropy)&lt;/td&gt;
&lt;td&gt;Pure determinism on a fault-exposed device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signing, measurable hardware&lt;/td&gt;
&lt;td&gt;Constant-time scalar multiplication&lt;/td&gt;
&lt;td&gt;Variable-time scalar multiplication an attacker can time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEAD, reliable counter&lt;/td&gt;
&lt;td&gt;AES-GCM, capped per key and rekeyed&lt;/td&gt;
&lt;td&gt;AES-GCM past its invocation limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AEAD, uncertain uniqueness&lt;/td&gt;
&lt;td&gt;AES-GCM-SIV or XChaCha20-Poly1305&lt;/td&gt;
&lt;td&gt;Plain AES-GCM with hopeful nonces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deterministic key-wrap&lt;/td&gt;
&lt;td&gt;AES-SIV (RFC 5297)&lt;/td&gt;
&lt;td&gt;A data-plane GCM key reused for wrapping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

These abstractions land on concrete Windows components. Signature and nonce generation run through the Cryptography API: Next Generation stack, whose internals the [CNG architecture article](/blog/cng-architecture-bcrypt-ncrypt-ksps) works through. TLS record nonces are the responsibility of [SChannel](/blog/rotating-every-cipher-schannel-and-the-twenty-year-algorithm), the platform&apos;s TLS implementation. And every one of those values ultimately draws on the [Windows RNG](/blog/a-key-is-only-as-unguessable-as-the-dice-that-made-it-inside), whose seeding and clone-defense decide whether the &quot;unique and unpredictable, once, per key&quot; contract actually holds in practice. Read those three together and this article&apos;s rules become a specific call stack.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Signing: derive the nonce (RFC 6979), prefer Ed25519, hedge on hardware. AEAD: use AES-GCM only with a guaranteed-unique counter and an invocation cap; otherwise reach for AES-GCM-SIV or a 192-bit-nonce cipher. One instinct sits under both: never trust a runtime draw to be unique and unpredictable &quot;just once.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

Sign the same message twice, with the same key, and compare the two signatures. If they are byte-for-byte identical, the signer is deterministic (RFC 6979 or Ed25519). If they differ, it is randomized or hedged. Ed25519 in most libraries will match exactly; Go&apos;s `crypto/ecdsa` will not, by design. That one comparison tells you which row of the Section 8 table you are standing in -- and whether an entropy failure at runtime would be survivable.
&lt;p&gt;Two rules for two primitives, resting on a single instinct. That instinct is strong enough to design around -- but a handful of persistent myths still surface in code review and threat models, and each one hides a real misunderstanding of the mechanism. Let us dismantle them one at a time.&lt;/p&gt;
&lt;h2&gt;12. Frequently Asked Questions&lt;/h2&gt;


No, and this distinction matters more than almost anything else here. A *repeated* nonce leaks the key from *two* signatures with certainty, through the subtraction in Section 3. A *biased* nonce is statistical: each signature is one noisy equation, and recovery needs *many* of them aggregated by a lattice or Fourier reduction [@ns] [@ladderleak]. One biased signature on its own leaks nothing you can use. &quot;One biased signature reveals your key&quot; is the single most common misstatement in this area.


Not against every adversary. Determinism removes the repeat and generation-side bias failures completely (it does not stop a side-channel leak of $k$ during the scalar multiplication $k \cdot G$, which needs a *constant-time* implementation, not determinism), but it turns the signer into a fixed, replayable target for differential fault analysis -- proven for de-randomized Fiat-Shamir schemes including EdDSA [@hedged], and flagged by FIPS 186-5 as a special concern for deterministic schemes on embedded and smartcard devices [@fips1865pdf]. That is exactly why hedged signatures exist.


No. It degrades gracefully instead of catastrophically. On a repeat it discloses only whether two messages were identical -- the provable minimum for any deterministic scheme -- never the key and never arbitrary plaintext [@rfc8452]. &quot;Graceful&quot; is not &quot;free.&quot; Do not reuse nonces on purpose in the belief that the SIV mode makes it costless.


No, and precision matters. A single collision instantly leaks $P_1 \oplus P_2$ and destroys confidentiality [@joux]. Recovering the authentication subkey $H$ is a *separate* step: one collision yields a set of candidate roots, and pinning $H$ uniquely generally needs two or more collisions [@joux]. Live TLS servers doing this were found forgeable in 2016 [@nda], but the mechanism is &quot;confidentiality gone at once, forgery after enough collisions,&quot; not &quot;one reuse equals the key.&quot;


Because you cannot certify unpredictability, only fail to falsify it -- and sub-bit bias is still fatal across enough signatures. LadderLeak won with less than one bit of leakage [@ladderleak]. Hardening the generator raises the bar; it does not change the structure. Deriving the number does.


Only within the birthday bound, and only if uniqueness genuinely holds. Random 96-bit nonces start colliding near $2^{48}$ messages, which is why usage caps per key exist [@rfc8452]. Worse, multi-writer keys, VM clones, and process forks silently re-emit the same nonce no matter how carefully you generated it. If uniqueness is uncertain, switch to AES-GCM-SIV [@rfc8452] or XChaCha20-Poly1305&apos;s 192-bit nonce [@xchacha]; this reuse has been observed in the wild [@nda].


No. Lattice- and Fiat-Shamir-based post-quantum signatures inherit the same sensitivity to per-signature randomness; several are precisely the de-randomized Fiat-Shamir schemes shown vulnerable to fault attacks [@hedged]. Migrating to post-quantum cryptography re-instantiates nonce discipline -- it does not retire it.

&lt;h2&gt;13. One Cure for Two Catastrophes&lt;/h2&gt;
&lt;p&gt;Return to that stage in Berlin in 2010, but now you can see the whole shape of the thing. The PlayStation 3 master key did not fall to a broken cipher or a weak curve; it fell to &lt;em&gt;one number, used twice&lt;/em&gt;, and two signatures&apos; worth of subtraction [@f0f]. The same shape swept real Bitcoin wallets when Android&apos;s generator repeated nonces [@bitcoin].&lt;/p&gt;
&lt;p&gt;Then the required leak kept shrinking -- bit-length timing on smart cards [@minerva], side channels in TPMs [@tpmfail], less than a single bit in LadderLeak [@ladderleak], nine fixed bits and sixty signatures in PuTTY [@putty] -- and the very same disease surfaced in a cipher, where 184 live TLS servers reusing a GCM nonce were shown forgeable [@nda].&lt;/p&gt;
&lt;p&gt;Every one of those failures shares a single sentence. Both primitives bet everything on a number that had to be unique and unpredictable exactly once, and both collapsed the moment that bet was lost. And the cures share a single sentence too. Deterministic signing derives the number from the key and the message so there is nothing left to repeat or bias [@rfc6979], while a &lt;em&gt;constant-time&lt;/em&gt; scalar multiplication closes the side-channel leaks that determinism never touched; hedged signing keeps a sliver of entropy so a fault has no fixed target [@hedged]; misuse-resistant encryption derives the IV from the message so a repeat leaks only that two messages matched [@rfc8452].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Two primitives, one disease, one cure: stop trusting runtime randomness for the once-per-operation number. Derive it, or make a repeat non-catastrophic. Everything else in this article is a footnote to that sentence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Part 2 of this series taught the two ways randomness betrays you; this was the promised descent into what those betrayals actually cost and how the field designed them away. The punchline you were promised at the start has been earned line by line: in cryptography, reusing one number can cost you everything -- so build systems that never require you to trust yourself to use it only once.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;nonce-reuse-ecdsa-aes-gcm&quot; keyTerms={[
  { term: &quot;Signing nonce (k)&quot;, definition: &quot;The per-signature secret scalar in (EC)DSA; it must be unique, secret, and unpredictable, exactly once per key.&quot; },
  { term: &quot;GCM nonce (IV)&quot;, definition: &quot;The per-message value in AES-GCM; it must be unique under a given key, though it may be public.&quot; },
  { term: &quot;Repeat vs. bias&quot;, definition: &quot;A repeated nonce leaks the key from two signatures with certainty; a biased nonce is statistical and needs many signatures.&quot; },
  { term: &quot;Hidden Number Problem (HNP)&quot;, definition: &quot;Recovering a secret from many samples that each reveal a few bits of a related product mod n; the frame for biased-nonce attacks.&quot; },
  { term: &quot;GHASH subkey (H)&quot;, definition: &quot;The secret AES-of-zero value that keys GCM&apos;s polynomial authenticator; recovering it enables universal forgery.&quot; },
  { term: &quot;Forbidden attack&quot;, definition: &quot;Joux&apos;s GCM attack: reusing an IV cancels the tag blind, exposing a polynomial whose roots are candidates for H.&quot; },
  { term: &quot;Deterministic signature&quot;, definition: &quot;A signature whose nonce is a pseudorandom function of the key and message, using no runtime randomness (RFC 6979, EdDSA).&quot; },
  { term: &quot;Hedged signature&quot;, definition: &quot;A nonce derived from key, message, and fresh entropy; safe if the RNG fails and against fault injection.&quot; },
  { term: &quot;Nonce-misuse-resistant AEAD&quot;, definition: &quot;An AEAD that on nonce reuse leaks at most message equality, never the key or arbitrary plaintext (AES-GCM-SIV, AES-SIV).&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>cryptography</category><category>ecdsa</category><category>aes-gcm</category><category>nonce-reuse</category><category>digital-signatures</category><category>aead</category><category>rfc-6979</category><category>applied-crypto</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>