mirror of
https://github.com/luxfi/corona.git
synced 2026-07-27 02:50:34 +00:00
threshold: make trusted-dealer keygen explicit, drop dead ReconstructSecret, pin sub-quorum soundness
Part of the Lux threshold-crypto security rip (killing trusted-dealer / full-key-reconstruction footguns). - Rename threshold.GenerateKeys -> threshold.GenerateKeysTrustedDealer. The function samples the full secret and sets sign.K / sign.Threshold in one process: it IS the trusted dealer. The new name mirrors keyera.BootstrapTrustedDealer so the trust model is explicit and greppable. Doc comment now states it is for test/KAT/CT/CLI/oracle use only, and that production chain keygen uses keyera.Bootstrap (dealerless Pedersen DKG). Stays exported; every caller updated (cli, ct/dudect, same-package tests) plus stale comment refs in cmd, keyera, wire, reshare. - Delete utils.ReconstructSecret. It Lagrange-interpolates the full secret from shares and had zero callers anywhere in the repo: a reconstruction-shaped footgun left dead. CompareSecrets and the section retained. - Include threshold/minority_soundness_test.go: an adversarial negative pinning that a strict sub-quorum cannot assemble a signature that verifies under the group key (uncancelled PRF mask fails the L2-norm gate). A sub-quorum forgery would be a catastrophic finality break.
This commit is contained in:
+1
-1
@@ -89,7 +89,7 @@ Examples:
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Generating %d-of-%d threshold key shares...\n", t, n)
|
||||
|
||||
shares, groupKey, err := threshold.GenerateKeys(t, n, rand.Reader)
|
||||
shares, groupKey, err := threshold.GenerateKeysTrustedDealer(t, n, rand.Reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate keys: %w", err)
|
||||
}
|
||||
|
||||
@@ -945,7 +945,7 @@ func emitSignVerify(outDir string) error {
|
||||
Description: "Full Corona Sign+Verify round-trip. For each (t,n,msg,seed): " +
|
||||
"Gen → SignRound1 (all parties) → SignRound2Preprocess+SignRound2 (all parties) → " +
|
||||
"SignFinalize → Verify. Note: corona/sign currently signs with K=Threshold=n " +
|
||||
"(see threshold.GenerateKeys). The n in this KAT is the total signer count; " +
|
||||
"(see threshold.GenerateKeysTrustedDealer). The n in this KAT is the total signer count; " +
|
||||
"the t is documented for the C++ port to validate threshold-aware sharing logic " +
|
||||
"once it is implemented downstream. SHA-256 hashes are used for large fields " +
|
||||
"(A, BTilde, sk shares, partial sigs) to keep file size finite while still " +
|
||||
|
||||
@@ -60,7 +60,7 @@ var (
|
||||
// populated R2 data map ready for Finalize.
|
||||
func makeCombineFixture(msg string) (*combineFixture, error) {
|
||||
const t, n = 2, 3
|
||||
shares, gk, err := threshold.GenerateKeys(t, n, rand.Reader)
|
||||
shares, gk, err := threshold.GenerateKeysTrustedDealer(t, n, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func signFresh() (*threshold.Signature, error) {
|
||||
// quorum for the dudect harness; the property under test is
|
||||
// content-dependent timing in Verify, independent of party count).
|
||||
const t, n = 2, 3
|
||||
shares, gk, err := threshold.GenerateKeys(t, n, rand.Reader)
|
||||
shares, gk, err := threshold.GenerateKeysTrustedDealer(t, n, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ func finishBootstrapPedersen(suite hash.HashSuite, suiteID string, t, n int, val
|
||||
}
|
||||
for j, v := range validators {
|
||||
// sShares[j] is in standard form; convert to NTT-Mont for the
|
||||
// signing-time multiplication path (matches threshold.GenerateKeys
|
||||
// signing-time multiplication path (matches threshold.GenerateKeysTrustedDealer
|
||||
// convention).
|
||||
skNTT := make(structs.Vector[ring.Poly], sign.N)
|
||||
for vi := 0; vi < sign.N; vi++ {
|
||||
|
||||
@@ -22,10 +22,10 @@ package reshare
|
||||
// RotateEpochKeys ReshareEpoch (or RotateEpochShares)
|
||||
// EpochKeys (struct) EpochShareState
|
||||
// RotateEpoch(newVals) RotateEpoch(newVals) calls Reshare(...)
|
||||
// → calls GenerateKeys instead of GenerateKeys
|
||||
// → calls GenerateKeysTrustedDealer instead of GenerateKeysTrustedDealer
|
||||
//
|
||||
// Concretely the existing `EpochManager.RotateEpoch` re-runs
|
||||
// `coronaThreshold.GenerateKeys(t, n, nil)` which generates a FRESH
|
||||
// `coronaThreshold.GenerateKeysTrustedDealer(t, n, nil)` which generates a FRESH
|
||||
// secret and a NEW group public key per epoch. With Reshare, the
|
||||
// master secret and group public key are PERSISTENT across epochs;
|
||||
// only the share distribution rotates. So:
|
||||
@@ -177,7 +177,7 @@ func (i *RefreshEpochInputs) BuildTranscript() TranscriptInputs {
|
||||
// name but change the body). Caller code in quasar.go and the
|
||||
// `validator-rotation` consensus path need updates.
|
||||
//
|
||||
// [2] Replace `coronaThreshold.GenerateKeys` call with a Reshare
|
||||
// [2] Replace `coronaThreshold.GenerateKeysTrustedDealer` call with a Reshare
|
||||
// invocation: extract OldShares from current EpochShareState,
|
||||
// feed into Reshare(r, oldShares, tOld, newSet, tNew, randSource).
|
||||
//
|
||||
@@ -220,7 +220,7 @@ func (i *RefreshEpochInputs) BuildTranscript() TranscriptInputs {
|
||||
// equivocation) is forwarded to the slashing module. The
|
||||
// slasher reverifies on-chain and emits the slashing tx.
|
||||
//
|
||||
// [12] Backwards compatibility: `EpochManager.GenerateKeys` is
|
||||
// [12] Backwards compatibility: `EpochManager.GenerateKeysTrustedDealer` is
|
||||
// KEPT as the genesis path (one-time bootstrap). Subsequent
|
||||
// epoch rotations go through ReshareEpoch only.
|
||||
//
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
// BenchmarkPulsarSign measures the wall-clock cost of the 2-round
|
||||
// Pulsar threshold protocol's *online* phase (Round1 + Round2 +
|
||||
// Finalize, given a fresh GenerateKeys epoch). The IEEE S&P 2025
|
||||
// Finalize, given a fresh GenerateKeysTrustedDealer epoch). The IEEE S&P 2025
|
||||
// Pulsar evaluation calls out a 0.6 s online phase across 5
|
||||
// continents at the production shape; this bench gives the local
|
||||
// upper bound (network RTT is excluded; the cost here is pure CPU /
|
||||
@@ -62,7 +62,7 @@ func benchPulsarSign(b *testing.B, n, thr int, gpuOn bool) {
|
||||
}
|
||||
b.Cleanup(cgpu.DisableAccelerator)
|
||||
|
||||
shares, _, err := GenerateKeys(thr, n, rand.Reader)
|
||||
shares, _, err := GenerateKeysTrustedDealer(thr, n, rand.Reader)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestE2EThresholdVariants(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run("", func(t *testing.T) {
|
||||
shares, gk, err := GenerateKeys(tc.t, tc.n, rand.Reader)
|
||||
shares, gk, err := GenerateKeysTrustedDealer(tc.t, tc.n, rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("(%d,%d) GenerateKeys: %v", tc.t, tc.n, err)
|
||||
}
|
||||
@@ -153,7 +153,7 @@ func TestE2EKATReplayDeterminism(t *testing.T) {
|
||||
return b
|
||||
}
|
||||
|
||||
shares, _, err := GenerateKeys(2, 3, rand.Reader)
|
||||
shares, _, err := GenerateKeysTrustedDealer(2, 3, rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys: %v", err)
|
||||
}
|
||||
|
||||
@@ -182,9 +182,9 @@ var (
|
||||
|
||||
func mustKernelCeremony(tb testing.TB) {
|
||||
kernelOnce.Do(func() {
|
||||
shares, gk, err := GenerateKeys(2, 3, rand.Reader)
|
||||
shares, gk, err := GenerateKeysTrustedDealer(2, 3, rand.Reader)
|
||||
if err != nil {
|
||||
tb.Fatalf("GenerateKeys: %v", err)
|
||||
tb.Fatalf("GenerateKeysTrustedDealer: %v", err)
|
||||
}
|
||||
kShares = shares
|
||||
kGroupKey = gk
|
||||
|
||||
@@ -20,9 +20,9 @@ import (
|
||||
// not Verify(...) = true.
|
||||
func FuzzVerifyParseSignature(f *testing.F) {
|
||||
// Seed corpus: one fresh valid signature.
|
||||
shares, gk, err := GenerateKeys(2, 3, rand.Reader)
|
||||
shares, gk, err := GenerateKeysTrustedDealer(2, 3, rand.Reader)
|
||||
if err != nil {
|
||||
f.Fatalf("GenerateKeys: %v", err)
|
||||
f.Fatalf("GenerateKeysTrustedDealer: %v", err)
|
||||
}
|
||||
signers := make([]*Signer, 3)
|
||||
for i, share := range shares {
|
||||
@@ -88,9 +88,9 @@ func FuzzVerifyParseSignature(f *testing.F) {
|
||||
// Together with FuzzVerifyParseSignature, this exercises BOTH the
|
||||
// structural (gob) and the byte-level interpretation paths.
|
||||
func FuzzVerifyRandomBytes(f *testing.F) {
|
||||
_, gk, err := GenerateKeys(2, 3, rand.Reader)
|
||||
_, gk, err := GenerateKeysTrustedDealer(2, 3, rand.Reader)
|
||||
if err != nil {
|
||||
f.Fatalf("GenerateKeys: %v", err)
|
||||
f.Fatalf("GenerateKeysTrustedDealer: %v", err)
|
||||
}
|
||||
|
||||
f.Add([]byte{0, 0, 0, 0})
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package threshold
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestThresholdSubQuorumCannotForge pins the permissionless threshold-
|
||||
// soundness invariant: a strict sub-quorum of the committee (a minority
|
||||
// that omits at least one signer) CANNOT assemble a signature that
|
||||
// verifies under the group public key.
|
||||
//
|
||||
// Why this holds at runtime: each party's Round-2 partial carries an
|
||||
// additive PRF mask term (mask / maskPrime in sign.SignRound2). Those
|
||||
// masks only telescope to zero when the partials are summed over the
|
||||
// FULL signing set T. Drop any signer and the aggregate z retains an
|
||||
// uncancelled, high-norm mask, so the L2-norm gate in sign.Verify
|
||||
// (CheckL2Norm against Bsquare) rejects it. This is the runtime
|
||||
// counterpart to the (t,n)-Shamir secrecy argument: fewer than the full
|
||||
// quorum can neither interpolate s nor emit a low-norm signature.
|
||||
//
|
||||
// If a sub-quorum signature EVER verified, that would be a catastrophic
|
||||
// forgery break (a minority producing valid finality certs), so this
|
||||
// test is an adversarial negative, not a smoke test.
|
||||
func TestThresholdSubQuorumCannotForge(t *testing.T) {
|
||||
const tt, n = 3, 5
|
||||
shares, gk, err := GenerateKeysTrustedDealer(tt, n, rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeysTrustedDealer(%d,%d): %v", tt, n, err)
|
||||
}
|
||||
|
||||
signers := make([]*Signer, n)
|
||||
for i, share := range shares {
|
||||
signers[i] = NewSigner(share)
|
||||
}
|
||||
signerIDs := make([]int, n)
|
||||
for i := range signerIDs {
|
||||
signerIDs[i] = i
|
||||
}
|
||||
|
||||
const sid = 1
|
||||
prfKey := make([]byte, 32)
|
||||
if _, err := rand.Read(prfKey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
message := "corona minority-soundness test"
|
||||
|
||||
// Round 1 — every committee member participates so masks are set up
|
||||
// over the full set T (the honest protocol).
|
||||
r1 := make(map[int]*Round1Data, n)
|
||||
for _, s := range signers {
|
||||
d, err := s.Round1(sid, prfKey, signerIDs)
|
||||
if err != nil {
|
||||
t.Fatalf("Round1 party %d: %v", s.share.Index, err)
|
||||
}
|
||||
r1[d.PartyID] = d
|
||||
}
|
||||
|
||||
// Round 2 — every committee member emits its z partial.
|
||||
r2 := make(map[int]*Round2Data, n)
|
||||
for _, s := range signers {
|
||||
d, err := s.Round2(sid, message, prfKey, signerIDs, r1)
|
||||
if err != nil {
|
||||
t.Fatalf("Round2 party %d: %v", s.share.Index, err)
|
||||
}
|
||||
r2[d.PartyID] = d
|
||||
}
|
||||
|
||||
// Positive control: the FULL quorum produces a verifying signature.
|
||||
fullSig, err := signers[0].Finalize(r2)
|
||||
if err != nil {
|
||||
t.Fatalf("Finalize (full quorum): %v", err)
|
||||
}
|
||||
if !Verify(gk, message, fullSig) {
|
||||
t.Fatal("full-quorum honest signature was rejected — test setup is wrong")
|
||||
}
|
||||
|
||||
// Adversarial negative: a minority of size t-1 = 2 (parties {0,1})
|
||||
// attempts to finalize using only its own partials. The aggregate z
|
||||
// is missing n-(t-1)=3 mask-cancelling terms.
|
||||
minority := map[int]*Round2Data{
|
||||
0: r2[0],
|
||||
1: r2[1],
|
||||
}
|
||||
if len(minority) >= n {
|
||||
t.Fatalf("minority set not a strict subset: |minority|=%d n=%d", len(minority), n)
|
||||
}
|
||||
forged, err := signers[0].Finalize(minority)
|
||||
if err != nil {
|
||||
// A hard error here is also an acceptable rejection of the minority
|
||||
// attempt; the invariant (no valid sub-quorum signature) still holds.
|
||||
return
|
||||
}
|
||||
if Verify(gk, message, forged) {
|
||||
t.Fatalf("CRITICAL: a %d-of-%d sub-quorum produced a signature that "+
|
||||
"VERIFIED under the group key — threshold soundness is broken "+
|
||||
"(minority forgery)", len(minority), n)
|
||||
}
|
||||
}
|
||||
+11
-3
@@ -111,9 +111,17 @@ type Signature struct {
|
||||
Delta structs.Vector[ring.Poly]
|
||||
}
|
||||
|
||||
// GenerateKeys generates threshold key shares for n parties with threshold t.
|
||||
// This runs once per epoch when the validator set changes.
|
||||
func GenerateKeys(t, n int, randSource io.Reader) ([]*KeyShare, *GroupKey, error) {
|
||||
// GenerateKeysTrustedDealer is the trusted-dealer, in-process threshold keygen.
|
||||
// It sets the global sign.K / sign.Threshold and samples the full secret in a
|
||||
// single process, then derives the n KeyShares for threshold t. Because one
|
||||
// party materializes the whole secret, this is a FOOTGUN for production: use it
|
||||
// ONLY for tests / KAT / constant-time harnesses / CLI / the reference oracle.
|
||||
//
|
||||
// Production chain keygen MUST use keyera.Bootstrap (the dealerless Pedersen
|
||||
// DKG), where no single party ever holds the secret. The TrustedDealer suffix
|
||||
// mirrors keyera.BootstrapTrustedDealer to keep the trust model explicit and
|
||||
// greppable.
|
||||
func GenerateKeysTrustedDealer(t, n int, randSource io.Reader) ([]*KeyShare, *GroupKey, error) {
|
||||
if n < 2 {
|
||||
return nil, nil, ErrInvalidPartyCount
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// deterministicReader is a SHA-256 keystream over a 32-byte seed,
|
||||
// suitable as an io.Reader for GenerateKeys. Two calls with the same
|
||||
// suitable as an io.Reader for GenerateKeysTrustedDealer. Two calls with the same
|
||||
// seed produce the same bytes, which fixes the trustedDealerKey across
|
||||
// the CPU and GPU legs of this test.
|
||||
type deterministicReader struct {
|
||||
@@ -88,9 +88,9 @@ func TestThresholdSign_CPU_vs_GPU_ByteIdentical(t *testing.T) {
|
||||
} else {
|
||||
cgpu.DisableAccelerator()
|
||||
}
|
||||
shares, groupKey, err := GenerateKeys(thr, k, newDeterministicReader(seed))
|
||||
shares, groupKey, err := GenerateKeysTrustedDealer(thr, k, newDeterministicReader(seed))
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys (gpu=%v): %v", gpuOn, err)
|
||||
t.Fatalf("GenerateKeysTrustedDealer (gpu=%v): %v", gpuOn, err)
|
||||
}
|
||||
|
||||
signers := make([]*Signer, k)
|
||||
|
||||
+12
-12
@@ -7,10 +7,10 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerateKeys(t *testing.T) {
|
||||
shares, groupKey, err := GenerateKeys(2, 3, nil)
|
||||
func TestGenerateKeysTrustedDealer(t *testing.T) {
|
||||
shares, groupKey, err := GenerateKeysTrustedDealer(2, 3, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys failed: %v", err)
|
||||
t.Fatalf("GenerateKeysTrustedDealer failed: %v", err)
|
||||
}
|
||||
|
||||
if len(shares) != 3 {
|
||||
@@ -38,9 +38,9 @@ func TestGenerateKeys(t *testing.T) {
|
||||
|
||||
func TestThresholdSigningFlow(t *testing.T) {
|
||||
// Generate 2-of-3 threshold keys
|
||||
shares, groupKey, err := GenerateKeys(2, 3, nil)
|
||||
shares, groupKey, err := GenerateKeysTrustedDealer(2, 3, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys failed: %v", err)
|
||||
t.Fatalf("GenerateKeysTrustedDealer failed: %v", err)
|
||||
}
|
||||
|
||||
// Create signers for all parties
|
||||
@@ -93,9 +93,9 @@ func TestThresholdSigningFlow(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestThresholdWrongMessage(t *testing.T) {
|
||||
shares, groupKey, err := GenerateKeys(2, 3, nil)
|
||||
shares, groupKey, err := GenerateKeysTrustedDealer(2, 3, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys failed: %v", err)
|
||||
t.Fatalf("GenerateKeysTrustedDealer failed: %v", err)
|
||||
}
|
||||
|
||||
signers := make([]*Signer, 3)
|
||||
@@ -140,7 +140,7 @@ func TestThresholdWrongMessage(t *testing.T) {
|
||||
// rejects them with ErrDuplicateSigner instead of silently overwriting the
|
||||
// duplicated z share (which would double-count one signer).
|
||||
func TestFinalize_DuplicatePartyID_Rejected(t *testing.T) {
|
||||
shares, _, err := GenerateKeys(2, 3, nil)
|
||||
shares, _, err := GenerateKeysTrustedDealer(2, 3, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys failed: %v", err)
|
||||
}
|
||||
@@ -190,7 +190,7 @@ func TestFinalize_DuplicatePartyID_Rejected(t *testing.T) {
|
||||
// for the Round 2 combine, where a repeated PartyID would overwrite the D
|
||||
// matrix / MAC entry collected for that party.
|
||||
func TestRound2_DuplicatePartyID_Rejected(t *testing.T) {
|
||||
shares, _, err := GenerateKeys(2, 3, nil)
|
||||
shares, _, err := GenerateKeysTrustedDealer(2, 3, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys failed: %v", err)
|
||||
}
|
||||
@@ -231,19 +231,19 @@ func TestRound2_DuplicatePartyID_Rejected(t *testing.T) {
|
||||
|
||||
func TestInvalidThreshold(t *testing.T) {
|
||||
// Threshold >= total
|
||||
_, _, err := GenerateKeys(3, 3, nil)
|
||||
_, _, err := GenerateKeysTrustedDealer(3, 3, nil)
|
||||
if err != ErrInvalidThreshold {
|
||||
t.Errorf("expected ErrInvalidThreshold, got %v", err)
|
||||
}
|
||||
|
||||
// Threshold = 0
|
||||
_, _, err = GenerateKeys(0, 3, nil)
|
||||
_, _, err = GenerateKeysTrustedDealer(0, 3, nil)
|
||||
if err != ErrInvalidThreshold {
|
||||
t.Errorf("expected ErrInvalidThreshold, got %v", err)
|
||||
}
|
||||
|
||||
// Too few parties
|
||||
_, _, err = GenerateKeys(1, 1, nil)
|
||||
_, _, err = GenerateKeysTrustedDealer(1, 1, nil)
|
||||
if err != ErrInvalidPartyCount {
|
||||
t.Errorf("expected ErrInvalidPartyCount, got %v", err)
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
// (Sign1/Sign2/Combine are the only public entry points).
|
||||
func freshSig(t testing.TB, message string) (*GroupKey, *Signature) {
|
||||
t.Helper()
|
||||
shares, groupKey, err := GenerateKeys(2, 3, nil)
|
||||
shares, groupKey, err := GenerateKeysTrustedDealer(2, 3, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys: %v", err)
|
||||
t.Fatalf("GenerateKeysTrustedDealer: %v", err)
|
||||
}
|
||||
signers := make([]*Signer, 3)
|
||||
for i, share := range shares {
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@ func (s *Signature) UnmarshalBinary(b []byte) error {
|
||||
// MarshalBinary serializes the public GroupKey to a canonical wire
|
||||
// form. Only the public matrix A and rounded public key BTilde are
|
||||
// emitted; Params is reconstructed on the verifier side from a fresh
|
||||
// NewParams() call (matches the GenerateKeys / Bootstrap conventions).
|
||||
// NewParams() call (matches the GenerateKeysTrustedDealer / Bootstrap conventions).
|
||||
//
|
||||
// Layout:
|
||||
//
|
||||
|
||||
@@ -15,15 +15,15 @@ import (
|
||||
// parsed group key over the same message. Byte-equal serialization is
|
||||
// enforced.
|
||||
//
|
||||
// NOTE: cannot t.Parallel — GenerateKeys mutates sign.K / sign.Threshold
|
||||
// package globals (see threshold.go:123-124). The wire codec itself is
|
||||
// NOTE: cannot t.Parallel — GenerateKeysTrustedDealer mutates sign.K / sign.Threshold
|
||||
// package globals (see threshold.go:131-132). The wire codec itself is
|
||||
// pure; this is a kernel-side limitation we accept rather than refactor
|
||||
// out from under sibling agents who own pulsar/.
|
||||
func TestSignatureWireRoundtrip(t *testing.T) {
|
||||
const tThr, n = 1, 2
|
||||
shares, gk, err := GenerateKeys(tThr, n, rand.Reader)
|
||||
shares, gk, err := GenerateKeysTrustedDealer(tThr, n, rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys: %v", err)
|
||||
t.Fatalf("GenerateKeysTrustedDealer: %v", err)
|
||||
}
|
||||
|
||||
// Two-party signing (smallest committee that exercises the protocol).
|
||||
@@ -224,9 +224,9 @@ func TestGroupKeyWireRejectMalformed(t *testing.T) {
|
||||
// GroupKey-magic frame in the Signature slot and vice versa — domain
|
||||
// separation must be effective.
|
||||
func TestVerifyBytesRejectsCrossWire(t *testing.T) {
|
||||
shares, gk, err := GenerateKeys(1, 2, rand.Reader)
|
||||
shares, gk, err := GenerateKeysTrustedDealer(1, 2, rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys: %v", err)
|
||||
t.Fatalf("GenerateKeysTrustedDealer: %v", err)
|
||||
}
|
||||
_ = shares
|
||||
gkBytes, err := gk.MarshalBinary()
|
||||
|
||||
@@ -352,22 +352,6 @@ func FormatBigIntSlice(slice []*big.Int) string {
|
||||
|
||||
// UTIL FUNCTIONS FOR LAGRANGE INTERPOLATION TESTING
|
||||
|
||||
// ReconstructSecret reconstructs the secret using the shares and Lagrange coefficients.
|
||||
func ReconstructSecret(r *ring.Ring, shares map[int]structs.Vector[ring.Poly], lagrangeCoeffs map[int]ring.Poly) structs.Vector[ring.Poly] {
|
||||
reconstructed := make(structs.Vector[ring.Poly], len(shares[0]))
|
||||
|
||||
for i := range reconstructed {
|
||||
reconstructed[i] = r.NewPoly()
|
||||
for partyID, share := range shares {
|
||||
temp := r.NewPoly()
|
||||
MulPolyNTT(r, share[i], lagrangeCoeffs[partyID], temp)
|
||||
r.Add(reconstructed[i], temp, reconstructed[i])
|
||||
}
|
||||
}
|
||||
|
||||
return reconstructed
|
||||
}
|
||||
|
||||
// CompareSecrets compares the reconstructed secret with the original.
|
||||
func CompareSecrets(r *ring.Ring, reconstructed, original structs.Vector[ring.Poly]) bool {
|
||||
for i := range reconstructed {
|
||||
|
||||
Reference in New Issue
Block a user