mirror of
https://github.com/luxfi/corona.git
synced 2026-07-27 02:50:34 +00:00
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.
116 lines
3.2 KiB
Go
116 lines
3.2 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package threshold
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"encoding/gob"
|
|
"testing"
|
|
)
|
|
|
|
// FuzzVerifyParseSignature exercises Corona threshold.Verify on
|
|
// attacker-supplied (gob-encoded) signature bytes. Verify holds no
|
|
// long-term secret state, so this is the input-handling fuzz target:
|
|
// any panic / data-race / out-of-bounds in the parser is a finding.
|
|
//
|
|
// The corpus seeds are derived from a fresh honest signature. Mutated
|
|
// bytes are very unlikely to verify; the test only asserts NO PANIC,
|
|
// not Verify(...) = true.
|
|
func FuzzVerifyParseSignature(f *testing.F) {
|
|
// Seed corpus: one fresh valid signature.
|
|
shares, gk, err := GenerateKeysTrustedDealer(2, 3, rand.Reader)
|
|
if err != nil {
|
|
f.Fatalf("GenerateKeysTrustedDealer: %v", err)
|
|
}
|
|
signers := make([]*Signer, 3)
|
|
for i, share := range shares {
|
|
signers[i] = NewSigner(share)
|
|
}
|
|
signerIDs := []int{0, 1, 2}
|
|
const sid = 1
|
|
prfKey := make([]byte, 32)
|
|
if _, err := rand.Read(prfKey); err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
message := "fuzz verify seed message"
|
|
r1 := make(map[int]*Round1Data)
|
|
for _, s := range signers {
|
|
d, err := s.Round1(sid, prfKey, signerIDs)
|
|
if err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
r1[d.PartyID] = d
|
|
}
|
|
r2 := make(map[int]*Round2Data)
|
|
for _, s := range signers {
|
|
d, err := s.Round2(sid, message, prfKey, signerIDs, r1)
|
|
if err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
r2[d.PartyID] = d
|
|
}
|
|
sig, err := signers[0].Finalize(r2)
|
|
if err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
var buf bytes.Buffer
|
|
if err := gob.NewEncoder(&buf).Encode(sig); err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
f.Add(buf.Bytes())
|
|
// Also seed a known-invalid empty input.
|
|
f.Add([]byte{})
|
|
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
// Decode; any panic in the parser is a finding.
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Fatalf("Verify parse panic on %d bytes: %v", len(data), r)
|
|
}
|
|
}()
|
|
var sig Signature
|
|
if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&sig); err != nil {
|
|
// Decode failure is fine -- the parser rejected malformed
|
|
// input.
|
|
return
|
|
}
|
|
// Verify on the decoded signature must not panic regardless of
|
|
// whether it accepts or rejects.
|
|
_ = Verify(gk, message, &sig)
|
|
})
|
|
}
|
|
|
|
// FuzzVerifyRandomBytes is the simpler raw-bytes input fuzz: random
|
|
// bytes treated directly as a Corona signature wire encoding.
|
|
//
|
|
// Together with FuzzVerifyParseSignature, this exercises BOTH the
|
|
// structural (gob) and the byte-level interpretation paths.
|
|
func FuzzVerifyRandomBytes(f *testing.F) {
|
|
_, gk, err := GenerateKeysTrustedDealer(2, 3, rand.Reader)
|
|
if err != nil {
|
|
f.Fatalf("GenerateKeysTrustedDealer: %v", err)
|
|
}
|
|
|
|
f.Add([]byte{0, 0, 0, 0})
|
|
f.Add([]byte{0xff, 0xff, 0xff, 0xff})
|
|
f.Add(make([]byte, 32))
|
|
f.Add(make([]byte, 256))
|
|
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Fatalf("decode panic on %d bytes: %v", len(data), r)
|
|
}
|
|
}()
|
|
var sig Signature
|
|
_ = gob.NewDecoder(bytes.NewReader(data)).Decode(&sig)
|
|
// We do NOT verify here -- random bytes may decode partially
|
|
// and the verify pipeline expects more structure than the gob
|
|
// parser enforces. The property under test is that the parser
|
|
// does not panic.
|
|
_ = gk
|
|
})
|
|
}
|