ring coverage 52%->74%: MatVecMul vs schoolbook, Add/Sub/ScalarMul,
DeriveUniformMatrix determinism+separation, GroupKey.Encode,
Profile.WithMatrices (ExpandA override seam), HighBitsVec.
HANDOFF.md names the exact live seams Phase 2 plugs into:
- mpc/ -> ring + vss Pedersen commit/verify (committed re-shares)
- cscp/ -> mpc committed BGW + ring.HighBitsVec (FIPS-204 boundary oracle)
- nonce/-> transcript (replay-bind) + channel (authenticated lifecycle)
- safety/-> blame complaint set + vss.RecheckBadDelivery
plus extension points (new Profile, WithMatrices, VerifyFault->blame) and
Phase-3 corona/pulsar wiring notes (the corona R^{-1} convention; the
documented stock-circl-signature obstruction = TALUS signer, not faked).
7.3 KiB
luxfi/dkg — Phase 1 foundation handoff
Phase 1 (this delivery) is the dealerless DKG foundation + 6 of the 9 malicious-
hardening components, built standalone. Phase 2 (a separate agent) builds the
malicious-secure mpc/, cscp/, nonce/, and safety/ on these seams. Phase 3
wires corona + pulsar onto the library. This file names the exact seams Phase 2
plugs into — nothing here is a stub; every seam is a live, tested API.
What Phase 1 delivers
| Package | Concern | Hardening components |
|---|---|---|
ring/ |
R_q substrate, Profile (one place per scheme), FIPS-204 Power2Round/Decompose |
— |
transcript/ |
byte-stable MPC transcript, SP 800-185 cSHAKE256/KMAC256 | 2 (networked MPC transcript) |
channel/ |
ML-KEM-768 sealed + ML-DSA-65 signed; share-only envelopes; commit-then-reveal | 3 (authenticated channels), 8 (last-mover bias) |
vss/ |
no-reconstruct Pedersen-VSS DKG, generic over ring.Profile |
5 (equivocation gate), 6 (malformed-share rejection) |
blame/ |
signed complaints, selective-abort adjudication | 4 (complaint/blame), 7 (selective-abort) |
Deferred to Phase 2: 1 (malicious CSCP), 9 (robust nonce lifecycle), plus
the mpc/ malicious BGW substrate and the safety/ theorem.
Hard invariants proven (do not regress)
- No-reconstruct.
vss/never forms s1, u, or a seed. Enforced byvss.TestNoReconstruct_SourceStructural(go/ast scan: noreconstruct/lagrange/interpolate/modinverse/keyfromseed/combinesharesdefined or called in non-test source) +TestNoReconstruct_Runtime. Any reconstruction Phase 2 needs (e.g. a signer's Lagrange combine) lives in the consumer or a clearly-separate package, NEVER invss/'s output path. - Ring fidelity.
vss.TestMLDSA_RingFidelity_Schoolbookpins the lattice multiply to an independent schoolbook convolution over q=8380417 and the Ringtail q. The group commitTis the TRUEA·s1+B·u(no Montgomery R scaling — seering.ConvertVecFromNTT, which is INTT-only by design; an IMForm there injects a spurious R^{-1}). - FIPS-204 key map.
vss.TestMLDSA_KeyFinalize_FIPSIdentity: ML-DSA t1 = Power2Round(T), reconstruction identityT = t1·2^13 + t0, 10-bit width.
Seams for Phase 2
mpc/ — malicious-secure BGW substrate
Plugs onto ring/ + vss/party.go.
- Build secure-mult / shared-random-bits over
ring.Profile(usering.MatVecMul,ring.ScalarMulVec,ring.VecAdd, the samplers). - The malicious layer = Feldman/Pedersen-committed re-shares with verified
openings. The Pedersen commit+verify primitive already exists and is
factored: reuse
vss.computeCommits/vss.verifyPedersenshapes (currently unexported invss/party.go; promote to a sharedpedersenhelper or call through a thin exported wrapper — they are pure functions of*ring.Profile). Every re-share carries a commit; receivers check the degree-(T−1) Pedersen identity before use — identical structure tovss.Party.Round3. - Re-share fault → feed a
vss.VerifyFault-shaped value into the blame bridge (vss.NewBadDeliveryComplaint) so a bad re-share is identifiably aborted by the SAME complaint machinery.
cscp/ — malicious-secure CarryCompare (TALUS Phase B)
Plugs onto mpc/ (committed BGW) + ring.HighBitsVec.
ring.HighBitsVec(r, T, gamma2)andring.Decomposeare the FIPS-204 boundary the secure comparison must realize WITHOUT any node forming w0/w.cscp/computes HighBits over committed shares; the in-the-clearring.HighBitsVecis the test ORACLE it must match on all residues (gamma2 = 261888 for ML-DSA-65/87).- The three semi-honest deviations to close (inconsistent re-share, non-{0,1}
random bit, equivocated mask open) each map to a
mpc/committed-opening check; a failure becomes ablame.Complaint(reuseblame.ReasonBadDeliveryor add aReasonCSCP*— theblamereason set is the extension point).
nonce/ — robust nonce-ticket lifecycle
Plugs onto transcript/ + channel/.
- Bind each ticket to
(epoch, committee, policy, digest)via atranscript.Transcript(Append the four, takeHash()as the ticket's replay-bound id).transcript.New()/NewWithDomainis the binding seam. - Consume-once / expiry state is authenticated with
channel.Sign/channel.Verifyunder a new context tag (addCtxNoncealongsidechannel.CtxBroadcast/CtxComplaint).
safety/ — abort-or-blame-not-leak theorem
Plugs onto blame/ + the property tests.
- The single source of truth
AssessMaliciousdescriptor enumerates each deviation and its resolution (detected+blamed via ablame.Complaint, OR downstream-rejected). Theblamecomplaint set +vss.RecheckBadDelivery(public re-check) are the evidence the theorem quantifies over. - Property tests: for every injected deviation, assert the outcome is a
blame.ComplaintthatComplaint.Verify+ the vss re-check accept, OR a liveness fault (retry) — NEVER a key leak or a forged signature.
Extension points (stable API, build against these)
- New scheme → a new
*ring.Profile(seering.Ringtail(),ring.MLDSA65()). Never bolt onto an existing one. - ExpandA(rho) binding (Phase 3 pulsar) →
Profile.WithMatrices(A, B)with A = ExpandA(rho) over FIPS-204's own NTT. The DKG is identical either way; this is how a deployment gets a chain-genesis-seed-bound public matrix. - Signer consumes
vss.ShareResult{Share, Blind, GroupKey}andring.GroupPublicKey{T, Finalized, Aux}(Aux = ML-DSA t0 for the hint path). - Malicious detection → blame via
vss.VerifyFault→vss.NewBadDeliveryComplaint→blame.Complaint.
Phase 3 wiring notes (corona / pulsar)
- The Ringtail profile uses corona's exact NUMS tags (
corona.dkg2.A.v1), but the library's group key is the trueA·s1+B·u(INTT-only). corona's current internal convention (ConvertVectorFromNTT= IMForm+INTT) yields an R^{-1}-scaled value; the refactor must adopt the library's true convention (or the corona verifier already absorbs the scaling — confirm against corona's group-key KAT before cutover). - pulsar's
mldsa_lattice.goself-containedpoly[256]uint32arithmetic is REPLACED byring/at q=8380417; itstranscript.gobytranscript/; itsidentity.goenvelope bychannel/(which fixes the v0.1 contribution leak — do not re-introduce a contribution field). The TALUS signer (distributed_bcc.go) consumes thevssshare output unchanged.
Stock-circl signature: the documented obstruction
A stock circl mldsa65.Verify-passing signature from the no-reconstruct
group key is NOT a Phase-1 deliverable and is NOT faked. T = A·s1+B·u has a
large s2 = B·u (M-LWE-indistinguishable from χ_η but not itself small), so the
standard FIPS-204 signing algorithm does not apply unchanged — the TALUS signer's
carry elimination (Phase 2) produces the stock-verifiable signature
(output-interchangeability is a SIGNER theorem, proofs/pulsar). Phase 1 proves
the ring + key-map fidelity the signer stands on, plus a stock-circl
keygen→sign→verify liveness round-trip (vss.TestMLDSA_StockCircl_Liveness).
Build
export SDKROOT="$(xcrun --show-sdk-path)"; export GOWORK=off
go build ./... && go test ./... -count=1 -race && go vet ./...
All green, race-clean, vet-clean at v0.1.0.