mirror of
https://github.com/luxfi/magnetar.git
synced 2026-07-27 02:53:47 +00:00
- .assurance/budget.txt: the SORRY=4/ADMIT=4 counts are axiom-budget.sh counting the WORDS 'sorry'/'admit' inside the scaffold BANNERS that describe the DELETED cheats, not live tactics. The proof tree has zero theorem/lemma/axiom/sorry/admit (verified). Document this so the budget is not misread as 8 unproven assertions. - THBS-SPEC.md: the v1.0 framing said Combine routes via circl/slhdsa.SignDeterministic directly; the live code uses the internal slhSignAtom engine (byte-identical to circl on the reconstructed master). Point to ASSEMBLE-INVARIANT.md as authoritative; reaffirm RESEARCH-ONLY. No code or proof-state change; M-family claims unchanged (all CONFIRMED).
236 lines
9.2 KiB
Markdown
236 lines
9.2 KiB
Markdown
# Magnetar THBS-SE v1.0 --- Threshold Hash-Based Signatures with Selected-Element Reconstruction
|
|
|
|
> Subpackage: `github.com/luxfi/magnetar/ref/go/pkg/magnetar`
|
|
> Status: v1.0.0 normative spec.
|
|
|
|
## What this is
|
|
|
|
THBS-SE is the ONE permissionless threshold construction shipped by
|
|
Magnetar v1.0. A t-of-n committee produces a SINGLE FIPS 205-shaped
|
|
signature on a slot-bound message; the public combiner role is open
|
|
to any peer (anyone can run Combine); no host is in the TCB at sign
|
|
time.
|
|
|
|
THBS-SE is the architectural counterpart of the per-validator
|
|
standalone primitive (`standalone.go`): both produce FIPS 205 wire
|
|
bytes that any unmodified verifier accepts. The per-validator
|
|
standalone path is the public-BFT primary; THBS-SE is the
|
|
permissionless threshold companion for deployments that need a single
|
|
FIPS 205-shaped signature from a committee instead of N independent
|
|
signatures.
|
|
|
|
## Hard invariant (verbatim from user spec)
|
|
|
|
```
|
|
A revealed value is allowed only if it is also present in the final
|
|
SLH-DSA signature.
|
|
```
|
|
|
|
| Reveal type | Allowed? | Notes |
|
|
|---|---|---|
|
|
| Per-round mask `r_i` | YES | Part of Round-2 PartialSig payload; legitimately public. |
|
|
| Masked share `s'_i = share_i XOR r_i` | YES | Part of Round-2 PartialSig payload; legitimately public. Combined with `r_i` recovers `share_i`, which is information-theoretically uniform-random with fewer than t leaves under GF(257) Shamir. |
|
|
| Round-1 commit hash `D_i` | YES | Public domain-separated hash. |
|
|
| Final FIPS 205 signature bytes | YES | The PUBLIC output of Combine. |
|
|
| `SK.seed` | NO | Forbidden in any party-local persistent form. Each party holds ONLY its Shamir leaf, never the seed. |
|
|
| `SK.prf` | NO | Derived from `SK.seed`. |
|
|
| Future-slot share material | NO | The slot guard refuses any same-slot re-emission. The share envelope is per-slot. |
|
|
| WOTS+ chain bases for unused chains | NO | (v1.1 strict-atom invariant.) |
|
|
| FORS leaves not selected by message digest | NO | (v1.1 strict-atom invariant.) |
|
|
| Public auth-path nodes | YES | Part of every SLH-DSA signature. |
|
|
|
|
## v1.0 ship state (honest open item)
|
|
|
|
The strictest formulation of the invariant --- "no party or combiner
|
|
EVER reconstructs SK.seed, even transiently in memory" --- requires
|
|
the v1.1 strict-atom-assembly path tracked at
|
|
`BLOCKERS.md::MAGNETAR-STRICT-ATOM-V11`.
|
|
|
|
Magnetar v1.0 routes the final FIPS 205 byte production via the
|
|
Magnetar-internal `slhSignAtom` engine (`thbsse_assemble.go`) on a
|
|
master reconstructed by the PUBLIC COMBINER; the emitted bytes are
|
|
byte-identical to `circl/slhdsa.SignDeterministic` on that same master
|
|
(pinned by `TestSlhdsaInternal_ByteEqualToCirclSign` and
|
|
`TestThbsSE_StrictAtom_Combine_ByteIdentityToCircl`). The master is
|
|
briefly present in the public combiner's memory for one Sign call and
|
|
is zeroized before return. The combiner role is PUBLIC --- anyone can
|
|
be the combiner --- and there is no long-lived secret material outside
|
|
party-local Shamir leaves. (The authoritative current description of
|
|
this path is `ASSEMBLE-INVARIANT.md`; this is the RESEARCH-ONLY leg,
|
|
not production --- see §1.0 of `SPEC.md`.)
|
|
|
|
This is materially stronger than a TEE-attested privileged-aggregator
|
|
model (no host is in the TCB) and materially weaker than the strict
|
|
invariant (a peer-local memory-disclosure adversary at exactly the
|
|
combine moment could observe the seed).
|
|
|
|
## Construction (v1.0 wire)
|
|
|
|
### Setup
|
|
|
|
```
|
|
1. Sample SLH-DSA seed S.
|
|
2. Derive (PK, SK) = slhdsa.Scheme().DeriveKey(S).
|
|
3. Byte-wise Shamir-share S across (n, t) committee via GF(257).
|
|
4. Publish PK + committee + (n, t).
|
|
5. Erase S. The dealer is in the TCB FOR SETUP ONLY; once setup
|
|
returns, no party including the dealer holds S.
|
|
```
|
|
|
|
Production deployments run the leaderless PVSS-DKG path via the
|
|
sibling `luxfi/threshold` DKG package and feed the result into the
|
|
same wire-shape share envelope. v1.0 reference ships a
|
|
deterministic-dealer setup (`NewThbsSeKey`) for KAT determinism.
|
|
|
|
### Sign Round 1 (party p_i, in parallel)
|
|
|
|
```
|
|
1. Sample per-round mask r_i.
|
|
2. Compute D_i = cSHAKE256(r_i || s'_i || tau)
|
|
where s'_i = share_i XOR r_i
|
|
and tau = SlotBinding.Encode() || msg || party_id.
|
|
3. Broadcast (party_id, slot_id, D_i, availability_bit).
|
|
4. Persist (slot_id, H(slot_id || msg)) in local SlotGuard.
|
|
Refuse if the same slot_id is already used for a different digest.
|
|
```
|
|
|
|
### Sign Round 2 (party p_i, after Round-1 quorum is observable)
|
|
|
|
```
|
|
1. Reveal PartialSig = r_i || s'_i.
|
|
2. Idempotent replay: re-issuing Round 1+2 for the same
|
|
(slot_id, msg) returns the persisted (R1, R2). A genuine
|
|
equivocation attempt (same slot_id, different msg) raises
|
|
*ThbsSeEquivocationError without emitting the second R1.
|
|
```
|
|
|
|
### Combine (anyone, public)
|
|
|
|
```
|
|
1. Collect >= t Round-2 reveals.
|
|
2. For each, re-derive D_i from PartialSig + slot_binding + msg + party_id.
|
|
Mismatch produces ThbsSeShareEvidence with reason=ThbsSeShareCommitMismatch.
|
|
3. Recover share_i = mask XOR masked_share via byte-wise XOR.
|
|
4. Lagrange-interpolate the seed via thbsseReconstructGF over GF(257).
|
|
5. Bind to slot via FIPS 205 ctx = tagThbsSeCtxPrefix || slot_id (32 bytes);
|
|
total <= 255B per FIPS 205 sec 10.2.
|
|
6. Call slhdsa.SignDeterministic(seed, msg, ctx).
|
|
7. Zeroize seed + intermediate buffers.
|
|
8. Return Signature{Mode, FIPS 205 wire bytes}.
|
|
```
|
|
|
|
### Verify (anyone, public)
|
|
|
|
Standard FIPS 205 `slhdsa.Verify(pk, msg, sig, ctx)`. The v1.0
|
|
reference exposes this as `VerifyBytesCtx` (wire.go) for stateless
|
|
dispatch --- no Magnetar code path on the verifier side.
|
|
|
|
## Slot binding
|
|
|
|
Every signature is bound to the slot tuple
|
|
`(chain_id, epoch, slot, height, committee_id, message_domain)`. The
|
|
binding flows into:
|
|
|
|
1. The cSHAKE256 commit transcript (so the same committee cannot
|
|
reuse the same share material on a different message).
|
|
2. The FIPS 205 ctx string at sign time (so any verifier holding the
|
|
slot tuple can derive ctx independently).
|
|
|
|
Wire layout (canonical, big-endian):
|
|
|
|
```
|
|
chain_id_len(4) || chain_id ||
|
|
epoch(8) || slot(8) || height(8) ||
|
|
committee_id_len(4) || committee_id ||
|
|
message_domain_len(4) || message_domain
|
|
```
|
|
|
|
The 32-byte `slot_id = cSHAKE256(encode(binding), 32, "MAGNETAR-THBSSE-SLOT-V1")`
|
|
is the canonical key in the local slot guard and the payload of the
|
|
FIPS 205 ctx string.
|
|
|
|
## Slashing evidence
|
|
|
|
### Equivocation
|
|
|
|
A party that signs two distinct messages at the same slot binding
|
|
raises `*ThbsSeEquivocationError` carrying:
|
|
|
|
```
|
|
SlotID, PartyID,
|
|
PriorDigest, PriorR1, PriorR2,
|
|
NewDigest, NewR1, NewR2
|
|
```
|
|
|
|
The wire-shaped `ThbsSeEvidence` blob is the on-chain transmission
|
|
shape. `VerifyThbsSeEvidence` is the canonical third-party verifier:
|
|
pure function with no committee state required.
|
|
|
|
### Malformed share
|
|
|
|
Combine emits typed `ThbsSeShareEvidence` for the following
|
|
malformations:
|
|
|
|
- `ThbsSeShareSlotMismatch` --- the Round-2 reveal carries a slot
|
|
ID that does not match the binding.
|
|
- `ThbsSeShareWireSize` --- the PartialSig is the wrong number of
|
|
bytes for the SLH-DSA mode.
|
|
- `ThbsSeShareCommitMismatch` --- the Round-1 commit does not
|
|
re-derive from the Round-2 reveal under the slot binding and
|
|
message.
|
|
|
|
`VerifyThbsSeShareEvidence` is the third-party verifier; pure
|
|
function.
|
|
|
|
## Over-selected committee
|
|
|
|
With `(n, t)` and `n > t`, up to `n - t` silent withholders are
|
|
tolerated. The Lagrange basis is determined by ANY t evaluation
|
|
points, so disjoint sub-quora of size t produce byte-equal final
|
|
signatures (the public combiner is a PURE function of its
|
|
inputs). The 8-th test gate
|
|
`TestThbsSE_PublicCombiner_Determinism` pins this.
|
|
|
|
## API surface
|
|
|
|
- `NewThbsSeKey(params, threshold, committee, rng) -> (*ThbsSeKey, error)`
|
|
- `ThbsSeRound1(params, share, binding, msg, guard, rng) -> (r1, r2, error)`
|
|
- `Combine(input ThbsSeCombineInput) -> (*Signature, []ThbsSeShareEvidence, error)`
|
|
- `ThbsSeSlotGuard` + `NewThbsSeSlotGuard`, `Record`, `Has`
|
|
- `VerifyThbsSeEvidence(params, ev, msgPrior, msgNew, bindingPrior, bindingNew) -> bool`
|
|
- `VerifyThbsSeShareEvidence(params, ev, binding, msg) -> bool`
|
|
|
|
Stateless wire dispatch (consumed by `luxfi/threshold/pkg/thresholdd`):
|
|
|
|
- `VerifyBytes(gpkBytes, message, sigBytes) -> bool`
|
|
- `VerifyBytesCtx(gpkBytes, message, ctx, sigBytes) -> bool`
|
|
|
|
## Test gates
|
|
|
|
The 8 mandated test gates plus 2 bonus correctness checks:
|
|
|
|
| Gate | Test | Status |
|
|
|---|---|---|
|
|
| 1 | `TestThbsSE_Wire_FIPS205Verifiable` (3 modes) | PASS |
|
|
| 2 | `TestThbsSE_RejectSeedReveal` | PASS |
|
|
| 3 | `TestThbsSE_RejectUnselectedFORS` | PASS |
|
|
| 4 | `TestThbsSE_RejectUnselectedWOTS` | PASS |
|
|
| 5 | `TestThbsSE_SlotReuseRejected` | PASS |
|
|
| 6 | `TestThbsSE_OverselectedCommittee` | PASS |
|
|
| 7 | `TestThbsSE_SlotBindingDomainSeparation` | PASS |
|
|
| 8 | `BenchmarkThbsSE_Sign_5of7` (192f < 100 ms/op) | PASS |
|
|
| bonus | `TestThbsSE_PublicCombiner_Determinism` | PASS |
|
|
| KAT | `TestKAT_ThbsSe` (n=7, t=4, 3 modes, 3 messages) | PASS |
|
|
|
|
## Citations
|
|
|
|
- McGrew, D., Wallace, C. and Whyte, W. (2019). *Threshold Hash-Based
|
|
Signatures.* IACR ePrint 2019/793.
|
|
- Cozzo, D. and Smart, N. P. (2019). *Sharing the LUOV.* EUROCRYPT.
|
|
- Bonte, C., Smart, N. P. and Tan, T. (2023). *Threshold SPHINCS+.*
|
|
- Schoenmakers, B. (1999). *A simple publicly verifiable secret
|
|
sharing scheme and its application to electronic voting.* CRYPTO.
|
|
- Shamir, A. (1979). *How to share a secret.* CACM.
|
|
- NIST FIPS 205 (2024). *Stateless Hash-Based Digital Signature
|
|
Standard.*
|