golang/go · error
mldsa: zero private key
Error message
mldsa: zero private key
What it means
Returned by PrivateKey.Sign when the underlying mldsa.PrivateKey field sk.k is the zero value, meaning the PrivateKey struct was never populated by key generation or decoding. This guards against signing with an uninitialized key, which would otherwise produce invalid or deterministic garbage. The check sk.k == (mldsa.PrivateKey{}) detects the all-zero internal state.
Source
Thrown at src/crypto/mldsa/mldsa_fips140v1.26.go:107
// 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() {
case 0:
var context string
if opts, ok := opts.(*Options); ok && opts != nil {
context = opts.Context
}
return mldsa.Sign(&sk.k, message, context)
case crypto.MLDSAMu:
return mldsa.SignExternalMu(&sk.k, message)
default:
return nil, errInvalidSignerOpts
}
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Generate the key first: sk, err := mldsa.GenerateKey(rand.Reader); then call sk.Sign(...).
- Decode from stored bytes using the documented Unmarshal/Decode functions and check the returned error before signing.
- Add a nil/zero-value assertion immediately after key construction in your loading code.
Example fix
// before
var sk mldsa.PrivateKey
sig, err := sk.Sign(nil, msg, nil) // "mldsa: zero private key"
// after
sk, err := mldsa.GenerateKey(rand.Reader)
if err != nil { return err }
sig, err := sk.Sign(nil, msg, nil) Defensive patterns
Strategy: validation
Validate before calling
// Ensure the key is non-zero before signing.
if sk == nil || len(sk.Bytes()) == 0 {
return errors.New("private key not initialized")
}
sig, err := sk.Sign(nil, msg, nil) Type guard
func (sk *PrivateKey) isInitialized() bool {
return sk != nil && len(sk.Bytes()) > 0
} Try / catch
sig, err := sk.Sign(nil, msg, opts)
if err != nil && strings.Contains(err.Error(), "zero private key") {
// re-initialize key from source, then retry once
}
return sig, err Prevention
- Always check the error from GenerateKey / key-decode functions.
- Centralize key construction in one factory that returns a fully populated key or an error.
- Assert key bytes are non-empty in tests after loading.
When it happens
Trigger: Declaring var sk mldsa.PrivateKey (or new(mldsa.PrivateKey)) and calling Sign without running it through GenerateKey or UnmarshalBinary. Loading a key from bytes via a code path that silently failed and left sk.k zero.
Common situations: Deserializing a key from disk/network where the decode error was ignored, leaving the zero-value key. Copying a PrivateKey by value into a new variable and forgetting to populate it. Test scaffolding that instantiates the struct directly.
Related errors
- mldsa: zero public key
- mldsa: invalid SignerOpts
- mldsa: nil 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/adafef97dcbd3bac.
Report an issue: GitHub.