pq

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: MIT Imports: 7 Imported by: 0

README

pq

pq defines the strict post-quantum profile that constrains which cryptographic operations a Quasar node accepts.

The package contains no cryptographic primitives. It expresses a policy.

Model

  • Profile is the value. Eight Forbid* flags, one per cryptographic primitive family. nil and the zero value both mean classical semantics; Strict() sets every flag true.
  • Op is the operation a precompile or signer reports. Sixteen stable identifiers cover the EVM precompile families plus polynomial commitments.
  • Refuse is the gate. Every constrained operation calls it once at the top of its entry point and otherwise proceeds in its own lane.

Policy and enforcement are decomplected. The profile does not know how the operations compute; the operations do not know how the profile is selected.

Use

import "github.com/luxfi/pq"

// Bind once at chain bootstrap.
pq.SetActive(pq.Strict())

// Every constrained primitive.
if err := pq.Refuse(pq.OpEcrecover); err != nil {
    return nil, err
}

Presets

Preset Constructor Effect
Strict pq.Strict() Forbids every primitive family.
Permissive pq.Permissive() Admits every operation (zero value also works).

Primitive families

Forbid* flag EVM addresses Op constants
ForbidEcrecover 0x01 OpEcrecover
ForbidP256Verify 0x100 (RIP-7212) OpP256Verify
ForbidSHA256 0x02 OpSHA256
ForbidRIPEMD160 0x03 OpRIPEMD160
ForbidBlake2F 0x09 OpBlake2F
ForbidBn256 0x06, 0x07, 0x08 OpBn256Add, OpBn256ScalarMul, OpBn256Pairing
ForbidBLS12381 0x0b0x11 (EIP-2537) OpBLS12381* (seven)
ForbidKZG 0x0a (Cancun) OpKZGPointEval

Each family has a matching Err*Forbidden error so dashboards and replay tools attribute violations to the right family.

Environment

QUASAR_STRICT_PQ=1 selects Strict via FromEnv. Mainnet binaries set this in their entrypoint.

Versioning

The canonical encoding is versioned. The current version is 2. Renaming or reordering fields requires a version bump and a consensus-level upgrade.

The strict and permissive profile hashes are pinned in pq_test.go. A change to either is consensus-breaking.

strict:     9efdbf424085b0557866c22b0a4e0c48e2ed90c8c9e3f699d17a3e0783cb2128
permissive: 256f4daa37543549e06bd101aab7f21a9b9dff46d322a9392e6b035cf4f0f638

Tests

go test ./... -count=1

Documentation

Overview

Package pq defines the strict post-quantum profile that constrains which cryptographic operations a Quasar node accepts.

The package contains no cryptographic primitives. It expresses a policy:

  • Profile is the value. Eight Forbid* flags, one per cryptographic primitive family. nil and the zero value both mean classical semantics; Strict sets every flag true.

  • Op is the operation a precompile reports. Sixteen stable identifiers cover the EVM precompile families plus polynomial commitments.

  • Refuse is the gate. Every constrained operation calls it once at the top of its entry point and otherwise proceeds in its own lane. The profile does not know how the operations compute; the operations do not know how the profile is selected.

One way only: one variable holds the active profile, one function installs it, one function reads it, one function gates each call.

pq.SetActive(pq.Strict())                          // chain bootstrap
if err := pq.Refuse(pq.OpEcrecover); err != nil    // every precompile

Profile values are immutable. Two helpers construct the canonical presets:

  • Strict forbids every classical primitive family
  • Permissive admits every operation

Profile.Hash is the SHA3-256 of the canonical encoding. Consensus emits this hash into block headers so any observer can verify which profile a block was produced under.

Environment override: when QUASAR_STRICT_PQ is set to a truthy value, FromEnv returns Strict; otherwise it returns Permissive. Mainnet binaries set this in their entrypoint.

Index

Constants

View Source
const EnvVar = "QUASAR_STRICT_PQ"

EnvVar is the single environment variable that selects the active profile at startup. Production binaries read it once in main and install the result via SetActive.

Variables

