mirror of
https://github.com/lux-apps/papers.git
synced 2026-10-30 18:15:21 +00:00
Ringtail (Boschini et al., ePrint 2024/1113) is third-party prior art; Lux's Corona (Ring-LWE = Module-LWE rank-1) builds on it; Pulsar is Module-LWE rank-k. Retired the conflated boschini2024corona/corona2024/coronago/... bib keys to the single canonical ringtail/corona/pulsar/magnetar keys, rewrote \cite + \bibitem by context, and fixed 'Pulsar (Ring-LWE)' mislabels (Corona where corona.metal/ single-ring/Ringtail-derived; Pulsar where Module-LWE rank-k/ML-DSA). Cryptographer-led.
324 lines
16 KiB
TeX
324 lines
16 KiB
TeX
\section{Implementation}
|
|
\label{sec:implementation}
|
|
|
|
The Magnetar reference implementation is the Go canonical at
|
|
\texttt{github.com/luxfi/magnetar} \cite{magnetar}, tag
|
|
\texttt{v1.1.0}. The artefact ships both the per-validator standalone
|
|
primary primitive
|
|
(\texttt{ref/go/pkg/magnetar/standalone.go} and \texttt{wire.go})
|
|
and the THBS-SE permissionless threshold companion
|
|
(\texttt{ref/go/pkg/magnetar/thbsse.go},
|
|
\texttt{thbsse\_field.go}, \texttt{thbsse\_assemble.go}, and
|
|
\texttt{slhdsa\_internal.go}). This section maps the
|
|
per-validator standalone construction of \S\ref{sec:construction}
|
|
and the THBS-SE strict-atom Combine path of \S\ref{sec:thbsse} to
|
|
the source layout.
|
|
|
|
\subsection{Strict-atom Combine path (v1.1)}
|
|
\label{sec:impl:strict-atom}
|
|
|
|
Magnetar v1.1 closes the v1.0 honest open item
|
|
(\texttt{MAGNETAR-STRICT-ATOM-V11}). The THBS-SE permissionless
|
|
threshold Combine path no longer materialises the FIPS 205 master
|
|
under a named \texttt{seed} / \texttt{SK.seed} / \texttt{SK.prf}
|
|
variable in the public combiner's scope. The strict-atom emit
|
|
path is implemented at
|
|
\texttt{ref/go/pkg/magnetar/thbsse\_assemble.go::assembleSignatureBytes}
|
|
backed by
|
|
\texttt{ref/go/pkg/magnetar/slhdsa\_internal.go::slhSignAtom} (a
|
|
Magnetar-internal FIPS 205 \S5--\S8 walk for the SHAKE family).
|
|
|
|
The discipline statement (load-bearing for v1.1):
|
|
|
|
\begin{quote}
|
|
At every line of \texttt{thbsse\_assemble.go} the audit grep
|
|
\[
|
|
\texttt{grep -rE "SK\textbackslash.seed|SK\textbackslash.prf|sk\_seed|sk\_prf"}
|
|
\]
|
|
returns zero matches. Enforced by
|
|
\texttt{TestThbsSE\_StrictAtom\_NoTransientSeed} (AST walk plus raw-byte
|
|
grep) and \texttt{scripts/checks/strict-atom-ast.sh} (shell gate
|
|
replicating the audit grep verbatim).
|
|
\end{quote}
|
|
|
|
The byte material of the FIPS 205 master expansion exists only as
|
|
positional slices of a SHAKE-expansion output buffer
|
|
(\texttt{derivedMaterial}) consumed by closures
|
|
(\texttt{makePRFClosure}, \texttt{makePRFMsgClosure}) that compose
|
|
the FIPS 205 \S11.2 PRF / PRF\_msg absorb inputs in per-call
|
|
transient buffers (\texttt{prfAbsorb}, \texttt{prfMsgAbsorb}) and
|
|
zeroize them at the closure boundary. The Magnetar-internal
|
|
FIPS 205 \S5--\S8 walk is FIPS 205 byte-conformant to
|
|
\texttt{cloudflare/circl/sign/slhdsa.SignDeterministic}, pinned by
|
|
\texttt{TestSlhdsaInternal\_ByteEqualToCirclSign} per SHAKE mode.
|
|
|
|
The honest residual gap: the bytes of the FIPS 205 master expansion
|
|
DO exist transiently inside \texttt{derivedMaterial} and
|
|
\texttt{derivedExpandInput} for the duration of the SHAKE absorb.
|
|
Closing this gap requires either full MPC over the SHAKE-256 hash
|
|
tree (open research) or a TEE-attested host in the TCB (sibling
|
|
primitive at \texttt{luxfi/threshold/protocols/slhdsa-tee}). The
|
|
strict-atom discipline is the strictest discipline available
|
|
without crossing into either regime. See
|
|
\texttt{ASSEMBLE-INVARIANT.md} for the load-bearing prose
|
|
statement.
|
|
|
|
\subsection{File layout (standalone primary)}
|
|
\label{sec:impl:layout}
|
|
|
|
\begin{table}[H]
|
|
\centering
|
|
\caption{Magnetar v1.1.0 file layout. The per-validator standalone
|
|
primary path lives in \texttt{standalone.go}; the THBS-SE
|
|
permissionless threshold companion lives in \texttt{thbsse.go},
|
|
\texttt{thbsse\_field.go}, \texttt{thbsse\_assemble.go}, and
|
|
\texttt{slhdsa\_internal.go}, and is documented in
|
|
\S\ref{sec:thbsse} and \S\ref{sec:impl:strict-atom}.}
|
|
\label{tab:magnetar-files-standalone}
|
|
\begin{tabular}{lp{8cm}}
|
|
\toprule
|
|
File & Role \\
|
|
\midrule
|
|
\multicolumn{2}{l}{\emph{Shared core}} \\
|
|
\midrule
|
|
\texttt{doc.go} & Package doc + v1.0 dual-primitive summary \\
|
|
\texttt{params.go} & FIPS 205 parameter table; Mode dispatch \\
|
|
\texttt{types.go} & Wire types ($\mathsf{NodeID}$, \texttt{Signature}, \texttt{PublicKey}, \texttt{PrivateKey}) \\
|
|
\texttt{keygen.go} & Single-party FIPS 205 KeyGen / DeriveKey passthrough \\
|
|
\texttt{sign.go} & Single-party FIPS 205 SignDeterministic / SignRandomized dispatch \\
|
|
\texttt{verify.go} & FIPS 205 Verify passthrough (no Magnetar envelope) \\
|
|
\texttt{wire.go} & Canonical MAGS / MAGG envelope codec; stateless \texttt{VerifyBytes} dispatcher \\
|
|
\texttt{transcript.go} & cSHAKE256 helper; THBS-SE share-deal customisation tag \\
|
|
\texttt{zeroize.go} & Constant-time wipes for bytes, uint16 slices, PrivateKeys \\
|
|
\texttt{dispatch.go} & ValidatorBatchTier provenance; ctEqualBytes \\
|
|
\midrule
|
|
\multicolumn{2}{l}{\emph{Per-validator standalone primary primitive}} \\
|
|
\midrule
|
|
\texttt{standalone.go} & \texttt{PerValidatorKeypair}, \texttt{ValidatorSign}, \texttt{ValidatorBatchVerify}, \texttt{ValidatorAggregateCert}, \texttt{BuildAggregateCert}, \texttt{VerifyAggregateCert} \\
|
|
\midrule
|
|
\multicolumn{2}{l}{\emph{THBS-SE permissionless threshold companion}} \\
|
|
\midrule
|
|
\texttt{thbsse.go} & \texttt{NewThbsSeKey}, \texttt{ThbsSeRound1}, \texttt{Combine}, \texttt{ThbsSeSlotGuard}, equivocation + share-evidence verifiers \\
|
|
\texttt{thbsse\_field.go} & Internal GF(257) byte-wise Shamir share arithmetic; \texttt{MaxCommittee257}, \texttt{EvalPointFromID} \\
|
|
\texttt{thbsse\_assemble.go} & \textbf{v1.1 strict-atom Combine path:} \texttt{assembleSignatureBytes}, \texttt{makePRFClosure}, \texttt{makePRFMsgClosure}, \texttt{lagrangeBasisAtZero}. Discharges \texttt{MAGNETAR-STRICT-ATOM-V11}. \\
|
|
\texttt{slhdsa\_internal.go} & \textbf{v1.1 Magnetar-internal FIPS 205 \S5--\S8 walk:} \texttt{slhSignAtom}, \texttt{forsSign}, \texttt{forsPkFromSig}, \texttt{htSign}, \texttt{xmssSign}, \texttt{wotsSign}, \texttt{wotsPkFromSig}. Byte-equal to \texttt{cloudflare/circl/sign/slhdsa.SignDeterministic} per \texttt{TestSlhdsaInternal\_ByteEqualToCirclSign}. \\
|
|
\midrule
|
|
\multicolumn{2}{l}{\emph{Tests}} \\
|
|
\midrule
|
|
\texttt{standalone\_test.go} & 7 tests: round-trip, determinism, independence, batch verify, bad-sig-counted-out, unknown-validator rejection, duplicate-dedupe \\
|
|
\texttt{thbsse\_test.go} & 8 gate tests + 2 bonus: \textbf{TestThbsSE\_Wire\_FIPS205Verifiable}, \textbf{TestThbsSE\_RejectSeedReveal}, \textbf{TestThbsSE\_RejectUnselectedFORS}, \textbf{TestThbsSE\_RejectUnselectedWOTS}, \textbf{TestThbsSE\_SlotReuseRejected}, \textbf{TestThbsSE\_OverselectedCommittee}, \textbf{TestThbsSE\_SlotBindingDomainSeparation}, \textbf{BenchmarkThbsSE\_Sign\_5of7} \\
|
|
\texttt{thbsse\_assemble\_test.go} & \textbf{v1.1 strict-atom invariant gates:} \textbf{TestThbsSE\_StrictAtom\_NoTransientSeed} (AST + raw-byte grep), \textbf{TestSlhdsaInternal\_ByteEqualToCirclSign} (byte-identity to circl per SHAKE mode), \textbf{TestThbsSE\_StrictAtom\_Combine\_ByteIdentityToCircl} (end-to-end), \textbf{TestThbsSE\_StrictAtom\_Combine\_DerivedPkSeedCrossCheck}, \textbf{BenchmarkThbsSE\_V10Equivalent\_Sign\_5of7}, \textbf{BenchmarkThbsSE\_StrictAtom\_Sign\_5of7} \\
|
|
\texttt{wire\_test.go} & MAGS / MAGG roundtrip + \textbf{TestMagnetar\_Wire\_FIPS205Verifiable} (load-bearing byte-identity gate) \\
|
|
\texttt{kat\_test.go} & KAT replay including \textbf{TestKAT\_ThbsSe} (deterministic $n=7,t=4$ vectors) \\
|
|
\texttt{sign\_test.go} / \texttt{keygen\_test.go} & Single-party FIPS 205 dispatch tests \\
|
|
\texttt{race\_off\_test.go} / \texttt{race\_on\_test.go} & \texttt{raceEnabled} build-tag pattern (FIPS 205 heavy paths skipped under race) \\
|
|
\midrule
|
|
\multicolumn{2}{l}{\emph{KAT generator}} \\
|
|
\midrule
|
|
\texttt{cmd/genkat/main.go} & Deterministic KAT regeneration binary \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{table}
|
|
|
|
The strict layering invariant: every standalone-path file's
|
|
package-public surface calls only into lower-level packages (FIPS
|
|
205 via \texttt{circl}, cSHAKE256); no cross-file circular
|
|
dependency. The standalone primitive is implemented top-down with
|
|
\texttt{VerifyAggregateCert} sitting at the highest layer, calling
|
|
\texttt{ValidatorBatchVerify}, which dispatches the
|
|
\texttt{circl.slhdsa.Verify} workers via the same goroutine
|
|
fork-join pattern as \texttt{luxfi/crypto/slhdsa.VerifyBatch}.
|
|
|
|
\subsection{Dependencies}
|
|
\label{sec:impl:deps}
|
|
|
|
The dependency graph is deliberately narrow.
|
|
|
|
\begin{itemize}[leftmargin=2em,nosep]
|
|
\item \texttt{github.com/cloudflare/circl}~v1.6.3 \cite{circl}:
|
|
FIPS 205 SLH-DSA reference (BSD-3-Clause). Magnetar uses
|
|
\texttt{circl/sign/slhdsa} for
|
|
$\textsc{KeyGen}$ / $\textsc{DeriveKey}$ /
|
|
$\textsc{SignDeterministic}$ / $\textsc{SignRandomized}$ /
|
|
$\textsc{Verify}$. The \texttt{circl} implementation is the
|
|
most widely-deployed Go FIPS 205 backend; its constant-time
|
|
posture has been audited under Cloudflare's internal review
|
|
and is used in \texttt{cloudflare/cf-mldsa-precompile} in
|
|
production.
|
|
\item \texttt{golang.org/x/crypto}~v0.32.0: provides
|
|
\texttt{sha3.NewCShake256} for cSHAKE256 and the SP 800-185
|
|
encoding primitives. Standard-library-adjacent dependency.
|
|
\item \texttt{golang.org/x/sys}~v0.29.0 (indirect): platform
|
|
syscall layer used by \texttt{crypto/rand}.
|
|
\end{itemize}
|
|
|
|
The dependency on \texttt{circl} is the load-bearing one. The
|
|
byte-identity claim of
|
|
$\textsc{TestMagnetar\_Wire\_FIPS205Verifiable}$ binds Magnetar's
|
|
output to the exact \texttt{circl}-emitted bytes. If \texttt{circl}
|
|
v1.6.3 were ever deprecated or its FIPS 205 implementation found
|
|
non-conformant, Magnetar would need to switch to a different FIPS 205
|
|
backend. Two alternative backends are candidate: a future Go
|
|
\texttt{crypto/slhdsa} once Go's \texttt{crypto} package gains FIPS
|
|
205 support, and a vendored Magnetar copy of the NIST FIPS 205
|
|
reference C implementation wrapped via cgo. Both are tracked in
|
|
\texttt{TRUSTED-COMPUTING-BASE.md}.
|
|
|
|
\subsection{Build and test discipline}
|
|
\label{sec:impl:build}
|
|
|
|
The reproducible build is:
|
|
\begin{lstlisting}[language=bash,caption={Magnetar reproducible build.}]
|
|
cd ref/go
|
|
GOWORK=off go build ./...
|
|
GOWORK=off go test -short ./pkg/magnetar/...
|
|
GOWORK=off go test -count=1 -run TestMagnetar_Wire_FIPS205Verifiable ./pkg/magnetar/...
|
|
\end{lstlisting}
|
|
|
|
The \texttt{GOWORK=off} flag is required to suppress a workspace
|
|
file that overlays unrelated Lux packages; without it, Go's module
|
|
loader pulls in transitive paths that may not be on the developer's
|
|
machine. (This is a long-standing Lux-internal annoyance, not a
|
|
Magnetar-specific quirk.)
|
|
|
|
The \texttt{-count=1} flag bypasses Go's test cache and forces a
|
|
fresh execution, which is the standard discipline for crypto
|
|
regression gates: a cached PASS on a stale binary is not evidence.
|
|
|
|
The \texttt{-short} flag selects the fast CI configuration; the
|
|
SLH-DSA-heavy test paths run only the cheap configurations under
|
|
\texttt{-short}.
|
|
|
|
\paragraph{Race detector pattern.} The \texttt{raceEnabled}
|
|
build-tag pattern at \texttt{race\_off\_test.go} /
|
|
\texttt{race\_on\_test.go} gates SLH-DSA-heavy tests behind
|
|
\texttt{!race}; the race detector adds 5--10x overhead on the
|
|
SLH-DSA hot path, and the tests are \emph{single-goroutine} so they
|
|
cannot detect a race regardless. The
|
|
$\textsc{TestMagnetar\_Wire\_FIPS205Verifiable}$ gate uses
|
|
\texttt{skipUnderRace} to skip under \texttt{-race}; race-on runs
|
|
cover the cheap wire-codec roundtrip tests. This pattern is shared
|
|
with Pulsar and Corona and is documented in their respective
|
|
READMEs.
|
|
|
|
\paragraph{KAT regeneration.} The KAT generator at
|
|
\texttt{cmd/genkat} accepts a single 48-byte master seed
|
|
(\texttt{masterSeedHex}) and produces deterministic vector files.
|
|
Re-running the binary on a clean checkout produces byte-identical
|
|
output --- this is the deterministic-fixture gate. Drift between
|
|
committed JSON and freshly-regenerated output is a CI failure.
|
|
|
|
\subsection{Headline regression: TestMagnetar\_Wire\_FIPS205Verifiable}
|
|
\label{sec:impl:wirefips}
|
|
|
|
The byte-identity claim is gated by the test at
|
|
\texttt{ref/go/pkg/magnetar/wire\_test.go:124-220}. The test routes
|
|
through the v0.5 per-validator standalone primary path:
|
|
|
|
\begin{lstlisting}[language=go,caption={Excerpt: \texttt{TestMagnetar\_Wire\_FIPS205Verifiable} core assertions.}]
|
|
sk, pk, _ := PerValidatorKeypair(params, newDetReader(seed))
|
|
sigBytes, _ := ValidatorSign(sk, nil, msg)
|
|
sig := &Signature{Mode: mode, Bytes: sigBytes}
|
|
|
|
// Frame both via the canonical wire codec.
|
|
gkWire, _ := MarshalGroupKey(pk)
|
|
sigWire, _ := sig.MarshalBinary()
|
|
|
|
// Strip the 11-byte header to recover the raw FIPS 205 payload.
|
|
fipsPK := extractFIPS205Payload(t, gkWire, wireMagicMagnetarGroupKey)
|
|
fipsSig := extractFIPS205Payload(t, sigWire, wireMagicMagnetarSig)
|
|
|
|
// Verify under cloudflare/circl directly. No magnetar code path
|
|
// is hit on the verify side. This is the load-bearing assertion.
|
|
if !verifyDirectCircl(mode, fipsPK, msg, fipsSig) {
|
|
t.Fatalf("FIPS 205 Verify rejected the unwrapped Magnetar bytes")
|
|
}
|
|
\end{lstlisting}
|
|
|
|
The test runs for all three SHAKE parameter sets (SHAKE-192s,
|
|
SHAKE-192f, SHAKE-256s) and passes under \texttt{-count=1} and
|
|
\texttt{-race}. It is the single test that pins
|
|
\S\ref{sec:security:byte-equality} empirically.
|
|
|
|
\subsection{Configuration-as-data}
|
|
\label{sec:impl:config}
|
|
|
|
The Mode-to-Params dispatch in \texttt{params.go} is a static table
|
|
with no runtime logic:
|
|
\begin{lstlisting}[language=go,caption={Mode dispatch in \texttt{params.go}.}]
|
|
var ParamsM192s = &Params{
|
|
Mode: ModeM192s,
|
|
N: 24,
|
|
PublicKeySize: 48,
|
|
PrivateKeySize: 96,
|
|
SignatureSize: 16224,
|
|
SeedSize: 96,
|
|
ID: slhdsa.SHAKE_192s,
|
|
ShamirPrime: 257,
|
|
}
|
|
\end{lstlisting}
|
|
|
|
The \texttt{Params.Validate()} method checks that a supplied
|
|
\texttt{*Params} matches one of the canonical tables exactly. Any
|
|
deviation --- a tampered \texttt{ShamirPrime}, a wrong
|
|
\texttt{SeedSize}, an unsupported \texttt{Mode} --- is rejected at
|
|
the entry point of \texttt{PerValidatorKeypair},
|
|
\texttt{ValidatorSign}, \texttt{ValidatorBatchVerify}, and
|
|
\texttt{VerifyAggregateCert}. The \texttt{*Params} argument is the
|
|
single point where parameter selection enters the crypto path; this
|
|
matches the ``one and only one way to do everything'' discipline.
|
|
|
|
\subsection{Concurrency posture}
|
|
\label{sec:impl:concurrency}
|
|
|
|
The standalone primary path's
|
|
\texttt{ValidatorBatchVerify} dispatches across
|
|
$\min(\mathit{GOMAXPROCS}, N)$ goroutines above the
|
|
\texttt{verifyAggregatedConcurrentThreshold} constant (currently
|
|
$\sim 16$); below that, the batch runs serial. Each worker
|
|
invokes \texttt{circl.slhdsa.Verify} on a disjoint range of the
|
|
input slices; there is no shared mutable state.
|
|
|
|
\texttt{ValidatorSign} is strictly single-goroutine: the FIPS 205
|
|
signing path is sequential per signature, and a deployment running
|
|
many concurrent signing sessions allocates one signer goroutine per
|
|
session. THBS-SE Combine is also strictly single-goroutine per
|
|
session: the FIPS 205 dispatch dominates the runtime and the
|
|
Lagrange interpolation is small enough that there is no
|
|
parallelization win at the round threshold sizes (typically
|
|
$t \le 21$).
|
|
|
|
\subsection{Lux Polaris integration}
|
|
\label{sec:impl:polaris}
|
|
|
|
Magnetar plugs into the Lux Polaris cert profile at the LSS
|
|
\cite{luxlssmpc} adapter layer. The integration is intentionally
|
|
mirror-symmetric with Pulsar and Corona at the public-BFT path: a
|
|
Polaris signer is the composition $\textsc{PolarisSigner} =
|
|
(\textsc{PulsarSigner}, \textsc{CoronaSigner},
|
|
\textsc{MagnetarSigner})$, with the three lanes signing the same
|
|
Prism-bound transcript. The Magnetar lane's
|
|
$\textsc{MagnetarSigner}$ is the per-validator standalone primitive
|
|
of this paper.
|
|
|
|
The activation cert composition: a Polaris generation transition
|
|
requires three independent per-lane operations in lockstep.
|
|
For Magnetar, this is per-validator rotation of $pk_i$ in the
|
|
registry; the standalone primitive does not have a DKG-style
|
|
``activation cert'' construction (it does not need one --- no
|
|
shared key to activate). The LSS adapter contract
|
|
\cite{luxlssmpc} is primitive-agnostic at its boundary; Magnetar's
|
|
standalone adapter implements an empty $\textsc{Reshare}$
|
|
(rotation is per-validator, not committee-wide) and a
|
|
$\textsc{KeyRotate}$ that signs the old $pk_i$ replacing under
|
|
$sk_i^{\mathrm{old}}$ before installing $(sk_i^{\mathrm{new}},
|
|
pk_i^{\mathrm{new}})$.
|
|
|
|
For THBS-SE (\S\ref{sec:thbsse}), the LSS lifecycle is
|
|
committee-wide and matches the Pulsar / Corona threshold adapters
|
|
shape: a Polaris generation transition rotates the committee's
|
|
threshold material via a fresh \texttt{NewThbsSeKey} ceremony (or
|
|
the production leaderless PVSS-DKG variant in the sibling
|
|
\texttt{luxfi/threshold} package) and re-publishes the new group
|
|
public key under the next-generation LSS adapter.
|