golang/go · error
ed25519: expected opts.HashFunc() zero (unhashed message, fo
Error message
ed25519: expected opts.HashFunc() zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)
What it means
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.
Source
Thrown at src/crypto/ed25519/ed25519.go:122
return nil, err
}
hash := opts.HashFunc()
context := ""
if opts, ok := opts.(*Options); ok {
context = opts.Context
}
switch {
case hash == crypto.SHA512: // Ed25519ph
return ed25519.SignPH(k, message, context)
case hash == crypto.Hash(0) && context != "": // Ed25519ctx
if fips140only.Enforced() {
return nil, errors.New("crypto/ed25519: use of Ed25519ctx is not allowed in FIPS 140-only mode")
}
return ed25519.SignCtx(k, message, context)
case hash == crypto.Hash(0): // Ed25519
return ed25519.Sign(k, message), nil
default:
return nil, errors.New("ed25519: expected opts.HashFunc() zero (unhashed message, for standard Ed25519) or SHA-512 (for Ed25519ph)")
}
}
// Options can be used with [PrivateKey.Sign] or [VerifyWithOptions]
// to select Ed25519 variants.
type Options struct {
// Hash can be zero for regular Ed25519, or crypto.SHA512 for Ed25519ph.
Hash crypto.Hash
// Context, if not empty, selects Ed25519ctx or provides the context string
// for Ed25519ph. It can be at most 255 bytes in length.
Context string
}
// HashFunc returns o.Hash.
func (o *Options) HashFunc() crypto.Hash { return o.Hash }
var cryptocustomrand = godebug.New("cryptocustomrand")View on GitHub (pinned to b6b368adc5)
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.
Example fix
// before opts := crypto.SHA256 // Ed25519 does not support SHA-256 sig, err := priv.Sign(rand.Reader, msg, opts) // -> error 249 // after sig, err := priv.Sign(rand.Reader, msg, crypto.Hash(0)) // standard Ed25519
Defensive patterns
Strategy: validation
Validate before calling
hv := opts.HashFunc()
if hv != crypto.Hash(0) && hv != crypto.SHA512 {
return errors.New("ed25519 requires Hash 0 or SHA-512")
} Type guard
func validEd25519SignHash(h crypto.Hash) bool {
return h == crypto.Hash(0) || h == crypto.SHA512
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ed25519: expected opts.Hash zero (unhashed message, for stan
- crypto/ed25519: use of Ed25519ctx is not allowed in FIPS 140
- crypto/ed25519: only crypto/rand.Reader is allowed in FIPS 1
- Ed25519 verification failure
- globalThis.crypto is not available, polyfill required (crypt
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/123c114aea602621.
Report an issue: GitHub.