Files
teleport/LEGACY-CLASSICAL.md
zeekay 65d4e361b6 submission: Tier A documentation package
Adds the Tier A bridge endpoint submission package:

  - SUBMISSION.md            cover sheet, headline claims, scope
  - SPEC.md                  bridge state machine, custody MPC,
                             oracle attestation, relay protocol
  - PROOF-CLAIMS.md          P1-P6 proved, N1-N6 not proved
  - TRUSTED-COMPUTING-BASE.md TCB inventory (HW/OS, primitives, quorums)
  - PATENTS.md               royalty-free patent grant
  - CRYPTOGRAPHER-SIGN-OFF.md  APPROVED WITH GATES, 5 gates listed
  - DEPLOYMENT-RUNBOOK.md    operator playbook (build / deploy / MPC
                             ceremony bootstrap / profile transition /
                             LSS rotation / pause / monitoring)
  - LEGACY-CLASSICAL.md      LUX_PROFILE=classical-compat opt-in docs
  - CHANGELOG.md             per-release changes (incl. Tier A pass)

Also strips the deprecated privacy-layer section from LLM.md (the
contracts it described were removed in commit 78e2dd7). The privacy
section is replaced with a brief pointer to git history and to the
Warp 2.0 envelope path which is the canonical surface today.

No code changes in this commit. The strict-PQ profile gate, six-
scheme threshold-signer bus, and EXTCODEHASH allow-list were all
landed in prior commits (b0ff75e, 5c64d64, bdfc56c, ddc5547). This
commit closes the documentation gap.

The cryptographer sign-off is APPROVED WITH GATES (5 gates listed,
all docs / runbook / interop deliverables, none algorithmic).
2026-05-19 08:40:58 -07:00

10 KiB

LEGACY-CLASSICAL — opt-in classical-compat profile

This document explains the LUX_PROFILE=classical-compat opt-in profile, when to use it, what its security tradeoffs are, and how to transition from it to LUX_PROFILE=quasar-strict-pq.

TL;DR

The default for Teleport deployments going forward is the strict-PQ profile:

export LUX_PROFILE=quasar-strict-pq
# MPC_SCHEME defaults to double-lattice (Pulsar ⊕ Corona)
# On-chain strict-PQ slot must hold a STARK/FRI/SHA-3-PQ or ML-DSA-65 verifier

The classical-compat profile is an opt-in for two narrow deployment contexts:

  1. Bridging to a classical L1 EVM (Ethereum mainnet, Base, Arbitrum, Optimism) where there is no PQ verifier precompile and the destination chain expects EIP-712 oracle signatures.

  2. Migration / interop testing where a deployment is mid-transition between Phases 1 and 5 of the Warp 2.0 rollout (see WARP2_BRIDGE.md §"Migration path") and needs the legacy ECDSA path live for back-compat.

For any new deployment that does NOT fall into one of these two buckets, the strict-PQ profile is the right choice.

How the profile resolves

The profile is set via the LUX_PROFILE environment variable:

Value Meaning
quasar-strict-pq PQ everywhere. MPC defaults to double-lattice. On-chain strict-PQ slot must hold a PQ verifier.
classical-compat Legacy ECDSA / GG18 path. MPC defaults to ecdsa. On-chain strict-PQ slot accepts any verifier (including BN254 / BLS12-381).
hybrid Reserved. Permissive on scheme; emits WARN; today behaves like classical-compat on the on-chain gate (refused under strict-PQ profile).
unset THROWS. Fail-closed — no implicit default.

There is no fourth option. The off-chain resolver (mpc/src/signers/index.ts) and the on-chain gate (StrictPQProfileGate.sol via @luxfi/pq-profile-ids) read the same profile constants from the same canonical package, so the two sides cannot drift.

Legacy STRICT_PQ=1 is still honored with a one-time deprecation warning — it is equivalent to LUX_PROFILE=quasar-strict-pq. New deployments should use the canonical variable name.

What classical-compat means for the off-chain pipeline

