frost: document deterministic_sign's nonce derivation domain
BIP 445's det_nonce_hash commits to the secret share, my_id, u, the
sorted ids, the aggothernonce, the x-only tweaked threshold public key
and the message. It does not commit to the pubshares, to the untweaked
threshold public key, or to the accumulated tweaks. Because Q and -Q
share an x-coordinate, two tweak caches can agree on everything the
derivation hashes and still disagree on the sign g*gacc that multiplies
the secret share -- a cache initialized from thresh_pk and one
initialized from its negation being the smallest example.
Two calls differing only in that emit the same pubnonce and partial
signatures s = k + e*lambda*d and s' = k - e*lambda*d over the identical
k = k1 + b*k2, so subtracting them yields the secret share. Demonstrated
on a sole signer (u = 1, ids = {0}, pubshares = NULL) with thresh_sk =
0x11.. and msg = 0x42..:
tweaked pk (cache A) 4f355bdc...075871aa
tweaked pk (cache B) 4f355bdc...075871aa same x-only key
pubnonce A == pubnonce B nonce reused
sA - sB 0d7d9c4e...aa748ffa
-2*e*d 0d7d9c4e...aa748ffa d recovered
Nothing inside a single call can catch this. The self-verification that
Sign performs passes in both cases, because each partial signature is
individually valid under the cache it was produced with;
validate_session_params likewise only ties the pubshares to the cache's
own Q0, which both caches satisfy by construction. Note also that
pubshares is optional, so there need not be a second value to disagree
with.
No code change: this is the specified derivation, and committing to Q0
or to gacc here would diverge from BIP 445 and invalidate the
det_sign test vectors. The obligation is the caller's, so state it where
the caller will meet it -- in the function's own documentation and
alongside the existing secnonce and session_secrand32 rules in frost.md.
The rule is that the tweak cache and the pubshares are fixed key
material settled at key generation, never per-session parameters taken
from a coordinator or a peer; under that discipline a repeated call is
byte-identical and harmless, which is the point of a deterministic
nonce.
Worth raising against the BIP: the spec could close this by hashing the
untweaked threshold public key, at the cost of new test vectors.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -96,6 +96,29 @@ Security notes
|
||||
unique for every call to `secp256k1_frost_nonce_gen`. Passing the secret
|
||||
share to `nonce_gen` is recommended as defense-in-depth against bad
|
||||
randomness.
|
||||
- `secp256k1_frost_deterministic_sign` has no `session_secrand32` to keep
|
||||
fresh; its safety rests instead on what the nonce derivation commits to. Per
|
||||
BIP 445's `det_nonce_hash` that is the secret share, `my_id`, `u`, the sorted
|
||||
ids, the aggothernonce, the **x-only** tweaked threshold public key, and the
|
||||
message — and nothing else. In particular it does not commit to the
|
||||
pubshares, to the untweaked threshold public key, or to the accumulated
|
||||
tweaks. Since `Q` and `-Q` share an x-coordinate, a tweak cache initialized
|
||||
from the threshold public key and one initialized from its negation present
|
||||
the same x-only key to the derivation while disagreeing on the sign `g*gacc`
|
||||
that multiplies the secret share. Two calls differing only in that produce
|
||||
the same pubnonce and partial signatures `s = k + e*lambda*d` and
|
||||
`s' = k - e*lambda*d`, where `k` is the identical `k1 + b*k2`; subtracting
|
||||
them yields `d` directly. The same holds for any two caches that agree on
|
||||
the tweaked x-only key but not on `g*gacc`.
|
||||
|
||||
This is a property of the specified derivation, not of this implementation,
|
||||
and it is not detectable from inside a single call: the self-verification in
|
||||
`Sign` passes in both cases, because each signature is individually valid
|
||||
under its own cache. The caller carries the obligation. Treat the tweak
|
||||
cache and the pubshares as fixed key material established once at key
|
||||
generation, and never accept either as a per-session parameter from the
|
||||
coordinator or another peer. Under that discipline a repeated call is
|
||||
byte-identical and harmless, which is what the deterministic nonce is for.
|
||||
- Final signatures produced by `secp256k1_frost_partial_sig_agg` are ordinary
|
||||
BIP340 signatures; they are verified with `secp256k1_schnorrsig_verify`
|
||||
against the (tweaked) x-only threshold public key.
|
||||
|
||||
Reference in New Issue
Block a user