golang/go · error
mldsa: invalid SignerOpts
Error message
mldsa: invalid SignerOpts
What it means
Returned by PrivateKey.Sign when the crypto.SignerOpts passed in has a HashFunc() that returns a value other than 0 (direct message signing) or crypto.MLDSAMu (pre-hashed external-μ signing). ML-DSA supports exactly these two signing modes per FIPS 204 / RFC 9881; any other hash identifier is rejected as unsupported. The error is the package-level var errInvalidSignerOpts.
Source
Thrown at src/crypto/mldsa/mldsa_fips140v1.26.go:94
return false
}
return sk.k.Equal(&other.k)
}
// PublicKey returns the corresponding [PublicKey] for this private key.
func (sk *PrivateKey) PublicKey() *PublicKey {
// Making a copy severs the pointer relationship between the private and
// public keys, so that keeping the public key around doesn't keep the
// private key alive. This costs a copy and an allocation.
return &PublicKey{p: *sk.k.PublicKey()}
}
// Bytes returns the private key seed.
func (sk *PrivateKey) Bytes() []byte {
return sk.k.Bytes()
}
var errInvalidSignerOpts = errors.New("mldsa: invalid SignerOpts")
// Sign returns a signature of the given message using this private key.
//
// If opts is nil or opts.HashFunc returns zero, the message is signed directly.
// If opts.HashFunc returns [crypto.MLDSAMu], the provided message must be a
// [pre-hashed μ message representative]. opts can be of type *[Options] if a
// context string is desired along with a directly-signed message. The io.Reader
// argument is ignored.
//
// [pre-hashed μ message representative]: https://www.rfc-editor.org/rfc/rfc9881.html#externalmu
func (sk *PrivateKey) Sign(_ io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
if sk.k == (mldsa.PrivateKey{}) {
return nil, errors.New("mldsa: zero private key")
}
if opts == nil {
opts = &Options{}
}
switch opts.HashFunc() {View on GitHub (pinned to b6b368adc5)
Solutions
- Pass nil or a *mldsa.Options with the zero-value hash for direct message signing: sk.Sign(rand, message, nil).
- For pre-hashed μ signing, use a SignerOpts whose HashFunc() returns crypto.MLDSAMu and pre-compute the μ representative.
- If you need to attach a context string, pass &mldsa.Options{Context: "ctx"} with HashFunc left at 0.
Example fix
// before
opts := &rsa.PSSOptions{Hash: crypto.SHA256}
sig, err := sk.Sign(rand.Reader, digest, opts) // errInvalidSignerOpts
// after
sig, err := sk.Sign(rand.Reader, message, nil) // direct signing Defensive patterns
Strategy: type-guard
Validate before calling
// Validate SignerOpts before calling Sign.
func validMldsaOpts(opts crypto.SignerOpts) bool {
if opts == nil { return true } // treated as direct signing
hf := opts.HashFunc()
return hf == 0 || hf == crypto.MLDSAMu
}
if !validMldsaOpts(opts) { return errInvalidSignerOpts }
sig, err := sk.Sign(nil, msg, opts) Type guard
func isMldsaApprovedOpts(opts crypto.SignerOpts) bool {
if opts == nil { return true }
hf := opts.HashFunc()
return hf == 0 || hf == crypto.MLDSAMu
} Try / catch
sig, err := sk.Sign(nil, msg, opts)
if err != nil {
if errors.Is(err, mldsaErrInvalidSignerOpts) {
// fix opts to nil or MLDSAMu and retry once
}
return err
} Prevention
- Standardize on nil or &mldsa.Options{} for direct signing across the codebase.
- Never reuse RSA/ECDSA SignerOpts structs with ML-DSA.
- Add a unit test asserting opts.HashFunc() returns 0 or crypto.MLDSAMu for every signing call site.
When it happens
Trigger: Calling sk.Sign(nil, msg, opts) where opts.HashFunc() returns a conventional hash crypto.Hash (e.g. crypto.SHA256) instead of 0 or crypto.MLDSAMu. Passing a custom SignerOpts whose HashFunc() yields an unrecognized uint value.
Common situations: Adapting existing ECDSA/Ed25519 signing code to ML-DSA and reusing a SHA-256 opts struct. Misunderstanding that ML-DSA is not a pre-hash-signature scheme by default and assuming opts.HashFunc must return a real hash. Forgetting to pass nil or &Options{}.
Related errors
- mldsa: zero private key
- mldsa: nil public key
- mldsa: zero public key
- mlkemtest: Encapsulate768: random must be 32 bytes
- crypto/mlkem/mlkemtest: use of derandomized encapsulation is
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/d39a3a6277e8adac.
Report an issue: GitHub.