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

  1. Validate/treat this as untrusted input: log and reject the peer, do not attempt correction.
  2. Ensure the bytes came from a peer's genuine ecdh.PublicKey.Bytes() output.
  3. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/401bd22bd58b3c34. Report an issue: GitHub.