ECDSA Nonce Reuse: How One Repeated Number Leaks the Private Key

ECDSA Nonce Reuse: How One Repeated Number Leaks the Private Key

Written by

in

ECDSA is the signature scheme behind Bitcoin, TLS certificates, SSH keys, and the code signing on game consoles. Every signature it makes pulls in one fresh random number, the nonce, written k. ECDSA nonce reuse is the failure where that number stops being fresh, and it is not a small leak. Reuse k once across two signatures and an attacker solves two short equations to recover your long term private key.

How ECDSA signing works

Start with the pieces. A curve has a fixed base point G and a large prime n called the order. Your private key is a secret number d. Your public key is the point Q = d*G, and the curve is built so that going from Q back to d is infeasible. To sign a message you first hash it down to a number z.

Then signing does this. Pick a random nonce k between 1 and n - 1. Compute the point R = k*G, and take its x coordinate modulo n to get the first half of the signature, r. Compute the second half as s = k^-1 * (z + r*d) mod n. The signature is the pair (r, s). Two things matter here. The value r depends only on k, not on the message. And k must be secret and unique, because s mixes k with your private key d in a single linear equation.

The nonce is a one time mask over the private key. Reuse the mask and the key shows through.

Why ECDSA nonce reuse leaks the private key

Here is the whole problem in one observation. Since r comes from k alone, two signatures made with the same k have the same r. That repeated r is a flashing light an attacker spots by scanning signatures, and once they see it the algebra is short.

Say you sign two different message hashes z1 and z2 with the same nonce k and the same key d:

s1 = k^-1 * (z1 + r*d) mod n
s2 = k^-1 * (z2 + r*d) mod n

Subtract the second from the first. The r*d term is identical in both, so it cancels:

s1 - s2 = k^-1 * (z1 - z2) mod n

Now k is the only unknown, and it falls out with one modular inverse:

k = (z1 - z2) / (s1 - s2) mod n

With k in hand, go back to the first equation and solve for d. Rearranging s1 = k^-1 * (z1 + r*d) gives:

d = (s1*k - z1) / r mod n

That is the private key. Two signatures, two subtractions, two inverses, and the secret the whole curve was designed to protect is gone.

A worked example with small numbers

Real curves use 256 bit numbers, so let us shrink everything to make the arithmetic readable. Take order n = 23 and private key d = 7. The signer reuses nonce k = 5, which produces r = 13. The two message hashes are z1 = 10 and z2 = 3. Working modulo 23, the signatures come out to:

s1 = 5^-1 * (10 + 13*7) = 14 * 9  = 11 mod 23
s2 = 5^-1 * (3  + 13*7) = 14 * 2  = 5  mod 23

The attacker sees two signatures that share r = 13, so they know k was reused. They recover it:

k = (z1 - z2) / (s1 - s2) = 7 / 6 = 7 * 4 = 5 mod 23

(Here 6^-1 = 4 because 6*4 = 24 = 1 mod 23.) Then they recover the key:

d = (s1*k - z1) / r = (11*5 - 10) / 13 = 45 / 13 = 22 * 16 = 7 mod 23

Out comes d = 7, the exact private key. No curve was broken. The only mistake was feeding the math the same k twice.

This really happened: Sony PlayStation 3

In December 2010, at the 27th Chaos Communication Congress, the group fail0verflow presented Console Hacking 2010, in a talk people remember as “PS3 Epic Fail.” Sony signed the code that the PS3 would trust using ECDSA. The requirement is that k be a fresh random value every time. Sony’s implementation did not pick a random k at all. It used a constant, the same fixed number in every signature.

A constant nonce is the worst case of the bug above. Every signed binary shared the same r, so any two of them gave up the private signing key through the same two subtractions. Researcher George Hotz later published Sony’s recovered key, after which anyone could sign code the console would accept as genuine. The key was baked into the hardware root of trust, so it could not be quietly rotated away. A single bad assumption about randomness undid the platform’s whole security model.

This really happened again: Android Bitcoin wallets

In August 2013 the same flaw drained real money. A bug in Android’s SecureRandom meant the generator was sometimes not seeded properly, so it returned predictable, repeating output. Bitcoin wallets on Android sign transactions with ECDSA, and a transaction with several inputs needs several signatures. With a broken generator, those signatures came out using the same k.

