golang/go · error
mldsa: invalid message hash length
Error message
mldsa: invalid message hash length
What it means
ML-DSA supports a pre-hash mode where the caller supplies an already-hashed message instead of the raw bytes. The internal μ computation expects the pre-hash to be exactly the output length mandated by the chosen HashMLDSA function (64 bytes for SHA3-512/SHA-512). errMessageHashLength fires when the supplied digest does not match that mandated length, so an incompatible hash function is caught before the signature is produced or checked.
Source
Thrown at src/crypto/internal/fips140/mldsa/mldsa.go:333
}
// We don't precompute A and t1Hat here, because they would make the
// PublicKey over 68KB. Unlike private keys, public keys are often used to
// verify a signature only once, so precomputation doesn't help as often,
// but they can stay around in memory, for example as part of a TLS
// connection's PeerCertificates, so their size is more of a concern.
// Instead, we compute A and t1Hat on demand in Verify.
pub.p = p
copy(pub.raw[:], pk)
pub.tr = computePublicKeyHash(pk)
return pub, nil
}
var (
errContextTooLong = errors.New("mldsa: context too long")
errMessageHashLength = errors.New("mldsa: invalid message hash length")
errRandomLength = errors.New("mldsa: invalid random length")
)
func Sign(priv *PrivateKey, msg []byte, context string) ([]byte, error) {
fipsSelfTest()
fips140.RecordApproved()
var random [32]byte
drbg.Read(random[:])
μ, err := computeMessageHash(priv.pub.tr[:], msg, context)
if err != nil {
return nil, err
}
return signInternal(priv, &μ, &random), nil
}
func SignDeterministic(priv *PrivateKey, msg []byte, context string) ([]byte, error) {
fipsSelfTest()
fips140.RecordApproved()View on GitHub (pinned to b6b368adc5)
Solutions
- Use SHA-512 or SHA3-512 (64-byte output) as the HashMLDSA function and pass the full digest.
- Re-hash the message with the mandated algorithm at the boundary rather than trusting an externally-supplied digest length.
- If a non-standard hash is unavoidable, use the non-pre-hash Sign/Verify path and let the library hash internally.
Example fix
// before digest := sha256.Sum256(msg) // 32 bytes sig, err := mldsa.SignHash(priv, digest[:], opts) // after digest := sha3.Sum512(msg) // 64 bytes, FIPS-mandated sig, err := mldsa.SignHash(priv, digest[:], opts)
Defensive patterns
Strategy: validation
Validate before calling
if len(digest) != 64 {
return errors.New("pre-hash digest must be 64 bytes (SHA-512/SHA3-512)")
} Type guard
func isMandatedHashLen(b []byte) bool { return len(b) == 64 } Prevention
- Standardize on SHA-512 or SHA3-512 for ML-DSA pre-hash mode.
- Prefer the non-pre-hash Sign/Verify path unless you have an external-hash requirement.
- Re-hash at the boundary rather than trusting externally-supplied digest lengths.
When it happens
Trigger: Using the pre-hash (HashMLDSA) sign/verify API with a digest whose length is not what the selected hash mandates (e.g. passing a 32-byte SHA-256 digest where 64 bytes are required).
Common situations: Switching the hash function behind the API (SHA-256 -> SHA-512) without updating the digest buffer size; truncating or padding a stored digest; interop with a peer that hashes with a different algorithm.
Related errors
- mldsa: invalid seed length
- mldsa: invalid public key length
- mldsa: context too long
- mldsa: invalid signature length
- crypto/ecdsa: use of hash functions other than SHA-2 or SHA-
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/189d381f2a3d962c.
Report an issue: GitHub.