BLOCKERS.md replaces known-limitations — red audit findings

13 production-go-live blockers from 4-red-agent + 1-scientist
adversarial audit (2026-05). The prior known-limitations framing
was too lenient; canonical bug list now at BLOCKERS.md.

The full per-finding spec (file:line refs, attack models, fix
sketches) lives at the submission mirror:
github.com/luxfi/pulsar-mptc/blob/main/BLOCKERS.md

This pulsar library remains shippable as research/reference (89.7%
coverage, Lean OutputInterchange+Unforgeability+Shamir zero-sorry,
KAT replay green). Production strict-PQ go-live is blocked on
closing CR-1 through CR-13.
This commit is contained in:
2026-05-11 22:44:59 -07:00
parent d0d51198e2
commit 3e2ed37580
10 changed files with 157 additions and 220 deletions
+94
View File
@@ -0,0 +1,94 @@
# Pulsar production go-live blockers
This file replaces the prior `spec/known-limitations.tex` framing.
The 4-red-agent + 1-scientist adversarial audit (2026-05) found that
the v0.1 reference implementation in `ref/go/pkg/pulsarm/` has
critical security gaps that block production deployment as a
strict-PQ threshold signer on an open public permissionless
blockchain under nation-state threat model.
The canonical, fully-detailed list with file:line refs, attack
models, and fix sketches lives at the **submission** mirror:
> <https://github.com/luxfi/pulsar-mptc/blob/main/BLOCKERS.md>
This `BLOCKERS.md` file is the production-library mirror. The
13 CRITICAL findings are summarised below; consult the submission
mirror for the canonical specification of each.
## 13 critical findings (summary)
### A. Strict-PQ profile is unenforced
1. **CR-1** `pq.active` is a process-global singleton. Multi-chain
nodes hosting strict-PQ + permissive chains have last-writer-wins
semantics.
2. **CR-2** `RefuseUnderStrictPQ` is dead code — no production
ChainConfig implements `StrictPQReporter`. Every classical
precompile (BLS12-381, KZG, alt_bn128, BabyJubJub, BLAKE3, etc.)
runs on chains pinning strict-PQ profile.
3. **CR-3** `SchemeGate.Classify` is never called from
`network.upgrade()`. Classical secp256k1 TLS certs are accepted
at peer-handshake time on strict-PQ chains.
4. **CR-4** `WitnessSet.MinPolicy` is never wired in production.
Chains silently downgrade from PolicyQuantum → PolicyQuorum
(BLS-only) under partial fault.
### B. PQ handshake is dead code
5. **CR-5** `InitiateHandshake` / `RespondHandshake` /
`FinishInitiatorHandshake` in `network/peer/handshake.go` are
never invoked in production. Every peer connection uses Go
`crypto/tls` (X25519 + secp256k1) — full quantum HNDL exposure.
### C. Pulsar-M threshold layer is hollow
6. **CR-6** DKG commit (`cSHAKE(c_i || blind_i)`) is never opened
in any later round. Malicious dealer biases joint pubkey.
7. **CR-7** `deriveMACKey` derives MAC keys from PUBLIC inputs
(pk + node IDs). Any network observer forges Round-1 messages
→ identifiable-abort fails with NO partition.
8. **CR-8** DKG envelopes are plaintext on the broadcast wire.
Passive surveillance recovers master key in one DKG ceremony.
### D. Validator identity is quantum-forgeable
9. **CR-9** `SignedIP` signs validator-IP gossip with classical TLS
+ BLS12-381. Both quantum-broken. Nation-state forges any
validator's IP claim.
10. **CR-10** Triple-mode QuasarCert is unenforced. `IsTripleMode()`
exists but never gates vote acceptance or cert verification.
11. **CR-11** BFT engine adapter inherits classical `luxfi/bft`
semantics under strict-PQ profile. No PQ-envelope enforcement.
12. **CR-12** `ComputeRoundDigest` does NOT bind effective
`policy_id`. Cross-policy replay possible.
### E. Other
13. **CR-13** Modulo-bias in three independent random samplers
(`photon/emitter.go`, `prism/cut.go`, `prism/stake_weighted_cut.go`).
Nation-state grinding biases committee sampling.
## What ships now
- The Pulsar Go library at v1.0.x as a **research / reference**
implementation. NIST submission paper-ready.
- Lean mechanization of OutputInterchange + Unforgeability + Shamir
(zero `sorry`).
- 89.7% test coverage on the reference impl.
- KAT vectors with deterministic regen and Class N1 E2E interop
via cloudflare/circl FIPS 204 verifier.
## What does NOT ship as production strict-PQ until blockers close
- Lux mainnet as an end-to-end strict-PQ enforceable claim.
- "Drop BLS safely" — current Pulsar threshold layer without BLS
would leave the chain with an unopened-DKG, forgeable-MAC, plaintext-
envelope finality primitive. BLS is presently the floor; dropping
it without closing CR-6/7/8 removes the floor.
Estimated remediation: 3-4 months of senior crypto-engineering plus
outside cryptographer review and KAT regen.
See the submission mirror at <https://github.com/luxfi/pulsar-mptc/blob/main/BLOCKERS.md>
for full per-finding details.
+2 -2
View File
@@ -67,7 +67,7 @@ pulsar/
│ ├── threat-model.md
│ ├── nist-mptc-category.md
│ ├── design-decisions.md
│ ├── known-limitations.md
│ ├── BLOCKERS.md
│ └── patent-notes-draft.md
├── spec/ LaTeX technical specification (MPTC package)
│ ├── pulsar.tex main spec
@@ -96,7 +96,7 @@ pulsar/
## Quickstart
> **Pulsar is in pre-spec stage.** Reference impl, vectors, and bench harness
> ship after the spec freezes. Track [spec/known-limitations.tex](spec/known-limitations.tex)
> ship after the spec freezes. Track [BLOCKERS.md](BLOCKERS.md)
> for what's stable vs in-flight.
```bash
+1 -1
View File
@@ -27,7 +27,7 @@ package pulsarm
// guarantees under M-LWE hardness (a stronger PQ-safe binding
// argument). Both targets are Class N4-eligible.
//
// See spec/known-limitations.tex for the v0.1 → v0.2 path.
// See BLOCKERS.md for the v0.1 → v0.2 path.
import (
"crypto/rand"
+2 -2
View File
@@ -179,7 +179,7 @@ func TestLarge_DKG_AboveCap(t *testing.T) {
// small-committee (GF(257)) path. Cross-committee reshare changes
// the committee root and therefore the cSHAKE256-mixed master seed
// in the v0.1 reconstruction-aggregator instantiation; see
// spec/known-limitations.tex.
// BLOCKERS.md.
func TestLarge_Reshare_SameCommittee_PubInvariant(t *testing.T) {
params := MustParamsFor(ModeP65)
committee := makeLargeCommittee(5)
@@ -301,7 +301,7 @@ func TestLarge_Reshare_SameCommittee_PubInvariant(t *testing.T) {
// is bound to the committee root. This test asserts that the reshare
// ceremony succeeds (new shares are produced) but not that pk is
// preserved across the committee change. The pk-preserving cross-
// committee reshare is on the v0.2 path; see spec/known-limitations.tex.
// committee reshare is on the v0.2 path; see BLOCKERS.md.
func TestLarge_Reshare_E2E(t *testing.T) {
params := MustParamsFor(ModeP65)
+1 -1
View File
@@ -28,7 +28,7 @@ package pulsarm
// pulsar-m.tex §4.2 form. The reconstruction-aggregator path collapses
// the Lagrange-linearity-of-z computation of §4.2 into a single FIPS
// 204 Sign call after share Lagrange reconstruction. This trade is
// documented in spec/known-limitations.tex; the v0.2 path implements
// documented in BLOCKERS.md; the v0.2 path implements
// the pure Lagrange-linearity sign where the secret is never
// reconstructed in any party's memory.
+1 -1
View File
@@ -170,7 +170,7 @@ type Round2Message struct {
// PartialSig is this party's contribution toward the FIPS 204
// signature. In the v0.1 reconstruction-aggregator instantiation
// (see spec/known-limitations.tex), this is the Shamir share of
// (see BLOCKERS.md), this is the Shamir share of
// the seed encrypted under the round transcript hash; the
// aggregator collects t shares, reconstructs the seed, and emits
// a single FIPS 204 signature. The Lagrange-linearity path of
+54
View File
@@ -0,0 +1,54 @@
% Production-go-live blockers + deferred theorems.
% Mirrors the BLOCKERS.md at the repository root, frozen at submission.
% Full canonical list with file:line refs and fix sketches: BLOCKERS.md.
\section{Production go-live blockers and deferred theorems}
\label{sec:blockers}
A 4-red-agent + 1-scientist adversarial audit (2026-05) found 13
production-go-live blockers across the strict-PQ stack. The earlier
``known-limitations'' framing was too lenient; the canonical bug list
is now \texttt{BLOCKERS.md} at the repository root.
\paragraph{Cryptographic gaps that block production deployment.}
\begin{itemize}[leftmargin=*, itemsep=0pt]
\item \textbf{DKG commit-opening (CR-6).} The current Round-1 commit
$\mathrm{cSHAKE256}(c_i \| \mathit{blind}_i)$ is broadcast but
$(c_i, \mathit{blind}_i)$ are never transmitted in any later round.
The commit binds nothing the protocol verifies. Fix: include
$(c_i, \mathit{blind}_i)$ in Round-2 reveal and verify the digest
opens, or drop \texttt{myCommit} entirely.
\item \textbf{Sign-round session-key derivation (CR-7).}
\texttt{deriveMACKey} derives per-pair MAC keys from public inputs.
Any network observer computes the key and forges Round-1 messages.
Fix: ephemeral session-key exchange (Noise / X3DH / ML-KEM-768) bound
to long-term ML-DSA identity.
\item \textbf{DKG envelope confidentiality (CR-8).} Round-1 envelopes
carry per-recipient Shamir shares plaintext on the broadcast wire.
A passive network observer recovers the master key from one DKG
ceremony. Fix: KEM-wrap envelopes (ML-KEM-768) per recipient.
\item \textbf{Adaptive corruption proof.} Round-1 proves TS-UF under
static corruption only; Game ADAPT is the only deferred theorem and
is the production-deployment blocker for nation-state corruption
modelling.
\item \textbf{Cross-domain isolation.} Pulsar (M-LWE) and Corona
(R-LWE) share the algebraic-lattice hardness substrate; co-deploying
them is not defense-in-depth. Real DiD requires a hash-based
(SLH-DSA) layer also wired into the consensus envelope.
\item \textbf{Verify constant-time.} \Cref{sec:verify} asserts but
does not measure constant-time behaviour. The \texttt{ct/dudect/}
harness is scaffolded; results pinned at the submission tag.
\end{itemize}
\paragraph{Round-1 deferred items.}
\begin{itemize}[leftmargin=*, itemsep=0pt]
\item Asynchronous network model: Round-1 assumes synchronous broadcast.
\item HSM-backed share custody: round-2 hardening item.
\item Physical side channels (power, EM, fault): out of scope round-1.
\item Cross-message restart hybrid: round-2 work.
\item Lattice estimator parameter pins: recorded in
\texttt{estimator/README.md}.
\end{itemize}
See \texttt{BLOCKERS.md} (repository root) for the canonical list with
specific file:line references and per-finding fix sketches.
-211
View File
@@ -1,211 +0,0 @@
% Pulsar-M known limitations -- included by pulsar-m.tex.
% Mirrors docs/known-limitations.md (now deleted; this file is the
% canonical LaTeX form, maintained as a living document).
Maintained as a living document. Better that reviewers see the gaps
before they find them.
\subsection{Spec / protocol}
\paragraph{Output interchangeability with FIPS 204 --- DEMONSTRATED
IN v0.1.}
The headline claim --- ``Pulsar-M signatures verify under unmodified
FIPS~204 ML-DSA.Verify'' --- holds for the v0.1 reference. The
\texttt{pulsarm.Verify} entry point dispatches to
\texttt{cloudflare/circl/sign/mldsa\{44,65,87\}.Verify} verbatim; the
threshold-sign output is the same byte-for-byte signature an
unmodified FIPS~204 signer would have produced on the reconstructed
seed. KAT cross-validation lives at
\texttt{ref/go/pkg/pulsarm/kat\_test.go:
TestKAT\_ThresholdSign\_Replay}, which verifies every committed
threshold signature under \texttt{pulsarm.Verify} and therefore
under the underlying FIPS~204 verifier.
\paragraph{v0.1 uses reconstruction-aggregator; v0.2 will use
Lagrange-linearity.}
v0.1 instantiates the threshold protocol via a
\textbf{reconstruction-aggregator} model: a threshold quorum
delivers byte-wise Shamir shares of the FIPS~204 seed to an
aggregator (any honest quorum member), which Lagrange-reconstructs
the master byte-sum in $\mathrm{GF}(257)$, runs the deterministic
cSHAKE256 mix that DKG committed to, and calls FIPS~204 Sign once.
The master seed lives in the aggregator's memory for the duration of
one Sign call.
The \texttt{pulsar-m.tex} \S4.2 \textbf{Lagrange-linearity sign}
path --- each party computes $\mathbf{z}_i = \mathbf{y}_i + c \cdot
\lambda_i \cdot \mathbf{s}_i$ locally and the aggregator sums them,
with the master secret never reconstructed --- is the v0.2 target.
It is mathematically equivalent at the output level (the produced
signature is byte-equal) but information-theoretically stronger
because no party ever holds the master secret.
The v0.1 model is acceptable for the round-1 MPTC submission when
paired with the reshare cadence specified in
\texttt{spec/system-model.tex} (no aggregator holds a secret across
more than one signature). v0.2 ships the stronger soundness property
that adaptive-corruption analysis requires.
\paragraph{Pedersen commitments: RO model in v0.1, M-LWE in v0.2.}
v0.1 uses cSHAKE256 RO-binding commitments
$C_i = \cSHAKE256(c_i \,\Vert\, \mathit{blind}_i)$. v0.2 replaces
them with the $\Rq^k$ Pedersen commitment of
\texttt{spec/pulsar-m.tex} \S3.2:
$C_{i,k} = \mathbf{A} \cdot c_{i,k} + \mathbf{B} \cdot r_{i,k}$. Both
bindings are sound; the v0.2 M-LWE binding is the post-quantum-safe
variant required for the formal proof to reduce to an M-LWE / MSIS
hardness assumption (the v0.1 RO-binding reduces to cSHAKE256
collision resistance, which itself reduces to SHA-3 security).
\paragraph{Per-byte Shamir: dual-field design.}
v0.1 Shamir-shares the FIPS~204 seed byte-wise. The reference
implementation supports two fields:
\begin{itemize}[leftmargin=*, itemsep=0pt]
\item $\mathrm{GF}(257)$ for committees with $n \le 256$ (default
for small validator sets, 64-byte wire share).
\item $\mathrm{GF}(q) = \mathrm{GF}(8\,380\,417)$ for committees with
$256 < n \le q - 1$ (mandatory for any large committee, 128-byte
wire share).
\end{itemize}
Auto-selection logic in
\texttt{ref/go/pkg/pulsarm/types.go::resolveField} picks the
narrowest field that fits. The canonical extreme committee target is
\texttt{TargetCommitteeSize = 1\_111\_111} (seven 1s); see
\texttt{spec/pulsar-m.tex} \S``Large committees'' for the formal
scaling theorem.
v0.2 will reshape this to polynomial Shamir over $\Rq^k$ matching
\texttt{spec/pulsar-m.tex} \S3.3. The output is the same FIPS~204
signature in all cases; the v0.2 reshape is required to make the
Lagrange-linearity sign path work without round-tripping through the
byte-sum representation.
\paragraph{Adaptive corruption proof not yet written.}
Static-corruption security is the baseline target.
Adaptive-corruption security is a stretch goal for the round-1
submission and a hard requirement for round 2.
\paragraph{Mobile-adversary (proactive resharing) proof inherits
Pulsar's gaps.}
Pulsar's \texttt{reshare/} package has documented soundness against
mobile adversaries with deterministic quorum selection (the F10
weakness in HIP-0077). Pulsar-M's \texttt{reshare/} ports this with
beacon-randomized quorum selection, which strictly improves on
Pulsar --- but the formal proof of the strengthened protocol is
still in draft.
\subsection{Implementation}
\paragraph{Reference implementation is Go-only for round 1.}
The C reference comes after spec encoding freeze. Round-1 reviewers
have only the Go reference. NIST MPTC permits any language; a C
companion is strongly recommended for round 2.
\paragraph{No optimized implementation.}
\texttt{ref/go/} is the boring, clear, slow reference. AVX-512 /
NEON / SHA-3-NI optimization is post-submission work. Performance
numbers in the experimental-evaluation report reflect the reference,
not optimized implementations.
\paragraph{No first-order masking.}
Power analysis resistance via masking is post-submission hardening.
The reference is constant-time at the algorithmic level (no
secret-dependent branches, no secret-dependent memory access
patterns in inner loops) but has no power-side-channel
countermeasures.
\paragraph{No HSM integration.}
Production-deployment patches (HSM integration, FIPS 140-3 module
shim) are post-submission. The reference is software-only.
\paragraph{Constant-time analysis is dudect-level.}
Go's compiler is not guaranteed constant-time at the IR level. We
use dudect (\texttt{ct/dudect/}) to check the leading-edge timing
distribution for secret-correlated values. This catches the obvious
leaks but does not provide the formal guarantee of e.g.\
Jasmin-verified C kernels. Formal verification
(Jasmin/Formosa/EasyCrypt) is post-submission.
\subsection{NIST process}
\paragraph{No prior NIST MPTC preview submission.}
We're starting from cold. The third-preview window deadline
(2026-Jul-20) is roughly two months out. Achieving a credible
preview writeup in that window is the immediate gate. If we miss the
third preview, we lose the ability to submit the round-1 package
without a preview, which is a hard requirement per IR 8214C \S5.1.
\paragraph{Patent claims not yet inventoried.}
Pulsar inherits Corona's IP posture. Pulsar-M needs a fresh
patent-claim review for the M-LWE adaptation.
\texttt{spec/patent-notes.tex} collects claims as they're discovered;
final notice for the package is on the critical path for
2026-Nov-16.
\paragraph{External cryptanalysis engagement not yet contracted.}
NIST MPTC submissions are strengthened by independent academic
cryptanalysis. We have not yet engaged a third party. A 6-month
engagement starting July 2026 is the target --- this aligns with the
post-package public-analysis window rather than blocking the package
itself.
\subsection{Repo / build}
\paragraph{Go module initialised.}
\texttt{go.mod} brings in \texttt{cloudflare/[email protected]} (FIPS~204
backbone) and \texttt{golang.org/x/crypto/sha3} (FIPS~202 / SP
800-185 transcripts). Cleanly builds with \texttt{GOWORK=off}.
\paragraph{Spec PDF not yet built.}
\texttt{spec/pulsar-m.tex} is \textbf{complete} as LaTeX source
(1500+ lines covering Front matter / Notation / Preliminaries /
Algorithms / Verification / Reshare / Identifiable abort / Security
analysis / Concrete parameters / Large committees / Encodings /
System model / Implementation considerations / Performance / Test
vectors / Complexity analysis / Deployment / Comparison / Patent
claims / Known limitations / Open questions). The companion files
\texttt{security-games.tex}, \texttt{system-model.tex}, and
\texttt{parameters.tex} are also complete. Building the PDF via
\texttt{latexmk -pdf spec/pulsar-m.tex} is on the active writing
track once the bib resolution lands.
\paragraph{KAT suite generated.}
\texttt{vectors/\{keygen,sign,verify,threshold-sign,dkg\}.json} are
committed. \texttt{scripts/gen\_vectors.sh} re-runs the generator
and verifies that re-running produces byte-identical output (the
deterministic-fixture gate). The CAVS-style \texttt{kat-v1.rsp} ACVP
companion is on the v0.2 path; ACVP support requires the
post-encoding-freeze byte layout.
\paragraph{Lattice-estimator parameter table.}
\texttt{spec/parameters.tex} now includes the classical + quantum
security-level table (numbers inherited verbatim from FIPS~204 \S8
because Pulsar-M does not alter the underlying M-LWE parameters).
The Albrecht et al.\ lattice-estimator script
\texttt{estimator/params.sage} reproduces the table; the estimator
commit hash is pinned at submission in
\texttt{estimator/README.md}.
\subsection{Compared to Pulsar (R-LWE)}
\begin{center}
\begin{tabular}{lll}
\toprule
dimension & Pulsar & Pulsar-M \\
\midrule
Algebraic basis & Ring-LWE & Module-LWE \\
Hash & SHA-3 cSHAKE / BLAKE3 legacy & SHA-3 cSHAKE only \\
FIPS~204 output interchange & no (R-LWE) & yes (target) \\
MPTC class & S1/S4 & N1/N4 \\
Parameter sets & Pulsar-internal & ML-DSA-44/65/87 \\
DKG & Pedersen over $\Rq$ & Pedersen over $\Rq^k$ \\
Reshare & deterministic quorum & beacon-randomized quorum \\
\bottomrule
\end{tabular}
\end{center}
Where Pulsar provides feature X and Pulsar-M doesn't yet, Pulsar-M's
goal is to inherit X verbatim from Pulsar's \texttt{dkg2/} and
\texttt{reshare/} packages with the M-LWE shape change.
+1 -1
View File
@@ -115,7 +115,7 @@ Optional but strongly recommended:
\item Executive summary (1-2 pages) at front of spec.
\item Threat-model document (\texttt{spec/threat-model.tex}).
\item System-model document (\texttt{spec/system-model.tex}).
\item Known-limitations appendix (so reviewers see the gaps before
\item BLOCKERS.md appendix (so reviewers see the gaps before
they find them).
\end{itemize}
+1 -1
View File
@@ -1625,7 +1625,7 @@ the public domain by this submission.
Frozen at submission. Round-1 MPTC limitations and round-2 roadmap
items:
\input{known-limitations}
\input{blockers}
\subsection{Round-1 / round-2 roadmap (inline)}