Nonce Reuse

The Schnorr page said the nonce kk must be fresh for every signature. This page shows exactly what breaks if it is reused: an attacker can recover your private key.

TLDR: Sign two different messages with the same nonce and anyone can compute your private key dd with one subtraction and one division.

What is a nonce?

A nonce is a "number used once". In Schnorr signing it is the random value kk.

Each signature needs a new kk. Reuse it across two signatures and the private key leaks.

SVG Image

Two signatures, one nonce

Recall the Schnorr signature (R,s)(R, s) from the previous page:

s=k+hd\mathbf{s = k + h \cdot d}

Here dd is the private key, kk the nonce, and hh the challenge hash tagged_hash("BIP0340/challenge", R || P || m) reduced mod nn (the order of the curve).

The nonce sets the nonce point R=kGR = k \cdot G. Same kk means the same RR. So two signatures made with one reused nonce carry the same RR in public.

SVG Image

Sign transaction 1 and transaction 2 with the same kk:

s1=k+h1d\mathbf{s_1 = k + h_1 \cdot d}
s2=k+h2d\mathbf{s_2 = k + h_2 \cdot d}

where h1=H(RPtx1)h_1 = H(R \, || \, P \, || \, \text{tx}_1) and h2=H(RPtx2)h_2 = H(R \, || \, P \, || \, \text{tx}_2). Only the challenge hash differs. kk and dd are identical in both lines.

Recovering the private key

Subtract the two equations. The nonce kk cancels:

s1s2=(h1h2)d\mathbf{s_1 - s_2 = (h_1 - h_2) \cdot d}

Divide to isolate dd:

d=s1s2h1h2(modn)\mathbf{d = \frac{s_1 - s_2}{h_1 - h_2} \pmod n}

Every value on the right is public. s1s_1 and s2s_2 sit in the two signatures. h1h_1 and h2h_2 anyone can recompute from RR, PP, and the two messages.

SVG Image

What does 'divide mod n' mean?

There is no fraction on the curve. Dividing by h1h2h_1 - h_2 means multiplying by its modular inverse mod nn, the curve order:


The real world

This is not hypothetical.

  • Android, August 2013. A flaw in SecureRandom made wallets repeat nonces. Coins were stolen from real addresses.
  • Sony PlayStation 3. Sony shipped a fixed k in its ECDSA signing. The same subtraction gave attackers the console's signing key.

Same equation both times. A "number used once" used twice.


Try it yourself

Sign two messages with the same nonce, then recover dd from the two signatures.

Helper code: secp256k1 point math

Programming Exercise: Recover a private key from a reused nonce

The helper above goes at the top of your file. Fill in the one line that recovers dd.

python

# paste the secp256k1 helper above this line

def normalize(scalar):
  # BIP340: the point must have even Y, so negate the scalar if it does not
  if mul(scalar)[1] % 2 != 0:
      return n - scalar
  return scalar

def sign(d, k, msg):
  P, R = mul(d), mul(k)
  h = int.from_bytes(tagged_hash("BIP0340/challenge",
      R[0].to_bytes(32, "big") + P[0].to_bytes(32, "big") + msg), "big") % n
  s = (k + h * d) % n
  return R[0], s, h  # (R.x, s), and the public challenge h

# Alice's private key, and ONE nonce she reuses by mistake
d = normalize(0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF)
k = normalize(0x00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C63)

# Two different transactions, signed with the SAME k
Rx1, s1, h1 = sign(d, k, b"Alice pays Bob 1 BTC")
Rx2, s2, h2 = sign(d, k, b"Alice pays Bob 2 BTC")
assert Rx1 == Rx2  # reused nonce -> identical R.x

# TODO: recover d from (s1, s2, h1, h2) with one subtraction and one division mod n
d_rec = 0  # <-- your line here

assert d_rec == d
print("recovered:", hex(d_rec))
print("Success!")

Solution code

How to avoid it

The rule is short: never sign two messages with the same nonce.

You do not do this by hand. BIP340 derives kk deterministically from the private key and the message:

rand = tagged_hash("BIP0340/nonce", t || P || m), then k = int(rand) mod n. Here tt is the private key masked with auxiliary randomness.

Different message, different kk. A correct signer never repeats a nonce, even with a broken random number generator.

This is the same aux_rand derivation you implemented in the Schnorr lesson. Every P2TR spend your wallet makes goes through it.

Deterministic nonces are not enough for multisig

The BIP340 derivation is safe for a single signer. It is insecure for multi-party signing: cosigners who share an aggregate key can be tricked into states that reuse a nonce, and the same subtraction recovers a share of the key. Multisig protocols need nonces built to resist this. That is the problem MuSig2 solves, at the end of this module.

That's the last of the BIP340 primitives. You now have all four: tagged hashes, x-only public keys, Schnorr signatures, and nonces.

Next we start building Taproot on top of them, beginning with The Taproot Tweak.


Suggest Edits