{"record":{"id":"123c114aea602621","repo":"golang/go","slug":"ed25519-expected-opts-hashfunc-zero-unhashed-m","errorCode":null,"errorMessage":"ed25519: expected opts.HashFunc() zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)","messagePattern":"ed25519: expected opts\\.HashFunc\\(\\) zero \\(unhashed message, for standard Ed25519\\) or SHA-512 \\(for Ed25519ph\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/ed25519/ed25519.go","lineNumber":122,"sourceCode":"\t\treturn nil, err\n\t}\n\thash := opts.HashFunc()\n\tcontext := \"\"\n\tif opts, ok := opts.(*Options); ok {\n\t\tcontext = opts.Context\n\t}\n\tswitch {\n\tcase hash == crypto.SHA512: // Ed25519ph\n\t\treturn ed25519.SignPH(k, message, context)\n\tcase hash == crypto.Hash(0) && context != \"\": // Ed25519ctx\n\t\tif fips140only.Enforced() {\n\t\t\treturn nil, errors.New(\"crypto/ed25519: use of Ed25519ctx is not allowed in FIPS 140-only mode\")\n\t\t}\n\t\treturn ed25519.SignCtx(k, message, context)\n\tcase hash == crypto.Hash(0): // Ed25519\n\t\treturn ed25519.Sign(k, message), nil\n\tdefault:\n\t\treturn nil, errors.New(\"ed25519: expected opts.HashFunc() zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)\")\n\t}\n}\n\n// Options can be used with [PrivateKey.Sign] or [VerifyWithOptions]\n// to select Ed25519 variants.\ntype Options struct {\n\t// Hash can be zero for regular Ed25519, or crypto.SHA512 for Ed25519ph.\n\tHash crypto.Hash\n\n\t// Context, if not empty, selects Ed25519ctx or provides the context string\n\t// for Ed25519ph. It can be at most 255 bytes in length.\n\tContext string\n}\n\n// HashFunc returns o.Hash.\nfunc (o *Options) HashFunc() crypto.Hash { return o.Hash }\n\nvar cryptocustomrand = godebug.New(\"cryptocustomrand\")","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/ed25519/ed25519.go#L104-L140","documentation":"Thrown by the default switch arm in ed25519.go:122 (PrivateKey.Sign) when opts.HashFunc() is neither crypto.Hash(0) (unhashed, for Ed25519/ctx) nor crypto.SHA512 (Ed25519ph). Ed25519 does not support arbitrary hash pre-hashing; only those two values are valid.","triggerScenarios":"Calling priv.Sign with a SignerOpts whose Hash is, e.g., crypto.SHA256 or crypto.BLAKE2b_512 — any hash other than 0 or SHA-512. Often from passing crypto.Hash(c.SHA256) derived from an x509 signature algorithm.","commonSituations":"Generic signing code that forwards a caller-supplied crypto.SignerOpts; misinterpreting Ed25519 as supporting SHA-256 pre-hashing; plumbing a TLS/JOSE hash identifier into ed25519 Sign.","solutions":["Pass crypto.Hash(0) for standard Ed25519 (sign the raw message), or crypto.SHA512 for Ed25519ph.","When forwarding SignerOpts, branch on the key type and pass ed25519.Options{Hash: crypto.Hash(0)} for Ed25519 keys.","Validate opts.HashFunc() is 0 or SHA-512 before calling Sign on an Ed25519 key."],"exampleFix":"// before\nopts := crypto.SHA256 // Ed25519 does not support SHA-256\nsig, err := priv.Sign(rand.Reader, msg, opts) // -> error 249\n\n// after\nsig, err := priv.Sign(rand.Reader, msg, crypto.Hash(0)) // standard Ed25519","handlingStrategy":"validation","validationCode":"hv := opts.HashFunc()\nif hv != crypto.Hash(0) && hv != crypto.SHA512 {\n    return errors.New(\"ed25519 requires Hash 0 or SHA-512\")\n}","typeGuard":"func validEd25519SignHash(h crypto.Hash) bool {\n    return h == crypto.Hash(0) || h == crypto.SHA512\n}","tryCatchPattern":null,"preventionTips":["Pass crypto.Hash(0) for standard Ed25519 and crypto.SHA512 for Ed25519ph.","In generic signing code, special-case Ed25519 keys rather than forwarding arbitrary SignerOpts.","Validate the hash option before calling Sign."],"tags":["go","crypto","ed25519","api-misuse"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}