{"record":{"id":"4592a71ebd793530","repo":"golang/go","slug":"crypto-rsa-input-must-be-hashed-message","errorCode":null,"errorMessage":"crypto/rsa: input must be hashed message","messagePattern":"crypto/rsa: input must be hashed message","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/rsa/fips.go","lineNumber":339,"sourceCode":"}\n\n// SignPKCS1v15 calculates the signature of hashed using\n// RSASSA-PKCS1-V1_5-SIGN from RSA PKCS #1 v1.5.  Note that hashed must\n// be the result of hashing the input message using the given hash\n// function. If hash is zero, hashed is signed directly. This isn't\n// advisable except for interoperability.\n//\n// The random parameter is legacy and ignored, and it can be nil.\n//\n// This function is deterministic. Thus, if the set of possible\n// messages is small, an attacker may be able to build a map from\n// messages to signatures and identify the signed messages. As ever,\n// signatures provide authenticity, not confidentiality.\nfunc SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {\n\tvar hashName string\n\tif hash != crypto.Hash(0) {\n\t\tif len(hashed) != hash.Size() {\n\t\t\treturn nil, errors.New(\"crypto/rsa: input must be hashed message\")\n\t\t}\n\t\thashName = hash.String()\n\t}\n\n\tif err := checkPublicKeySize(&priv.PublicKey); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif boring.Enabled && priv.N.BitLen() >= 1024 {\n\t\tbkey, err := boringPrivateKey(priv)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn boring.SignRSAPKCS1v15(bkey, hash, hashed)\n\t}\n\n\tif err := checkFIPS140OnlyPrivateKey(priv); err != nil {\n\t\treturn nil, err","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/rsa/fips.go#L321-L357","documentation":"Thrown by SignPKCS1v15 when hash is non-zero and len(hashed) != hash.Size(). PKCS#1 v1.5 signing requires the input to be exactly the digest of the named hash function; a mismatched length means the caller passed the wrong data (e.g. the raw message instead of its hash, or a different hash's output). This is basic input validation, not FIPS-specific.","triggerScenarios":"Calling rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, rawMessageBytes) — passing the plaintext instead of sha256.Sum256(...). Passing a SHA-512 digest (64 bytes) with crypto.SHA256 (expects 32 bytes). Passing a truncated or padded hash slice.","commonSituations":"Forgetting to hash the message first. Mismatch between the hash used to produce 'hashed' and the 'hash' argument. Off-by-slice (passing a 33-byte slice that includes a length prefix).","solutions":["Hash first: h := sha256.Sum256(msg); then pass h[:] with crypto.SHA256.","Ensure the hash constant passed as 'hash' matches the function that produced 'hashed'.","If hash == 0 (sign pre-hashed/raw), pass the data directly — but only when you intentionally bypass the digest requirement."],"exampleFix":"// before\nsig, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, message)\n\n// after\ndigest := sha256.Sum256(message)\nsig, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, digest[:])","handlingStrategy":"validation","validationCode":"if hash != 0 && len(hashed) != hash.Size() {\n    return fmt.Errorf(\"hashed input is %d bytes, expected %d for %s\", len(hashed), hash.Size(), hash)\n}\nsig, err := rsa.SignPKCS1v15(rand.Reader, priv, hash, hashed)","typeGuard":"func digestMatchesHash(hash crypto.Hash, hashed []byte) bool {\n    return hash == 0 || len(hashed) == hash.Size()\n}","tryCatchPattern":null,"preventionTips":["Always hash the message immediately before the Sign call using the same hash constant.","Never pass the raw message where a digest is expected.","Wrap signing in a helper that hashes+signs together to prevent the mismatch."],"tags":["crypto","rsa","pkcs1v15","validation","signing"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}