golang/go · critical

P256 point not on curve

Error message

P256 point not on curve

What it means

After decoding an uncompressed P-256 point's x and y coordinates (both individually < p), p256CheckOnCurve verifies the curve equation y² == x³ - 3x + b over the field. If the equation does not hold, the (x,y) pair is not a point on P-256, even though each coordinate passed its individual range check. This is a critical cryptographic validity check that prevents invalid-curve attacks.

Source

Thrown at src/crypto/internal/fips140/nistec/p256_asm.go:164

	p256NegCond(threeX, 1)

	p256B := &p256Element{0xd89cdf6229c4bddf, 0xacf005cd78843090,
		0xe5a220abf7212ed6, 0xdc30061d04874834}

	p256Add(x3, x3, threeX)
	p256Add(x3, x3, p256B)

	*y2 = *x3
	return y2
}

func p256CheckOnCurve(x, y *p256Element) error {
	// y² = x³ - 3x + b
	rhs := p256Polynomial(new(p256Element), x)
	lhs := new(p256Element)
	p256Sqr(lhs, y, 1)
	if p256Equal(lhs, rhs) != 1 {
		return errors.New("P256 point not on curve")
	}
	return nil
}

// p256LessThanP returns 1 if x < p, and 0 otherwise. Note that a p256Element is
// not allowed to be equal to or greater than p, so if this function returns 0
// then x is invalid.
func p256LessThanP(x *p256Element) int {
	var b uint64
	_, b = bits.Sub64(x[0], p256P[0], b)
	_, b = bits.Sub64(x[1], p256P[1], b)
	_, b = bits.Sub64(x[2], p256P[2], b)
	_, b = bits.Sub64(x[3], p256P[3], b)
	return int(b)
}

func p256BigToLittle(l *p256Element, b *[32]byte) {
	bytesToLimbs((*[4]uint64)(l), b)

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Reject the input key — it is not a valid P-256 point
  2. Re-obtain the key from a trusted, authenticated source
  3. Always validate points before use in cryptographic operations (the library does this automatically via SetBytes)
Defensive patterns

Strategy: try-catch

Try / catch

_, err := point.SetBytes(b)
if err != nil {
    // Point failed on-curve check — possible invalid-curve attack.
    // Log the event and reject the key.
    log.Printf("rejected off-curve P-256 point: %v", err)
    return fmt.Errorf("invalid P-256 point: %w", err)
}

Prevention

When it happens

Trigger: Calling SetBytes with a 65-byte uncompressed point where x and y are both valid field elements (< p) but their combination does not satisfy y² = x³ - 3x + b mod p.

Common situations: Adversarially crafted public keys designed for invalid-curve attacks; corrupted key material where the corruption happens to keep each coordinate < p; manually assembling x and y from independent sources that don't correspond to the same point.

Related errors


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