The number that must never repeat
An ECDSA signature is a pair of integers, (r, s). Producing it takes three inputs: the private key d, the message hash z, and a fresh secret integer k — the nonce, short for "number used once." The signer picks k in the range [1, n−1], where n is the order of the curve's base point G, and computes:
R = k * G (a point on the curve)
r = x-coordinate of R mod n
s = k^-1 * (z + r * d) mod n
signature = (r, s)Look at where each output comes from. s mixes everything together — the nonce, the hash and the key. But r is derived from k and nothing else: it is the x-coordinate of kG, a function of the nonce and the curve alone. The message does not enter into it. The private key does not enter into it.
That is the whole problem in one sentence. Two signatures produced with the same k will carry the same r, and r is published with every signature. A repeated nonce is not a secret failure that has to be discovered — it announces itself in plain sight, in a field that every verifier reads and every block explorer stores forever.
Two signatures, two unknowns
Suppose a signer used the same k and the same key d for two different messages with hashes z1 and z2. Both signatures share the same r, so we have:
s1 = k^-1 * (z1 + r * d) mod n
s2 = k^-1 * (z2 + r * d) mod nEverything here is known to the public except k and d. Two equations, two unknowns. Multiply both sides of each by k to clear the inverse:
k * s1 = z1 + r * d mod n
k * s2 = z2 + r * d mod nThe term r * d is identical in both lines, because the same key signed both messages and the same nonce produced the same r. Subtract the second equation from the first and it cancels, taking the private key out of the picture entirely:
k * s1 - k * s2 = z1 - z2 mod n
k * (s1 - s2) = z1 - z2 mod n
k = (z1 - z2) * (s1 - s2)^-1 mod nThat recovers the nonce. Substituting it back into either of the step-2 equations gives the key, since r is now the only thing multiplying d:
k * s1 = z1 + r * d mod n
r * d = k * s1 - z1 mod n
d = (s1 * k - z1) * r^-1 mod nFour steps, no search, no guessing. The cost is two modular inversions and a handful of multiplications — microseconds on any machine, from data that is already public.
One practical wrinkle: encodings do not always preserve the exact s a signer computed. Bitcoin and several libraries normalize to the "low-S" form, replacing s with n − s when it lands in the upper half of the range — a malleability fix proposed in BIP-62 and specified as a relay rule in BIP-146, unrelated to nonces but overlapping with this arithmetic. The recovered value may then correspond to −k rather than k, and the sign has to be resolved before the numbers line up. It changes nothing about the difficulty; the encoding topic is covered separately in ECDSA signature formats.
Not a thought experiment
This is the most productive class of real-world ECDSA failure, and none of it was caused by the mathematics. Every case is an implementation supplying a nonce it should not have supplied.
- PlayStation 3, 2010. Sony's firmware signing implementation used a constant value where
kbelonged. Every signature it produced carried an identicalr, and the console's master signing key fell out of any two of them. The failure was demonstrated publicly at 27C3 in the talk Console Hacking 2010. It is the purest illustration available: the algorithm was implemented correctly except for one variable that never varied. - Android wallets, 2013. On 14 August 2013 the Android Developers Blog published Some SecureRandom Thoughts, after investigating what the post describes as "the compromise of a bitcoin transaction that led to the update of multiple Bitcoin applications on August 11." The root cause was that applications using the Java Cryptography Architecture "may not receive cryptographically strong values on Android devices due to improper initialization of the underlying PRNG." Wallets signing on affected devices drew nonces from a generator that could come up in the same state repeatedly. Google shipped a
PRNGFixesworkaround for API levels 16–18 and patched the platform. Note what broke: not the wallet's crypto code, not the curve, but the operating system primitive underneath both. - The wider class: biased nonces. Full repetition is the loud version. The quiet version is a nonce that is merely skewed — a few bits that are not uniformly distributed, because the generator returns values in a slightly wrong range, or truncates, or leaks timing. There is no clean algebraic shortcut there, but the problem can be recast as finding a short vector in a lattice, and lattice reduction handles it. Breitner and Heninger's Biased Nonce Sense (Financial Cryptography 2019) applied this to public blockchains and internet-wide scans, recovering hundreds of Bitcoin keys plus dozens of Ethereum, Ripple, SSH and HTTPS keys — from nonces that were never repeated, only imperfect. The lesson is the harsh one: "mostly random" is not a defence.
None of these needed a flaw in ECDSA, and none are fixed by a bigger curve. The algorithm demands a perfect uniform secret per signature and gives no warning when it does not get one.
What the chain shows today
Repeated r values are still sitting in the Bitcoin blockchain, permanently, because blockchains do not forget. Our Nonce Reuse Checker pulls an address's recent transactions from the mempool.space API, extracts the DER signatures from inputs that spend from that address, and groups them by (public key, r). Here is what it returned for 1BFhrfTTZP3Nw4BNy4eX4KFLsn9ZeijcMm on 20 August 2026:
address 1BFhrfTTZP3Nw4BNy4eX4KFLsn9ZeijcMm (P2PKH)
transactions returned 50 <- API cap, not the full history
spending from it 30
inputs from address 110
signatures parsed 110 (unattributed 0, taproot 0, unparsed 0)
distinct public keys 1
distinct r values 73 <- 110 signatures, only 73 nonces
repeated-r groups 6
identical duplicates 0One key, 110 signatures, 73 distinct nonces. The gap is the whole story: 43 of the 110 signatures sit in a group that shares an r with at least one other signature, and in every one of the six groups the s values differ, which rules out the benign explanation of a duplicated signature. The largest group:
r = d47ce4c025c35ec440bc81d99834a624875161a26bf56ef7fdc0f5d52f843ad1
pubkey = 04dbd0c6...49c5ff (uncompressed)
ad698b53bbba9e7d807559ec7d3a466fccb669dd3659a1604a206eeb9965c527 vin 0 2012-12-21
b4922bc8cc3624487cdcf4b0ed5a98a21a4552a48dfe495490a7443dd546821e vin 0 2012-12-21
8ab813e5a4039eb79484c315e9c0e201c9d80385c3f3650a4c4b974db9172148 vin 0 2012-12-21
0b5315f4d472a22a6937a20a8d44b8e77099f2a951b65c53c9ebf912352504fa vin 0 2012-12-19
952d8301780ca74fca5f6541db44ba49e47e8514dfdef36bc7e3d534085dbbf6 vin 0 2012-12-19
... 5 more, the oldest on 2012-08-16Two details are worth pausing on. First, the second-largest group is another ten signatures sharing a different r — and in both groups every signature sits at the same input index of its transaction (index 0 for one, index 1 for the other). That is not what an occasional RNG collision looks like. It is what a generator looks like when it is reinitialized to the same state at the start of each signing operation and then walked forward: input 0 always gets the first value, input 1 the second. Second, the reuse runs from August to December 2012 — almost a year before the Android SecureRandom disclosure. The defect class was live in the wild before it had a public name, a reminder that such announcements mark when a problem was noticed, not when it began.
The fix: stop rolling dice
The durable answer is not "use a better random number generator." It is to remove the random number generator from the signing path entirely.
RFC 6979, deterministic ECDSA. Instead of sampling k, derive it: seed an HMAC-based deterministic random bit generator (HMAC-DRBG) with the private key and the message hash, and take k from its output, redrawing if the value falls outside [1, n−1]. The result is uniform-looking, unpredictable without the key, and — critically — a function of the message. A different message necessarily produces a different k, so accidental reuse becomes structurally impossible rather than merely unlikely. Signing the same message twice reproduces the same bytes, which is harmless and doubles as a convenient testing property. No entropy source is consulted at signing time, so a broken one cannot hurt you. FIPS 186-5 permits deterministic ECDSA alongside the randomized form.
RFC 8032, EdDSA. Ed25519 and Ed448 make the same idea non-optional by building it into the scheme: the per-signature secret is derived by hashing a key-derived prefix together with the message, and there is no API surface through which an implementer could substitute a random value. If you are choosing a scheme rather than inheriting one, this removes the failure mode by construction. Bitcoin's Taproot signatures follow the same philosophy — BIP-340 mandates a deterministic nonce derivation with optional auxiliary randomness.
Hedged signatures. Pure determinism has one trade-off worth knowing about: a fault induced during signing — a glitched clock or voltage on a smartcard — can make a deterministic signer produce two different signatures over the same message with the same derived k, which is the reusable-nonce condition all over again. RFC 6979 anticipates this in §3.6. The hedged answer mixes fresh randomness into the deterministic derivation rather than replacing it, giving a nonce that is safe if either the derivation or the RNG is sound. Go's crypto/ecdsa signs this way; @noble/curves exposes it as an extraEntropy option; BIP-340's auxiliary randomness is the same idea.
Where the common libraries stand, by default:
| Library | Deterministic nonce by default? | Detail |
|---|---|---|
| libsecp256k1 (Bitcoin Core) | Yes — RFC 6979 | A different nonce function can be passed in if you need one. |
@noble/curves | Yes — RFC 6979 | Hedged mode available via extraEntropy. |
| Rust p256 / k256 (RustCrypto) | Yes — RFC 6979 | Same key and message always produce the same bytes; a randomized variant is opt-in. |
| Go crypto/ecdsa | Hedged, not RFC 6979 | Derives the nonce from key, message and fresh randomness together, so bytes differ per run and cannot be pinned in tests. |
| Python ecdsa (pure Python) | Opt-in | Deterministic signing is a separate method; the plain sign() call uses randomness. |
| Python cryptography (pyca) | Opt-in | Recent releases accept a deterministic-signing flag, and that path needs a recent OpenSSL underneath. |
| OpenSSL CLI | Opt-in (library only) | Deterministic ECDSA exists in recent 3.x as a signature parameter, but dgst and pkeyutl do not expose it. |
| Node.js node:crypto | No — random nonce | Same key and message produce different signature bytes on every call. |
| WebCrypto (crypto.subtle) | No — random nonce | The API gives you no way to supply your own nonce either. |
| Java JCA (SunEC) | No — random nonce | Deterministic ECDSA is reachable by adding a provider such as Bouncy Castle. |
| PHP openssl_* / Ruby OpenSSL | No — random nonce | Neither exposes a deterministic mode. |
A "No" in that table is not a bug report. A randomized nonce drawn from a healthy OS CSPRNG is a perfectly conformant way to sign, and it is what FIPS 186-5 has always specified. The column measures dependency, not correctness: the "No" rows stake your private key on the entropy source at the moment of signing, and the "Yes" rows do not. Whether that matters is a question about your deployment — a hardened server is a very different bet from a freshly booted embedded device, a cloned VM image, or a 2013 phone. The Library Interop Cheatsheet carries this and the other default-behaviour differences side by side.
What to actually do about it
- Never supply your own
k. If a signing API accepts a nonce parameter, treat it as a footgun rather than a feature — and never reach for it just because a test needed reproducible bytes. Deterministic signing already gives you those, safely. - Never write the generator yourself. "We seed it with the timestamp and the request ID" is precisely how these incidents start. Use the platform CSPRNG, or a library that does not need one.
- Check which nonce strategy you are actually running. A two-line test tells you most of it — but read the result carefully, because it cannot distinguish random from hedged:
import { generateKeyPairSync, sign } from "node:crypto";
const { privateKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
const msg = Buffer.from("same message, twice");
const a = sign("sha256", msg, privateKey);
const b = sign("sha256", msg, privateKey);
console.log(a.equals(b));
// false in node:crypto -> random or hedged nonce
// true -> deterministic derivation (RFC 6979 or EdDSA-style)If the bytes differ, you have not learned whether the nonce is purely random or hedged — only the library's documentation settles that. If they match, confirm it is RFC 6979 and not something home-grown, by checking your output against the known-answer vectors in RFC 6979 Appendix A.2. Our ECDSA Test Vector Generator produces RFC 6979 signatures for P-256, P-384, P-521 and secp256k1 from a seed, so the same vectors regenerate byte-for-byte later.
- Be suspicious of low-entropy moments. First boot on embedded hardware, VMs and containers restored from a shared snapshot, and early init before the entropy pool is seeded are where a random nonce is most likely to disappoint. Deterministic signing removes the question.
- If you find reuse, the key is gone. Not weakened — gone, retroactively, for anyone who has ever seen those two signatures. Move the funds, rotate the key everywhere it is used, and treat every signature it ever made as attributable to someone else.
Primary sources
- RFC 6979 — Deterministic Usage of DSA and ECDSA. The HMAC-DRBG derivation, the rationale, and known-answer vectors in Appendix A; the fault-attack caveat is in §3.6.
- RFC 8032 — EdDSA (Ed25519, Ed448), where deterministic nonce derivation is part of the scheme rather than an option.
- FIPS 186-5 — the current Digital Signature Standard, which specifies ECDSA and permits deterministic nonce generation.
- Some SecureRandom Thoughts — Android Developers Blog, 14 August 2013: the platform PRNG initialization defect, its link to the Bitcoin wallet compromise, and the
PRNGFixesworkaround. - Biased Nonce Sense — Breitner and Heninger, IACR ePrint 2019/023 / Financial Cryptography 2019: lattice attacks against merely biased nonces, measured against real blockchain and internet-wide data.
- Console Hacking 2010 — 27C3, the public demonstration of the PlayStation 3 constant-nonce failure.
- BIP-62 and BIP-146 — low-S canonicalization (a malleability fix, not a nonce fix, but the reason recovered values may need a sign correction), plus BIP-340 for Taproot's mandated deterministic nonce.
- libsecp256k1 and @noble/curves — two widely audited implementations defaulting to RFC 6979, whose source is the clearest documentation of how nonce derivation should look.
Why this nonce requirement is one of the few genuine operational advantages RSA still holds, and how the two algorithms compare elsewhere, is covered in ECDSA vs RSA.