Documentation
¶
Overview ¶
Package mldsatss implements threshold ML-DSA (FIPS 204) signing.
The protocol is the ML-DSA variant from "Threshold Signatures Reloaded: ML-DSA and Enhanced Raccoon with Identifiable Aborts" by Borin, Celi, del Pino, Espitau, Niot, Prest (ePrint 2025/1166). It produces byte-identical FIPS 204 signatures that verify against a stock ML-DSA public key.
The current implementation targets ML-DSA-44 and supports any (threshold t, parties n) with 2 ≤ t ≤ n ≤ 6. Key generation uses a trusted dealer (matching the paper's reference); a distributed key generation protocol is not yet defined for this scheme and is left as future work.
WARNING: This is an academic-grade prototype. It has NOT received independent cryptanalytic review and is NOT suitable for production use.
In particular, the scheme's security rests on the (as-yet unproven in this setting) masking argument of ePrint 2025/1166: each party makes a local rejection decision and reveals its per-try w_i and z_i, which in principle leak information about the secret's norm. Whether the hyperball masking fully hides that leakage is a construction- and parameter-level question for expert human review. Do not deploy this package, or vary its (t, n) parameters, without such review.
Index ¶
Constants ¶
const ( MsgTypeR1_44 = "mldsa44:sign:round1" MsgTypeR2_44 = "mldsa44:sign:round2" MsgTypeR3_44 = "mldsa44:sign:round3" )
Message type strings routed through tss.MessageBroker.
const MaxParties = 6
MaxParties is the upper bound on N in the (t, n) parameter table. The table is derived from params/recover.py in the reference implementation at github.com/GuilhemN/threshold-ml-dsa-and-raccoon and is only defined for N ≤ 6.
Variables ¶
var ErrAllTriesRejected = errors.New("mldsatss: all tries rejected; retry")
ErrAllTriesRejected is returned when every one of the K parallel tries in a single 3-round signing exchange is rejected (by party-side bound or Combine-side correctness). Callers should retry with a fresh attempt.
Functions ¶
func TrustedDealerKeygen44 ¶
func TrustedDealerKeygen44(seed [32]byte, params *ThresholdParams44) (*PublicKey, []*Key44, error)
TrustedDealerKeygen44 derives a threshold ML-DSA-44 public key and N per-party private-key shares from a 32-byte seed. It mirrors thmldsa44/internal/NewThresholdKeysFromSeed from the reference implementation (a trusted dealer; no DKG).
The returned PublicKey is byte-identical to a stock FIPS 204 ML-DSA-44 public key and can be used with mldsa.PublicKey44.Verify.
Types ¶
type ErrPartyResponseInvalid ¶ added in v2.3.1
type ErrPartyResponseInvalid struct {
Slot int // committee slot of the offending party
KeyId uint8 // Key44.Id of the offending party
Try int // which of the K tries failed
Cause string // human-readable reason
}
ErrPartyResponseInvalid is returned by combine when a specific party's round-3 response block fails the per-party validity check. Unlike ErrAllTriesRejected (a non-attributable retry signal), this error names the offending committee slot so the abort is identifiable. The caller should drop/penalize that party rather than blindly retry.
NOTE: the check this error reports is a *partial* identifiable-abort check (a structural L2 bound on each z_i, mirroring the L-part of the party's own rejection gate). A full algebraic check — verifying A·z_i − c·t_i matches the committed w_i — requires each party's public key share t_i = A·s1_i + s2_i, which this protocol never transmits or stores. See validatePartyResponses.
func (*ErrPartyResponseInvalid) Error ¶ added in v2.3.1
func (e *ErrPartyResponseInvalid) Error() string
type Key44 ¶
type Key44 struct {
Id uint8 `json:"id"`
Rho [32]byte `json:"rho"`
Tr [64]byte `json:"tr"`
T1 [mldsa.K44]mldsa.RingElement `json:"t1"`
A [mldsa.K44 * mldsa.L44]mldsa.NttElement `json:"-"` // reconstructed from Rho
}
Key44 is one party's full secret-key material for threshold ML-DSA-44. It contains all shares whose honest-signer mask includes this party's Id, plus the public t1 vector needed for signature assembly.
func (*Key44) AddShare ¶
AddShare inserts or replaces a share. If s1h/s2h are zero-valued the NTT caches are recomputed from S1/S2 here.
func (*Key44) Destroy ¶ added in v2.3.1
func (k *Key44) Destroy()
Destroy best-effort wipes every secret share held by this key (both the plain and NTT-domain s1/s2 polynomials) and clears the share map. The public fields (Rho, Tr, T1, A) are left intact. Like the package's other zeroization, this is best-effort: Go's GC may already have copied the secret elsewhere. After Destroy, the key can no longer participate in signing (Validate will fail with "key has no shares").
type Parameters ¶
type Parameters struct {
// contains filtered or unexported fields
}
Parameters bundles the session configuration for a threshold ML-DSA-44 signing run.
func NewParameters ¶
func NewParameters( partyID *tss.PartyID, parties *tss.PeerContext, thParams *ThresholdParams44, keyIds []uint8, broker tss.MessageBroker, ) (*Parameters, error)
NewParameters builds a Parameters value. parties must be the sorted signing committee (length equal to thParams.T), keyIds must be the Key44.Id of each party in the same order.
func (*Parameters) SetAttemptID ¶
func (p *Parameters) SetAttemptID(id uint32)
SetAttemptID sets a per-session id that is appended to the message type strings, so that multiple attempts on the same broker do not collide.
func (*Parameters) SetRand ¶
func (p *Parameters) SetRand(r io.Reader)
SetRand overrides the randomness source (defaults to crypto/rand.Reader).
type PublicKey ¶
type PublicKey = mldsa.PublicKey44
PublicKey is a threshold ML-DSA-44 public key. It is byte-identical to a stock FIPS 204 public key and can be used with mldsa.PublicKey44.Verify.
type Share44 ¶
type Share44 struct {
}
Share44 is one (s1, s2) share held by a party for a specific honest-signer subset. The share's identity is the subset mask under which it was drawn.
type SignatureData ¶
type SignatureData struct {
Signature []byte
}
SignatureData is the output of a successful threshold ML-DSA-44 signing session. Signature is byte-identical to a stock FIPS 204 signature and verifies with mldsa.PublicKey44.Verify.
type Signing44 ¶
type Signing44 struct {
Done chan *SignatureData
Err chan error
// contains filtered or unexported fields
}
Signing44 drives the 3-round threshold ML-DSA-44 signing protocol for one attempt. If all K tries are rejected, Signing44 signals ErrAllTriesRejected via Err; callers can retry by creating a new Signing44 with a fresh attempt id.
func NewSigning44 ¶
func NewSigning44(ctx context.Context, params *Parameters, key *Key44, msg, msgCtx []byte) (*Signing44, error)
NewSigning44 starts a threshold ML-DSA-44 signing session. It registers round-1/2/3 receivers on the params.Broker and immediately broadcasts the party's Round 1 commitment.
The returned Signing44 emits the final signature on Done, or an error on Err. If every try in this attempt is rejected, Err receives ErrAllTriesRejected; the caller should retry with a new attempt id.
type ThresholdParams44 ¶
type ThresholdParams44 struct {
T uint8 // threshold: minimum signers required
N uint8 // total parties
K uint16 // parallel signing tries per attempt (fresh w per try)
Nu float64 // ν: anisotropic scaling factor of the L-part in the hyperball
R float64 // r: primary ν-scaled L2 radius (party rejection bound)
Rp float64 // r': secondary L2 radius used in Combine-side correctness check
}
ThresholdParams44 holds the parameters for one (t, n) configuration of threshold ML-DSA-44. See ePrint 2025/1166 for the meaning of K, R, Rp, Nu.
func GetThresholdParams44 ¶
func GetThresholdParams44(t, n int) (*ThresholdParams44, error)
GetThresholdParams44 returns the (t, n) parameters if supported.