{"record":{"id":"189d381f2a3d962c","repo":"golang/go","slug":"mldsa-invalid-message-hash-length","errorCode":null,"errorMessage":"mldsa: invalid message hash length","messagePattern":"mldsa: invalid message hash length","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/mldsa/mldsa.go","lineNumber":333,"sourceCode":"\t}\n\n\t// We don't precompute A and t1Hat here, because they would make the\n\t// PublicKey over 68KB. Unlike private keys, public keys are often used to\n\t// verify a signature only once, so precomputation doesn't help as often,\n\t// but they can stay around in memory, for example as part of a TLS\n\t// connection's PeerCertificates, so their size is more of a concern.\n\t// Instead, we compute A and t1Hat on demand in Verify.\n\n\tpub.p = p\n\tcopy(pub.raw[:], pk)\n\tpub.tr = computePublicKeyHash(pk)\n\n\treturn pub, nil\n}\n\nvar (\n\terrContextTooLong    = errors.New(\"mldsa: context too long\")\n\terrMessageHashLength = errors.New(\"mldsa: invalid message hash length\")\n\terrRandomLength      = errors.New(\"mldsa: invalid random length\")\n)\n\nfunc Sign(priv *PrivateKey, msg []byte, context string) ([]byte, error) {\n\tfipsSelfTest()\n\tfips140.RecordApproved()\n\tvar random [32]byte\n\tdrbg.Read(random[:])\n\tμ, err := computeMessageHash(priv.pub.tr[:], msg, context)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn signInternal(priv, &μ, &random), nil\n}\n\nfunc SignDeterministic(priv *PrivateKey, msg []byte, context string) ([]byte, error) {\n\tfipsSelfTest()\n\tfips140.RecordApproved()","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/mldsa/mldsa.go#L315-L351","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\ndigest := sha256.Sum256(msg)            // 32 bytes\nsig, err := mldsa.SignHash(priv, digest[:], opts)\n\n// after\ndigest := sha3.Sum512(msg)              // 64 bytes, FIPS-mandated\nsig, err := mldsa.SignHash(priv, digest[:], opts)","handlingStrategy":"validation","validationCode":"if len(digest) != 64 {\n    return errors.New(\"pre-hash digest must be 64 bytes (SHA-512/SHA3-512)\")\n}","typeGuard":"func isMandatedHashLen(b []byte) bool { return len(b) == 64 }","tryCatchPattern":null,"preventionTips":["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."],"tags":["crypto","mldsa","fips","validation","hash"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}