Nonce Reuse
The Schnorr page said the nonce 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 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 .
Each signature needs a new . Reuse it across two signatures and the private key leaks.
Two signatures, one nonce
Recall the Schnorr signature from the previous page:
Here is the private key, the nonce, and the challenge hash tagged_hash("BIP0340/challenge", R || P || m) reduced mod (the order of the curve).
The nonce sets the nonce point . Same means the same . So two signatures made with one reused nonce carry the same in public.
Sign transaction 1 and transaction 2 with the same :
where and . Only the challenge hash differs. and are identical in both lines.
Recovering the private key
Subtract the two equations. The nonce cancels:
Divide to isolate :
Every value on the right is public. and sit in the two signatures. and anyone can recompute from , , and the two messages.
What does 'divide mod n' mean?
There is no fraction on the curve. Dividing by means multiplying by its modular inverse mod , the curve order:
The real world
This is not hypothetical.
- Android, August 2013. A flaw in
SecureRandommade wallets repeat nonces. Coins were stolen from real addresses. - Sony PlayStation 3. Sony shipped a fixed
kin 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 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 .
# 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 deterministically from the private key and the message:
rand = tagged_hash("BIP0340/nonce", t || P || m), then k = int(rand) mod n. Here is the private key masked with auxiliary randomness.
Different message, different . 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.
