The flag-signing service will sign any message you send and hand back
(r, s). It signs two of them before it rotates the nonce. Find the key.
ECDSA is unforgiving about one thing above all others: the per-signature nonce must be secret and never repeated. Repeat it once and the private key falls out with grade-school algebra.
the signing relation#
For a curve of order , private key , message hash and nonce , a signature is the pair with and
Everything here is mod , the group order — not the field prime. Mixing the two moduli is the most common way to spend an afternoon debugging a correct attack.
| symbol | meaning | known to me? |
|---|---|---|
| order of the base point | yes (public) | |
| private key | target | |
| per-signature nonce | secret | |
| truncated to bits | yes | |
| the signature | yes |
reuse collapses it#
Two signatures and on hashes share the same — which is the tell, since depends only on . Equal means equal . Subtract the two signing relations and drops out:
Solve for the nonce directly,
and back-substitute into either signature to recover the key:
That last form is the one to memorise — given , two values and two hashes, it's a single modular division.
|
spotting it in the wild
You don't need the source to know a nonce repeated — you need two signatures with an identical . Collect a handful, sort by , look for a collision. The same trick works across different keys if a bad RNG seeds them in lockstep.
when the nonce is only biased#
Real leaks are rarely a clean repeat. More often is short — the top bits are zero — and one signature tells you nothing, but a few hundred do. This is the Hidden Number Problem: each signature is a noisy linear equation in ,
where every is small. Stack the equations as a lattice whose short vectors encode the answer:
Run LLL (or BKZ for tight bias), and the row closest to the origin spells out the — and with them, . Two-thirds of a leaked nonce over ~80 signatures is plenty.1
the flag
Once you have d, sign the challenge token yourself:
segfault{n0nce_r3us3_1s_a_pr1v4te_k3y_l3ak}
-
The bridge from "biased nonce" to "lattice" is the part worth internalising: a bound on the unknown becomes a short vector, and short vectors are what LLL finds. Most ECDSA CTF crypto is some disguise over this one reduction. ↩