mirror of
https://github.com/luxfi/pq.git
synced 2026-10-30 15:22:02 +00:00
126 lines
4.6 KiB
Go
126 lines
4.6 KiB
Go
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
// gate.go — the strict-PQ admission gate, decomplected.
|
|
//
|
|
// Before this file lived, each consumer (warp.RequireMLDSACertSetForProfile,
|
|
// zap.RequireAttestationForProfile, dex.VerifyOrderForProfile, …)
|
|
// reimplemented the same three-step shape:
|
|
//
|
|
// 1. if mode doesn't require PQ → accept
|
|
// 2. if mode requires PQ and no PQ evidence present → refuse
|
|
// 3. if PQ evidence present → call the consumer's verifier
|
|
//
|
|
// Each copy was a place where a future Mode could grow a fourth
|
|
// arm and silently miss one of the gates. Five copies = five
|
|
// places to update; one gate = one place.
|
|
//
|
|
// Decomplection:
|
|
//
|
|
// • Mode (mode.go) — the value
|
|
// • PQEvidencer + Verify — the per-input contract a consumer
|
|
// fulfills to plug into the gate
|
|
// • ValidateMode + RequireMode — the gates (this file)
|
|
//
|
|
// Each consumer:
|
|
//
|
|
// • Makes its input implement HasPQEvidence() bool
|
|
// • Passes a Verify (func() error) that does the consumer-
|
|
// specific verification work
|
|
// • Calls pq.ValidateMode(mode, evidence, verify) — one line.
|
|
//
|
|
// Concerns separated:
|
|
//
|
|
// verification logic = caller's `verify` closure
|
|
// policy enforcement = pq.ValidateMode
|
|
// evidence shape = caller's HasPQEvidence()
|
|
// error vocabulary = pq.ErrClassicalAuthForbidden (single sentinel)
|
|
//
|
|
// One concept, one function, one error, one mode. No copies.
|
|
|
|
package pq
|
|
|
|
// 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 PQEvidencer interface {
|
|
HasPQEvidence() bool
|
|
}
|
|
|
|
// 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.
|
|
type Verify func() 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).
|
|
func ValidateMode(mode Mode, evidence PQEvidencer, verify Verify) error {
|
|
if !mode.IsPQAware() {
|
|
// Classical: no PQ enforcement, no validation called.
|
|
return nil
|
|
}
|
|
hasEvidence := evidence != nil && evidence.HasPQEvidence()
|
|
if !hasEvidence {
|
|
if mode.IsPostQuantum() {
|
|
return ErrClassicalAuthForbidden
|
|
}
|
|
// Hybrid + no evidence: accept, classical fallback.
|
|
return nil
|
|
}
|
|
// Evidence present: verify if the caller asked us to.
|
|
if verify == nil {
|
|
return nil
|
|
}
|
|
return verify()
|
|
}
|
|
|
|
// 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 RequireMode(evidence PQEvidencer, verify Verify) error {
|
|
return ValidateMode(ModeStrictPQ, evidence, verify)
|
|
}
|