golang/go · error
point not on curve
Error message
point not on curve
What it means
Raised by boring.NewPublicKeyECDH when BoringCrypto's EC_POINT_oct2point rejects the bytes — the coordinates do not satisfy the curve equation, so the bytes do not encode a valid point. This is distinct from a length error (290): length is correct but the point is mathematically invalid.
Source
Thrown at src/crypto/internal/boring/ecdh.go:58
nid, err := curveNID(curve)
if err != nil {
return nil, err
}
group := C._goboringcrypto_EC_GROUP_new_by_curve_name(nid)
if group == nil {
return nil, fail("EC_GROUP_new_by_curve_name")
}
defer C._goboringcrypto_EC_GROUP_free(group)
key := C._goboringcrypto_EC_POINT_new(group)
if key == nil {
return nil, fail("EC_POINT_new")
}
ok := C._goboringcrypto_EC_POINT_oct2point(group, key, (*C.uint8_t)(unsafe.Pointer(&bytes[0])), C.size_t(len(bytes)), nil) != 0
if !ok {
C._goboringcrypto_EC_POINT_free(key)
return nil, errors.New("point not on curve")
}
k := &PublicKeyECDH{curve, key, append([]byte(nil), bytes...)}
// Note: Because of the finalizer, any time k.key is passed to cgo,
// that call must be followed by a call to runtime.KeepAlive(k),
// to make sure k is not collected (and finalized) before the cgo
// call returns.
runtime.SetFinalizer(k, (*PublicKeyECDH).finalize)
return k, nil
}
func (k *PublicKeyECDH) Bytes() []byte { return k.bytes }
func NewPrivateKeyECDH(curve string, bytes []byte) (*PrivateKeyECDH, error) {
if len(bytes) != curveSize(curve) {
return nil, errors.New("NewPrivateKeyECDH: wrong key length")
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Validate/treat this as untrusted input: log and reject the peer, do not attempt correction.
- Ensure the bytes came from a peer's genuine ecdh.PublicKey.Bytes() output.
- If generating keys, use ecdh.GenerateKey which always yields valid points.
Example fix
// before
pub, err := boring.NewPublicKeyECDH("P-256", attackerBytes) // length ok, point invalid
// after
if _, err := boring.NewPublicKeyECDH("P-256", peerBytes); err != nil {
return errors.New("reject peer: invalid ECDH point")
} Defensive patterns
Strategy: try-catch
Validate before calling
// No pre-check can fully validate on-curve status cheaply; rely on the API.
// But you can reject obvious garbage:
func maybeValidPoint(b []byte) bool { return len(b) > 0 && b[0] == 0x04 } Try / catch
if _, err := boring.NewPublicKeyECDH(curve, peerBytes); err != nil {
if err.Error() == "point not on curve" {
// untrusted peer: reject and log, do not fall back
return ErrUntrustedPeer
}
return err
} Prevention
- Treat public keys from untrusted peers as hostile; let the API validate them.
- Never skip point validation in ECDH code paths.
- Prefer ecdh.PublicKey.Bytes() round-trips for your own keys.
When it happens
Trigger: Calling NewPublicKeyECDH with bytes of correct length but whose X,Y do not lie on the curve: corrupted coordinates, adversarially crafted points, or random data of the right size.
Common situations: Tampered public keys in transit; malicious peer sending an invalid point (classic ECDH point-validation attack); bit errors; deserialization from untrusted input without validation.
Related errors
- crypto/ecdh: invalid private key
- NewPublicKeyECDH: wrong key length
- NewPrivateKeyECDH: wrong key length
- boringcrypto: unknown elliptic curve
- crypto/ecdh: private key and public key curves do not match
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/401bd22bd58b3c34.
Report an issue: GitHub.