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
- Reject the input key — it is not a valid P-256 point
- Re-obtain the key from a trusted, authenticated source
- 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
- Treat off-curve point rejections as potential attacks, not just data errors
- Always validate points via SetBytes before any scalar multiplication
- Use high-level APIs (crypto/ecdh, crypto/ecdsa) that never skip validation
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
- P521 point not on curve
- invalid P256 compressed point encoding
- invalid P256 point encoding
- P256 point not on curve
- P256 point is the point at infinity
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/aa7d8b32df97f74f.
Report an issue: GitHub.