Workspace-wide sync. luxfi/node already shipped on 1.26.4 in v1.30.6 (commit 121aca1fa9); this is the cross-repo catch-up.
7.6 KiB
LEGACY-CLASSICAL — Operator-Facing Opt-In Policy
This document explains the
signature.Config.LegacyClassicalEnabledflag, why it exists, what it permits, and the deprecation timeline for classical primitives on the Lux network.
Why this flag exists
Lux Warp is PQ-native by default. The canonical signature registry constructor
r := signature.NewPQNativeRegistry()
returns a registry where:
- The preferred scheme is
signature.SchemeMLDSA65(FIPS 204 ML-DSA-65, NIST security level 3). - Classical primitives (
SchemeBLS,SchemeEd25519,SchemeSecp256k1) cannot be installed without an explicit opt-in. Attemptingr.Register(SchemeBLS, ...)returnssignature.ErrClassicalRequiresOptIn.
A chain that wants classical primitives available MUST opt in:
cfg := signature.Config{
LegacyClassicalEnabled: true,
}
r := signature.NewRegistryFromConfig(cfg)
r.Register(signature.SchemeBLS, ...) // OK
r.Register(signature.SchemeEd25519, ...) // OK
r.Register(signature.SchemeSecp256k1, ...) // OK
The flag is explicit and audit-grep'able: every chain
config that wants classical fallback has LegacyClassicalEnabled: true written in it. No implicit fallback is possible.
What the flag does NOT do
The LegacyClassicalEnabled flag is at the registry layer.
It controls which schemes the registry will install.
The chain-level posture gate is pq.Mode (one of classical,
hybrid, strict-pq). These two gates are decomplected:
| Scenario | Registry gate | Chain gate | Outcome |
|---|---|---|---|
| Default chain, default registry | classical refused | strict-pq | classical envelope refused by both gates |
| Default chain, opt-in registry | classical admitted | strict-pq | classical envelope refused by chain gate at verification |
| Hybrid chain, opt-in registry | classical admitted | hybrid | classical envelope accepted with stale-PQ warning |
| Classical chain, opt-in registry | classical admitted | classical | classical envelope accepted as trust root |
In the second row, the registry installs BLS but the chain mode gate STILL refuses a classical-only envelope. This is by design: the gates are decomplected so a hostile build that bypasses one gate STILL hits the other.
When to set LegacyClassicalEnabled = true
Set the flag to true ONLY when ALL of the following hold:
- The chain is pinned
pq.ModeClassicalorpq.ModeHybrid(notstrict-pq). - The operator has decided to accept the Shor-vulnerability of classical primitives for the chain's threat model.
- Documentation in the chain's deployment runbook explicitly names the classical scheme(s) the chain trusts.
Do NOT set the flag to true on a strict-PQ chain. The opt-in
will succeed at the registry layer, but the chain gate will still
refuse classical envelopes — the flag will be effectively a no-op
that confuses readers of the config.
Deprecation timeline
Classical primitives on the Lux ecosystem follow a three-phase deprecation:
Phase 1 — Classical default (historical, pre-2025)
Lux Warp 1.x was BLS-only. Every chain implicitly trusted the BLS aggregate as the verification root. No PQ posture existed.
This phase is over. The Tier A push deletes the implicit classical default at the registry layer.
Phase 2 — PQ-aware (2025 → 2026) [CURRENT]
The Tier A push (this revision, 2026-05-18):
- Adds PQ-native default at the registry layer.
- Adds explicit
LegacyClassicalEnabledopt-in for classical. - Keeps classical primitives available under opt-in so existing wallets can migrate.
- Lux primary network + Hanzo + Zoo + Pars chains pinned
hybrid(classical fallback during validator-key rotation).
Operators of classical or hybrid chains MUST add
LegacyClassicalEnabled: true to their chain config when
upgrading from Warp 1.18.x to Warp 1.19.x. Without the flag,
classical scheme installs will fail at chain boot with
signature.ErrClassicalRequiresOptIn. The chain will refuse to
start; the operator gets a clear error message naming the flag.
Phase 3 — Classical refused (target 2027-Q4)
Target: 2027-Q4. By this date:
- Every Lux ecosystem chain pinned
strict-pq. - Classical primitives REMOVED from the registry (not just opt-in-gated).
- Classical scheme identifiers reserved-but-refused on the wire (parsing a classical signature returns an error).
- Wallet migration completed.
The LegacyClassicalEnabled flag will be REMOVED in Phase 3.
Chain configs that still set the flag will fail to parse.
This timeline is contingent on:
- Hanzo, Zoo, Pars subnet validator-key rotation completing.
- PQ-TLS at JSON-RPC ingress (currently using classical TLS 1.3).
- libp2p handshake migration to PQ-aware Noise / hybrid TLS 1.3.
See PQ_PROFILES.md "What's needed for FULL PQ end-to-end" for
the per-layer dependency tracking.
Migration path for operators
To migrate a chain config from Warp 1.18.x to Warp 1.19.x:
- Read the current chain's
warpProfile(one ofclassical,hybrid,strict-pq). - Decide whether the chain still needs classical primitives. For most strict-PQ chains, the answer is NO.
- If classical is no longer needed (target state for strict-pq):
- Remove any
signature.Config{...}construction that setsLegacyClassicalEnabled: true. - Use
signature.NewPQNativeRegistry()instead.
- Remove any
- If classical is still needed (transitional state for
hybrid / classical):
- Add
LegacyClassicalEnabled: trueto theConfigliteral. - Document the rationale in the chain's deployment runbook.
- Schedule a follow-up to remove the flag once validator-key rotation completes.
- Add
API surface
The opt-in flag is defined in
github.com/luxfi/warp/crypto/signature/interface.go:
// Config is the operator-facing posture knob for the signature
// registry.
type Config struct {
LegacyClassicalEnabled bool
PreferredScheme Scheme
}
// DefaultConfig is the canonical PQ-native default.
func DefaultConfig() Config {
return Config{
LegacyClassicalEnabled: false,
PreferredScheme: SchemeMLDSA65,
}
}
// NewPQNativeRegistry returns the canonical PQ-native registry.
func NewPQNativeRegistry() *Registry
// NewRegistryFromConfig returns a registry pinned to the given Config.
func NewRegistryFromConfig(cfg Config) *Registry
Frequently asked
Q. Why a flag instead of a separate constructor like NewLegacyRegistry?
A flag on Config makes the chain's posture greppable across
the codebase. An operator's chain config has the literal text
LegacyClassicalEnabled: true next to its other knobs — easier to
audit than tracking which constructor was called.
Q. Does the flag affect runtime performance?
No. The flag is checked at Register time and at SetPreferred
time, not on every signature verification. Steady-state
verification cost is unchanged.
Q. Why is SchemeHybrid not affected by the flag?
SchemeHybrid composes BLS + Corona over the same transcript;
both lanes verify. It is the canonical migration path. A chain
pinned hybrid mode needs SchemeHybrid installable WITHOUT the
opt-in flag, otherwise migration is blocked. The registry treats
hybrid as PQ-aware (not classical) and admits it under the
default config.
Q. What about SchemeMLDSA44 / SchemeMLDSA87?
Not currently registered. ML-DSA-65 (NIST level 3) is the canonical Warp default; ML-DSA-44 (level 2) and ML-DSA-87 (level 5) can be added in future revisions if a downstream chain requests them. Adding them would NOT require a flag change since they are PQ schemes.
Document metadata
- Name:
LEGACY-CLASSICAL.md - Version: v1.0 (Tier A)
- Date: 2026-05-18