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

  1. Use SHA-512 or SHA3-512 (64-byte output) as the HashMLDSA function and pass the full digest.
  2. Re-hash the message with the mandated algorithm at the boundary rather than trusting an externally-supplied digest length.
  3. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/189d381f2a3d962c. Report an issue: GitHub.