Files
consensus/protocol/auth/contract_profile_test.go
zeekay c40e832a9e consensus: drop Quasar/Annulus/Lux from profile identifiers
First-principles cleanup: family names (Quasar, Annulus) and the Lux
brand prefix belong in papers/docs, not Go identifiers. Canonical names:

  ProfileLuxStrictPQ    -> ProfileStrictPQ
  ProfileLuxPermissive  -> ProfilePermissive
  ProfileLuxFIPS        -> ProfileFIPS
  QuasarStrictPQ()      -> removed (was 0x06 byte-identical to 0x01)
  AnnulusStrictPQ()     -> removed (was alias for the above)
  ProfileNameLuxStrictPQ etc. -> removed

ProfileZooStrictPQ (0x04) and ProfileHanzoStrictPQ (0x05) kept — chain-
specific siblings, not crypto family branding. Wire-format cSHAKE256
customization tags (LUX_PQ_PERMIT_V1 etc.) intentionally stay brand-
prefixed as domain separators.

IsPQ(), ProfileByID(), validatePolicy() switches updated.
ProfileName strings drop LUX_ prefix: "LUX_STRICT_PQ" -> "STRICT_PQ".
Golden ProfileHash in security_profile_e2e_pq_test.go re-pinned.
2026-05-11 16:50:46 -07:00

181 lines
5.9 KiB
Go

// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package auth
import (
"errors"
"testing"
"github.com/luxfi/consensus/config"
)
// TestContractAuthProfile_Validate_HappyPath — every action pinned at
// a PQ scheme under a strict-PQ chain validates cleanly.
func TestContractAuthProfile_Validate_HappyPath(t *testing.T) {
chain := config.StrictPQ()
c := &ContractAuthProfile{
AdminSchemeID: ContractAuthMLDSA87,
UpgradeSchemeID: ContractAuthMLDSA87,
PauseSchemeID: ContractAuthMLDSA65,
PermitSchemeID: ContractAuthMLDSA65,
AllowClassical: false,
}
if err := c.Validate(chain); err != nil {
t.Fatalf("strict-PQ happy path failed: %v", err)
}
}
// TestContractAuthProfile_Validate_RejectsClassicalUnderStrictPQ — a
// strict-PQ chain refuses ANY classical scheme regardless of the
// contract's AllowClassical flag.
func TestContractAuthProfile_Validate_RejectsClassicalUnderStrictPQ(t *testing.T) {
chain := config.StrictPQ()
// Trying each per-action field with the classical marker MUST fail.
cases := []struct {
name string
mut func(*ContractAuthProfile)
}{
{
"Admin classical",
func(c *ContractAuthProfile) { c.AdminSchemeID = ContractAuthECDSASecp256k1Legacy },
},
{
"Upgrade classical",
func(c *ContractAuthProfile) { c.UpgradeSchemeID = ContractAuthECDSASecp256k1Legacy },
},
{
"Pause classical",
func(c *ContractAuthProfile) { c.PauseSchemeID = ContractAuthECDSASecp256k1Legacy },
},
{
"Permit classical",
func(c *ContractAuthProfile) { c.PermitSchemeID = ContractAuthECDSASecp256k1Legacy },
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
// AllowClassical=true MUST NOT save us under a strict-PQ chain.
c := &ContractAuthProfile{
AdminSchemeID: ContractAuthMLDSA65,
UpgradeSchemeID: ContractAuthMLDSA65,
PauseSchemeID: ContractAuthMLDSA65,
PermitSchemeID: ContractAuthMLDSA65,
AllowClassical: true,
}
tc.mut(c)
if err := c.Validate(chain); !errors.Is(err, ErrContractAuthClassicalForbidden) {
t.Fatalf("classical scheme accepted under strict-PQ with AllowClassical=true: %v", err)
}
})
}
}
// TestContractAuthProfile_Validate_RejectsFIPSClassical — FIPS profile
// also refuses classical regardless of AllowClassical.
func TestContractAuthProfile_Validate_RejectsFIPSClassical(t *testing.T) {
chain := config.FIPS()
c := &ContractAuthProfile{
AdminSchemeID: ContractAuthECDSASecp256k1Legacy,
UpgradeSchemeID: ContractAuthMLDSA65,
PauseSchemeID: ContractAuthMLDSA65,
PermitSchemeID: ContractAuthMLDSA65,
AllowClassical: true,
}
if err := c.Validate(chain); !errors.Is(err, ErrContractAuthClassicalForbidden) {
t.Fatalf("FIPS profile accepted classical: %v", err)
}
}
// TestContractAuthProfile_Validate_PermissiveAllowsClassicalOnlyWithFlag
// — under a permissive chain profile, classical schemes pass ONLY when
// AllowClassical=true.
func TestContractAuthProfile_Validate_PermissiveAllowsClassicalOnlyWithFlag(t *testing.T) {
chain := config.Permissive()
// Without flag — refused.
cNoFlag := &ContractAuthProfile{
AdminSchemeID: ContractAuthECDSASecp256k1Legacy,
UpgradeSchemeID: ContractAuthMLDSA65,
PauseSchemeID: ContractAuthMLDSA65,
PermitSchemeID: ContractAuthMLDSA65,
AllowClassical: false,
}
if err := cNoFlag.Validate(chain); !errors.Is(err, ErrContractAuthClassicalForbidden) {
t.Fatalf("permissive without flag accepted classical: %v", err)
}
// With flag — permitted (testnet relaxation).
cFlag := *cNoFlag
cFlag.AllowClassical = true
if err := cFlag.Validate(chain); err != nil {
t.Fatalf("permissive with flag rejected classical: %v", err)
}
}
// TestContractAuthProfile_Validate_RejectsUnsetScheme — None on any
// action is a typed error.
func TestContractAuthProfile_Validate_RejectsUnsetScheme(t *testing.T) {
chain := config.StrictPQ()
cases := []struct {
name string
mut func(*ContractAuthProfile)
}{
{"Admin", func(c *ContractAuthProfile) { c.AdminSchemeID = ContractAuthNone }},
{"Upgrade", func(c *ContractAuthProfile) { c.UpgradeSchemeID = ContractAuthNone }},
{"Pause", func(c *ContractAuthProfile) { c.PauseSchemeID = ContractAuthNone }},
{"Permit", func(c *ContractAuthProfile) { c.PermitSchemeID = ContractAuthNone }},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
c := &ContractAuthProfile{
AdminSchemeID: ContractAuthMLDSA65,
UpgradeSchemeID: ContractAuthMLDSA65,
PauseSchemeID: ContractAuthMLDSA65,
PermitSchemeID: ContractAuthMLDSA65,
}
tc.mut(c)
if err := c.Validate(chain); !errors.Is(err, ErrContractAuthSchemeUnset) {
t.Fatalf("%s=None accepted: %v", tc.name, err)
}
})
}
}
// TestContractAuthProfile_Validate_RejectsUnknownScheme — an out-of-band
// byte (not in the defined enum set) is refused.
func TestContractAuthProfile_Validate_RejectsUnknownScheme(t *testing.T) {
chain := config.StrictPQ()
c := &ContractAuthProfile{
AdminSchemeID: ContractAuthID(0x7F), // unknown
UpgradeSchemeID: ContractAuthMLDSA65,
PauseSchemeID: ContractAuthMLDSA65,
PermitSchemeID: ContractAuthMLDSA65,
}
if err := c.Validate(chain); !errors.Is(err, ErrContractAuthSchemeUnknown) {
t.Fatalf("unknown scheme accepted: %v", err)
}
}
// TestContractAuthProfile_Validate_RejectsNilArguments — nil receiver
// or nil chain profile are typed errors.
func TestContractAuthProfile_Validate_RejectsNilArguments(t *testing.T) {
chain := config.StrictPQ()
var nilProfile *ContractAuthProfile
if err := nilProfile.Validate(chain); !errors.Is(err, ErrContractAuthNilProfile) {
t.Errorf("nil profile: got %v", err)
}
c := &ContractAuthProfile{
AdminSchemeID: ContractAuthMLDSA65,
UpgradeSchemeID: ContractAuthMLDSA65,
PauseSchemeID: ContractAuthMLDSA65,
PermitSchemeID: ContractAuthMLDSA65,
}
if err := c.Validate(nil); !errors.Is(err, ErrContractAuthNilChainProfile) {
t.Errorf("nil chain profile: got %v", err)
}
}