golang/go · error
ed25519: bad Ed25519ph message hash length: {l}
Error message
ed25519: bad Ed25519ph message hash length: {l} What it means
Returned by SignPH (Ed25519ph) when the message argument is not exactly sha512Size (64) bytes. Ed25519ph signs a pre-computed SHA-512 hash of the message, so the API contract requires the caller to pass that 64-byte digest, not the raw message.
Source
Thrown at src/crypto/internal/fips140/ed25519/ed25519.go:190
func sign(signature []byte, priv *PrivateKey, message []byte) []byte {
fipsSelfTest()
fips140.RecordApproved()
return signWithDom(signature, priv, message, domPrefixPure, "")
}
func SignPH(priv *PrivateKey, message []byte, context string) ([]byte, error) {
// Outline the function body so that the returned signature can be
// stack-allocated.
signature := make([]byte, signatureSize)
return signPH(signature, priv, message, context)
}
func signPH(signature []byte, priv *PrivateKey, message []byte, context string) ([]byte, error) {
fipsSelfTest()
fips140.RecordApproved()
if l := len(message); l != sha512Size {
return nil, errors.New("ed25519: bad Ed25519ph message hash length: " + strconv.Itoa(l))
}
if l := len(context); l > 255 {
return nil, errors.New("ed25519: bad Ed25519ph context length: " + strconv.Itoa(l))
}
return signWithDom(signature, priv, message, domPrefixPh, context), nil
}
func SignCtx(priv *PrivateKey, message []byte, context string) ([]byte, error) {
// Outline the function body so that the returned signature can be
// stack-allocated.
signature := make([]byte, signatureSize)
return signCtx(signature, priv, message, context)
}
func signCtx(signature []byte, priv *PrivateKey, message []byte, context string) ([]byte, error) {
fipsSelfTest()
// FIPS 186-5 specifies Ed25519 and Ed25519ph (with context), but not Ed25519ctx.
fips140.RecordNonApproved()View on GitHub (pinned to b6b368adc5)
Solutions
- Pre-hash the message with SHA-512 and pass the resulting 64-byte digest to SignPH.
- If you want the library to hash for you, use the pure Ed25519 Sign instead of Ed25519ph.
- Verify interoperability: both signer and verifier must agree on the SHA-512 pre-hash variant.
Example fix
// before
sig, err := ed25519.SignPH(priv, []byte("hello"), "")
// after
digest := sha512.Sum512([]byte("hello"))
sig, err := ed25519.SignPH(priv, digest[:], "") Defensive patterns
Strategy: validation
Validate before calling
if len(message) != 64 {
digest := sha512.Sum512(message)
message = digest[:]
}
sig, err := ed25519.SignPH(priv, message, context) Try / catch
sig, err := ed25519.SignPH(priv, message, context)
if err != nil {
if strings.Contains(err.Error(), "bad Ed25519ph message hash length") {
return nil, fmt.Errorf("SignPH needs a 64-byte SHA-512 digest, got %d bytes", len(message))
}
return nil, err
} Prevention
- Document that Ed25519ph expects a SHA-512 digest, not the raw message.
- Use pure Ed25519 Sign if you want the library to hash internally.
- Make signer and verifier agree on the variant.
When it happens
Trigger: Calling fips140/ed25519.SignPH(priv, message, context) where len(message) != 64 — typically because the raw message or a SHA-256 hash was passed instead of a SHA-512 hash.
Common situations: Passing the plaintext message instead of its SHA-512 hash; passing a SHA-256 (32-byte) or SHA-384 hash; assuming SignPH hashes internally (it does not — it expects the digest).
Related errors
- ed25519: bad Ed25519ph context length: {l}
- ed25519: bad seed length: {l}
- ed25519: bad private key length: {l}
- ed25519: bad public key length: {l}
- ed25519: bad Ed25519ctx context length: {l}
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/e78ae703f38d23c3.
Report an issue: GitHub.