golang/go · error
crypto/ecdh: invalid public key
Error message
crypto/ecdh: invalid public key
What it means
Thrown by fips140/ecdh.NewPublicKey when the key is empty or its first byte is not 0x04. This package only accepts the uncompressed point encoding (prefix 0x04); it explicitly rejects the point-at-infinity, compressed encodings (0x02/0x03), and any malformed leading byte. Subsequent SetBytes then validates that (x,y) are in [0,p-1] and on the curve.
Source
Thrown at src/crypto/internal/fips140/ecdh/ecdh.go:219
panic("crypto/ecdh: internal error: nistec ScalarBaseMult failed for a fixed-size input")
}
publicKey := p.Bytes()
if len(publicKey) == 1 {
// The encoding of the identity is a single 0x00 byte. This is
// unreachable because the only scalar that generates the identity is
// zero, which is rejected above.
panic("crypto/ecdh: internal error: public key is the identity element")
}
k := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}
return k, nil
}
func NewPublicKey[P Point[P]](c *Curve[P], key []byte) (*PublicKey, error) {
// Reject the point at infinity and compressed encodings.
if len(key) == 0 || key[0] != 4 {
return nil, errors.New("crypto/ecdh: invalid public key")
}
// SetBytes checks that x and y are in the interval [0, p - 1], and that
// the point is on the curve. Along with the rejection of the point at
// infinity (the identity element) above, this fulfills the requirements
// of NIST SP 800-56A Rev. 3, Section 5.6.2.3.4.
if _, err := c.newPoint().SetBytes(key); err != nil {
return nil, err
}
return &PublicKey{curve: c.curve, q: bytes.Clone(key)}, nil
}
func ECDH[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {
fipsSelfTest()
fips140.RecordApproved()
return ecdh(c, k, peer)
}View on GitHub (pinned to b6b368adc5)
Solutions
- Convert the point to uncompressed (0x04 || X || Y) form before passing it in.
- Validate len(key) > 0 and key[0] == 4 before calling NewPublicKey.
- If the source genuinely uses compressed points, decompress them on the curve first.
Example fix
// before
pub, err := ecdh.NewPublicKey(curve, compressed) // compressed[0] == 0x02 or 0x03
// after: supply uncompressed encoding
uncompressed := append([]byte{0x04}, append(x, y...)...)
pub, err := ecdh.NewPublicKey(curve, uncompressed) Defensive patterns
Strategy: validation
Validate before calling
// Require uncompressed (0x04) encoding before NewPublicKey.
if len(key) == 0 || key[0] != 4 {
return errors.New("ecdh public key must be uncompressed (0x04 prefix)")
}
return ecdh.NewPublicKey(curve, key) Type guard
func isUncompressedPoint(key []byte) bool {
return len(key) > 0 && key[0] == 0x04
} Prevention
- Convert compressed points to uncompressed at the ingestion boundary.
- Reject empty/malformed key buffers early.
- Document the 0x04 requirement in your key-import API.
When it happens
Trigger: Constructing an ECDH public key from a compressed encoding, an empty slice, or a buffer that does not begin with the 0x04 uncompressed prefix.
Common situations: Interoperating with libraries or wire formats (TLS, COSE, JWT) that use compressed points; parsing a malformed/corrupted key; feeding a raw coordinate pair without the prefix.
Related errors
- crypto/ecdh: invalid private key
- crypto/ecdh: mismatched curves
- crypto/ecdh: public key is the identity element
- ecdsa: invalid private key length
- ecdsa: invalid public key encoding
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/2e3b4a0362948b29.
Report an issue: GitHub.