View Source
var (
	ErrEcrecoverForbidden  = errors.New("ecrecover forbidden by chain security profile (PQ)")
	ErrP256VerifyForbidden = errors.New("p256Verify forbidden by chain security profile (PQ)")
	ErrSHA256Forbidden     = errors.New("sha256 forbidden by chain security profile (PQ)")
	ErrRIPEMD160Forbidden  = errors.New("ripemd160 forbidden by chain security profile (PQ)")
	ErrBlake2FForbidden    = errors.New("blake2F forbidden by chain security profile (PQ)")
	ErrBn256Forbidden      = errors.New("alt_bn128 (BN254) family forbidden by chain security profile (PQ)")
	ErrBLS12381Forbidden   = errors.New("BLS12-381 family forbidden by chain security profile (PQ)")
	ErrKZGForbidden        = errors.New("KZG point evaluation forbidden by chain security profile (PQ)")
)

Family errors. Each names the specific primitive family it refuses; dashboards and replay tools attribute violations to the right family.

View Source
var ErrClassicalAuthForbidden = errors.New(
	"pq: classical authentication forbidden under strict-PQ mode")

ErrClassicalAuthForbidden is the umbrella sentinel returned by every consumer-layer gate when a strict-PQ chain is presented classical authentication material. errors.Is(err, pq.ErrClassicalAuthForbidden) works across luxfi/warp, luxfi/zap, lux/dex, luxfi/evm, lux/fhe — audit pipelines grep ONE identifier across every refusal site in the system.

Per-family precompile errors (ErrEcrecoverForbidden, etc.) live alongside this sentinel in refuse.go; they fire INSIDE a contract execution, this one fires at the chain-level authentication boundary (envelope / order / handshake / tx pool).

Functions

func Refuse deprecated

func Refuse(op Op) error

Refuse reports whether the package-level profile admits op.

Deprecated: call (*Profile).RefuseUnder on the per-chain profile. Retained as a shim that reads the deprecated package-level state.

func RequireMode added in v1.0.3

func RequireMode(evidence PQEvidencer, verify Verify) error

RequireMode is the strict-PQ-only variant: refuses if no PQ evidence is attached regardless of mode. Use this when the CALLER has already decided "I am a strict-PQ consumer, refuse without evidence." Equivalent to:

ValidateMode(ModeStrictPQ, evidence, verify)

Exists as a separate name so call sites that mean "strict-PQ only, no hybrid fallback" are grep-able.

func SetActive deprecated

func SetActive(p *Profile)

SetActive installs the package-level profile.

Deprecated: store the profile per chain (e.g. ChainConfig.PQ) and use (*Profile).RefuseUnder. The package-global path has last-writer-wins semantics across chains hosted in one process.

func ValidateMode added in v1.0.3

func ValidateMode(mode Mode, evidence PQEvidencer, verify Verify) error

ValidateMode is the canonical strict-PQ admission gate. One function dispatches the entire mode policy:

  • ModeClassical: returns nil (PQ evidence ignored).

  • ModeHybrid: evidence present → validate via the supplied Verify; evidence absent → return nil (caller logs stale-PQ warning; classical verification path remains the trust root).

  • ModeStrictPQ: evidence absent → ErrClassicalAuthForbidden; evidence present → validate via the supplied Verify.

verify may be nil — the gate still enforces the "evidence present under strict-PQ" invariant but skips the verification call. Useful for callers that want the gate to run BEFORE the expensive verification work (e.g. a quick reject of envelopes missing MLDSACertSet at the receive boundary; full verification happens later).

evidence may be nil — equivalent to "no PQ material attached" (HasPQEvidence returns false implicitly).

Types

type Mode added in v1.0.2

type Mode int

Mode is the chain-wide strict-PQ posture. A chain pins exactly one Mode at genesis and never flips it at runtime without a hard fork.

  • ModeClassical: BLS Beam / ECDSA tx sigs / secp256k1 SignedOrder trusted as auth root. MLDSACertSet / SignedOrderPQ ignored even if present.

  • ModeHybrid: validates MLDSACertSet / SignedOrderPQ when present, falls back to classical with a stale-PQ warning when absent. Safe migration middle — operator turns ON PQ validation today and turns OFF classical trust later.

  • ModeStrictPQ: REFUSES every classical authentication root. MLDSACertSet REQUIRED on Warp envelopes; SignedOrderPQ REQUIRED on DEX orders; MLDSATxType REQUIRED on EVM tx-pool admission; ML-DSA-65 channel binding REQUIRED on ZAP connections; PN9QP27_STD128Q params REQUIRED on FHE. Canonical Liquid default; strict Lux + Zoo profile.

