mirror of
https://github.com/luxfi/magnetar.git
synced 2026-07-27 02:53:47 +00:00
True no-reconstruction threshold SLH-DSA is impossible in the no-dealer /
no-preprocessing model a public, leaderless, permissionless chain needs
(Kondi-Kumar-Vanegas: extractable hash-based signatures cannot be thresholded
by black-box hash use). Stop pretending otherwise.
De-cheat:
- DELETE the circular/vacuous proofs that manufactured false assurance:
Magnetar_N1_Atom_Refinement.ec, Magnetar_N1_SHAKE_Expand.ec,
Magnetar_N4_KeyDeriveStable.ec, lemmas/{Magnetar_CT,SLHDSA_Functional}.ec.
The headline "strict-atom byte-equality" theorem was `apply <axiom that
restates the theorem>`; the Lean side was `sorry`/`:= True`.
- Re-open MAGNETAR-STRICT-ATOM (BLOCKERS): the public combiner DOES
reconstruct the full FIPS 205 master every signature; the v1.1 "closure"
only renamed identifiers. PROOF-CLAIMS / AXIOM-INVENTORY / TCB docs now say
what the code does.
- Re-label the name-grep "strict-atom" / "CT" checks as identifier-hygiene
lint, NOT security or constant-time properties.
- PVSS-DKG open-reveal (publishes the master to any observer) is gated as a
TEST-ONLY path with HONEST LIMITATIONS; production does not rely on it.
- Remove dead htRootCompute; staticcheck clean.
Honest three-leg posture (SPEC 1.0, BLOCKERS):
- Permissionless production = INDEPENDENT FIPS 205 sigs + the weighted quorum
certificate (luxfi/consensus), optionally STARK/FRI-compressed (luxfi/p3q).
No key sharing, no reconstruction.
- Trusted-hardware custody = TEE-attested combiner (trust-relocation, NOT MPC).
- THBS-SE = RESEARCH-ONLY (transient seed reconstruction at the combiner);
the T-SLH-DSA-MPC track (MPC over SHAKE) is the other research escape hatch.
Tests green (CGO=1, 71s). Net -923 lines.
271 lines
11 KiB
Markdown
271 lines
11 KiB
Markdown
# Magnetar --- Deployment runbook
|
|
|
|
Operator-facing deployment guidance for Magnetar v1.0. **READ THIS
|
|
BEFORE DEPLOYING.**
|
|
|
|
## 0. Choose your primitive
|
|
|
|
Magnetar v1.0 ships **TWO distinct signing primitives** with
|
|
different trust models. Picking the wrong one is the single largest
|
|
deployment risk.
|
|
|
|
| Primitive | Trust model | No-leak / sound | Use case |
|
|
|---|---|---|---|
|
|
| `ValidatorSign` + `VerifyAggregateCert` (per-validator standalone, `standalone.go`) | Per-validator standalone keys, no shared seed, NO reconstruction | YES (SOUND) | **Lux public-BFT validator quorum (PRODUCTION DEFAULT)** |
|
|
| TEE-attested combiner pool (`luxfi/threshold/protocols/slhdsa-tee`) | Seed reconstructed INSIDE a measured enclave on t hosts; trust-relocation (adds a host to the TCB), NOT MPC | YES under attested-hardware trust | Opt-in custody / single-signature certificates |
|
|
| `NewThbsSeKey` + `ThbsSeRound1` + `Combine` (THBS-SE, `thbsse.go`) | t-of-n committee, public combiner that **RECONSTRUCTS the full FIPS 205 master every signature** | NO --- RESEARCH-GRADE; whoever runs Combine sees the seed (a host IS effectively in the TCB) | Permissionless threshold ONLY where the combiner host is trusted by policy. NOT no-leak. |
|
|
|
|
### Quick decision
|
|
|
|
- **Public-BFT consensus on a Lux chain** (mainnet, testnet, devnet,
|
|
white-label) --> per-validator standalone. The consensus layer
|
|
collects N validator signatures into a `ValidatorAggregateCert`
|
|
and counts valid signers; the quorum policy decision lives at the
|
|
consumer. Wire cost is `N x |sigma|`; Z-Chain Groth16 rollup
|
|
compresses to ~192 bytes.
|
|
|
|
- **Permissionless threshold signing for verifier-side
|
|
single-signature certificates** --> THBS-SE. Read sec 2 below for
|
|
the v1.0 honest open item on transient seed reconstruction at the
|
|
public combiner.
|
|
|
|
- **HSM-attested MPC custody** (M-Chain bridge custody, A-Chain
|
|
confidential compute) --> NOT in this package. Operator-controlled
|
|
MPC custody uses TEE-attestation-gated variants shipped under
|
|
`luxfi/threshold/protocols/{slhdsa,mldsa,rlwe}-tee` via the
|
|
`lux/mpc` sibling tree.
|
|
|
|
## 1. Per-validator standalone (production primary)
|
|
|
|
### 1.1 Keygen
|
|
|
|
Each validator runs `PerValidatorKeypair` ONCE on its own host:
|
|
|
|
```go
|
|
sk, pk, err := magnetar.PerValidatorKeypair(params, rng)
|
|
```
|
|
|
|
- `params` SHOULD be `magnetar.ParamsM192s` (smallest signatures at
|
|
NIST PQ category 3, >=192-bit classical security). Use
|
|
`ParamsM192f` only if signing latency is the dominant constraint
|
|
and you can absorb the 2.2x larger signature; use `ParamsM256s`
|
|
only if you need category 5 (>=256-bit classical security).
|
|
- `rng` SHOULD be `crypto/rand.Reader` (nil also works; the function
|
|
defaults to `crypto/rand.Reader`). Pass a deterministic reader
|
|
ONLY for KAT reproducibility.
|
|
|
|
Persist `(sk, pk)` durably. Losing `sk` means dropping out of the
|
|
quorum until rotating to a fresh keypair. Standard HSM-backed key
|
|
storage applies; CIRCL slhdsa private keys are ~96 bytes at
|
|
ModeM192s.
|
|
|
|
Register `pk` in the validator-set commitment via the consensus
|
|
layer's normal key-registration ceremony.
|
|
|
|
### 1.2 Signing
|
|
|
|
```go
|
|
sig, err := magnetar.ValidatorSign(sk, nil, message)
|
|
```
|
|
|
|
Pass `nil` for the RNG to get the deterministic variant (FIPS 205
|
|
SignDeterministic). The output is the raw `params.SignatureSize`
|
|
bytes; the consensus layer's wire format wraps them.
|
|
|
|
Bind chain-id / block-height / consensus epoch into the message
|
|
upstream of this call --- the consensus layer's transcript-hash
|
|
pattern handles this (see `luxfi/consensus` QuasarCert).
|
|
|
|
### 1.3 Verification
|
|
|
|
```go
|
|
cert, _ := magnetar.BuildAggregateCert(params, signers, pubKeys, sigs)
|
|
count, err := magnetar.VerifyAggregateCert(cert, message, knownValidators)
|
|
if count >= quorumThreshold { ... }
|
|
```
|
|
|
|
- `knownValidators` is the consensus layer's authoritative
|
|
`NodeID -> pubkey` map.
|
|
- The function returns the COUNT of valid signers. Unknown /
|
|
pubkey-mismatched signers are counted as INVALID (not fatal --- a
|
|
defense against impersonation that does not abort the entire
|
|
batch).
|
|
- The quorum decision lives at the consumer.
|
|
|
|
## 2. THBS-SE (permissionless threshold)
|
|
|
|
### 2.1 v1.0 honest open item
|
|
|
|
THBS-SE v1.0 routes the final FIPS 205 byte production via a PUBLIC
|
|
COMBINER that holds the seed for the duration of one
|
|
`slhdsa.SignDeterministic` call and zeroizes. The combiner role is
|
|
PUBLIC --- anyone can be the combiner --- and there is no
|
|
long-lived secret material outside party-local Shamir leaves.
|
|
|
|
This is materially stronger than a TEE-attested
|
|
privileged-aggregator model (no host is in the TCB; the combiner
|
|
is a pure function any peer can run on its own substrate).
|
|
|
|
This is materially weaker than the strict invariant "no party or
|
|
combiner EVER reconstructs SK.seed, even transiently in memory" ---
|
|
a peer-local memory-disclosure adversary at exactly the combine
|
|
moment could observe the seed. The strict-atom-assembly path is the
|
|
v1.1 work item tracked at
|
|
`BLOCKERS.md::MAGNETAR-STRICT-ATOM-V11`.
|
|
|
|
**Operational implications.** If your threat model includes
|
|
"peer-local memory-disclosure adversary at the precise sub-second
|
|
combine moment," do NOT deploy THBS-SE v1.0 for new committee
|
|
ceremonies. Either:
|
|
|
|
1. Wait for v1.1 strict-atom-assembly.
|
|
2. Route the threshold signing via the TEE-attested variants at
|
|
`luxfi/threshold/protocols/slhdsa-tee` (operator-controlled MPC
|
|
custody, host in TCB by policy).
|
|
3. Use per-validator standalone instead --- the public-BFT primary
|
|
has no seed reconstruction at any point.
|
|
|
|
### 2.2 Setup
|
|
|
|
```go
|
|
key, err := magnetar.NewThbsSeKey(params, threshold, committee, setupRng)
|
|
```
|
|
|
|
- `params` and the threshold `t` are the same shape as
|
|
per-validator standalone.
|
|
- `committee` MUST be sorted ascending by `NodeID` with distinct
|
|
non-zero IDs.
|
|
- `setupRng` defaults to `crypto/rand.Reader`.
|
|
|
|
v1.0 reference uses a deterministic-dealer setup. The dealer is in
|
|
the TCB FOR SETUP ONLY; once `NewThbsSeKey` returns, no party
|
|
(including the dealer) holds the master seed. The returned
|
|
`*ThbsSeKey` carries: the public key `PublicKey`, the per-party
|
|
shares `Shares[]`, the setup transcript `SetupTranscript`, and
|
|
metadata `(Threshold, N, Params)`.
|
|
|
|
Production deployments needing the leaderless PVSS-DKG variant
|
|
route through the sibling `luxfi/threshold` DKG package; that
|
|
DKG produces shares in the SAME wire-shape envelope. See
|
|
`BLOCKERS.md::MAGNETAR-PVSS-DKG-V11` for the v1.1 closure path.
|
|
|
|
### 2.3 Signing rounds
|
|
|
|
Each party runs:
|
|
|
|
```go
|
|
r1, r2, err := magnetar.ThbsSeRound1(params, share, binding, msg, guard, rng)
|
|
```
|
|
|
|
- `binding` is the slot tuple
|
|
`(chain_id, epoch, slot, height, committee_id, message_domain)`.
|
|
- `guard` is a `*ThbsSeSlotGuard` the party persists across calls.
|
|
- Idempotent replay: re-calling with the same `(binding, msg)`
|
|
returns the persisted `(r1, r2)`. A genuine equivocation attempt
|
|
(same slot, different message) returns
|
|
`*ThbsSeEquivocationError` carrying slashable evidence.
|
|
|
|
The party broadcasts `r1` (the commit) and, after the Round-1
|
|
quorum is observable, `r2` (the reveal).
|
|
|
|
### 2.4 Combine
|
|
|
|
Any peer (validator, block proposer, RPC node, passive watcher) can
|
|
run:
|
|
|
|
```go
|
|
sig, evidences, err := magnetar.Combine(magnetar.ThbsSeCombineInput{
|
|
Key: key, Binding: binding, Message: msg,
|
|
Round1: r1s, Round2: r2s,
|
|
})
|
|
```
|
|
|
|
- `err == nil` and `evidences` empty: the signature was produced
|
|
from `>=t` honest reveals.
|
|
- `err == nil` and `evidences` non-empty: the signature was
|
|
produced from `>=t` honest reveals, AND some malformed reveals
|
|
were observed; the consensus layer can slash the named parties
|
|
via `VerifyThbsSeShareEvidence`.
|
|
- `err == ErrInsufficientQuor`: fewer than `t` valid reveals were
|
|
observed. `evidences` carries the malformed-share evidence the
|
|
consensus layer can act on before retrying.
|
|
|
|
### 2.5 Verification
|
|
|
|
```go
|
|
ok := magnetar.VerifyBytesCtx(gpkBytes, message, ctxFromSlot(binding), sigBytes)
|
|
```
|
|
|
|
The `VerifyBytesCtx` helper is bytes-in, bool-out: it strips the
|
|
MAGS/MAGG envelopes and routes through the FIPS 205 verifier
|
|
verbatim. Any unmodified slhdsa verifier accepts the inner FIPS 205
|
|
bytes.
|
|
|
|
## 3. Operational hardening
|
|
|
|
### 3.1 RNG sourcing
|
|
|
|
`crypto/rand.Reader` on Linux/macOS reads from `getrandom(2)` /
|
|
`/dev/urandom`. On non-trivial deployments, additionally:
|
|
|
|
- Use the OS kernel's hardware-RNG entropy when available
|
|
(`/dev/hwrng` on Linux with a HW source like RDRAND, the TPM, or
|
|
a Yubikey-Bio).
|
|
- For HSM-backed keys, route keygen through the HSM's FIPS
|
|
140-validated RNG.
|
|
|
|
### 3.2 Slot-guard persistence
|
|
|
|
The `ThbsSeSlotGuard` is in-memory only. For production deployments
|
|
that must survive restarts, persist the guard's state to disk
|
|
between Round-1 broadcasts (mirrored from
|
|
`SlotGuard.Record(slotID, digest, r1, r2)` --- ALL four fields are
|
|
required to verify slashing evidence after restart).
|
|
|
|
### 3.3 Equivocation slashing
|
|
|
|
`VerifyThbsSeEvidence` is a pure function. The consensus layer's
|
|
slashing pipeline:
|
|
|
|
1. Receive `ThbsSeEvidence` blob via gossip / on-chain submission.
|
|
2. Verify via `VerifyThbsSeEvidence(params, ev, msgPrior, msgNew,
|
|
bindingPrior, bindingNew)`.
|
|
3. If valid, slash the party named in `ev.PartyID`.
|
|
|
|
The evidence blob is sufficient on its own --- no committee state,
|
|
no re-running of the protocol, no membership-proof gymnastics
|
|
required.
|
|
|
|
### 3.4 Mode-mixing prevention
|
|
|
|
Magnetar's wire codec rejects mode-mismatch at parse time. A
|
|
`Signature` parsed under `ModeM192s` cannot be cross-verified
|
|
against a `GroupKey` parsed under `ModeM256s` --- `VerifyBytesCtx`
|
|
returns false on mismatch.
|
|
|
|
### 3.5 Race detector
|
|
|
|
The Magnetar test suite passes under `go test -race`. SLH-DSA-heavy
|
|
tests self-skip via the `raceEnabled` build tag pattern (race-on
|
|
overhead is 5-10x and the tests carry no inter-goroutine
|
|
concurrency that would benefit from race coverage); race-on runs
|
|
cover the cheap unit tests (transcript, types, wire codec).
|
|
|
|
## 4. Migration from Magnetar v0.x
|
|
|
|
The v0.x `CombineWithSeedReconstruction` and `AggregateSignatures`
|
|
API surfaces have been removed. v0.x consumers migrate as follows:
|
|
|
|
| v0.x | v1.0 |
|
|
|---|---|
|
|
| `magnetar.GenerateValidatorKey` | `magnetar.PerValidatorKeypair` |
|
|
| `magnetar.AggregateSignatures` + `SignedBundle` | `magnetar.BuildAggregateCert` + `ValidatorAggregateCert` |
|
|
| `magnetar.VerifyAggregated` | `magnetar.VerifyAggregateCert` |
|
|
| `magnetar.CombineWithSeedReconstruction` | `magnetar.Combine` (THBS-SE, different semantics --- see THBS-SPEC.md) |
|
|
| `pkg/thbs` (legacy true-HBS) | DELETED. THBS-SE in `pkg/magnetar` is the v1.0 threshold construction. |
|
|
|
|
The signature payload bytes are byte-equal across primitives: a v0.x
|
|
`AggregateSignatures` output and a v1.0 `BuildAggregateCert` output
|
|
carry identical FIPS 205 signature bytes per validator. The only
|
|
difference is the envelope shape.
|