HIP-0087: PQ Permit (replaces EIP-2612)
Abstract
HIP-0087 specifies the post-quantum replacement for EIP-2612 permit.
A PQ Permit is a typed off-chain authorization signed by the token
owner's ML-DSA-65 key over a TupleHash256 transcript bound by the
cust string PERMIT-V1 (SP 800-185). The transcript commits to
(profile_id, chain_id, verifying_contract, owner_account_id, spender_account_id, value, nonce, deadline). A contract verifies the
permit via the Z-Chain auth precompile (HIP-0104) or directly via the
ML-DSA.Verify precompile. The replay protection follows EIP-2612's
per-owner nonce pattern. The whole flow is profile-gated; a
strict-PQ chain refuses any classical permit.
Specification
Canonical reference: luxfi/consensus/protocol/auth/permit.go
(auth-pq-surface branch).
PermitMessage {
version uint8 = 0x01
profile_id uint8
identity_scheme_id uint8 = 0x42
hash_suite_id uint8 = 0x01
chain_id uint64
verifying_contract [20]byte // EVM-form
owner_account_id [48]byte
spender_account_id [48]byte
value [32]byte // u256 big-endian
nonce uint64
deadline uint64
}
transcript = TupleHash256(
"PERMIT-V1",
[ version, profile_id, identity_scheme_id, hash_suite_id,
chain_id_be8, verifying_contract, owner_account_id,
spender_account_id, value, nonce_be8, deadline_be8 ],
384
)
signature = ML-DSA.Sign(owner_account_key, transcript)
The owner publishes (PermitMessage, owner_pubkey, signature). The
contract calls a thin verifier (either the Z-Chain proof precompile in
HIP-0104 or a direct ML-DSA.Verify precompile at address 0x14)
and on success increments the owner's nonce and updates
allowance[owner][spender] = value.
Acceptance:
profile_idmatches chain pin; otherwise reject.chain_idmatches.verifying_contract == msg.sender.now <= deadline.nonce == nonces[owner].AccountID == cSHAKE256(profile_be4 || chain_be4 || u8(scheme) || owner_pubkey, 48, "", "LUX_ACCOUNT_ID_V1"), per HIP-0085.ML-DSA.Verify(owner_pubkey, transcript, signature) == true.
Failure of any check is a hard revert; no recovery.
Backwards compatibility
None. EIP-2612 permits are refused under strict-PQ. Token contracts deployed on strict-PQ chains expose only the PQ Permit interface; contracts that need both expose two separate methods and operators disable the classical method on profile activation.
Reference implementation
luxfi/consensus/protocol/auth/permit.go (auth-pq-surface). EVM
precompile binding: luxfi/coreth/core/vm/contracts_pq.go. KAT
vectors: luxfi/consensus/protocol/auth/testdata/permit_v1.json.
Security considerations
deadline bounds the permit's validity; off-chain leakage past the
deadline is harmless. Per-owner nonce prevents replay within validity
window. Cross-chain replay prevented by chain_id. Cross-contract
replay prevented by verifying_contract. Cross-profile replay
prevented by profile_id. ML-DSA-65 signature size (~3309 B) makes
the permit unsuitable for tightly gas-constrained chains but well
within Lux gas budget; profile activation should be accompanied by
gas-schedule update (HIP-0104 §"Gas schedule").
References
- EIP-2612 — secp256k1 baseline (superseded under strict-PQ).
- HIP-0085, HIP-0086 — AccountID and TxAuthEnvelope.
- HIP-0104 — Z-Chain auth precompile.
- NIST FIPS 204, FIPS 202, SP 800-185, SP 800-57.
luxfi/consensus/protocol/auth/permit.go.
Copyright
CC0.