Component Classical-compat behaviour
MPC service default MPC_SCHEME=ecdsa (GG18 spawn)
Admissible MPC schemes {ecdsa, cggmp21, frost}
Forbidden MPC schemes {pulsar, corona, double-lattice} — throws "incompatible" error
API signing path ECDSA EIP-712 via bridge.ts (legacy)
PQ envelope path Not used (isStrictPQ() returns false)
Wire format Identical to v1 EIP-712 oracle signatures (no Warp 2.0 envelope)

The classical-compat profile preserves the legacy v1 wire format byte-for-byte. A deployment running classical-compat is indistinguishable from a pre-2026 Teleport deployment on the wire.

What classical-compat means for the on-chain endpoint

Component Classical-compat behaviour
BridgeV2.activeProfile PROFILE_CLASSICAL_COMPAT
BridgeV2.activeVerifier slot Any verifier (BN254 / BLS12-381 / STARK / ML-DSA all admissible)
BridgeV2.p3qVerifier slot Same — any verifier admissible (still restricted to POLICY_STARK_FRI_SHA3_PQ for the P3Q slot specifically, by the setP3QVerifier install-time check, which is profile-independent)
StrictPQProfileGate.refuseUnderStrictPQ No-op — returns immediately under classical-compat
Bridge.sol (v1) Still operational; same EIP-712 path as pre-Tier-A

The classical-compat profile is the back-compat surface for Phase 1 deployments (Bridge.sol only) and for destination chains that have no PQ precompile.

When to opt in

Use case 1: Bridging to a classical L1 EVM

If the destination chain is Ethereum mainnet, Base, Arbitrum, Optimism, or any other EVM L1 that does NOT have:

  • BN254 pairing precompile (0x06, 0x07, 0x08) — needed for classical Groth16/PLONK/KZG verifier, OR
  • STARK/FRI precompile (LP-2517 equivalent) — needed for strict-PQ verifier, OR
  • ML-DSA-65 precompile (LP-2517) — needed for strict-PQ verifier

…then the destination chain cannot run the strict-PQ verifier on-chain. The bridge falls back to a classical envelope verifier (Beam BLS only) on those chains, and the off-chain pipeline runs in classical-compat profile.

This is expected and acceptable: the classical-compat profile is the migration surface for chains that have not yet shipped PQ precompiles. The bridge inherits whatever the destination chain provides; it does not force the destination chain to be PQ-capable.

Note that on Ethereum mainnet specifically, the BLS12-381 precompile (EIP-2537, addresses 0x0b-0x11) is available from the Prague hardfork; on Lux EVM chains it is available from genesis. The classical-compat profile uses the BLS12-381 Beam lane only.

Use case 2: Migration testing

During the Warp 2.0 rollout (Phases 2-5 in WARP2_BRIDGE.md), operators may need to keep the v1 EIP-712 oracle path live while the v2 envelope path warms up. The classical-compat profile keeps the v1 path operational for this transition.

The recommended sequence:

  1. Deploy BridgeV2 alongside Bridge with activeProfile = PROFILE_CLASSICAL_COMPAT.
  2. Configure BridgeV2 with the same token allowlist as Bridge.
  3. Migrate TVL gradually from Bridge to BridgeV2 (user-driven, no forced migration).
  4. Once Warp 2.0 envelope path is battle-tested:
    • Switch BridgeV2.activeProfile = PROFILE_QUASAR_STRICT_PQ.
    • Install a PQ verifier in the strict-PQ slot.
    • Deprecate Bridge (pause + emergency-withdraw).

The classical-compat profile is the back-compat handle that lets this sequence run without downtime.

Security tradeoffs

A classical-compat deployment carries classical security properties:

Property Classical-compat Strict-PQ
Quantum break of secp256k1 Forges bridge claims Does not forge
Quantum break of BLS12-381 Forges Beam-lane envelopes Does not forge if Pulse + cert set also active
Quantum break of M-LWE N/A Pulsar lane fails, but Beam + ML-DSA still required if all three are mandatory
Quantum break of R-LWE N/A Corona lane fails, Pulsar lane still required
Verifier downgrade attack Possible by ADMIN_ROLE (no gate refusal) Refused by StrictPQProfileGate at install + EXTCODEHASH allow-list
Operations key compromise Can swap verifiers freely Limited to verifiers in allowedVerifierCodeHashes (set by DEFAULT_ADMIN_ROLE)

