Files
zeekay 4088c90239 consensus/quasar: KMAC256 replaces HMAC-SHA256 for canonical MACs
SP 800-185 KMAC256 (cSHAKE-256-based) replaces HMAC-SHA256 in the
strict-PQ hot paths:
  - Quasar event-horizon MACs (protocol/quasar/bls.go phaseII)
  - createHorizonSignature BLS + PQ halves
  - generateBLSAggregate / generatePQCertificate

Each call site uses its own SP 800-185 customization string so two
MACs over the same (key, message) pair are independent oracles:
  "QUASAR_EVENT_HORIZON_BLS_MAC_V1"
  "QUASAR_EVENT_HORIZON_PQ_MAC_V1"
  "QUASAR_HORIZON_SIG_BLS_MAC_V1"
  "QUASAR_HORIZON_SIG_PQ_MAC_V1"

Internal preimage helpers (buildVoteDigest, deriveProposalID,
createHorizonSignature digest) also move to cSHAKE256, keeping the
entire MAC chain in the SP 800-185 family that the strict-PQ profile
pins via HashSuiteSHA3NIST.

Canonical MAC width is 48 bytes (SHA3-384), matching MinHashOutputBits
on the LuxStrictPQ profile.

BLS.SetProfile attaches a ChainSecurityProfile; under strict-PQ /
FIPS, CertBundle.VerifyWithKeysUnderProfile refuses sub-canonical MAC
widths outright -- a 32-byte HMAC-SHA256 bundle is rejected before
the MAC comparison even runs. Classical-compat profiles retain the
ability to roundtrip narrower widths.

Tests added in protocol/quasar/kmac256_test.go:
  TestKMAC256_Deterministic
  TestKMAC256_DomainSeparation_DifferentCustomizationsDifferentTags
  TestQuasarEventHorizonMAC_UsesKMAC256_UnderStrictPQ
  TestConsensusMAC_RejectsHMACSHA256_UnderStrictPQ
  TestKMAC256_CustomizationStrings_Pinned

Golden width vectors re-pinned (32-byte HMAC -> 48-byte KMAC256):
  TestBLS_GenerateBLSAggregate_WithKey
  TestBLS_GeneratePQCertificate_WithKey
  TestBLS_CreateHorizonSignature (64-byte fusion -> 96-byte)
  TestCertBundle_MessageDigestStored

Patch-bump.
2026-05-10 20:22:11 -07:00

63 lines
1.6 KiB
Go

package quasar
import (
"testing"
)
// TestCertBundle_Verify_C2_Panics is the regression test for C-2.
// The deprecated Verify method must panic to prevent accidental use
// of the non-cryptographic verification path.
func TestCertBundle_Verify_C2_Panics(t *testing.T) {
cert := &CertBundle{
BLSAgg: []byte{0x01, 0x02, 0x03},
PQCert: []byte{0x04, 0x05, 0x06},
}
defer func() {
r := recover()
if r == nil {
t.Fatal("CertBundle.Verify must panic")
}
}()
cert.Verify(nil)
}
// TestCertBundle_VerifyWithKeys_Valid tests the correct verification path.
func TestCertBundle_VerifyWithKeys_Valid(t *testing.T) {
blsKey := []byte("bls-key-for-testing-32-bytes!")
pqKey := []byte("pq-key-for-testing-32-bytes!!")
message := []byte("test-message-data")
cert := &CertBundle{
BLSAgg: kmac256(blsKey, message, kmacMACOutLen, customQuasarEventHorizonBLSMAC),
PQCert: kmac256(pqKey, message, kmacMACOutLen, customQuasarEventHorizonPQMAC),
Message: message,
}
if !cert.VerifyWithKeys(blsKey, pqKey) {
t.Error("valid cert should pass VerifyWithKeys")
}
// Wrong BLS key
if cert.VerifyWithKeys([]byte("wrong-key"), pqKey) {
t.Error("wrong BLS key should fail")
}
// Wrong PQ key
if cert.VerifyWithKeys(blsKey, []byte("wrong-key")) {
t.Error("wrong PQ key should fail")
}
// Nil cert
var nilCert *CertBundle
if nilCert.VerifyWithKeys(blsKey, pqKey) {
t.Error("nil cert should fail")
}
// Missing message
noMsg := &CertBundle{BLSAgg: cert.BLSAgg, PQCert: cert.PQCert}
if noMsg.VerifyWithKeys(blsKey, pqKey) {
t.Error("cert with no message should fail")
}
}