Add two-group signing with group-tagged binding factors - #8
Add two-group signing with group-tagged binding factors#8keenan-lightspark wants to merge 1 commit into
Conversation
Generalizes nested signing to a second participant group that is itself a t-of-n FROST group with its own identifier space. Collections are keyed by (group, identifier) and the binding-factor preimage is domain-separated by a one-byte group tag (0x00/0x01), in both the per-signer preimage and each commitment-list entry, so identifiers that collide numerically across groups never alias. Group commitment, challenge, share computation/verification, and even-Y handling reuse the Ciphersuite hooks unchanged; existing entry points are untouched. Aggregation carries verifying shares for the primary group only: share-level blame is assigned for primary signers, while a failure that lies with the secondary group (whose verifying shares are not available) is reported as InvalidSignature without blame. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Greptile SummaryAdds an explicit two-group FROST signing scheme with group-tagged binding factors.
Confidence Score: 4/5The PR appears safe to merge, with a non-blocking test-coverage gap around deterministic exercise of both aggregate-nonce parities. The implementation consistently hashes and aggregates both tagged groups, while the only accepted concern is that one randomized test can pass without covering both parity branches it claims to exercise. Files Needing Attention: frost-secp256k1-tr/tests/two_group_tests.rs
|
| Filename | Overview |
|---|---|
| frost-core/src/two_group.rs | Implements tagged two-group packages, binding factors, per-group interpolation, signing, aggregation, and primary-share verification. |
| frost-secp256k1-tr/tests/two_group_tests.rs | Adds broad two-group coverage, but its randomized loop does not guarantee the claimed coverage of both aggregate-nonce parities. |
| frost-core/src/lib.rs | Exposes the new two_group module through the public frost-core API. |
Sequence Diagram
sequenceDiagram
participant P as Primary signers
participant S as Secondary signers
participant A as Aggregator
P->>A: Tagged commitments (0x00, id)
S->>A: Tagged commitments (0x01, id)
A-->>P: TwoGroupSigningPackage
A-->>S: TwoGroupSigningPackage
P->>P: Compute tagged binding factor and primary-group Lagrange coefficient
S->>S: Compute tagged binding factor and secondary-group Lagrange coefficient
P->>A: Primary signature shares
S->>A: Secondary signature shares
A->>A: Sum all shares and verify combined signature
alt Aggregate invalid
A->>A: Verify primary shares for blame
end
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
frost-secp256k1-tr/tests/two_group_tests.rs:121
**Parity coverage is nondeterministic**
The fixed random loop never records the aggregate commitment parity, so all eight iterations can exercise the same branch while the test still passes, leaving a parity-specific regression undetected.
Reviews (1): Last reviewed commit: "Add two-group signing with group-tagged ..." | Re-trigger Greptile
| for _ in 0..8 { | ||
| let groups = make_groups((5, 3), (3, 2), None, None)?; | ||
| let message = b"two-group message"; | ||
|
|
There was a problem hiding this comment.
Parity coverage is nondeterministic
The fixed random loop never records the aggregate commitment parity, so all eight iterations can exercise the same branch while the test still passes, leaving a parity-specific regression undetected.
Prompt To Fix With AI
This is a comment left during a code review.
Path: frost-secp256k1-tr/tests/two_group_tests.rs
Line: 121
Comment:
**Parity coverage is nondeterministic**
The fixed random loop never records the aggregate commitment parity, so all eight iterations can exercise the same branch while the test still passes, leaving a parity-specific regression undetected.
How can I resolve this? If you propose a fix, please make it concise.
What
Adds
frost_core::two_group: FROST signing where the second participant group (Spark: the user side) is itself a t-of-n FROST group with its own identifier space, per the settled F2 design (SP-2884).(group, identifier); sub-user positions may numerically collide with operator identifiers and never alias.0x00primary /0x01secondary), present both in each signer's preimage and in every commitment-list entry hashed by H5:aggregateonly sums shares.Ciphersuitehooks unchanged — even-Y parity is decided by the single R over both groups' commitments.InvalidSignatureShare); a failure attributable to the secondary group (no verifying material on the wire by design) is reported asInvalidSignaturewithout blame.Tests
frost-secp256k1-tr/tests/two_group_tests.rs:InvalidSignaturewithout blametweak()at signing so the taptweak scalar enters the sum exactly once)Note:
serialization_tests::check_signing_package_postcard_serializationfails onmainbefore this PR (stale upstream snapshot vs the fork's addedSigningPackagefields); not addressed here.