For deployments that secure value at risk of quantum advance, strict-PQ is the right choice. For deployments that secure value already at classical risk anyway (because the destination chain is classical, e.g. an Ethereum mainnet bridge), classical-compat is the right choice — adding PQ to a strict-classical bridge would not improve the residual.

Hybrid profile (reserved)

LUX_PROFILE=hybrid is reserved for a future deployment model where the bridge signs with BOTH classical AND PQ in parallel and the destination chain verifies BOTH:

  • Beam BLS (Beam-lane fallback for back-compat)
  • ML-DSA-65 cert set (per-validator PQ identity proof)
  • Pulse Pulsar threshold (per-round PQ threshold proof)

…and admits a claim only if all three verify. This is stronger than strict-PQ because it requires both a classical AND a PQ break to forge.

The hybrid profile is implemented in the off-chain resolver (permits any MPC scheme with a WARN log) but is not yet wired in the on-chain gate — under PROFILE_QUASAR_STRICT_PQ only the PQ schemes are admissible; classical schemes are refused. A future revision will add PROFILE_HYBRID to StrictPQProfileGate with a "Beam-required + any-PQ-lane-required" predicate.

For now, treat hybrid as "classical-compat with the off-chain freedom to also produce PQ signatures for testing".

Migration to strict-PQ

To migrate a live deployment from classical-compat to strict-PQ:

  1. Deploy a PQ verifier contract that implements IVerifier (and IPolicyId) and returns POLICY_MLDSA65_FIPS204 or POLICY_STARK_FRI_SHA3_PQ from policyId().
  2. Verify the bytecode of the PQ verifier on-chain (Etherscan verify or equivalent).
  3. DEFAULT_ADMIN_ROLE (multi-sig + timelock) calls setAllowedVerifierCodeHash(extcodehash(verifier), true) to extend the allow-list.
  4. ADMIN_ROLE calls setActiveProfile(PROFILE_QUASAR_STRICT_PQ, verifier). The call reverts if (a) the verifier is address(0), (b) the verifier's policyId is not in the PQ allow-list, or (c) the verifier's bytecode hash is not allow-listed.
  5. Off-chain: update the API / MPC service config to LUX_PROFILE=quasar-strict-pq. The MPC default flips to double-lattice; classical schemes (ecdsa, cggmp21, frost) are refused.

Both sides MUST be migrated in the same maintenance window. A deployment with LUX_PROFILE=quasar-strict-pq on the off-chain side and PROFILE_CLASSICAL_COMPAT on the on-chain side will fail the KAT (the off-chain double-lattice signature is not a valid input to the classical verifier) — the test suite catches this in mpc/__tests__/signers.test.ts (case "LUX_PROFILE=classical-compat

  • PQ MPC_SCHEME throws clear error (symmetric)").

Rollback

If a strict-PQ deployment hits a verifier bug or an operational issue, the rollback path is:

  1. PAUSER_ROLE calls pause() to stop new claims.
  2. ADMIN_ROLE investigates: is the issue in the PQ verifier (e.g. a precompile bug), in the off-chain signer (e.g. an mpcd crash), or in the envelope verifier?
  3. If safe to do so, ADMIN_ROLE calls setActiveProfile(PROFILE_CLASSICAL_COMPAT, anyVerifier) to fall back to classical-compat. The off-chain config must also flip to LUX_PROFILE=classical-compat.
  4. PAUSER_ROLE calls unpause() once the rollback is verified.

Rollback to classical-compat does NOT bypass the EXTCODEHASH allow-list (the allow-list is profile-independent for the strict-PQ slot — once allow-listed, a verifier can be installed under either profile). It DOES allow a wider set of verifiers in the slot (including BN254 / BLS12-381 ones), which is the back-compat handle.

References

  • WARP2_BRIDGE.md — Phase 1-5 migration plan
  • SPEC.md §3 — Strict-PQ profile gate
  • SPEC.md §4.2 — MPC profile resolution
  • CRYPTOGRAPHER-SIGN-OFF.md — Gate analysis
  • @luxfi/pq-profile-ids — Canonical profile / policy ID constants