const (
	// ModeClassical is the default for legacy chains with no
	// ML-DSA validator material yet generated.
	ModeClassical Mode = iota

	// ModeHybrid validates PQ material when present, accepts
	// classical-only with a stale-PQ warning.
	ModeHybrid

	// ModeStrictPQ refuses every classical authentication root.
	ModeStrictPQ
)

func ModeFromPQFlag added in v1.0.2

func ModeFromPQFlag(pq bool) Mode

ModeFromPQFlag lifts a chain-config "pq" boolean (the same flag liquidity/operator writes into /data/configs/chains/<id>/config.json) to a Mode. true → ModeStrictPQ; false → ModeClassical.

Intentionally binary: a chain that wants strict-PQ shouldn't have a fallback path opened by a future operator turning the same flag from true → "hybrid". Strict-PQ is a one-way door. Operators that want hybrid pin the profile via ModeFromString using the explicit "hybrid" string.

func ModeFromString added in v1.0.2

func ModeFromString(s string) (Mode, error)

ModeFromString parses an operator-supplied profile string. Refuses unknown values rather than defaulting; the gate at every layer assumes the mode is well-known.

func (Mode) IsPQAware added in v1.0.2

func (m Mode) IsPQAware() bool

IsPQAware reports whether this mode VALIDATES PQ material when the peer presents it. Both ModeHybrid and ModeStrictPQ return true; ModeClassical ignores PQ material even when present.

func (Mode) IsPostQuantum added in v1.0.2

func (m Mode) IsPostQuantum() bool

IsPostQuantum reports whether this mode REFUSES classical-only authentication. Only ModeStrictPQ returns true; hybrid is PQ- AWARE (validates PQ when present) but not PQ-only.

func (Mode) Profile added in v1.0.2

func (m Mode) Profile() *Profile

Profile returns the EVM-precompile-level Profile that this Mode installs. ModeStrictPQ → Strict() (every classical primitive refused at the precompile boundary); ModeClassical + ModeHybrid → Permissive() (every primitive admitted). Hybrid validates PQ authentication at higher layers (envelope / order / handshake) but doesn't refuse classical primitives inside contracts.

func (Mode) String added in v1.0.2

func (m Mode) String() string

String returns the canonical wire name. Audit pipelines match on these strings; renaming here breaks every downstream consumer.

type Op

type Op uint8

Op names a classical cryptographic primitive a profile may refuse. Op values are categorical at the precompile level: Byzantium and Istanbul variants of the same operation report the same Op.

Stable identifiers. Append-only.

const (
	OpUnknown Op = iota

	// Asymmetric signatures.
	OpEcrecover  // ecrecover (0x01)
	OpP256Verify // p256Verify (0x100, RIP-7212)

	// Hash / compression.
	OpSHA256    // 0x02
	OpRIPEMD160 // 0x03
	OpBlake2F   // 0x09 (Blake2b compression — not BLAKE3)

	// alt_bn128 (BN254).
	OpBn256Add       // 0x06
	OpBn256ScalarMul // 0x07
	OpBn256Pairing   // 0x08

	// BLS12-381 (EIP-2537).
	OpBLS12381G1Add   // 0x0b
	OpBLS12381G1MSM   // 0x0c
	OpBLS12381G2Add   // 0x0d
	OpBLS12381G2MSM   // 0x0e
	OpBLS12381Pairing // 0x0f
	OpBLS12381MapG1   // 0x10
	OpBLS12381MapG2   // 0x11

	// Polynomial commitment.
	OpKZGPointEval // 0x0a (Cancun)
)

The complete set of recognised operations.

func (Op) String

func (o Op) String() string

String returns the canonical name of an op. Unknown values render as "Op(0xNN)" so failures in unfamiliar callers stay identifiable.

type PQEvidencer added in v1.0.3

type PQEvidencer interface {
	HasPQEvidence() bool
}

