Two-round threshold signing producing FIPS 204 ML-DSA signatures
byte-equal to what a single-party FIPS 204 signer would emit. This
is the Class N1 (signing) deliverable.
Protocol shape (v0.1 reconstruction-aggregator instantiation):
Round 1 (party i): sample per-round mask r_i ← {0,1}^512.
Compute masked share s'_i = share_i ⊕ r_i. Commit
D_i = cSHAKE256(r_i || s'_i || tau_1)
where tau_1 = (sid, kappa, T, i, pk, mu). Note: binding BOTH
halves under D_i closes the masked-half-rotation gap a v0.1
audit would otherwise expose.
MAC D_i to every peer j ∈ T\{i} under the long-lived MAC key
K_{i,j} via KMAC256 (PULSAR-M-SIGN-R1-MAC-V1). Broadcast
(D_i, {MAC_{i,j}}).
Round 2 (party i): verify every received MAC in constant time
against the locally-known K_{i,j}. On MAC mismatch, emit
ComplaintMACFailure. On success, reveal (r_i, s'_i) so peers
can re-derive D_i and combine.
Combine: aggregator gathers t Round-2 reveals, re-derives each
D_i = cSHAKE256(mask || masked || tau_1) and checks it equals
the Round-1 commit. Lagrange-interpolates the byte-sum at x=0
in GF(257), reduces it via the same cSHAKE256 mix the DKG
used, calls mldsa.SignTo on the recovered seed. Returns the
FIPS 204 signature.
The resulting signature satisfies pulsarm.Verify (which is FIPS 204
ML-DSA.Verify verbatim). The v0.2 path replaces the
reconstruction-aggregator step with the Lagrange-linearity sign of
spec/pulsar-m.tex §4.2, where the master secret is never
reconstructed in any party's memory; output bytes are identical.
Tests (vs. n,t ∈ {(5,3),(7,4),(10,7)}):
- RoundTrip: threshold-produced sig passes FIPS 204 Verify
- BadMAC: tampered Round-1 MAC triggers ComplaintMACFailure
- DifferentAttempt: kappa mismatch rejected at Round 2
- TamperedReveal: any byte-flip in Round-2 reveal caught at
Combine via commit-mismatch (both mask and masked are bound)
- DifferentQuorum_SameMessage: two disjoint quorums both produce
valid FIPS 204 sigs on the same message under the same pk
- QuorumTooSmall: empty / non-member rejection
Signed-off-by: Lux Industries <[email protected]>
Pulsar-M
Threshold ML-DSA — a 2-round threshold signing and DKG system whose generated signatures are verifiable by unmodified FIPS 204 ML-DSA verification. Targeting NIST MPTC Class N1 (signing) + N4 (ML keygen / DKG).
Pulsar-M is the Module-LWE sibling of Pulsar
(Ring-LWE). Pulsar's 2-round threshold protocol structure is transplanted onto
ML-DSA-65's polynomial-vector-over-R_q algebra so the per-party-aggregated
signature is bit-identical to a single-party FIPS 204 signature on the same
message + public key.
Status: Research / Reference (not production hardened, not FIPS validated). NIST-profile vectors use SHAKE / cSHAKE / KMAC. Any BLAKE3 deltas are experimental and out-of-scope for the MPTC submission.
Why
NIST FIPS 204 (ML-DSA) is the only NIST-approved post-quantum digital signature in 2026. Threshold variants of ML-DSA are not yet standardized — NIST's Multi-Party Threshold Cryptography project is collecting them now (IR 8214C, January 2026; first call package deadline expected 2026-Nov-16).
Pulsar-M aims to enter that process with a credible, output-interchangeable
threshold ML-DSA candidate — built from the production-tested protocol
machinery already shipping in luxfi/pulsar (R-LWE), retargeted to the
M-LWE primitives ML-DSA itself uses.
The win, if Pulsar-M's Sign output is byte-equal to FIPS 204 Sign:
- Threshold-produced signatures verify under unmodified FIPS 204 verifiers.
- Existing FIPS-validated ML-DSA modules (BoringSSL FIPS, AWS-LC, OpenSSL 3.0 PQ provider) consume Pulsar-M certs without code changes.
- The threshold layer can be Class-N-claimed at NIST without a parallel algorithm standardization track.
Repository layout
pulsar-m/
├── docs/ human-readable design notes
│ ├── threat-model.md
│ ├── nist-mptc-category.md
│ ├── design-decisions.md
│ ├── known-limitations.md
│ └── patent-notes-draft.md
├── spec/ LaTeX technical specification (MPTC package)
│ ├── pulsar-m.tex main spec
│ ├── security-games.tex EUF-CMA / TS-UF / robustness / adaptive corr.
│ ├── system-model.tex network / setup / abort / preprocessing
│ ├── parameters.tex concrete parameter sets, lattice-estimator
│ └── references.bib
├── ref/
│ ├── go/ reference implementation (Go, no assembly)
│ │ ├── cmd/ CLI entry points
│ │ ├── internal/ private helpers
│ │ └── pkg/ public API (sign/, dkg/, primitives/, hash/, fmt/)
│ └── c/ conformance implementation (post-encoding-freeze)
├── vectors/ Known Answer Tests (KATs)
│ ├── kat-v1.json input/output vectors per MPTC §IO-Testing
│ ├── kat-v1.rsp CAVS-style response file (compatibility)
│ └── transcripts/ full-protocol KATs (n,t sweeps)
├── bench/ reproducible benchmark harness
├── test/ fuzz / negative / interoperability tests
├── ct/dudect/ constant-time analysis harness
├── estimator/ lattice-estimator parameter scripts
├── scripts/ build.sh / test.sh / bench.sh / gen_vectors.sh / sbom.sh
└── go.mod
Quickstart
Pulsar-M is in pre-spec stage. Reference impl, vectors, and bench harness ship after the spec freezes. Track docs/known-limitations.md for what's stable vs in-flight.
git clone https://github.com/luxfi/pulsar-m
cd pulsar-m
./scripts/build.sh # checks spec compile + Go build
./scripts/test.sh # runs unit + KAT suite (when available)
./scripts/bench.sh # reproduces bench/results/ (when available)
./scripts/gen_vectors.sh # regenerates KATs from reference impl (when available)
NIST MPTC submission
| package element | location | status |
|---|---|---|
| Technical Specification | spec/pulsar-m.pdf (built from spec/pulsar-m.tex) |
draft |
| Reference Implementation | ref/go/ |
skeleton |
| Report on Experimental Evaluation | bench/results/REPORT.md |
TBD |
| Notes on Patent Claims | docs/patent-notes-draft.md |
TBD |
| Open-source license | LICENSE (Apache-2.0) |
✓ |
| Build/test/benchmark scripts | scripts/ |
skeleton |
| I/O test vectors | vectors/kat-v1.{json,rsp} |
TBD |
Target dates:
- 2026-Jul-20 preview writeup (NIST third preview deadline)
- 2026-Nov-16 package submission (NIST first call deadline)
Relationship to upstream
| repo | what | hash family |
|---|---|---|
| luxfi/corona | academic R-LWE 2-round threshold sig (Boschini–Kaviani–Lai–Malavolta–Takahashi–Tibouchi, ePrint 2024/1113) | BLAKE3 |
| luxfi/pulsar | production fork of Corona with Pedersen DKG + proactive resharing | SHA-3 / cSHAKE256 (canonical), BLAKE3 (legacy) |
| luxfi/pulsar-m (this repo) | Module-LWE sibling: threshold ML-DSA | SHA-3 / SHAKE256 (NIST profile) only |
Security
SECURITY.md describes how to disclose vulnerabilities and what's in-scope for
bug bounty.
License
Apache-2.0 — same as luxfi/pulsar and luxfi/corona. See LICENSE.