Anyone watching the public blockchain could scan for two signatures from one address that shared an r value. Each collision exposed that wallet’s private key, and with the key an attacker simply moved the coins. The official Bitcoin alert named the affected wallets, and observers tracked dozens of bitcoin swept out of vulnerable addresses. The cryptography was fine. The random source underneath it was not.

Even a tiny bias is fatal

Full reuse is the loud case, but a slightly imperfect nonce is just as fatal. If k is even slightly predictable, say the top few bits are always zero because of a sloppy generator, each signature becomes a noisy hint about d. Collect a few hundred and the problem turns into a lattice problem, the hidden number problem, which solvers crack efficiently. The Biased Nonce Sense paper and real timing side channel attacks such as Minerva recovered full private keys from signatures that leaked only a handful of nonce bits. Partial nonce leakage is still total key compromise. Nonce reuse is just the loudest version of a problem that starts the moment k stops being uniform and secret.

How to do it right

The fix is to stop trusting a random number generator with something this fragile. Two approaches dominate.

  • RFC 6979 deterministic ECDSA. Instead of sampling k from a generator, derive it with HMAC from the private key and the message hash together. The same message and key always yield the same k, two different messages yield unrelated values, and there is no entropy source left to fail. It keeps the ECDSA wire format unchanged, so it drops into existing systems.
  • EdDSA and Ed25519. A newer scheme that makes deterministic nonces part of the design rather than a patch. The nonce is computed by hashing a secret prefix of the key with the message, so reuse across different messages cannot happen by construction. Ed25519 also avoids several other ECDSA footguns and is a strong default for new work.

Both share one idea. Take the nonce out of the hands of a random generator that can be misseeded or cloned, and compute it from inputs you already trust.

The shape of this bug rhymes with others where the primitive is sound and a usage rule gets broken. AES GCM nonce reuse hands over the authentication key the same way, by repeating a value that was supposed to be unique, and a padding oracle attack turns one stray error message into full plaintext recovery. In each case the math is fine and the assumption around it is what fails.

The assumption that breaks

ECDSA assumes one thing about every signature: that k is fresh, secret, and uniform. The curve, the key sizes, and the hardness proofs all stand on that single premise. Sony assumed a constant would do. The Android wallets assumed the platform generator was seeded. Both assumptions looked fine on the line of code that made the signing call, and both handed over the private key. This is the kind of flaw you find by asking what a system quietly takes for granted and checking whether anything can make it false, not by scanning for a known bad pattern. That is exactly what an autonomous researcher built to test assumptions is meant to do. Read more on our about page.

Frequently asked questions

What is ECDSA nonce reuse?

It is when an ECDSA signer uses the same per signature random number k for two different messages under the same private key. Because the signature value r depends only on k, both signatures end up with the same r, which is an obvious tell. From two such signatures an attacker solves a short pair of linear equations to recover k and then the long term private key.

How does reusing k actually recover the private key?

Two signatures with the same k give s1 = k^-1(z1 + r*d) and s2 = k^-1(z2 + r*d). Subtracting cancels the r*d term, so k = (z1 - z2) / (s1 - s2) mod n. Once k is known, the key is d = (s1*k - z1) / r mod n. It is two subtractions and two modular inverses, with no need to break the curve itself.

What real systems were broken by this?

The Sony PlayStation 3 used a constant k in every code signing signature, which fail0verflow exposed in 2010, letting anyone recover Sony’s private key. In 2013 a bug in Android’s SecureRandom produced repeating nonces, so Bitcoin wallets reused k and attackers swept coins out of affected addresses. In both cases the cryptography was sound and the nonce source was the failure.

How do you prevent ECDSA nonce reuse?

Stop relying on a random generator for k. RFC 6979 deterministic ECDSA derives k with HMAC from the private key and the message, so the same input always gives the same nonce and there is no entropy source to misfire. EdDSA and Ed25519 make deterministic nonces part of the design. Note that even partial nonce bias is fatal, since lattice attacks recover keys from many signatures that leak only a few nonce bits.