Aligns cover-sheet revision date with the submission-2026-06-05 cut. Spec PDF is built fresh per cut by scripts/build.sh and bundled into the tarball at cut-time (PDF is gitignored — generated artifact).
41 KiB
NIST MPTC Submission — Pulsar
This document is the cover sheet for the Pulsar submission to the NIST Multi-Party Threshold Cryptography (MPTC) project. It is written for NIST reviewers and points at every artifact a reviewer needs.
The pulsar repository is the single canonical home for the
submission: it carries the Go reference implementation under
ref/go/pkg/pulsar/, the cover sheet, the LaTeX spec, the high-
assurance proof artifacts (EasyCrypt, Lean bridge, Jasmin), the
KAT vector format, the constant-time evidence, and the tarball-cut
tooling. There is exactly one canonical implementation, exactly one
canonical spec, and exactly one canonical proof set. A NIST reviewer
gets a self-contained checkout that does not require network access.
The repository is active (not frozen). The submission tarball is
cut from a tag on main at NIST's deadline via
scripts/cut-submission.sh; reviewer feedback and post-submission
patches land in this same repository so the artifact chain stays
auditable.
Date stamp (this revision): 2026-06-05.
Maturity stamp: v0.1 ready. This submission is not NIST-ratified, not FIPS 140-3 validated, not ACVP-validated. It is the algorithm-level reference plus reproducibility tooling plus high-assurance proof artifacts. ACVP/CAVP/FIPS 140-3 are downstream of this submission (see §"Layer 4" below).
At a glance
| Field | Value |
|---|---|
| Submission name | Pulsar |
| Submitting organisation | Lux Industries, Inc. |
| Algorithm | Threshold ML-DSA-65 (Module-LWE, FIPS 204-aligned) |
| Target NIST MPTC classes | N1 (single-party-compatible threshold signing) + N4 (multi-party key generation with public-key preservation across resharing) |
| Underlying primitive | FIPS 204 ML-DSA-65 |
| Round count | 2 rounds per signature |
| Signature output | Byte-identical to single-party FIPS 204 ML-DSA-65 |
| Repository | https://github.com/luxfi/pulsar (single canonical home: code, spec, proofs, KAT, cut tool) |
| Algorithm source | Same repository, ref/go/pkg/pulsar/ (canonical Go reference). Tagged v1.0.7 (commit 174941a) is the cited stable release at this revision. |
| Tarball cut tool | scripts/cut-submission.sh (tags from main, regenerates KATs, snapshots vendor tree, tars) |
| Submission tag | submission-YYYY-MM-DD (cut from main at deadline) |
| Spec PDF | spec/pulsar.pdf (built via scripts/build.sh) |
| License | Apache-2.0 (code) — see LICENSE |
| Patent posture | Royalty-free grant — see docs/patents.md (public-facing grant text) and docs/patent-claims.md (attorney-prep claim drafts). Lux Industries grants a worldwide, royalty-free, irrevocable patent license to any FIPS 204 ML-DSA-conformant implementation under Apache-2.0 or compatible OSI license, OR any NIST MPTC / PQC / ACVP submission, validation, or interoperability test. Defensive termination mirrors Apache-2.0 §3 and extends to all NIST-standardized PQ signature schemes. |
| Tier | Tier 1: Threshold ML-DSA-65 (this submission). Tier 2: SLH-DSA (FIPS 205) single-party compatibility — consume the FIPS 205 reference directly. Tier 3: Threshold SLH-DSA — research-only track at docs/magnetar.md. |
Headline claim
Every signature produced by a Pulsar threshold ceremony (DKG → Round-1 → Round-2 → Combine) is bit-identical to a signature produced by single-party FIPS 204 ML-DSA-65 on the same message and group public key.
This is the Class N1 claim. A FIPS-validated ML-DSA verifier (BoringSSL FIPS, AWS-LC, OpenSSL 3.0 PQ provider) accepts a Pulsar signature without modification.
Two-variant disclosure. The submission ships both the v0.1 reveal-and-aggregate flow (the runnable Go reference + KAT vectors
- 19/19 N1 interop subtests) AND the v0.2 FSwA Lagrange-linearity flow (the Jasmin sources type-check against this; EasyCrypt N1 reduction is stated against this). Both variants produce a FIPS 204 byte-equal
σ = (c̃, z, h). v0.1 carries a documented reconstruction-aggregator trust caveat (the aggregator briefly reassembles the master ML-DSA seed in memory viaKeyFromSeed, zeroized on every exit perref/go/pkg/pulsar/zeroize.go); v0.2 avoids the reconstruction via Lagrange-linearity but is not yet the reference runtime. SeeBLOCKERS.md"Spec ↔ Go-reference protocol drift" for full disclosure and the per-deployment guidance indocs/cryptographer-sign-off.md§Gates GATE-1. The Class N1 byte-equality theorem in EasyCrypt is stated and proved against the centralized recovery model; both wire variants emitσ = (c̃, z, h)byte-equal to single-party FIPS 204.
Theorem framing — accepted-path correctness. The Class N1
byte-equality theorem is conditional on acceptance: if the threshold
combine (and the single-party comparator) accepts — i.e., passes the
ML-DSA norm and rejection checks and the kappa rejection-sampling loop
converged — then the produced byte string equals the centralized
ML-DSA-65 signature on the same protocol-level inputs, under the
stated layout, algebraic, and byte-walk assumptions. Acceptance
probability is tracked separately through the accept_signing_attempt
predicate and the mldsa_accept_lower_bound operational bound; ML-DSA
rejection sampling remains probabilistic per FIPS 204, and the
deterministic EC model captures the accepted-path conditioning via
the predicate rather than via probabilistic Hoare logic.
Cross-validation evidence: every KAT vector in vectors/ is verified
by three independent ML-DSA implementations
(test/interoperability/):
- The canonical Pulsar reference implementation at
github.com/luxfi/pulsarv1.0.7 (commit174941a), pinned viago.modand snapshotted intovendor/at tarball-cut time - The FIPS 204 reference (pq-crystals Dilithium C reference)
- A third independent implementation (
cloudflare/circlFIPS 204 verifier; BoringSSL FIPS or OpenSSL 3.0 PQ provider when available)
Algorithm scope and audit-response closure
The algorithm being submitted is the merged Pulsar implementation
at luxfi/pulsar v1.0.7 (commit 174941a). v1.0.6 consolidated the
prior submission-grade implementation back into the production
canonical library; v1.0.7 ported the identity-stage uniformity to
the large-committee GF(q) path so a single Go module is both
production and submission across both committee-size paths.
The following audit-response items are closed on both the small-committee (≤256, GF(257)) and large-committee (>256, GF(q)) paths as of v1.0.7:
| ID | Issue | Resolution |
|---|---|---|
| CR-6 | DKG round-1 commit was vacuous | Removed. Each party's coefficient commitment is now bound to the long-term identity public key and to the DKG session identifier. Small path closed in v1.0.6; large path closed in v1.0.7. |
| CR-7 | Threshold-sign session keys were absent | Each (sender, receiver, session-id, transcript) quadruple derives a fresh 32-byte session key from the authenticated ML-KEM-768 + HKDF stage; per-pair SymmetricSession. Small path closed in v1.0.6; large path closed in v1.0.7 (legacyDeriveMACKeyLarge removed). |
| CR-8 | DKG and reshare envelopes shipped in plaintext | Envelopes are now KEM-wrapped (ML-KEM-768) and authenticated under the long-term ML-DSA-65 identity key. Identity layer at pulsar.GenerateIdentity / IdentityKey / IdentityDirectory is mandatory. Small path closed in v1.0.6; large path closed in v1.0.7 via uniform sealEnvelope / sealOpenEnvelope slice API. |
Committee-size coverage: v1.0.7 makes both paths uniform under
the identity stage. KAT vectors in vectors/ exercise the
small-committee path (n ≤ 10, t ≤ 7 in the threshold-sign sweep;
n ≤ 7 in the DKG sweep); the large-committee path has its own
test suite (large_e2e_test.go, largeshamir_test.go) but no
committed KAT vectors in this submission package. KAT vectors are
byte-identical between v1.0.6 and v1.0.7 because the large-path
changes do not touch the small-committee byte stream.
Proof artifact counts (algorithm v1.0.7):
| Artifact | Count | Location |
|---|---|---|
| EasyCrypt files compiling clean, 0/0 admits | 13/13 | proofs/easycrypt/ (gate: scripts/checks/ec-compile.sh + scripts/checks/ec-admits.sh) |
| Lean ↔ EC bridge files (algebraic identities) | 5/5 | ~/work/lux/proofs/lean/Crypto/ + proofs/lean-easycrypt-bridge.md (gate: scripts/check-lean-bridge.sh) |
| jasmin-ct blocking targets on the threshold layer | 3/3 | jasmin/threshold/{round1,round2,combine}.jazz (gate: scripts/checks/jasmin.sh) |
These artifact counts continue to refer to the merged Pulsar
implementation at v1.0.7; the proof artifacts themselves live in
./{proofs/easycrypt, jasmin}/ and are unaffected by
the merge or the v1.0.6 → v1.0.7 large-committee identity-stage
port (the algebraic identities the proofs cover are independent
of the envelope-encryption layer).
What to read first
A reviewer with limited time should read in this order:
SUBMISSION.md(this file) — submission cover sheet, metadata, headline claimspec/pulsar.pdf— full algorithm specification- §1 Introduction + §2 System model
- §3 Parameters (ML-DSA-44 / 65 / 87)
- §4 Protocol (DKG, Round-1, Round-2, Combine, Reshare)
- §5 Security games (EUF-CMA threshold, identifiable abort)
- §6 Output-interchangeability proof (the Class N1 claim)
- §7 NIST MPTC category mapping
docs/proof-claims.md— what's proved vs what's not (narrow claim)docs/proof-axiom-inventory.md— residual EC trust base, per-axiom closure plandocs/tcb.md— EC/Jasmin/OCaml TCBdocs/fips-204-traceability.md— op/lemma → FIPS 204 § mapdocs/evaluation.md— performance + correctness + CT + sec-param evidencedocs/patents.md— royalty-free patent grant textREADME.md— repository layout and how to reproducevectors/README.md— KAT format + cross-validation gatesBLOCKERS.md— closed-finding registry (Open: none); each closure cites commit + tag. Includes the PULSAR-V03-1 v1.0.20 ExpandA-convention fix and the v0.1 / v0.2 / v0.3 wire-variant disclosuredocs/cryptographer-sign-off.md— independent cryptographer review (APPROVED WITH GATES) covering construction soundness, proof-artifact verification, test surface, and the four pre-publish disclosure gates
What to run
The reproducibility gate is scripts/build.sh against the tarball
extract — the entire submission is self-contained, so no network
access is required:
tar xzf submission-YYYY-MM-DD.tar.gz
cd pulsar
scripts/build.sh # builds Go ref + spec PDF
scripts/test.sh # runs unit + KAT + interoperability tests
scripts/bench.sh # produces signature/verification benchmarks
scripts/gen_vectors.sh # regenerates KAT vectors (deterministic)
scripts/build.sh exits non-zero on any failure. CI runs the same
script on every commit; the reproducibility property is the load-
bearing one for the submission.
To cut a fresh tarball (maintainer-side):
scripts/cut-submission.sh # dry-run, no tarball
scripts/cut-submission.sh submission-2026-11-16 # production cut + tag
The cut script verifies a clean tree, verifies all proof gates are green, regenerates the KATs from the in-tree canonical implementation, re-runs the round-trip replay tests, tars the entire submission checkout, and prints the SHA-256.
What's in this package
./
├── SUBMISSION.md # this file
├── README.md # repository layout + how to use
├── LICENSE # Apache-2.0
├── SECURITY.md # threat model + responsible disclosure
├── CONTRIBUTING.md # external-contribution policy (post-submission)
├── go.mod # module: github.com/luxfi/pulsar
├── spec/ # LaTeX specification source
│ ├── pulsar.tex # main spec document
│ ├── parameters.tex # ML-DSA-44/65/87 parameter sets
│ ├── system-model.tex # threshold network / adversary model
│ ├── security-games.tex # EUF-CMA + identifiable-abort games
│ ├── references.bib # bibliography
│ └── pulsar.pdf # built PDF (committed for reviewer convenience)
├── ref/go/ # canonical Go reference implementation
│ ├── pkg/pulsar/ # canonical algorithm sources (this is what
│ │ # is being submitted)
│ └── cmd/genkat/ # KAT vector generator (imports ref/go/pkg/pulsar)
├── vectors/ # KAT test vectors
│ ├── README.md
│ ├── dkg.json # DKG transcripts
│ ├── keygen.json # key-generation vectors
│ ├── sign.json # single-party signing vectors (for cross-validation)
│ ├── threshold-sign.json # threshold-signing vectors
│ ├── verify.json # FIPS 204 verification vectors
│ └── transcripts/ # full-protocol KATs per (n, t) sweep
├── test/
│ ├── negative/ # malformed-input + protocol-deviation tests
│ ├── interoperability/ # cross-validation with FIPS 204 verifiers
│ └── fuzz/ # fuzz harnesses (Go native)
├── ct/dudect/ # constant-time analysis (dudect statistical tests)
├── bench/ # benchmark configurations
├── estimator/ # security-parameter estimator
├── jasmin/ # high-assurance Jasmin sources (initial track)
│ ├── ml-dsa-65/ # libjade single-party baseline (fetched on demand)
│ └── threshold/ # Pulsar threshold layer (stubs)
├── proofs/easycrypt/ # high-assurance EasyCrypt theories (13 files)
│ ├── Pulsar_N1.ec # Class N1 protocol spec + generic theorem
│ ├── Pulsar_N4.ec # Class N4 reshare pk-preservation
│ ├── Pulsar_N1_Memory.ec # byte-memory model (0 axioms)
│ ├── Pulsar_N1_Signature_Codec.ec # FIPS 204 sig codec
│ ├── Pulsar_N1_{Combine,Sign}_Layout.ec # per-side ABI byte layout
│ ├── Pulsar_N1_{Combine,Sign}_Refinement.ec # per-side byte-walk refinement scaffold
│ ├── Pulsar_N1_{Combine,Sign}_Wrapper.ec # per-side wrapper module + bridge lemma
│ ├── Pulsar_N1_Extracted.ec # concrete extracted N1 corollary
│ ├── lemmas/MLDSA65_Functional.ec # FIPS 204 functional ops
│ └── lemmas/Pulsar_CT.ec # constant-time obligations
├── proofs/lean-easycrypt-bridge.md # Lean↔EC algebraic-bridge correspondence
├── scripts/ # per-push + nightly gate orchestrators
│ ├── check-high-assurance.sh, test.sh # per-push (REAL — under 60s)
│ ├── nightly.sh # cron-scheduled REAL-budget gate
│ ├── cut-submission.sh # tarball cut
│ ├── checks/ # per-check independent scripts
│ └── build / bench / gen_vectors / SBOM / extract-jasmin-ec
└── docs/ # design notes + decision-record archive
Class N1 — Output interchangeability
The N1 claim is asserted at four levels of evidence:
| Evidence | Where |
|---|---|
| Algorithmic argument | spec/pulsar.tex §6, Theorem 6.1 |
| Symbolic / Lean proof | proofs/lean/Crypto/Pulsar/OutputInterchange.lean (out-of-repo, separate audit artifact) |
| Test harness | test/interoperability/ runs every KAT through 3 independent verifiers |
| Cross-implementation KATs | vectors/sign.json shares vectors with FIPS 204 reference |
Class N4 — Public-key preservation across resharing
Multi-party proactive resharing preserves the group public key across committee rotations (epoch boundaries), so a single long-lived public identity persists while the secret-share custodians rotate.
| Evidence | Where |
|---|---|
| Algorithmic argument | spec/pulsar.tex §4.5 (Reshare protocol) |
| Symbolic / Lean proof | proofs/lean/Crypto/Pulsar/Shamir.lean (Shamir + ring extension) |
| Test harness | vectors/transcripts/n*-t*-reshare.jsonl carry pre/post-reshare public keys + verify both verify under unmodified ML-DSA |
High-assurance track (Jasmin + EasyCrypt)
Pulsar ships with a Jasmin + EasyCrypt high-assurance track aimed at the same formal-method footing libjade gives the single-party ML-DSA implementation: Jasmin sources whose verified compiler produces bit-identical assembly, and EasyCrypt theories that machine-check both functional correctness and constant-time against the Barthe-Grégoire-Laporte leakage model.
The EasyCrypt track is not theory shells — every lemma in the
13-file tree is closed (admit budget enforced 0/0; see
scripts/checks/ec-admits.sh). What remains in the dependency cone
of the extracted N1 byte-equality theorem is a small, localized
axiom set:
| Artifact | Status | Location |
|---|---|---|
| libjade ML-DSA-65 single-party baseline (Jasmin + EasyCrypt) | Verified upstream; pinned, fetched on demand | jasmin/ml-dsa-65/fetch.sh |
| Pulsar Round-1 commit (Jasmin) | Implemented (~400 lines) | jasmin/threshold/round1.jazz |
| Pulsar Round-2 response (Jasmin) | Implemented (~600 lines) | jasmin/threshold/round2.jazz |
| Pulsar Combine (Jasmin) | Implemented (~400 lines) | jasmin/threshold/combine.jazz |
| Jasmin → EC extraction sanity | Per-push gate | scripts/checks/extraction.sh |
| jasmin-ct threshold (round1, round2, combine) | BLOCKING — green | scripts/checks/jasmin.sh |
| jasmin-ct libjade sign | Allowed-failure under #2 (precise write-up: ct/jasmin-ct-libjade.md) |
same |
| Class N1 byte-equality (concrete extracted corollary) | Proven as a lemma (pulsar_n1_byte_equality_extracted) — composes the per-side wrapper-bridge equivs |
proofs/easycrypt/Pulsar_N1_Extracted.ec |
| Class N1 byte-equality (generic, parametric) | Proven as a theorem (pulsar_n1_byte_equality) inside section ClassN1 |
proofs/easycrypt/Pulsar_N1.ec |
| Class N4 public-key preservation | Proven (concrete ReshareHonest module + reshare_preserves_secret_honest lemma) |
proofs/easycrypt/Pulsar_N4.ec |
| Wrapper bridges (combine + sign) | Proven as lemmas (both — neither is an axiom) | Pulsar_N1_{Combine,Sign}_Wrapper.ec |
| Body separation (combine + sign) | Proven as lemmas | Pulsar_N1_{Combine,Sign}_Refinement.ec |
| Memory frame laws | Proven (0 axioms) | Pulsar_N1_Memory.ec |
| Build wiring | Per-check orchestrator; per-check scripts independently runnable | scripts/check-high-assurance.sh |
Trust footprint of the extracted N1 corollary — 22 named axioms total, each with file:line in EC and Lean. The c_tilde stage has been decomposed and structurally factored: every top-level c_tilde byte-walk obligation is a derived lemma; trust localises into strictly narrower sub-axioms aligned to concrete FIPS 204 computation boundaries.
| Category | Count | Notes |
|---|---|---|
| Stage-level byte-walks (post v12) | 1 | sign_body_z_spec (the only stage-level byte-walk that survives the v8 / v11 / v12 splits) |
| w-stage matrix_a / mask_y sub-axioms (v12) | 4 | combine + sign × {matrix_a, mask_y} |
| Combine-side z extraction (v8) | 2 | combine_body_z_via_aggregation_spec (aggregation shape) + combine_body_partial_responses_spec (per-party byte-walk) |
| w_low sub-axioms (h-stage, v10) | 2 | {combine,sign}_body_w_low_spec |
| Codec mu_input layout (v9) | 4 | combine: 3 per-range; sign: 1 collapsed sign_layout_m_buffer_external_mu |
| Accepted-path no-reject | 2 | {combine,sign}_no_reject_on_accepted_honest_layout |
| Codec roundtrip | 1 | pack_unpack_n1_signature_roundtrip |
| Subtotal — implementation refinement | 17 | byte-walk + codec round-trip + honest-execution no-reject |
| Lean-bridged algebraic (v8) | 5 | lagrange_inverse_eval, reconstruct_linear, shamir_correct, add_share_zeroR, threshold_partial_response_identity |
| Total named axioms | 22 | each with file:line in EC and Lean |
| Derived c_tilde / mu / w / w1 / h / combine_z lemmas | 11+ | *_body_{c_tilde,mu,w,w1,mu_input,h}_spec × 2 sides + combine_body_z_spec |
v8 — combine z-stage Lean-bridged: combine_body_z_spec is no
longer a primitive axiom; it is a derived lemma composing two
narrower combine-side facts (combine_body_z_via_aggregation_spec
on the aggregation shape, combine_body_partial_responses_spec on
per-party byte-walk) with the Lean Lagrange theorem
Crypto.Threshold.Lagrange.threshold_partial_response_identity
(lean/Crypto/Threshold_Lagrange.lean:121). The Lean theorem's
preconditions (uniq quorum, size match, polynomial degree bound,
honest sharing — collectively the threshold interpolation
well-formedness bundle) are now propagated as preconditions of
pulsar_n1_byte_equality and pulsar_n1_byte_equality_extracted.
The first two conjuncts (uniq quorum, size shares = size quorum)
were already present in the wrapper context; v8 threads the
remaining two (degree bound, honest evaluation) through to the
top-level equivalence statements so the bridge backstops the
derivation.
This is not full mechanized closure of the z stage. Trust has
moved from one stage-level byte-walk axiom (combine z) to one
narrower partial-response extraction axiom (a byte-walk) plus a
proven Lean theorem (the algebra). The next narrow target on this
side is combine_body_partial_responses_spec itself — a byte-walk
proving that the round-2 messages decode to per-party
per_party_partial_response values.
Detail on the 22 named axioms — the live source for the per-axiom
file:line is docs/proof-axiom-inventory.md. Summary by category:
- Stage-level byte-walk (1):
sign_body_z_spec. Combine's z-stage is a derived lemma composingcombine_body_z_via_aggregation_speccombine_body_partial_responses_specwith the Lean Lagrange bridgethreshold_partial_response_identity.
- w-stage matrix_a / mask_y sub-axioms (4) at the accepting kappa,
per FIPS 204 §6.2:
{combine,sign}_body_matrix_a_specand{combine,sign}_body_mask_y_spec. The HighBits step is a structural definition on both sides (Pulsar_N1.high_bits_of_w), so*_body_w1_specis derived, and*_body_c_tilde_speccomposes viashake256_to_mu+ the v5 SHAKE composition. - Combine z extraction (2):
combine_body_z_via_aggregation_spec(aggregation shape) +combine_body_partial_responses_spec(per-party byte-walk). With the Lean bridge,combine_body_z_specis derived. - w_low sub-axioms for the h-stage (2):
{combine,sign}_body_w_low_spec. Both*_body_h_specare derived viamake_hint_of_wstructural composition. - ExternalMu codec layout (4): combine side carries three per-range
sub-axioms over the protocol-witness buffer
(
combine_body_mu_input_{prefix,ctx_bytes,m_bytes}_specper FIPS 204 §5.4.1); sign side carries one collapsedsign_layout_m_buffer_external_mubecause sign ownsm_ptr/ctx_ptrin its layout.*_body_mu_specare derived viashake256_to_mu. - Codec round-trip (1):
pack_unpack_n1_signature_roundtripinPulsar_N1.ec—unpack_n1_signature (pack_n1_signature c z h) = (c, z, h)per FIPS 204 §3.5.5. - Honest-execution no-reject (2):
combine_no_reject_on_accepted_honest_layoutandsign_no_reject_on_accepted_honest_layout. Each conditionsstatus = 0on the protocol-levelaccept_signing_attemptpredicate; the kappa rejection-sampling probability boundmldsa_accept_lower_bound(≈ 1 − 2^-128 after the κ-bounded loop) is tracked operationally per the standard FIPS 204 treatment. - Lean-bridged algebraic (5):
lagrange_inverse_eval(Pulsar_N1.ec),add_share_zeroR/reconstruct_linear/shamir_correct(Pulsar_N4.ec), andthreshold_partial_response_identity(Pulsar_N1.ec, v8). Each carries an inline citation comment naming the Lean theorem and file; the bridge correspondence is pinned inproofs/lean-easycrypt-bridge.mdand enforced byscripts/check-lean-bridge.sh.
Beyond the corollary cone: ~21 per-type FIPS 204 codec round-trip
axioms across Pulsar_N1_Sign_Layout, Pulsar_N1_Combine_Layout,
Pulsar_N1_Signature_Codec, and Pulsar_N1 — encode/decode pairs
guarded by wf_* well-formedness predicates with per-component
length identities (sk: ρ/K/tr/s1/s2/t0 per FIPS 204 §3.5.4) and
share-polynomial-vector views. Each reduces to the corresponding
MLDSA65_Functional bit-level pack/unpack identity; the Barbosa-
Barthe-Dupressoir Dilithium mechanization (CRYPTO 2023) is the
template path.
0 section-local module-contract axioms in the extracted corollary's cone — the corollary uses the concrete wrapper modules plus proved bridge lemmas, never the section's declare-axiom hypotheses.
What this gives the NIST reviewer at submission time:
- The libjade single-party verified baseline as the kernel under Pulsar's threshold layer — real, machine-checked, citable.
- Real Jasmin sources for all three threshold-layer routines that call into the pinned libjade kernel.
- The Class N1 byte-equality theorem proven in EasyCrypt as
pulsar_n1_byte_equality_extracted, instantiating the genericpulsar_n1_byte_equalitywith concrete wrapper modules. Trust reduces to 22 named axioms (17 implementation-refinement: 14 byte-walk + 1 signature-codec round-trip + 2 honest-execution no-reject; plus 5 Lean-bridged algebraic), each with file:line in EC and Lean. Per-axiom enumeration:docs/proof-axiom-inventory.md. The composite*_body_{c_tilde,mu,w,w1,mu_input,h}_speclemmas andcombine_body_z_specare derived; the structural identity layerPulsar_N1.high_bits_of_wis a shared definition, not an axiom. - The Class N4 reshare-preservation theorem proven as a
concrete lemma on
ReshareHonest. - jasmin-ct blocking on the threshold layer (round1, round2, combine all CT-clean); libjade sign advisory with a documented fix path.
Each of the 22 axioms is independently attackable through the per-
axiom closure plan in docs/proof-axiom-inventory.md. Sub-step
roadmaps with named obligations live under
proofs/easycrypt/extraction/{combine,sign}-byte-walk-roadmap.md.
scripts/check-high-assurance.sh runs every per-push EC + jasmin-ct
- extraction-sanity + bridge-guard + admit-budget + regression-guard
check at real budget.
scripts/nightly.shruns the heavier 1-hour fuzz + 10⁹-sample dudect runs; results check intoct/dudect/results/.
Path toward full mechanized closure
The Class N1 byte-equality theorem is proved as a lemma in
EasyCrypt. The trust footprint reduces to the 22 named axioms
enumerated in docs/proof-axiom-inventory.md, the ~21 per-type
FIPS 204 codec round-trips, and the EasyCrypt / Jasmin / OCaml
TCB in docs/tcb.md. Each axiom is independently attackable.
Per-axiom closure paths
| Axiom | Closure path |
|---|---|
combine_body_partial_responses_spec |
Byte-walk through round-2 message parsing in extraction; mirrors per_party_partial_response. |
combine_body_z_via_aggregation_spec |
Structural identity through Pulsar_N1.lagrange_aggregate_responses. |
{combine,sign}_body_matrix_a_spec, {combine,sign}_body_mask_y_spec |
BArray ↔ R_q polynomial-view bridge through expand_a / expand_mask. |
{combine,sign}_body_w_low_spec |
Mirror lemma through decompose_vec_k low-bits. |
sign_body_y_spec, sign_body_cs1_spec |
expand_mask + accepted-κ selection / sample_in_ball + vec_l_scale. |
sign_body_z_spec |
y + c·s1 structural composition through mldsa_compute_z. |
{combine,sign}_body_mu_input_*_spec, sign_layout_m_buffer_external_mu |
FIPS 204 §5.4.1 byte-layout proof; per-range slice arithmetic. |
{combine,sign}_no_reject_on_accepted_honest_layout |
κ-rejection-loop model conditioned on accept_signing_attempt. The operational bound mldsa_accept_lower_bound (≈ 1 − 2⁻¹²⁸) tracks the probability per the standard FIPS 204 treatment. |
pack_unpack_n1_signature_roundtrip |
Bridge to MLDSA65_Functional.pack_signature. |
| 5 Lean-bridged algebraic | Either port the Mathlib polynomial-Lagrange theory into EC, or build a checked Lean ↔ EC translation artifact. |
| ~21 per-type FIPS 204 codec round-trips | Concretize abstract types against MLDSA65_Functional bit-level ops, Barbosa-Barthe-Dupressoir (CRYPTO 2023) template. |
What full mechanized closure would buy
The Pulsar N1 byte-equality reduces purely to the verified libjade Jasmin compilation pipeline plus the FIPS 204 standard text — no hand-bridged identities, no operational probability bounds, no abstract op surfaces. The trust footprint composes to: trust the published FIPS 204 standard and the Jasmin verified compiler, and you trust every Pulsar signature.
PQ security validation — evidence layers
A compiling EasyCrypt proof is one layer of PQ security validation, not the whole story. NIST FIPS 204 standardizes ML-DSA for digital signatures and states that it is believed secure even against adversaries with a large-scale quantum computer (https://csrc.nist.gov/pubs/fips/204). A submission claim of "post-quantum secure threshold ML-DSA" needs evidence across six layers:
Layer 1 — Algorithm-level PQ strength (assumed, not proved here)
| Item | This submission |
|---|---|
| Algorithm | ML-DSA (FIPS 204) — not pre-standard Dilithium |
| Parameter set | ML-DSA-65 (NIST security category 3) |
| Claimed strength | Inherits the NIST ML-DSA-65 hardness analysis (Module-LWE / Module-SIS) |
| Hash/XOF usage | SHAKE128 / SHAKE256 per FIPS 204 |
| Encoding | pk / sk / sig / μ / w1 / z / h match FIPS 204 §3.5–§5.4 |
| Rejection sampling | Distribution and acceptance behavior per FIPS 204 §6.2 |
| Domain separation | Context string + pre-hash mode + message binding per FIPS 204 §5.4.1 ExternalMu |
The claim is: this implementation targets FIPS 204 ML-DSA-65 semantics, assuming the ML-DSA-65 hardness assumptions and NIST security-category analysis. It is not a lattice-hardness proof — the EasyCrypt refinement chain is an implementation correctness result.
Layer 2 — Implementation correctness (this is where the EC proof lives)
Refinement chain:
machine code / Jasmin / extracted implementation
refines
low-level EasyCrypt model (Pulsar_N1_{Combine,Sign}_Refinement.ec)
refines
centralized ML-DSA functional model (MLDSA65_Functional.ec)
conforms to
FIPS 204 ML-DSA algorithm
Evidence delivered:
| Area | Evidence |
|---|---|
| EasyCrypt compile | scripts/checks/ec-compile.sh — 13/13 files, 0/0 admits |
| Axiom inventory | This document (residual trust base, below) |
| Derived lemmas | *_body_c_tilde_spec, *_body_mu_spec, *_body_w1_spec, *_body_compute_{components,sig}_spec, *_body_{spec,separation} — all no longer primitive |
| FIPS traceability | Per-axiom mapping below; per-op MLDSA65_Functional bridges |
| Extraction/model gap | Abstract ops central_w, high_bits_of_w, shake_mu_w1, shake256_to_mu, external_mu_layout, pack_n1_signature named with FIPS §-refs |
| Test vectors | vectors/ directory (KAT format); cross-validated against cloudflare/circl FIPS 204 verifier in test/interoperability/ (19/19 N1 subtests PASS); ACVP/CAVP cross-validation is downstream lab work |
| Differential testing | test/interoperability/ — 3 independent ML-DSA verifiers |
| Negative tests | test/negative/ — malformed inputs, boundary cases |
Primitive EasyCrypt trust base after v7:
| Axiom | Category | FIPS section | Residual risk | Closure plan |
|---|---|---|---|---|
combine_body_w_spec |
byte-walk / polynomial | §6.2 | A·y at accepted κ + threshold aggregation | split into ExpandA, ExpandMask, mat_vec_mul + Lean Lagrange bridge for combine |
sign_body_w_spec |
byte-walk / polynomial | §6.2 | A·y at accepted κ | split into ExpandA, ExpandMask, mat_vec_mul |
combine_body_z_spec |
DERIVED LEMMA (v8) | §6.2 | n/a | replaced by combine_body_partial_responses_spec + threshold_partial_response_identity Lean bridge |
combine_body_z_via_aggregation_spec |
byte-walk / aggregation shape (v8) | §6.2 | extracted z's Lagrange shape | mechanical structural identity |
combine_body_partial_responses_spec |
byte-walk / per-party PR (v8) | §6.2 | per-party z_i extraction | narrow byte-walk through round-2 message parsing |
threshold_partial_response_identity |
Lean-bridged algebraic (v8) | §6.2 (FROST) | Lagrange-interpolation response identity | discharged in lean/Crypto/Threshold_Lagrange.lean:121 |
sign_body_z_spec |
byte-walk / response | §6.2 | y + c·s1 at accepted κ | reduce via vec ops + accepted-κ model |
combine_body_h_spec |
byte-walk / hints | §6.2 | MakeHint over aggregated w_low/w_high | bridge to MLDSA65_Functional.vec_k_make_hint |
sign_body_h_spec |
byte-walk / hints | §6.2 | same as combine sans aggregation | same bridge |
combine_body_mu_input_spec |
codec layout | §5.4.1 | ExternalMu byte serialization | byte-level layout proof (mechanical) |
sign_body_mu_input_spec |
codec layout | §5.4.1 | same | same |
combine_no_reject_on_accepted_honest_layout |
protocol acceptance | §6.2 | honest accepted path | probabilistic Hoare logic on κ loop |
sign_no_reject_on_accepted_honest_layout |
protocol acceptance | §6.2 | same | same |
pack_unpack_n1_signature_roundtrip |
codec roundtrip | §3.5.5 | sig packing | bridge to MLDSA65_Functional.pack_signature |
lagrange_inverse_eval |
Lean-bridged algebraic | §6.2 (FROST) | Lagrange identity at 0 | replace with checked translation artifact |
add_share_zeroR, reconstruct_linear, shamir_correct |
Lean-bridged algebraic | (N4 cone) | Shamir/Lagrange algebra | same |
| ~21 per-type FIPS 204 codec round-trips | codec roundtrip | §3 | encode/decode pairs | Barbosa-Barthe-Dupressoir style bit-level mechanization |
| EasyCrypt / Jasmin / OCaml compiler TCB | trusted base | — | tooling correctness | external (compiler verification project) |
Proof claim (narrow): Under these axioms and trusted components, the N1 combine/sign implementation produces the same signature components as the centralized ML-DSA-65 functional model.
Layer 3 — Side-channel and fault security (separate evidence)
PQ implementations are typically broken in the implementation, not the math. Required evidence:
| Risk | Validation status |
|---|---|
| Timing leakage on secret ops | scripts/checks/jasmin.sh — jasmin-ct blocking on threshold layer (round1, round2, combine all CT-clean); libjade sign advisory under #2 |
| Memory access leakage | Same (Jasmin-CT analysis) |
| Rejection sampling leakage | Documented in ct/dudect/README.md — pulsar.Sign is intentionally non-CT per FIPS 204 §3.3 |
| Randomness misuse | ct/dudect/ — dudect statistical tests at 10⁹ samples (nightly) |
| Fault attacks | Threshold layer is deterministic given per-party randomness; fault-injection at the deployed-binary layer is a separate evaluation track per docs/evaluation.md §6 |
| Key erasure | Landed in luxfi/pulsar v1.0.7 (zeroize.go); fuzz harness and N1 byte-equality test ride alongside it. |
| Encoding malleability | test/negative/ exercises malformed pk / sk / sig / ctx-length / message-length boundary cases on every per-push gate |
Sensitive regions per FIPS 204: ExpandMask, sampling of y, w = A·y, HighBits/LowBits, rejection checks, hint generation, secret-key unpacking, any branch depending on secret or rejection conditions. The EC functional refinement does NOT by itself prove constant-time behavior; the jasmin-ct analysis provides that for the threshold layer.
Layer 4 — Federal/compliance validation (separate tracks)
| Track | Status |
|---|---|
| ACVP / CAVP algorithm validation | Downstream lab work against the NIST ACVP ML-DSA harness (https://pages.nist.gov/ACVP/draft-celi-acvp-ml-dsa.html); the reference implementation produces ACVP-compatible JSON via scripts/gen_vectors.sh |
| FIPS 140-3 module validation | Applies to a packaged crypto module, not to this reference implementation; engagement is a deployed-product concern |
For federal procurement, "we implement ML-DSA" is weaker than "ACVP/CAVP-validated ML-DSA implementation plus FIPS 140-3 validated module". This submission delivers the algorithm-level reference implementation; module packaging + lab validation are downstream of this submission.
Layer 5 — Test evidence (delivered, partial)
Currently delivered (scripts/test.sh):
- KAT vectors against pq-crystals reference (Dilithium3) via differential testing
- BoringSSL FIPS / OpenSSL 3.0 PQ provider cross-validation (when available)
- Internal KAT vectors in
vectors/(deterministic generation)
Required for full validation:
- NIST ACVP-style KATs (ACVP ML-DSA test vector format)
- Randomized signing vectors with seed control
- Malformed pk/sk/sig tests
- Context-string boundary tests (0, 1, 255 bytes)
- Message-length boundary tests
- Cross-implementation differential tests
- Decoder/verifier fuzz testing
Layer 6 — Standard conformance audit (external)
The EasyCrypt refinement chain says the implementation matches a functional model that conforms to FIPS 204 — but the conformance itself is by inspection, not machine-checked. A formal conformance audit by an accredited lab (or NIST-recognized review) is the external evidence step.
What this submission delivers vs. what it doesn't
Delivered:
- Layer 2 (implementation correctness) at the strongest level short of full mechanized closure — EC refinement proof with enumerated residual axioms;
- Layer 3 (side-channel) on the threshold layer (Jasmin-CT blocking green; libjade sign advisory documented under #2);
- Layer 5 (test evidence) for differential and KAT validation.
Outside the algorithm-level reference scope:
- Layer 1 PQ hardness — inherited from NIST FIPS 204's M-LWE / M-SIS analysis;
- Layer 4 ACVP / CAVP / FIPS 140-3 — accredited-lab tracks downstream;
- Layer 6 standard conformance audit — accredited reviewer track downstream;
- Full Layer 2 mechanized closure of the 22 residual axioms — each
axiom is independently attackable through the per-axiom closure
plan in
docs/proof-axiom-inventory.md.
Recommended next proof work (post-submission)
Per the user's review prioritization (revised after v7):
*_body_z_spec— likely easier than full w_spec with the Lean Lagrange bridge already stable. UseCrypto.Threshold.Lagrange.threshold_partial_response_identityfor combine; reduce via vec ops + accepted-κ model for sign.*_body_mu_input_spec— byte-layout proof, mechanical and high-confidence once the FIPS 204 §5.4.1 byte serialization is concretised.*_body_h_spec— bridge towardvec_k_make_hint.*_body_w_spec— the hardest target; requires the loop/fixed-point accepted-κ model + ExpandA/ExpandMask/mat-vec.
This reverses the earlier ordering (which had w as the natural next target after w1's HighBits decomposition). The revised ordering optimizes for fastest residual-trust reduction.
What this submission does NOT claim
The construction's scope boundary:
- Identifiable abort on asynchronous networks — Pulsar identifies aborting parties under synchronous network assumptions. Asynchronous identifiable-abort attribution routes through a separate consensus- layer accountability artifact.
- 1-round signing — the construction is 2-round by design. FIPS 204 ML-DSA's rejection-sampling step precludes a 1-round threshold variant under any NIST-standard preprocessing oracle.
- DKG bias resistance under collusion — Pulsar DKG produces unbiased coefficients under honest-majority assumptions. Production deployments bind a randomness beacon at the consensus layer (chain-level concern, not algorithm-level).
Comparison to related submissions
| Submission | Round count | Output interchange | Underlying lattice |
|---|---|---|---|
| Pulsar (this) | 2 | Byte-equal to FIPS 204 ML-DSA | Module-LWE (M-LWE) |
| Lux Corona (R-LWE sibling) | 2 | Byte-equal to FIPS 204 ML-DSA | Ring-LWE (R-LWE) |
| Raccoon | 3 | Compatible verification | Module-LWE |
| Corona (upstream academic) | 2 | Not interchange-tested at submission time | R-LWE |
The R-LWE sibling library lives at https://github.com/luxfi/corona and is not part of this submission. It is included only in the comparison because the production Lux Quasar consensus uses both kernels as parallel options selectable per-chain.
Contact
- Primary: z@lux.network (Lux Industries, Inc.)
- Submission coordination: mptc@lux.network
- Security disclosure: see
SECURITY.md - Public discussion: https://github.com/luxfi/pulsar/discussions
Reproducibility commitment
The build, test, vector-generation, and benchmark scripts are
deterministic from a 48-byte seed. A reviewer reproducing the
submission tarball from submission- should obtain
byte-identical artifacts. Drift is a build bug; please open an issue.