mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
Before: protocol/auth/scheme_ids.go declared its own KeyExchangeID type with values 0x52/0x53 — disjoint from config/pq_mode.go (0x01/0x02) and node/network/kem/scheme.go (0x62/0x63). Three packages, three numberings of the same primitive. After: auth.KeyExchangeID is a type alias of config.KeyExchangeID. The canonical wire bytes (0x01 = ML-KEM-768, 0x02 = ML-KEM-1024, 0x90 = X25519Unsafe) live in config and are re-exported by auth. Methods (String, IsPostQuantum, IsForbiddenInPQMode) are inherited from config. Dropped: KeyExchangeMLKEM512 (NIST Cat 1, below strict-PQ floor; never deployed) and KeyExchangeX25519Legacy (renamed to X25519Unsafe to match config). No production code referenced these. New test (auth_test package): TestKEMSchemeIDs_AllUseCanonicalNumbering proves auth's exported KeyExchange* constants are byte-identical to config's, with wire bytes 0x01 / 0x02 / 0x90.
251 lines
7.7 KiB
Go
251 lines
7.7 KiB
Go
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package auth
|
|
|
|
import "testing"
|
|
|
|
// TestWalletSchemeID_String pins the wire-name table so a rename is
|
|
// immediately visible in CI. Audit tooling and logs read these strings;
|
|
// flipping one silently is a backwards-compat break we want loud.
|
|
func TestWalletSchemeID_String(t *testing.T) {
|
|
cases := []struct {
|
|
id WalletSchemeID
|
|
want string
|
|
}{
|
|
{WalletSchemeNone, "none"},
|
|
{WalletSchemeECDSASecp256k1Legacy, "ecdsa-secp256k1-legacy"},
|
|
{WalletSchemeMLDSA44, "ml-dsa-44"},
|
|
{WalletSchemeMLDSA65, "ml-dsa-65"},
|
|
{WalletSchemeMLDSA87, "ml-dsa-87"},
|
|
{WalletSchemeSLHDSA128f, "slh-dsa-128f"},
|
|
{WalletSchemeSLHDSA192f, "slh-dsa-192f"},
|
|
{WalletSchemeSLHDSA256f, "slh-dsa-256f"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := c.id.String(); got != c.want {
|
|
t.Errorf("WalletSchemeID(0x%02x).String() = %q, want %q", uint8(c.id), got, c.want)
|
|
}
|
|
}
|
|
unknown := WalletSchemeID(0xFE)
|
|
if got := unknown.String(); got != "wallet-scheme(0xfe)" {
|
|
t.Errorf("unknown WalletSchemeID.String() = %q, want wallet-scheme(0xfe)", got)
|
|
}
|
|
}
|
|
|
|
// TestWalletSchemeID_Predicates pins the predicate truth table.
|
|
func TestWalletSchemeID_Predicates(t *testing.T) {
|
|
rawMLDSA := []WalletSchemeID{WalletSchemeMLDSA44, WalletSchemeMLDSA65, WalletSchemeMLDSA87}
|
|
slhDSA := []WalletSchemeID{WalletSchemeSLHDSA128f, WalletSchemeSLHDSA192f, WalletSchemeSLHDSA256f}
|
|
classical := []WalletSchemeID{WalletSchemeECDSASecp256k1Legacy}
|
|
|
|
for _, s := range rawMLDSA {
|
|
if !s.IsRawMLDSA() {
|
|
t.Errorf("%s should IsRawMLDSA", s)
|
|
}
|
|
if s.IsSLHDSA() {
|
|
t.Errorf("%s should not IsSLHDSA", s)
|
|
}
|
|
if !s.IsPostQuantum() {
|
|
t.Errorf("%s should IsPostQuantum", s)
|
|
}
|
|
if s.IsLegacyClassical() {
|
|
t.Errorf("%s should not IsLegacyClassical", s)
|
|
}
|
|
}
|
|
for _, s := range slhDSA {
|
|
if s.IsRawMLDSA() {
|
|
t.Errorf("%s should not IsRawMLDSA", s)
|
|
}
|
|
if !s.IsSLHDSA() {
|
|
t.Errorf("%s should IsSLHDSA", s)
|
|
}
|
|
if !s.IsPostQuantum() {
|
|
t.Errorf("%s should IsPostQuantum", s)
|
|
}
|
|
}
|
|
for _, s := range classical {
|
|
if s.IsPostQuantum() {
|
|
t.Errorf("%s should not IsPostQuantum", s)
|
|
}
|
|
if !s.IsLegacyClassical() {
|
|
t.Errorf("%s should IsLegacyClassical", s)
|
|
}
|
|
}
|
|
// None is neither PQ nor classical-legacy: it's the unspecified
|
|
// wire byte that every locked profile refuses.
|
|
none := WalletSchemeNone
|
|
if none.IsPostQuantum() || none.IsLegacyClassical() || none.IsRawMLDSA() || none.IsSLHDSA() {
|
|
t.Errorf("WalletSchemeNone predicates must all be false; got PQ=%v classical=%v rawML=%v slh=%v",
|
|
none.IsPostQuantum(), none.IsLegacyClassical(), none.IsRawMLDSA(), none.IsSLHDSA())
|
|
}
|
|
}
|
|
|
|
// TestTxSchemeID_String pins the wire-name table.
|
|
func TestTxSchemeID_String(t *testing.T) {
|
|
cases := []struct {
|
|
id TxSchemeID
|
|
want string
|
|
}{
|
|
{TxSchemeNone, "none"},
|
|
{TxSchemeLegacyECDSA, "tx-legacy-ecdsa"},
|
|
{TxSchemePQAuthV1, "tx-pq-auth-v1"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := c.id.String(); got != c.want {
|
|
t.Errorf("TxSchemeID(0x%02x).String() = %q, want %q", uint8(c.id), got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestTxSchemeID_IsPostQuantum pins the PQ predicate.
|
|
func TestTxSchemeID_IsPostQuantum(t *testing.T) {
|
|
if !TxSchemePQAuthV1.IsPostQuantum() {
|
|
t.Error("TxSchemePQAuthV1 must IsPostQuantum")
|
|
}
|
|
if TxSchemeLegacyECDSA.IsPostQuantum() {
|
|
t.Error("TxSchemeLegacyECDSA must not IsPostQuantum")
|
|
}
|
|
if TxSchemeNone.IsPostQuantum() {
|
|
t.Error("TxSchemeNone must not IsPostQuantum")
|
|
}
|
|
}
|
|
|
|
// TestContractAuthID_String pins the wire-name table.
|
|
func TestContractAuthID_String(t *testing.T) {
|
|
cases := []struct {
|
|
id ContractAuthID
|
|
want string
|
|
}{
|
|
{ContractAuthNone, "none"},
|
|
{ContractAuthECDSASecp256k1Legacy, "ecdsa-secp256k1-legacy"},
|
|
{ContractAuthMLDSA44, "ml-dsa-44"},
|
|
{ContractAuthMLDSA65, "ml-dsa-65"},
|
|
{ContractAuthMLDSA87, "ml-dsa-87"},
|
|
{ContractAuthSLHDSA128f, "slh-dsa-128f"},
|
|
{ContractAuthSLHDSA192f, "slh-dsa-192f"},
|
|
{ContractAuthSLHDSA256f, "slh-dsa-256f"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := c.id.String(); got != c.want {
|
|
t.Errorf("ContractAuthID(0x%02x).String() = %q, want %q", uint8(c.id), got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestKeyExchangeID_String pins the wire-name table. The KeyExchangeID
|
|
// type is now an alias of config.KeyExchangeID, so String() comes from
|
|
// config; this test pins the canonical names through the auth package's
|
|
// re-exported constants.
|
|
func TestKeyExchangeID_String(t *testing.T) {
|
|
cases := []struct {
|
|
id KeyExchangeID
|
|
want string
|
|
}{
|
|
{KeyExchangeInvalid, "invalid"},
|
|
{KeyExchangeMLKEM768, "ml-kem-768"},
|
|
{KeyExchangeMLKEM1024, "ml-kem-1024"},
|
|
{KeyExchangeX25519Unsafe, "x25519-unsafe-classical-forbidden-in-pq"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := c.id.String(); got != c.want {
|
|
t.Errorf("KeyExchangeID(0x%02x).String() = %q, want %q", uint8(c.id), got, c.want)
|
|
}
|
|
}
|
|
// IsPostQuantum: ML-KEM family yes, X25519 no.
|
|
pq := []KeyExchangeID{KeyExchangeMLKEM768, KeyExchangeMLKEM1024}
|
|
for _, k := range pq {
|
|
if !k.IsPostQuantum() {
|
|
t.Errorf("%s should IsPostQuantum", k)
|
|
}
|
|
}
|
|
if KeyExchangeX25519Unsafe.IsPostQuantum() {
|
|
t.Error("X25519Unsafe must not IsPostQuantum")
|
|
}
|
|
// Canonical wire bytes — pins the Bug 3 fix at 0x01/0x02.
|
|
if uint8(KeyExchangeMLKEM768) != 0x01 {
|
|
t.Errorf("KeyExchangeMLKEM768 wire byte = 0x%02x, want 0x01", uint8(KeyExchangeMLKEM768))
|
|
}
|
|
if uint8(KeyExchangeMLKEM1024) != 0x02 {
|
|
t.Errorf("KeyExchangeMLKEM1024 wire byte = 0x%02x, want 0x02", uint8(KeyExchangeMLKEM1024))
|
|
}
|
|
}
|
|
|
|
// TestRecoverySchemeID_String pins the wire-name table.
|
|
func TestRecoverySchemeID_String(t *testing.T) {
|
|
cases := []struct {
|
|
id RecoverySchemeID
|
|
want string
|
|
}{
|
|
{RecoverySchemeNone, "none"},
|
|
{RecoverySchemeNoneByDesign, "recovery-none-by-design"},
|
|
{RecoverySchemeSocialKofN, "recovery-social-k-of-n"},
|
|
{RecoverySchemeTimelockPQ, "recovery-timelock-pq"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := c.id.String(); got != c.want {
|
|
t.Errorf("RecoverySchemeID(0x%02x).String() = %q, want %q", uint8(c.id), got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestRecoverySchemeID_IsKnown pins the membership predicate.
|
|
func TestRecoverySchemeID_IsKnown(t *testing.T) {
|
|
for _, s := range []RecoverySchemeID{
|
|
RecoverySchemeNone,
|
|
RecoverySchemeNoneByDesign,
|
|
RecoverySchemeSocialKofN,
|
|
RecoverySchemeTimelockPQ,
|
|
} {
|
|
if !s.IsKnown() {
|
|
t.Errorf("%s should be IsKnown", s)
|
|
}
|
|
}
|
|
if (RecoverySchemeID(0x99)).IsKnown() {
|
|
t.Error("unknown byte 0x99 reported IsKnown")
|
|
}
|
|
}
|
|
|
|
// TestContractAuthID_PostQuantumPredicate pins the truth table.
|
|
func TestContractAuthID_PostQuantumPredicate(t *testing.T) {
|
|
pq := []ContractAuthID{
|
|
ContractAuthMLDSA44, ContractAuthMLDSA65, ContractAuthMLDSA87,
|
|
ContractAuthSLHDSA128f, ContractAuthSLHDSA192f, ContractAuthSLHDSA256f,
|
|
}
|
|
for _, c := range pq {
|
|
if !c.IsPostQuantum() {
|
|
t.Errorf("%s should IsPostQuantum", c)
|
|
}
|
|
}
|
|
if ContractAuthECDSASecp256k1Legacy.IsPostQuantum() {
|
|
t.Error("ECDSA legacy must not IsPostQuantum")
|
|
}
|
|
if !ContractAuthECDSASecp256k1Legacy.IsLegacyClassical() {
|
|
t.Error("ECDSA legacy must IsLegacyClassical")
|
|
}
|
|
if ContractAuthNone.IsPostQuantum() {
|
|
t.Error("None must not IsPostQuantum")
|
|
}
|
|
}
|
|
|
|
// TestPrecompileAddresses pins the canonical EVM precompile addresses
|
|
// so a renumbering accident shows up in CI rather than at the wiring
|
|
// layer (~/work/lux/coreth/precompile/pqverify).
|
|
func TestPrecompileAddresses(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
got uint16
|
|
want uint16
|
|
}{
|
|
{"pq_verify_mldsa65", PrecompileAddrPQVerifyMLDSA65, 0x301},
|
|
{"pq_verify_mldsa87", PrecompileAddrPQVerifyMLDSA87, 0x302},
|
|
{"pq_verify_slh_dsa", PrecompileAddrPQVerifySLHDSA, 0x303},
|
|
{"pq_verify_z_auth_proof", PrecompileAddrPQVerifyZAuthProof, 0x304},
|
|
}
|
|
for _, c := range cases {
|
|
if c.got != c.want {
|
|
t.Errorf("precompile %s: got 0x%03x want 0x%03x", c.name, c.got, c.want)
|
|
}
|
|
}
|
|
}
|