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]>
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]>
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]>
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]>
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]>