golang/go · error
ed25519: bad private key length: {l}
Error message
ed25519: bad private key length: {l} What it means
Returned by NewPrivateKey when privBytes is not exactly privateKeySize (64 = 32-byte seed + 32-byte public key) bytes. The FIPS PrivateKey type bundles seed and public key as specified by the standard encoding.
Source
Thrown at src/crypto/internal/fips140/ed25519/ed25519.go:114
s, err := priv.s.SetBytesWithClamping(h[:32])
if err != nil {
panic("ed25519: internal error: setting scalar failed")
}
A := (&edwards25519.Point{}).ScalarBaseMult(s)
copy(priv.pub[:], A.Bytes())
copy(priv.prefix[:], h[32:])
}
func NewPrivateKey(priv []byte) (*PrivateKey, error) {
p := &PrivateKey{}
return newPrivateKey(p, priv)
}
func newPrivateKey(priv *PrivateKey, privBytes []byte) (*PrivateKey, error) {
fips140.RecordApproved()
if l := len(privBytes); l != privateKeySize {
return nil, errors.New("ed25519: bad private key length: " + strconv.Itoa(l))
}
copy(priv.seed[:], privBytes[:32])
hs := sha512.New()
hs.Write(priv.seed[:])
h := hs.Sum(make([]byte, 0, sha512Size))
if _, err := priv.s.SetBytesWithClamping(h[:32]); err != nil {
panic("ed25519: internal error: setting scalar failed")
}
// Note that we are not decompressing the public key point here,
// because it takes > 20% of the time of a signature generation.
// Signing doesn't use it as a point anyway.
copy(priv.pub[:], privBytes[32:])
copy(priv.prefix[:], h[32:])
View on GitHub (pinned to b6b368adc5)
Solutions
- Supply exactly 64 raw bytes in the order [32-byte seed][32-byte public key].
- If you only have the seed, use NewPrivateKeyFromSeed and then read the public key from the result.
- For PKCS#8 PEM inputs, parse with the standard encoding package first and extract the inner 32-byte seed, then call NewPrivateKeyFromSeed.
Example fix
// before: only the 32-byte seed priv, err := ed25519.NewPrivateKey(seed32) // after priv, err := ed25519.NewPrivateKeyFromSeed(seed32) // or, if you have seed+pub concatenated (64 bytes): priv, err := ed25519.NewPrivateKey(seedPlusPub64)
Defensive patterns
Strategy: validation
Validate before calling
const ed25519PrivSize = 64
if len(b) != ed25519PrivSize {
return nil, fmt.Errorf("ed25519 private key must be %d bytes, got %d", ed25519PrivSize, len(b))
}
return ed25519.NewPrivateKey(b) Try / catch
priv, err := ed25519.NewPrivateKey(b)
if err != nil {
if strings.Contains(err.Error(), "bad private key length") {
// caller passed wrong-sized blob; surface clearly
return nil, fmt.Errorf("invalid ed25519 private key encoding: %w", err)
}
return nil, err
} Prevention
- Parse PKCS#8 / PEM with stdlib first; extract the inner 32-byte seed; use FromSeed.
- Document the 64-byte seed+pub layout at every import boundary.
- Test key import with both seed (32B) and full private key (64B) inputs.
When it happens
Trigger: Calling fips140/ed25519.NewPrivateKey(privBytes) with a byte slice whose length is not 64 — common offenders are 32 (seed only), 96 (some PKCS#8 forms), or arbitrary raw bytes.
Common situations: Confusing the 32-byte seed with the 64-byte private key; passing a PKCS#8 / OpenSSH-wrapped private key without first extracting the raw 64-byte core; passing the 32-byte scalar instead of the seed+pubkey blob.
Related errors
- ed25519: bad seed length: {l}
- ed25519: bad public key length: {l}
- ed25519: bad Ed25519ph message hash length: {l}
- ed25519: bad Ed25519ph context length: {l}
- ed25519: bad Ed25519ctx context length: {l}
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/0d1c0dada5d6c9fd.
Report an issue: GitHub.