golang/go · error
crypto/ecdh: invalid private key size
Error message
crypto/ecdh: invalid private key size
What it means
X25519 private keys are exactly 32 bytes (x25519PrivateKeySize). NewPrivateKey checks len(key) != x25519PrivateKeySize and returns this error for any other length. (The FIPS check runs first; this fires only when FIPS mode is not enforced.)
Source
Thrown at src/crypto/ecdh/x25519.go:54
func (c *x25519Curve) GenerateKey(r io.Reader) (*PrivateKey, error) {
if fips140only.Enforced() {
return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode")
}
r = rand.CustomReader(r)
key := make([]byte, x25519PrivateKeySize)
if _, err := io.ReadFull(r, key); err != nil {
return nil, err
}
return c.NewPrivateKey(key)
}
func (c *x25519Curve) NewPrivateKey(key []byte) (*PrivateKey, error) {
if fips140only.Enforced() {
return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode")
}
if len(key) != x25519PrivateKeySize {
return nil, errors.New("crypto/ecdh: invalid private key size")
}
publicKey := make([]byte, x25519PublicKeySize)
x25519Basepoint := [32]byte{9}
x25519ScalarMult(publicKey, key, x25519Basepoint[:])
// We don't check for the all-zero public key here because the scalar is
// never zero because of clamping, and the basepoint is not the identity in
// the prime-order subgroup(s).
return &PrivateKey{
curve: c,
privateKey: bytes.Clone(key),
publicKey: &PublicKey{curve: c, publicKey: publicKey},
}, nil
}
func (c *x25519Curve) NewPublicKey(key []byte) (*PublicKey, error) {
if fips140only.Enforced() {
return nil, errors.New("crypto/ecdh: use of X25519 is not allowed in FIPS 140-only mode")
}View on GitHub (pinned to b6b368adc5)
Solutions
- Supply exactly 32 raw bytes: ensure len(key) == 32 before calling.
- Decode hex/base64 to raw bytes first (hex.DecodeString / base64.StdEncoding.DecodeString).
- Prefer GenerateKey(rand.Reader) to obtain correctly-sized keys rather than hand-constructing them.
Example fix
// before priv, err := ecdh.X25519().NewPrivateKey([]byte(hexString)) // wrong length // after raw, _ := hex.DecodeString(hexString) // 32 bytes priv, err := ecdh.X25519().NewPrivateKey(raw)
Defensive patterns
Strategy: validation
Validate before calling
func newX25519Priv(key []byte) (*ecdh.PrivateKey, error) {
if len(key) != 32 {
return nil, fmt.Errorf("X25519 private key must be 32 bytes, got %d", len(key))
}
return ecdh.X25519().NewPrivateKey(key)
} Type guard
func isX25519PrivSize(key []byte) bool { return len(key) == 32 } Prevention
- Decode hex/base64 to raw bytes before constructing the key.
- Assert len(key)==32 in a test for any key-loading code path.
- Prefer GenerateKey over hand-loading raw bytes.
When it happens
Trigger: Calling ecdh.X25519().NewPrivateKey(key) with len(key) != 32, e.g. a 31-byte key, a hex/base64 string instead of raw bytes, or a longer buffer.
Common situations: Passing a hex-encoded string instead of decoded bytes; truncated read; concatenating extra metadata into the key buffer; off-by-one slicing.
Related errors
- crypto/ecdh: private key and public key curves do not match
- crypto/ecdh: invalid private key
- crypto/ecdh: invalid public key
- crypto/ecdh: use of X25519 is not allowed in FIPS 140-only m
- crypto/dsa: invalid public key
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/c307f34646f7b9e0.
Report an issue: GitHub.