PQEvidencer is the per-input contract: does the input carry the post-quantum authentication material the gate needs?

  • warp.EnvelopeV2 → HasPQEvidence = HasMLDSACertSet()
  • zap.Attestation → HasPQEvidence = att != nil && len(Sig) > 0
  • dex.SignedOrderPQ → HasPQEvidence = true (the type itself is the evidence)
  • dex.SignedOrder → HasPQEvidence = false (classical, no PQ evidence)
  • lqd tx (MLDSATxType) → HasPQEvidence = tx.Type() == MLDSATxType

Nil-safe: the gate checks for a nil PQEvidencer before calling HasPQEvidence, so consumers can pass nil to mean "no evidence attached at all".

type Profile

type Profile struct {
	// Asymmetric signatures.
	ForbidEcrecover  bool // ecrecover at 0x01 (secp256k1 recovery)
	ForbidP256Verify bool // p256Verify at 0x100 (RIP-7212, secp256r1)

	// Non-SHA3 hash / compression precompiles.
	ForbidSHA256    bool // 0x02
	ForbidRIPEMD160 bool // 0x03
	ForbidBlake2F   bool // 0x09 (Blake2b compression — not BLAKE3)

	// Pairing-friendly curves, per family.
	ForbidBn256    bool // alt_bn128 at 0x06–0x08 (BN254)
	ForbidBLS12381 bool // EIP-2537 at 0x0b–0x11

	// Polynomial commitment.
	ForbidKZG bool // kzgPointEvaluation at 0x0a (Cancun)
}

Profile selects which classical operations the strict-PQ boundary admits. A small immutable value; safe to copy and to publish in block headers via Profile.Hash.

Each Forbid* field names a cryptographic primitive family — not an abstract category. A chain operator typically sets every field at once via Strict; a nil profile means "admit every operation".

Flag layout matches the EVM precompile addresses one-to-one so a node binary can install a profile, the EVM can refuse a precompile, and an observer can read Profile.Hash from a block header — all describing the same eight booleans.

func Active deprecated

func Active() *Profile

Active returns the package-level profile, or nil if none has been installed (classical semantics).

Deprecated: see SetActive.

func DecodeProfile

func DecodeProfile(b [16]byte) (*Profile, bool)

DecodeProfile reconstructs a profile from its canonical encoding. Returns (nil, false) on unknown version or non-zero reserved bytes.

func FromEnv

func FromEnv() *Profile

FromEnv returns Strict when EnvVar is set to a truthy value (case-insensitive) and Permissive otherwise.

func Permissive

func Permissive() *Profile

Permissive returns a profile with every Forbid* flag false. The zero value Profile{} is observationally equivalent and a nil *Profile is the canonical "no profile installed" state — all three admit every operation.

func Strict

func Strict() *Profile

Strict returns the canonical strict-PQ profile: every Forbid* flag is true. Mainnet PQ chains install this.

func (*Profile) Encode

func (p *Profile) Encode() [16]byte

Encode returns the canonical 16-byte encoding of a profile.

byte 0   version (currently 2)
byte 1   flags: bit i = the i'th Forbid* field
bytes 2..15  reserved, must be zero

New primitive families extend the flags region; renaming or reordering fields requires a version bump.

func (*Profile) Forbids

func (p *Profile) Forbids(op Op) bool

Forbids reports whether the profile refuses op. A nil profile returns false (classical semantics).

func (*Profile) Hash

func (p *Profile) Hash() [32]byte

Hash returns the SHA3-256 of the canonical encoding. Block producers publish this hash so observers can confirm which profile a block was produced under.

func (*Profile) RefuseUnder added in v1.0.1

func (p *Profile) RefuseUnder(op Op) error

RefuseUnder reports whether p admits op. Returns nil when admissible (including when p is nil — classical semantics) or family-specific error otherwise. RefuseUnder(OpUnknown) always returns nil.

This is the canonical, per-profile gate. Callers should hold a *Profile (typically from chain config) and call this method directly.

type Verify added in v1.0.3

type Verify func() error

Verify is the per-consumer verification closure. The gate invokes it ONLY after confirming the evidence is present. Each consumer captures whatever inputs the verification needs (transcript hash, pubkey, validator-set membership check) in the closure — the gate doesn't know or care about the shape.

Returning nil = evidence verified. Returning non-nil = the gate propagates the error verbatim.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL