golang/go · error

{{.P}} point not on curve

Error message

{{.P}} point not on curve

What it means

Generated from generate.go:271. Thrown by {{p}}CheckOnCurve when, for a candidate affine (x,y), y^2 != x^3 - 3x + b. The point fails the curve equation; it is not a valid member of the group.

Source

Thrown at src/crypto/internal/fips140/nistec/generate.go:271

// {{.p}}Polynomial sets y2 to x³ - 3x + b, and returns y2.
func {{.p}}Polynomial(y2, x *{{.Element}}) *{{.Element}} {
	y2.Square(x)
	y2.Mul(y2, x)

	threeX := new({{.Element}}).Add(x, x)
	threeX.Add(threeX, x)
	y2.Sub(y2, threeX)

	return y2.Add(y2, {{.p}}B())
}

func {{.p}}CheckOnCurve(x, y *{{.Element}}) error {
	// y² = x³ - 3x + b
	rhs := {{.p}}Polynomial(new({{.Element}}), x)
	lhs := new({{.Element}}).Square(y)
	if rhs.Equal(lhs) != 1 {
		return errors.New("{{.P}} point not on curve")
	}
	return nil
}

// Bytes returns the uncompressed or infinity encoding of p, as specified in
// SEC 1, Version 2.0, Section 2.3.3. Note that the encoding of the point at
// infinity is shorter than all other encodings.
func (p *{{.P}}Point) Bytes() []byte {
	// This function is outlined to make the allocations inline in the caller
	// rather than happen on the heap.
	var out [1+2*{{.p}}ElementLength]byte
	return p.bytes(&out)
}

func (p *{{.P}}Point) bytes(out *[1+2*{{.p}}ElementLength]byte) []byte {
	if p.z.IsZero() == 1 {
		return append(out[:0], 0)
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Confirm the (x,y) coordinates came from the same curve as the check function.
  2. Re-obtain the point from a trusted serialized form via SetBytes which validates internally.
  3. For externally supplied public keys, run CheckOnCurve and reject on error; do not attempt repair.
  4. Verify intermediate results of custom scalar multiplication against the curve's own Add/ScalarMult, not hand-rolled field ops.

Example fix

// before
if err := nistec.NewP256Point()./*manual*/; ... // coords hand-assembled
// after (parse through the validated path)
p, err := nistec.NewP256Point().SetBytes(sec1)
if err != nil { return fmt.Errorf("invalid point: %w", err) }
// SetBytes already enforces on-curve; no separate CheckOnCurve needed
Defensive patterns

Strategy: try-catch

Validate before calling

// Prefer the validated point parser; explicit CheckOnCurve is rarely needed.
p, err := curve.NewPoint().SetBytes(sec1)
if err != nil { return err }

Type guard

func pointOnCurve(p *nistec.P256Point) bool {
    // SetBytes already enforces on-curve; this is a defensive secondary check.
    return p.Bytes()[0] != 0 || /* infinity allowed */ true
}

Try / catch

if err := nistecCheckOnCurve(x, y); err != nil {
    return fmt.Errorf("point not on curve: %w", err)
}

Prevention

When it happens

Trigger: Passing an (x,y) pair where one coordinate was corrupted, an (x,y) from a different curve, or an uncompressed point whose y was reconstructed incorrectly. Triggered during explicit on-curve verification (not during SetBytes, which already validates).

Common situations: Cross-curve confusion (P-384 point checked against P-256 equation), tampered public key, manual point arithmetic that bypassed the curve's invariants, or test data with a transposed coordinate.

Related errors


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