nonce-fn-defense-in-depth-inputs
Why this note exists
Triggered by a review comment on bitcoin/bips#2070 suggesting XonlyPk instead of PlainPk for thresh_pk inside nonce_gen. The interesting part isn't the choice itself — it's the framing that makes the choice obvious.
The underlying concept: hedged (synthetic) nonces
Schnorr nonce must be unpredictable and never reused across different messages with the same key — reuse leaks the secret. Three strategies:
- Pure random: . Catastrophic if RNG fails.
- Pure deterministic (RFC 6979): . Catastrophic under fault attacks; and in multi-party DL signatures (MuSig/FROST) it enables key-recovery attacks by a malicious co-signer who replays the session with a different message — this is why the BIP explicitly forbids fully deterministic nonces.
- Hedged/synthetic (BIP340, BIP327, Ed25519-hedged): . Secure if either
randis good or the deterministic inputs uniquely bind the session. This is whatnonce_gendoes.
Core references worth reading:
- BIP340 Default Signing — the synthetic-nonce construction, and the rationale note explaining why
aux_randis XORed intosk(link) - BIP327 NonceGen — MuSig2's version, essentially what we copied (link)
- Aranha, Novaes, Takahashi, Yarom, Tibouchi — "LadderLeak" / "Hedged Public-Key Signatures in the Standard Model" (PKC 2020, eprint 2019/956) — the formal model for why hedging gives you "best of both worlds."
- RFC 6979 — the deterministic baseline you're hedging against.
- NIST SP 800-186 / FIPS 186-5 §6.3 — nonce requirements for ECDSA/EdDSA.
Choosing inputs to the hash
The question "what goes in the hash" separates cleanly into two layers.
Layer 1 — what's needed for security when rand' is uniform.
Answer: nothing else. A uniform 32-byte rand' hashed through SHA256 already gives a uniform 32-byte nonce. Every other input is redundant in this case.
Layer 2 — what's needed for security when rand' fails (repeats, low entropy, biased, or attacker-controlled).
Then the hash inputs must, collectively, uniquely identify the signing session so that two distinct sessions can never collide on the hash input. For a signer that means binding:
- its own secret share (so different keys → different ),
- the message (so message-substitution attacks can't reuse ),
- the group context (threshold pubkey / other signers' shares) — so a malicious peer can't trick you into reusing across different groups,
- anything else session-specific (
extra_in).
This is the "defense-in-depth" framing in the BIP. The guiding principle is domain separation / session binding: the hash input should be a canonical, injective encoding of the session. Length prefixes (len(x) ‖ x) are exactly for this — they prevent ambiguous concatenation ("ab" ‖ "c" vs "a" ‖ "bc").
XonlyPk vs PlainPk for thresh_pk
Given the framing above, here's the actual tradeoff:
- When
rand'is good: identical. Both choices produce uniform output; the 1-bit y-parity difference is invisible after SHA256. - When
rand'fails: still effectively identical. What matters is thatthresh_pkuniquely identifies this group. Both XonlyPk (32B) and PlainPk (33B) do that — the y-parity is a deterministic function of x for a fixed curve point, so it adds zero real entropy about "which group." - Collision resistance: SHA256 gives you ~128-bit collision resistance regardless. 1 extra bit of input is meaningless.
- Consistency: BIP340 and BIP327 both use the x-only form of the (aggregate) pubkey in their nonce hashes. A FROST signer ultimately produces a BIP340 signature under the x-only threshold pubkey, so XonlyPk is the canonical identity of the group from the verifier's perspective. Using the same representation in
nonce_genmeans there's one canonical "group ID" throughout the spec. - PlainPk being slightly beneficial: technically true (1 extra bit, no canonicalization step), but the bit is cryptographically worthless and the canonicalization is trivial.
So the reviewer's argument is essentially: since security doesn't depend on this choice, pick the one that matches the rest of the Bitcoin/BIP340/BIP327 ecosystem. That's a reasonable "principle of least surprise" call. It "doesn't matter too much" — and that's precisely why consistency wins the tiebreak.
The meta-principle
A useful way to think about these design choices: security-critical inputs (here: rand', and secshare as the hedge) must be chosen for cryptographic reasons; defense-in-depth inputs (here: pubshare, thresh_pk, m, extra_in) should be chosen for engineering reasons — canonicality, consistency with neighboring specs, ease of auditing, absence of ambiguity. Debating the cryptographic merit of XonlyPk vs PlainPk is a category error: neither is "more secure," so you decide it on engineering grounds, and ecosystem consistency is the strongest engineering ground available.