mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
Followup to the QuasarCert field rename (commit 4c9af053). This sweeps
the remaining Ringtail identifiers and prose out of the consensus repo
entirely:
- Identifiers (Go code):
ringtailThreshold → coronaThreshold (import alias)
ringtailGroupKey → coronaGroupKey
ringtailSigners → coronaSigners
ringtailShares → coronaShares
ringtailGobEncode → coronaGobEncode
ringtailGobDecode → coronaGobDecode
decodeRingtailSig → decodeCoronaSig
EncodeRingtailSig → EncodeCoronaSig
RingtailRound1{,Data,State} → CoronaRound1{,Data,State}
RingtailGroupKey/Pub/Shares → Corona*
- File rename:
protocol/quasar/ringtail_gob.go → corona_gob.go
- PQMode constants:
PQModeRingtail → PQModeNasua
SigSchemeRingtailAcademic → SigSchemeNasua
"ringtail" / "ringtail-academic" string forms → "nasua"
- Prose & comments throughout the repo: "Ringtail" → "Corona" when
naming the production Ring-LWE library, "Nasua" when naming the
historical academic upstream (luxfi/nasua, formerly luxfi/ringtail).
Canonical Pulsar family taxonomy after this rename:
Nasua — academic R-LWE 2-round threshold sig (Boschini et al,
ePrint 2024/1113); trusted-dealer DKG; kept as historical
reference fork only. NOT for an open public chain.
Corona — production Ring-LWE threshold ML-DSA (Pedersen DKG over R_q
+ proactive resharing); `github.com/luxfi/corona`. The
Ring-LWE leg of QuasarCert.
Pulsar — production Module-LWE threshold ML-DSA (FIPS 204
byte-equal); `github.com/luxfi/pulsar`. The Module-LWE
leg of QuasarCert. Pure-PQ pair = Corona + Pulsar.
Wire-format unchanged. Tests: 28/28 packages green (-short suite,
quasar 29s, integration 58s, no regressions).
118 lines
4.7 KiB
Go
118 lines
4.7 KiB
Go
package quasar
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"testing"
|
|
|
|
"github.com/luxfi/crypto/banderwagon"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
// TestF002_ConstantTimeProofComparison verifies that witness proof verification
|
|
// uses constant-time comparison (crypto/subtle.ConstantTimeCompare), not
|
|
// bytes.Equal which leaks timing information about matching prefix length.
|
|
//
|
|
// Regression test for F-002 from the Dragonfire security review.
|
|
//
|
|
// The proof primitive migrated from sha256 to SHA3-cSHAKE256 (48-byte
|
|
// output, FIPS 202 family) per red-team F100; this test re-derives the
|
|
// expected proof under the new primitive so the verification path stays
|
|
// covered.
|
|
func TestF002_ConstantTimeProofComparison(t *testing.T) {
|
|
// Create a known commitment using the same pattern as witness.go
|
|
var commitment banderwagon.Element
|
|
path := []byte("test-path-for-witness-verification")
|
|
|
|
// Compute the expected proof (same as verifyIPAOpening internals):
|
|
// cSHAKE256 / KMAC256 with the customisation tag witness.go pins,
|
|
// 48-byte (SHA3-384 width) output.
|
|
h := sha3.NewCShake256([]byte("KMAC"), []byte("LUX-VERKLE-IPA-PROOF-V1"))
|
|
commitmentBytes := commitment.Bytes()
|
|
_, _ = h.Write(commitmentBytes[:])
|
|
_, _ = h.Write(path)
|
|
correctProof := make([]byte, 48)
|
|
_, _ = h.Read(correctProof)
|
|
|
|
// Correct proof should verify
|
|
require.True(t, verifyIPAOpening(&commitment, path, correctProof),
|
|
"correct proof must verify")
|
|
|
|
// Wrong proof should not verify
|
|
wrongProof := make([]byte, len(correctProof))
|
|
copy(wrongProof, correctProof)
|
|
wrongProof[0] ^= 0xFF
|
|
require.False(t, verifyIPAOpening(&commitment, path, wrongProof),
|
|
"wrong proof must not verify")
|
|
|
|
// Empty proof should not verify
|
|
require.False(t, verifyIPAOpening(&commitment, path, []byte{}),
|
|
"empty proof must not verify")
|
|
|
|
// Wrong length proof should not verify
|
|
// subtle.ConstantTimeCompare returns 0 for different lengths
|
|
shortProof := correctProof[:16]
|
|
require.False(t, verifyIPAOpening(&commitment, path, shortProof),
|
|
"short proof must not verify")
|
|
|
|
// Verify the comparison is constant-time by confirming behavior
|
|
// matches subtle.ConstantTimeCompare semantics
|
|
require.Equal(t, 1, subtle.ConstantTimeCompare(correctProof, correctProof),
|
|
"identical proofs must match via constant-time compare")
|
|
require.Equal(t, 0, subtle.ConstantTimeCompare(correctProof, wrongProof),
|
|
"different proofs must not match via constant-time compare")
|
|
require.Equal(t, 0, subtle.ConstantTimeCompare(correctProof, shortProof),
|
|
"different-length proofs must not match via constant-time compare")
|
|
}
|
|
|
|
// TestDualSignRound1_ErrorPropagation verifies that DualSignRound1
|
|
// propagates errors from BOTH the BLS and Corona signing paths.
|
|
// Previously, the Corona error was silently discarded (line 329).
|
|
//
|
|
// Regression test for the silent error drop found during review analysis.
|
|
func TestDualSignRound1_ErrorPropagation(t *testing.T) {
|
|
// Attempting dual sign with unconfigured validator should error
|
|
s, err := newSigner(2) // threshold of 2
|
|
require.NoError(t, err)
|
|
|
|
_, _, err = s.DualSignRound1(nil, "nonexistent-validator", []byte("msg"), 1, []byte("prf"))
|
|
require.Error(t, err, "DualSignRound1 must error for unconfigured validator")
|
|
require.Contains(t, err.Error(), "not configured for dual signing")
|
|
}
|
|
|
|
// TestCertBundle_VerifyPanics verifies the deprecated Verify method panics
|
|
// to prevent accidental use of the non-cryptographic path.
|
|
func TestCertBundle_VerifyPanics(t *testing.T) {
|
|
cert := &CertBundle{
|
|
BLSAgg: []byte{0x01, 0x02, 0x03},
|
|
PQCert: []byte{0x04, 0x05, 0x06},
|
|
}
|
|
require.Panics(t, func() { cert.Verify(nil) }, "Verify must panic to enforce VerifyWithKeys usage")
|
|
}
|
|
|
|
// TestCertBundle_VerifyWithKeys_Regression tests cryptographic KMAC256 verification.
|
|
func TestCertBundle_VerifyWithKeys_Regression(t *testing.T) {
|
|
blsKey := []byte("regression-bls-key-32bytes!!")
|
|
pqKey := []byte("regression-pq-key-32bytes!!!")
|
|
message := []byte("regression test")
|
|
|
|
cert := &CertBundle{
|
|
BLSAgg: kmac256(blsKey, message, kmacMACOutLen, customQuasarEventHorizonBLSMAC),
|
|
PQCert: kmac256(pqKey, message, kmacMACOutLen, customQuasarEventHorizonPQMAC),
|
|
Message: message,
|
|
}
|
|
require.True(t, cert.VerifyWithKeys(blsKey, pqKey), "valid cert passes KMAC256 check")
|
|
|
|
// Nil cert fails
|
|
var nilCert *CertBundle
|
|
require.False(t, nilCert.VerifyWithKeys(blsKey, pqKey), "nil cert must fail")
|
|
|
|
// Empty BLS fails
|
|
emptyBLS := &CertBundle{BLSAgg: []byte{}, PQCert: cert.PQCert, Message: message}
|
|
require.False(t, emptyBLS.VerifyWithKeys(blsKey, pqKey), "empty BLS must fail")
|
|
|
|
// Empty PQ fails
|
|
emptyPQ := &CertBundle{BLSAgg: cert.BLSAgg, PQCert: []byte{}, Message: message}
|
|
require.False(t, emptyPQ.VerifyWithKeys(blsKey, pqKey), "empty PQ must fail")
|
|
}
|