Commit Graph
14 Commits
Author SHA1 Message Date
zeekay 0df99b7364 pulsar: README — drop -M suffix (family rename) 2026-05-11 15:05:58 -07:00
zeekay 604a32a47d pulsar-m: drop unnecessary Lux prefix from round identifiers
LuxRoundContext / LuxRoundSessionID / LuxRoundCommitteeRoot /
LuxRoundSigShare / LuxRoundQuorumPolicy / DefaultLuxRoundQuorumPolicy
→ RoundContext / RoundSessionID / RoundCommitteeRoot / RoundSigShare /
RoundQuorumPolicy / DefaultRoundQuorumPolicy.

Files: luxround.go luxround_math.go luxround_test.go → round.go
round_test.go (math helpers folded into round.go). The protocol name
"Lux round" stays in prose / spec / paper titles -- only the Go
identifiers drop the brand prefix. The cSHAKE customization tag
PULSAR-M-LUXROUND-COMMITTEE-V1 is wire-format-stable and stays.
2026-05-11 13:42:08 -07:00
zeekay 52eea92a11 pulsar-m: convert NIST MPTC deliverables docs/*.md → spec/*.tex (CLAUDE.md LaTeX-only rule)
Per ~/.claude/CLAUDE.md ("papers/proofs/audits stay in LaTeX, never .md"
+ "NEVER write random summary files"), the six NIST MPTC submission
deliverables move from docs/ Markdown into spec/ LaTeX as
\input{...} chapters of pulsar-m.tex:

  docs/nist-mptc-category.md   -> spec/nist-mptc-category.tex
  docs/patent-notes-draft.md   -> spec/patent-notes.tex
  docs/threat-model.md         -> spec/threat-model.tex
  docs/known-limitations.md    -> spec/known-limitations.tex
  docs/family-architecture.md  -> spec/family-architecture.tex
  docs/design-decisions.md     -> spec/design-decisions.tex

Content preserved verbatim; only LaTeX-syntax stylistic edits.
pulsar-m.tex now \inputs each as its own section / appendix.
Go-source comments that referenced docs/* updated to spec/*.tex.
README + CONTRIBUTING refreshed.

GOWORK=off go test -count=1 ./... clean.
2026-05-11 12:31:43 -07:00
zeekay 38319c9fc8 pulsar-m: LLM.md — module-pin reconciliation plan (HIGH-4 prep) 2026-05-11 12:25:34 -07:00
zeekay 7e7d0d7cf2 pulsar-m: end-to-end test threshold Combine → FIPS 204 verify with precompile ctx (CRIT-2 closure)
CRIT-2: the Class N1 manifesto claim that a Pulsar threshold
Combine output verifies as a single-party FIPS 204 ML-DSA
signature under the EVM precompile ctx
(`lux-evm-precompile-pulsar-v1`) had no end-to-end test. Every
prior Combine call site passed ctx=nil, so the ctx-propagation
guarantee Combine -> mldsaSign -> circl SignTo was never
exercised against the production precompile string.

New test file precompile_e2e_test.go covers both Combine paths:

  - TestPrecompile_E2E_Combine_FIPS204_VerifyCtx exercises the
    GF(257) path at (T,N)=(3,2) for ModeP65.
  - TestPrecompile_E2E_LargeCombine_FIPS204_VerifyCtx exercises
    the GF(q) path at (T,N)=(3,2) for ModeP65.

Each test:
  1. Runs DKG to install a group public key.
  2. Drives ThresholdSigner / LargeThresholdSigner Round1+Round2
     on a message M.
  3. Calls Combine / LargeCombine with ctx=precompileCtx,
     randomized=false to produce the threshold signature.
  4. Asserts that BOTH
       (a) raw FIPS 204 cloudflare/circl mldsa65.Verify(pk, M,
           ctx=precompileCtx, sig), and
       (b) pulsarm.VerifyCtx(params, pk, M, ctx=precompileCtx,
           sig)
     accept the signature. This is the empirical Class N1
     closure for the precompile ctx.
  5. Negative control: the same signature must NOT verify under
     ctx=nil, proving ctx is actually load-bearing in the
     Combine output.

No ctx-propagation fix was required: threshold.go Combine and
large_threshold.go LargeCombine already forward ctx to
mldsaSign, which forwards to mldsa{44,65,87}.SignTo. The tests
verify this end-to-end.
2026-05-11 12:03:14 -07:00
zeekay 7acc764a4a pulsar-m: GF(q) Large* stack + luxround helpers + family rename docs
Adds the M-LWE large-committee track and metastable signing helpers,
plus the consolidated migration plan for the Pulsar/Corona/Corona/
Quasar rebrand:

- GF(q) byte-wise Shamir secret sharing (shamir_gfq.go) for committees
  beyond 256 parties, with q = 8,380,416 (ML-DSA q) and 128-byte wire
  share format.
- Large* protocol stack (large_types, large_dkg, large_threshold,
  large_reshare, largeshamir) targeting TargetCommitteeSize = 1,111,111
  with end-to-end tests at (3,2), (3,3), (5,3), (6,4).
- Wave-driven metastable signing helpers (luxround.go, luxround_math.go)
  with beta-round end-to-end coverage.
- Spec updates: parameters.tex, pulsar-m.tex, system-model.tex —
  large-committees section, security games, Lux-round composition.
- LLM.md: consolidates MIGRATION.md (deleted) into a single source of
  truth per CLAUDE.md repository rules. Family migration plan,
  destructive-action checklist, cleanliness audit.
- docs/family-architecture.md: scrub dated header.
- docs/known-limitations.md: track MPTC deadlines.

Tests: GOWORK=off go test -count=1 ./... is green.
2026-05-11 11:46:21 -07:00
zeekay fb4f37d989 pulsar-m: NIST MPTC submission readiness — benchmarks + CI gates
Final v0.1 polish:

  bench_test.go — reproducible benchmark harness covering KeyGen,
                   Sign, Verify across all three FIPS 204 parameter
                   sets, plus DKG, ThresholdSign, and Reshare at
                   representative committee sizes for ML-DSA-65.

The benchmark harness drives scripts/bench.sh; results land in
bench/results/{fingerprint.txt,go-bench.txt}. Apple M1 Max baseline
(Go 1.26.2, -benchtime=2s -count=3):

  KeyGen     P44  ~ 0.3 ms,  P65  ~ 0.5 ms,  P87  ~ 0.7 ms
  Sign       P44  ~ 1.6 ms,  P65  ~ 2.5 ms,  P87  ~ 3.0 ms
  Verify     P44  ~ 0.3 ms,  P65  ~ 0.5 ms,  P87  ~ 0.7 ms
  DKG        (5,3) ~ 3.5 ms,  (7,4) ~ 7 ms,   (10,7) ~ 12 ms
  Threshold  (5,3) ~ 2.2 ms,  (7,4) ~ 4.5 ms, (10,7) ~ 7 ms
  Reshare    (5→5,t=3) ~ 0.5 ms

The threshold path is dominated by FIPS 204 Sign latency for small
quorums; at (n=10, t=7) the 2-round overhead becomes comparable to
the FIPS 204 cost.

This commit closes the v0.1 reference impl gates:
  - scripts/build.sh   passes
  - scripts/test.sh    passes (61 tests, race-clean)
  - scripts/gen_vectors.sh passes; KATs deterministic
  - scripts/bench.sh   passes
  - Class N1 interchangeability demonstrated by every threshold KAT
    in vectors/threshold-sign.json passing under pulsarm.Verify
    (i.e. under unmodified mldsa{44,65,87}.Verify)

docs/known-limitations.md updated to reflect v0.1 reality: the
reconstruction-aggregator path ships in v0.1; the Lagrange-linearity
sign and R_q^k Pedersen are documented v0.2 paths.

Signed-off-by: Lux Industries <[email protected]>
2026-05-10 20:26:56 -07:00
zeekay 1793dd64ac pulsar-m: KAT vectors for keygen / sign / verify / threshold / dkg
Canonical Known Answer Tests for the Pulsar-M v0.1 reference,
generated by ref/go/cmd/genkat. Re-running scripts/gen_vectors.sh on
a clean checkout reproduces these files byte-identically — the
deterministic-fixture gate.

Coverage:

  vectors/keygen.json        — 9 KATs (3 seeds × 3 modes)
                                 seed → (public_key, private_key)
  vectors/sign.json          — 9 KATs (3 messages × 3 modes)
                                 (seed, message) → signature
  vectors/verify.json        — 6 KATs (3 positive + 3 negative)
                                 (pubkey, message, signature, valid)
  vectors/threshold-sign.json — 4 KATs at (n,t) ∈ {(3,2),(5,3),
                                 (7,4),(10,7)} for Pulsar-M-65;
                                 every signature passes
                                 mldsa65.Verify (Class N1 demo)
  vectors/dkg.json           — 3 KATs at (3,2), (5,3), (7,4) for
                                 Pulsar-M-65; per-party shares and
                                 transcript hash committed

The replay harness in kat_test.go reads each JSON at test time and
verifies the round-trip:

  - Keygen replay: KeyFromSeed(seed) reproduces (pk, sk)
  - Sign replay:   Sign(deterministic) reproduces signature
  - Verify replay: pulsarm.Verify accepts every positive case and
                   rejects every negative case
  - Threshold:     every committed signature passes pulsarm.Verify
                   (which dispatches to FIPS 204 ML-DSA.Verify
                   verbatim — the load-bearing Class N1 property)
  - DKG:           wire-format invariants (sizes match parameter
                   set, transcript hash is 48 bytes, share count
                   matches committee count)

scripts/gen_vectors.sh + scripts/test.sh are now functional CI
gates.

Signed-off-by: Lux Industries <[email protected]>
2026-05-10 20:26:37 -07:00
zeekay f8f0306d16 pulsar-m: proactive resharing with beacon-randomized quorum
HJKY97-style proactive resharing: rotate the share distribution from
an old committee to a new committee without changing the master
public key and without reconstructing the master secret in any
party's memory. Implements the spec/pulsar-m.tex §4.4 protocol with
the DD-006 mitigation (beacon-randomized quorum).

Three rounds, structurally similar to DKG except dealer contributions
have constant term λ_i^Q · share_i (pre-multiplied Lagrange
coefficient over the OLD quorum, computed in GF(257)). By Lagrange
linearity, the sum of dealer polynomials evaluated at X=0 IS the
master byte-sum — guaranteeing pk invariance (Theorem reshare-pkinv).

Beacon-randomized quorum selection: the old-committee quorum is the
first t_old members under the cSHAKE256(beacon || NodeID) permutation.
This blocks the mobile-adversary corruption-set positioning that
deterministic smallest-ID selection allows (HIP-0077 finding F10).

Tests:
  - SameCommittee_PubInvariant: a reshare with the same committee on
    both sides produces new shares that recover the SAME master
    public key via the canonical cSHAKE256 mix
  - NewCommittee: a one-member-rotation reshare produces shares for
    the new committee members
  - BeaconPermutation: different beacons produce different quorums;
    no-beacon falls back to canonical order
  - LagrangeAtZero: numerical correctness of the GF(257) Lagrange
    coefficients (sum to 1 mod p, individual values match algebra)
  - RejectInvalidCommittees: empty / threshold-too-large rejection

Signed-off-by: Lux Industries <[email protected]>
2026-05-10 20:26:26 -07:00
zeekay 44b38e2496 pulsar-m: threshold sign (2-round commit + reveal)
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]>
2026-05-10 20:26:13 -07:00
zeekay 1685a8604b pulsar-m: epoch DKG with identifiable abort
Three-round Pedersen DKG over R_q with byte-wise Shamir share
distribution. Produces a FIPS 204-shaped group public key + a
per-party KeyShare; identifies the misbehaving party with a signed
AbortEvidence on detected deviation.

Protocol (matching spec/pulsar-m.tex §4.1):

  Round 1: each party samples a 32-byte contribution c_i, broadcasts
           an RO-binding cSHAKE256 commit C_i = cSHAKE256(c_i ||
           blind_i) under the PULSAR-M-DKG-COMMIT-V1 customisation
           tag, and Shamir-shares c_i byte-wise to the committee.

  Round 2: each party broadcasts the digest of the Round-1 messages
           it received from every dealer. The Round-1.5 equivocation
           gate: cross-recipient digest mismatch produces a signed
           ComplaintEquivocation with the offending party's NodeID.

  Round 3: each party verifies digest agreement (constant-time over
           48 bytes) and aggregates its share by summing per-byte
           envelope shares in GF(257). The master public key is
           derived by Lagrange-reconstructing the byte-sum from the
           first t committee positions and feeding the cSHAKE256-
           mixed result to FIPS 204 KeyFromSeed.

The output transcript hash is the 48-byte binding the chain pins to
the validator-set commitment. The KeyShare wire is parameter-set-
independent (64 bytes per share).

Identifiable abort (abort.go):
  ComplaintEquivocation — distinct commits to distinct recipients
  ComplaintBadDelivery  — envelope fails recipient-side check
  ComplaintMACFailure   — Round-1 / Round-2 MAC mismatch
  ComplaintRangeFailure — out-of-bounds aggregator contribution

The complaint wire format is forward-stable; new kinds append.

Tests:
  - HappyPath sweep (n,t) ∈ {(3,2),(5,3),(7,4),(10,7)} — every
    party arrives at the same group public key and the same
    transcript hash
  - Equivocation detection (tampered Round-2 digest produces a
    ComplaintEquivocation with the correct accused party)
  - ProducesValidPubkey_VerifiableSign: end-to-end demonstration
    that the DKG output recovers a FIPS 204-verifiable signing key
  - Committee duplicate / empty / non-member rejection
  - AbortEvidence wire-format round-trip, self-accusation rejection,
    transcript stability, and ComplaintKind string formatting

Signed-off-by: Lux Industries <[email protected]>
2026-05-10 20:25:59 -07:00
zeekay 553b038e59 pulsar-m: byte-wise Shamir secret sharing over GF(257)
Foundation primitive for the threshold layer. Shamir-shares a
32-byte secret as 32 independent GF(257) instances, one per byte.
Each party's share is a 32 × uint16 wire blob (64 bytes total),
parameter-set-independent.

Why GF(257): 257 is the smallest prime > 255 so it admits every byte
value as a distinct field element. Per-byte sharing keeps the share
wire size minimal and audit-friendly. The DKG and Reshare layers
build directly on this; the cSHAKE256 final-mix step (in
DKG.reconstructByteSum and Combine) flattens any mod-256 vs. mod-257
byte-pattern bias before the resulting 32-byte seed is fed to FIPS
204 KeyFromSeed.

The GF(257) variant shamirDealRandomGF / shamirReconstructGF
preserves the 256-th element so HJKY97-style reshare can rotate
shares without changing the master public key.

Tests:
  - DealAndReconstruct (5 quorum sizes)
  - All-zero and all-0xff edge secrets
  - Duplicate / zero / out-of-range eval-point rejection
  - Bad threshold and oversize committee rejection
  - shareToBytes / shareFromBytes wire round-trip
  - modInvSmall correctness over the full GF(257) range
  - EvalPointFromID non-zero guarantee

Signed-off-by: Lux Industries <[email protected]>
2026-05-10 20:25:42 -07:00
zeekay e082b5a9eb pulsar-m: keygen + sign + verify (single-party)
Wires single-party FIPS 204 ML-DSA through the canonical pulsarm
package surface. The Verify entry point is the Class N1 manifesto:
it dispatches to cloudflare/circl/sign/mldsa{44,65,87}.Verify
verbatim, so any FIPS-validated ML-DSA verifier consumes pulsarm
signatures with no code change.

Files:
  types.go        — PublicKey, PrivateKey, Signature, KeyShare,
                    Round{1,2}Message, DKG{Round1,Round2}Msg,
                    DKGOutput, AbortEvidence, ComplaintKind
  params.go       — Mode + Params for P44/P65/P87, ParamsFor +
                    Validate, all FIPS 204 §4 Table 1 numeric
                    constants pinned
  transcript.go   — FIPS 202 + SP 800-185 (cSHAKE256, KMAC256,
                    TupleHash-style transcript binding) with the
                    canonical PULSAR-M-* customisation tags
  keygen.go       — GenerateKey + KeyFromSeed; dispatches to circl
                    NewKeyFromSeed for FIPS 204-shaped output
  sign.go         — Sign over mldsa.SignTo; supports hedged and
                    deterministic variants
  verify.go       — Canonical Verify entry point; thin dispatch to
                    mldsa.Verify

Tests covering all three FIPS 204 parameter sets, deterministic
seed-based keygen reproducibility, signature-validation negatives
(tampered sig, wrong message, wrong pubkey, wrong mode, wrong size,
nil args), and SP 800-185 encoder NIST examples.

Signed-off-by: Lux Industries <[email protected]>
2026-05-10 20:25:27 -07:00
zeekay 6fd38d451f pulsar-m: bootstrap NIST MPTC submission package
Initial commit. Holds the technical specification (LaTeX), the design
docs, the build/test/bench scripts, the package layout, and the empty
ref/go/pkg/pulsarm skeleton.

The spec at spec/pulsar-m.tex (11,300 words of LaTeX) is the canonical
description of the protocol. docs/nist-mptc-category.md states the
Class N1 + N4 claim. CONTRIBUTING.md and SECURITY.md pin the IP and
disclosure posture.

Subsequent commits land the reference implementation in stages.

Signed-off-by: Lux Industries <[email protected]>
2026-05-10 20:25:09 -07:00