mirror of
https://github.com/luxfi/threshold.git
synced 2026-10-30 15:15:53 +00:00
Closes the magnetar v1.1 strict-atom transient-SHAKE-bytes residual for
the strict-PQ chain profile.
Why
---
magnetar v1.1's strict-atom Combine path (thbsse_assemble.go) reduces
the FIPS 205 master's residency window to a few microseconds inside
the public combiner's SHAKE-expansion buffers. That window is
acceptable for legacy-compat (public combiner, no host in TCB) but
not for strict-PQ deployments (regulated custody, sovereign
settlement, high-value bridge). Closing the residual fully requires
either MPC-over-SHAKE (open research) or a TEE-attested combiner.
This commit takes the second path: route every strict-PQ Combine
through luxfi/threshold/protocols/slhdsa-tee.
What
----
* `protocols/slhdsa-tee/profile.go` --- `ChainSecurityProfile` value
type (LegacyCompat / StrictPQ) + canonical refusal sentinels
(`ErrMagnetarNoTEEAttestation` = `ERR_MAGNETAR_NO_TEE_ATTESTATION`,
`ErrMagnetarStaleAttestation`, `ErrMagnetarInsufficientQuorum`,
`ErrMagnetarSignatureDivergence`).
* `protocols/slhdsa-tee/pool.go` --- `CombinerPool` t-of-n attested-
combiner registry. Each member binds a `Signer` to its
`LastVerifiedReport`; `Attest` refreshes freshness state via
`env.VerifyEvidence` and pins vendor against `KnownIssuers`;
`Combine` selects the first `t` fresh members, drives each signer's
full slhdsa-tee.Sign machinery, and refuses byte-divergent output.
Defaults: Threshold=2, RotationWindow=60s, KnownIssuers={amd.sev.snp}.
* `pkg/thresholdd/magnetar.go` --- scheme-bound profile gate. Single
function `magnetarRefuseUnderStrictPQ` (one function, one place ---
mirrors `precompile/contract.RefuseUnderStrictPQ`). Called at the
top of `Sign` and `Sign_Ctx`. Under strict-PQ both refuse with the
canonical sentinel; only `Sign_TEE` (single-host) and the new
`Combine_TEE` (t-of-n pool) produce signatures. Three new accessors
on the scheme: `SetChainSecurityProfile`, `SetCombinerPool`,
`AttestCombinerMember`.
* `Combine_TEE` --- the canonical strict-PQ surface. Takes a slice of
`PoolCombineMember` (per-member attestation payload + name) + shared
jobID/msg/ctx, drives `CombinerPool.Combine`, returns the agreed
wire bytes + per-member audit signatures.
Composition with the existing resolver gate
-------------------------------------------
The sister-agent resolver gate (`thresholdd.RefuseUnderStrictPQ` +
`ChainProfileResolver`) fires first in the JSON-RPC routing layer
when the per-request `X-Chain-ID` resolves to strict-PQ. The new
scheme-bound gate is the orthogonal second axis: it refuses whenever
the process-wide profile binding says strict-PQ, regardless of
chain-ID resolution state. Both pass = sign proceeds. Either refuses
= hard refusal.
Tests
-----
* `pkg/thresholdd/magnetar_tee_gate_test.go` (5 tests):
- `TestMagnetarCombine_StrictPQProfile_RequiresTEE`
- `TestMagnetarCombine_AttestationVerified_AllowsSign`
- `TestMagnetarCombine_StaleAttestation_RejectsSign`
- `TestMagnetarCombine_TwoOfThreeAttestedCombiners_MatchSig`
- `TestMagnetarCombine_SignatureDivergence_HardRefusal`
* `protocols/slhdsa-tee/pool_test.go` (5 tests):
- `TestCombinerPool_Constructor_Defaults`
- `TestCombinerPool_AddMember`
- `TestCombinerPool_Attest_VendorPin`
- `TestCombinerPool_Combine_FreshnessGate`
- `TestCombinerPool_Combine_ByteEqualityAcrossQuorum`
- `TestCombinerPool_Combine_Divergence`
All 11 new tests green against the committed AMD Milan SEV-SNP
fixture. Full `./pkg/thresholdd/...` + `./protocols/slhdsa-tee/...`
suites pass.
Production attestation
----------------------
Strict-PQ deployments today MUST use SEV-SNP --- the only `cc/attest`
verifier currently production-implemented. TDX + NRAS verifiers
return `ErrNotImplemented` (tracked at lux/mpc#222 stages 2-3) and
the pool's `KnownIssuers` allowlist will admit them once shipped ---
no magnetar-side code change required.
76 lines
3.4 KiB
Go
76 lines
3.4 KiB
Go
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package slhdsatee
|
|
|
|
import "errors"
|
|
|
|
// ChainSecurityProfile is the value the consensus / chain layer
|
|
// publishes to express the residency posture it is willing to tolerate
|
|
// for SLH-DSA Combine.
|
|
//
|
|
// Decomplecting: profile is a value, not a place. The chain layer
|
|
// constructs it from its ChainConfig (e.g. the precompile
|
|
// StrictPQReporter the EVM precompiles use today) and hands it down
|
|
// at Sign call time. No global, no init() hook, no env var. The
|
|
// dispatcher reads it the same way the precompile contract.RefuseUnderStrictPQ
|
|
// helper reads its StrictPQReporter: ONE function, ONE place, ONE
|
|
// canonical refusal sentinel.
|
|
type ChainSecurityProfile int
|
|
|
|
const (
|
|
// ProfileLegacyCompat tolerates the strict-atom commodity-host
|
|
// Combine path. The FIPS 205 master bytes transiently exist in
|
|
// the public combiner's SHAKE-expansion buffers for a few
|
|
// microseconds. Acceptable for permissionless / community
|
|
// validation paths where no host is in the TCB; refused for
|
|
// strict-PQ deployments. This is the default.
|
|
ProfileLegacyCompat ChainSecurityProfile = 0
|
|
|
|
// ProfileStrictPQ requires every Combine to route through a
|
|
// TEE-attested combiner pool. The master bytes only ever exist
|
|
// inside a measured enclave (SEV-SNP / TDX / NRAS attested
|
|
// host) whose binary / firmware match the operator-asserted RIM
|
|
// and chain-validate to the vendor root. No commodity-host
|
|
// fallback; refusal is hard.
|
|
ProfileStrictPQ ChainSecurityProfile = 1
|
|
)
|
|
|
|
// String reports the canonical wire label for the profile.
|
|
func (p ChainSecurityProfile) String() string {
|
|
switch p {
|
|
case ProfileStrictPQ:
|
|
return "strict-PQ"
|
|
case ProfileLegacyCompat:
|
|
return "legacy-compat"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// ErrMagnetarNoTEEAttestation is the canonical refusal returned when
|
|
// a strict-PQ chain profile attempts to Combine without a valid TEE
|
|
// quote. The wire-stable identifier is "ERR_MAGNETAR_NO_TEE_ATTESTATION".
|
|
//
|
|
// Callers SHOULD switch on errors.Is(err, ErrMagnetarNoTEEAttestation)
|
|
// to distinguish profile-gate refusal from upstream attestation /
|
|
// release-gate errors.
|
|
var ErrMagnetarNoTEEAttestation = errors.New("ERR_MAGNETAR_NO_TEE_ATTESTATION: strict-PQ chain profile requires TEE-attested combiner")
|
|
|
|
// ErrMagnetarStaleAttestation is returned when an attested combiner's
|
|
// last successful re-attestation lies outside the configured rotation
|
|
// window. The bytes of the attestation chain-validate but the freshness
|
|
// guarantee has lapsed — operators MUST re-attest before the next sign.
|
|
var ErrMagnetarStaleAttestation = errors.New("ERR_MAGNETAR_STALE_ATTESTATION: combiner attestation outside rotation window")
|
|
|
|
// ErrMagnetarInsufficientQuorum is returned when fewer than the
|
|
// configured threshold-of-attested-combiners agreed on byte-identical
|
|
// output. The pool refuses to surface a signature whose origin is
|
|
// less than t attested combiners.
|
|
var ErrMagnetarInsufficientQuorum = errors.New("ERR_MAGNETAR_INSUFFICIENT_QUORUM: fewer than threshold attested combiners produced matching signature")
|
|
|
|
// ErrMagnetarSignatureDivergence is returned when the configured
|
|
// quorum DID produce signatures but they were not byte-identical.
|
|
// Hard refusal — divergence indicates a misconfigured or compromised
|
|
// combiner; the pool MUST NOT silently pick a winner.
|
|
var ErrMagnetarSignatureDivergence = errors.New("ERR_MAGNETAR_SIGNATURE_DIVERGENCE: attested combiners produced non-matching signature bytes")
|