mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
pulsar v1.2.0 removes the small-committee threshold-signer surface (NewThresholdSigner / ThresholdSigner / Round1Message / Round2Message / Combine) in favour of the TALUS no-reconstruct BCC/CEF path. The Pulsar leg of a Quasar cert verifies under the UNMODIFIED FIPS-204 verifier, so a single-key signature under the group key is valid leg evidence — no DKG, no ceremony is needed to GENERATE valid leg bytes for a cert-composition test. Replace signPulsarThresholdLeg (deleted threshold ceremony) with signPulsarLegStock: GenerateKey + Sign via threshold/protocols/pulsar's stock single-key path, returning the same *pulsar.Signature / *pulsar.PublicKey alias types that ComposePolaris and pulsarGroupPK.MarshalBinary already consume. The negative tests (removed / tampered / missing-key Pulsar leg) still reject, so the leg bytes remain load-bearing, not a stub. Corona keygen (threshold/protocols/corona.GenerateKeys) is unchanged — the trusted-dealer fixture name survives in the threshold corona adapter, so no rename was needed in these consensus tests. Production build unaffected (0 non-test refs to the removed APIs). go test ./protocol/quasar/... ./protocol/auth/... ./config/... green; go vet ./protocol/quasar/ clean.
320 lines
12 KiB
Go
320 lines
12 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
||
// See the file LICENSE for licensing terms.
|
||
|
||
package quasar
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/rand"
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/luxfi/crypto/bls"
|
||
"github.com/luxfi/crypto/mldsa"
|
||
magnetar "github.com/luxfi/magnetar/ref/go/pkg/magnetar"
|
||
coronaThreshold "github.com/luxfi/threshold/protocols/corona"
|
||
"github.com/luxfi/threshold/protocols/pulsar"
|
||
)
|
||
|
||
// TestQuasarCert_ComposesAllThreeSchemes is the gate test the audit
|
||
// task pins. It builds a Polaris-profile QuasarCert that carries all
|
||
// three production PQ schemes (Pulsar + Corona + Magnetar) and
|
||
// verifies that:
|
||
//
|
||
// 1. Each leg's bytes are produced by the leg's real signing primitive
|
||
// (no stubs, no zero-fill).
|
||
// 2. The cert round-trips through MarshalBinary / UnmarshalBinary
|
||
// without loss.
|
||
// 3. The post-decode cert reports IsPolaris() == true.
|
||
// 4. Cryptographic verification through VerifyWithRealKeysPolaris
|
||
// accepts the cert under the right keys and refuses under wrong
|
||
// keys.
|
||
//
|
||
// This is the load-bearing assertion that the headline "double-lattice
|
||
// + hash-based" PQ defense-in-depth (papers §04) is REAL, not
|
||
// aspirational — every leg has a working code path that the cert
|
||
// composer exercises end-to-end.
|
||
func TestQuasarCert_ComposesAllThreeSchemes(t *testing.T) {
|
||
// SLH-DSA-via-magnetar in the leg-3 path is unavoidably ~30s under
|
||
// `-race` (NIST FIPS 205 hash-tree depth × goroutine instrumentation
|
||
// overhead). Gate the heavy compose under -short so the full quasar
|
||
// suite stays under the 10m -race timeout. See SUMMARY.md for the
|
||
// race-hot-path bottleneck investigation that pinned this.
|
||
if testing.Short() {
|
||
t.Skip("skipping SLH-DSA-bound compose test under -short")
|
||
}
|
||
|
||
// One round digest signed by every leg. Bound out-of-band by the
|
||
// QBlock TranscriptHash pattern in production; here we use a raw
|
||
// fixed digest for the round-trip exercise.
|
||
digest := []byte("polaris-compose-all-three-schemes-test-digest")
|
||
|
||
// =========================================================
|
||
// Leg 1: Pulsar — Module-LWE, FIPS-204 ML-DSA-65.
|
||
// Stock single-key signature under the group key. The Pulsar leg of a
|
||
// Quasar cert verifies through the unmodified empty-ctx FIPS-204 verifier
|
||
// (pulsarwire.VerifyBytes), so a group-key signature is valid leg evidence —
|
||
// the no-reconstruct t-of-n producer is gate-C, and permissionless safety
|
||
// comes from the dealerless Corona leg in AND-mode. The cert's domain
|
||
// separation lives in the TupleHash transcript that produced the round
|
||
// digest, not in the ML-DSA ctx string. See signPulsarLegStock.
|
||
// =========================================================
|
||
pulsarParams := pulsar.MustParamsFor(pulsar.ModeP65)
|
||
const n, threshold = 3, 2
|
||
pulsarSig, pulsarGroupPK := signPulsarLegStock(t, pulsarParams, digest)
|
||
if pulsarSig == nil || len(pulsarSig.Bytes) == 0 {
|
||
t.Fatal("Pulsar leg produced empty signature")
|
||
}
|
||
// Smoke check: byte-equal to FIPS 204 ML-DSA.Verify under empty
|
||
// ctx. If this fails the rest of the test is moot.
|
||
if err := pulsar.VerifyCtx(pulsarParams, pulsarGroupPK, digest, nil, pulsarSig); err != nil {
|
||
t.Fatalf("Pulsar leg failed standalone FIPS 204 verify: %v", err)
|
||
}
|
||
|
||
// =========================================================
|
||
// Leg 2: Corona — Ring-LWE threshold ML-DSA.
|
||
// Trusted-dealer keygen (acceptable for an in-process test;
|
||
// production runs Pedersen DKG via keyera.Bootstrap).
|
||
// =========================================================
|
||
coronaShares, coronaGroupKey, err := coronaThreshold.GenerateKeys(threshold, n, nil)
|
||
if err != nil {
|
||
t.Fatalf("corona.GenerateKeys: %v", err)
|
||
}
|
||
if len(coronaShares) != n {
|
||
t.Fatalf("corona.GenerateKeys returned %d shares, want %d", len(coronaShares), n)
|
||
}
|
||
coronaSigners := make([]*coronaThreshold.Signer, threshold)
|
||
signerIDs := make([]int, threshold)
|
||
for i := 0; i < threshold; i++ {
|
||
coronaSigners[i] = coronaThreshold.NewSigner(coronaShares[i])
|
||
signerIDs[i] = coronaShares[i].Index
|
||
}
|
||
const coronaSessionID = 31415
|
||
coronaPRFKey := []byte("polaris-compose-corona-prf-32B!!")
|
||
coronaRound1 := make(map[int]*coronaThreshold.Round1Data, threshold)
|
||
for i := 0; i < threshold; i++ {
|
||
r1, err := coronaSigners[i].Round1(coronaSessionID, coronaPRFKey, signerIDs)
|
||
if err != nil {
|
||
t.Fatalf("corona.Round1[%d]: %v", i, err)
|
||
}
|
||
coronaRound1[signerIDs[i]] = r1
|
||
}
|
||
coronaRound2 := make(map[int]*coronaThreshold.Round2Data, threshold)
|
||
for i := 0; i < threshold; i++ {
|
||
r2, err := coronaSigners[i].Round2(coronaSessionID, string(digest), coronaPRFKey, signerIDs, coronaRound1)
|
||
if err != nil {
|
||
t.Fatalf("corona.Round2[%d]: %v", i, err)
|
||
}
|
||
coronaRound2[signerIDs[i]] = r2
|
||
}
|
||
coronaSig, err := coronaSigners[0].Finalize(coronaRound2)
|
||
if err != nil {
|
||
t.Fatalf("corona.Finalize: %v", err)
|
||
}
|
||
// Smoke check: corona verify accepts.
|
||
if !coronaThreshold.Verify(coronaGroupKey, string(digest), coronaSig) {
|
||
t.Fatal("Corona leg failed standalone Verify")
|
||
}
|
||
|
||
// =========================================================
|
||
// Leg 3: Magnetar — SLH-DSA hash-based.
|
||
// Per-validator standalone: each validator holds its own
|
||
// FIPS 205 keypair and signs the digest independently. The
|
||
// cert collects all N signatures into a ValidatorAggregateCert.
|
||
// =========================================================
|
||
const magN = 3
|
||
magParams := magnetar.ParamsM192s
|
||
magPubBytes := make([][]byte, magN)
|
||
magSigBytes := make([][]byte, magN)
|
||
magSigners := make([]magnetar.NodeID, magN)
|
||
magKnown := make(map[magnetar.NodeID][]byte, magN)
|
||
for i := 0; i < magN; i++ {
|
||
sk, pk, err := magnetar.PerValidatorKeypair(magParams, rand.Reader)
|
||
if err != nil {
|
||
t.Fatalf("magnetar.PerValidatorKeypair[%d]: %v", i, err)
|
||
}
|
||
sig, err := magnetar.ValidatorSign(sk, nil, digest)
|
||
if err != nil {
|
||
t.Fatalf("magnetar.ValidatorSign[%d]: %v", i, err)
|
||
}
|
||
var id magnetar.NodeID
|
||
id[0] = byte(i + 1)
|
||
id[1] = 'M'
|
||
id[2] = 'A'
|
||
id[3] = 'G'
|
||
magSigners[i] = id
|
||
magPubBytes[i] = pk.Bytes
|
||
magSigBytes[i] = sig
|
||
magKnown[id] = pk.Bytes
|
||
}
|
||
magCert, err := magnetar.BuildAggregateCert(magParams, magSigners, magPubBytes, magSigBytes)
|
||
if err != nil {
|
||
t.Fatalf("magnetar.BuildAggregateCert: %v", err)
|
||
}
|
||
// Smoke check: magnetar verify accepts every signer.
|
||
if validCount, err := magnetar.VerifyAggregateCert(magCert, digest, magKnown); err != nil {
|
||
t.Fatalf("magnetar.VerifyAggregateCert: %v", err)
|
||
} else if validCount != magN {
|
||
t.Fatalf("magnetar leg expected %d valid sigs, got %d", magN, validCount)
|
||
}
|
||
|
||
// =========================================================
|
||
// Leg 4 (optional classical fast-path): BLS-12-381 aggregate.
|
||
// =========================================================
|
||
blsSK, err := bls.NewSecretKey()
|
||
if err != nil {
|
||
t.Fatalf("bls.NewSecretKey: %v", err)
|
||
}
|
||
blsPK := blsSK.PublicKey()
|
||
blsSig, err := blsSK.Sign(digest)
|
||
if err != nil {
|
||
t.Fatalf("bls.Sign: %v", err)
|
||
}
|
||
// Single-signer aggregate is the signature itself; this exercises
|
||
// the BLS leg byte path without dragging in a full N-party aggregate.
|
||
|
||
// =========================================================
|
||
// Leg 5 (optional): ML-DSA-65 per-validator rollup. Emit one
|
||
// per-validator signature so HasIdentityRollup() returns true.
|
||
// =========================================================
|
||
mldsaSK, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
|
||
if err != nil {
|
||
t.Fatalf("mldsa.GenerateKey: %v", err)
|
||
}
|
||
mldsaSig, err := mldsaSK.Sign(rand.Reader, digest, nil)
|
||
if err != nil {
|
||
t.Fatalf("mldsa.Sign: %v", err)
|
||
}
|
||
mldsaRollup := EncodeMLDSASigs([][]byte{mldsaSig})
|
||
|
||
// =========================================================
|
||
// Compose the Polaris cert.
|
||
// =========================================================
|
||
legs := PolarisLegs{
|
||
BLS: blsSig,
|
||
Pulsar: pulsarSig,
|
||
Corona: coronaSig,
|
||
Magnetar: magCert,
|
||
MLDSARollup: mldsaRollup,
|
||
Epoch: 42,
|
||
Finality: time.Unix(1730000000, 0),
|
||
Validators: n,
|
||
}
|
||
cert, err := ComposePolaris(legs)
|
||
if err != nil {
|
||
t.Fatalf("ComposePolaris: %v", err)
|
||
}
|
||
if !cert.IsPolaris() {
|
||
t.Fatal("ComposePolaris result reports IsPolaris() = false")
|
||
}
|
||
if !cert.IsDoubleLattice() {
|
||
t.Fatal("ComposePolaris result reports IsDoubleLattice() = false")
|
||
}
|
||
if !cert.HasHashBased() {
|
||
t.Fatal("ComposePolaris result reports HasHashBased() = false")
|
||
}
|
||
if !cert.HasClassicalFastPath() {
|
||
t.Fatal("ComposePolaris result reports HasClassicalFastPath() = false")
|
||
}
|
||
if !cert.HasIdentityRollup() {
|
||
t.Fatal("ComposePolaris result reports HasIdentityRollup() = false")
|
||
}
|
||
|
||
// =========================================================
|
||
// Round-trip through the wire codec.
|
||
// =========================================================
|
||
raw, err := cert.MarshalBinary()
|
||
if err != nil {
|
||
t.Fatalf("cert.MarshalBinary: %v", err)
|
||
}
|
||
if raw[0] != CertSchemeQuasar {
|
||
t.Fatalf("scheme byte = 0x%02x, want 0x%02x", raw[0], CertSchemeQuasar)
|
||
}
|
||
|
||
decoded := &QuasarCert{}
|
||
if err := decoded.UnmarshalBinary(raw); err != nil {
|
||
t.Fatalf("decoded.UnmarshalBinary: %v", err)
|
||
}
|
||
if !bytes.Equal(decoded.BLS, cert.BLS) {
|
||
t.Fatal("BLS leg round-trip mismatch")
|
||
}
|
||
if !bytes.Equal(decoded.Corona, cert.Corona) {
|
||
t.Fatal("Corona leg round-trip mismatch")
|
||
}
|
||
if !bytes.Equal(decoded.Pulsar, cert.Pulsar) {
|
||
t.Fatal("Pulsar leg round-trip mismatch")
|
||
}
|
||
if !bytes.Equal(decoded.Magnetar, cert.Magnetar) {
|
||
t.Fatal("Magnetar leg round-trip mismatch")
|
||
}
|
||
if !bytes.Equal(decoded.MLDSARollup, cert.MLDSARollup) {
|
||
t.Fatal("MLDSARollup leg round-trip mismatch")
|
||
}
|
||
if decoded.Epoch != cert.Epoch {
|
||
t.Fatalf("Epoch round-trip mismatch: %d vs %d", decoded.Epoch, cert.Epoch)
|
||
}
|
||
if decoded.Finality.Unix() != cert.Finality.Unix() {
|
||
t.Fatalf("Finality round-trip mismatch: %v vs %v", decoded.Finality, cert.Finality)
|
||
}
|
||
if decoded.Validators != cert.Validators {
|
||
t.Fatalf("Validators round-trip mismatch: %d vs %d", decoded.Validators, cert.Validators)
|
||
}
|
||
if !decoded.IsPolaris() {
|
||
t.Fatal("decoded cert reports IsPolaris() = false")
|
||
}
|
||
|
||
// =========================================================
|
||
// Cryptographic verification of every leg through the
|
||
// VerifyWithRealKeysPolaris path.
|
||
// =========================================================
|
||
|
||
// Build the pulsar group key wire bytes (PULG-framed) so the
|
||
// Polaris verifier's pulsar leg can route through
|
||
// pulsarwire.VerifyBytes.
|
||
pulsarGroupKeyWire, err := pulsarGroupPK.MarshalBinary()
|
||
if err != nil {
|
||
t.Fatalf("pulsarGroupPK.MarshalBinary: %v", err)
|
||
}
|
||
|
||
mldsaPubs := []*mldsa.PublicKey{mldsaSK.PublicKey}
|
||
|
||
if !decoded.VerifyWithRealKeysPolaris(
|
||
digest,
|
||
blsPK,
|
||
coronaGroupKey,
|
||
pulsarGroupKeyWire,
|
||
mldsaPubs,
|
||
magKnown,
|
||
) {
|
||
t.Fatal("VerifyWithRealKeysPolaris rejected a well-formed Polaris cert")
|
||
}
|
||
|
||
// =========================================================
|
||
// Tamper checks — each leg's bytes are load-bearing.
|
||
// =========================================================
|
||
|
||
// Tampered Magnetar bytes must reject.
|
||
tampered := *decoded
|
||
tampered.Magnetar = append([]byte(nil), decoded.Magnetar...)
|
||
tampered.Magnetar[len(tampered.Magnetar)-1] ^= 0xFF
|
||
if tampered.VerifyWithRealKeysPolaris(digest, blsPK, coronaGroupKey, pulsarGroupKeyWire, mldsaPubs, magKnown) {
|
||
t.Fatal("VerifyWithRealKeysPolaris accepted a tampered Magnetar leg")
|
||
}
|
||
|
||
// Tampered Pulsar bytes must reject. Flip a byte deep in the
|
||
// signature payload (skip the wire frame header).
|
||
tampered = *decoded
|
||
tampered.Pulsar = append([]byte(nil), decoded.Pulsar...)
|
||
if len(tampered.Pulsar) > 32 {
|
||
tampered.Pulsar[len(tampered.Pulsar)/2] ^= 0xAA
|
||
}
|
||
if tampered.VerifyWithRealKeysPolaris(digest, blsPK, coronaGroupKey, pulsarGroupKeyWire, mldsaPubs, magKnown) {
|
||
t.Fatal("VerifyWithRealKeysPolaris accepted a tampered Pulsar leg")
|
||
}
|
||
|
||
// Verify under a wrong message must reject.
|
||
if decoded.VerifyWithRealKeysPolaris([]byte("wrong-message"), blsPK, coronaGroupKey, pulsarGroupKeyWire, mldsaPubs, magKnown) {
|
||
t.Fatal("VerifyWithRealKeysPolaris accepted a wrong-message verification")
|
||
}
|
||
}
|