golang/go · critical
P521 point not on curve
Error message
P521 point not on curve
What it means
p521CheckOnCurve verifies that a decoded P-521 point satisfies y² = x³ - 3x + b over the P-521 prime field. If the equation fails, the (x,y) pair is not on the curve. This is the P-521 equivalent of error 406 and is critical for preventing invalid-curve attacks on the larger NIST curve.
Source
Thrown at src/crypto/internal/fips140/nistec/p521.go:137
// p521Polynomial sets y2 to x³ - 3x + b, and returns y2.
func p521Polynomial(y2, x *fiat.P521Element) *fiat.P521Element {
y2.Square(x)
y2.Mul(y2, x)
threeX := new(fiat.P521Element).Add(x, x)
threeX.Add(threeX, x)
y2.Sub(y2, threeX)
return y2.Add(y2, p521B())
}
func p521CheckOnCurve(x, y *fiat.P521Element) error {
// y² = x³ - 3x + b
rhs := p521Polynomial(new(fiat.P521Element), x)
lhs := new(fiat.P521Element).Square(y)
if rhs.Equal(lhs) != 1 {
return errors.New("P521 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 *P521Point) Bytes() []byte {
// This function is outlined to make the allocations inline in the caller
// rather than happen on the heap.
var out [1 + 2*p521ElementLength]byte
return p.bytes(&out)
}
func (p *P521Point) bytes(out *[1 + 2*p521ElementLength]byte) []byte {
if p.z.IsZero() == 1 {
return append(out[:0], 0)
}View on GitHub (pinned to b6b368adc5)
Solutions
- Reject the public key — it is not a valid P-521 point
- Re-obtain the key from an authenticated source
- Use the standard crypto/ecdsa or crypto/tls APIs which enforce validation
Defensive patterns
Strategy: try-catch
Try / catch
_, err := p521Point.SetBytes(b)
if err != nil {
// Off-curve P-521 point — possible attack or corruption.
log.Printf("rejected off-curve P-521 point: %v", err)
return fmt.Errorf("invalid P-521 point: %w", err)
} Prevention
- Treat off-curve P-521 rejections as potential invalid-curve attacks
- Always validate points via SetBytes before scalar multiplication
- Use crypto/ecdsa or crypto/tls which enforce validation automatically
When it happens
Trigger: Decoding an uncompressed P-521 point (133 bytes, 0x04 prefix) where both coordinates are valid field elements but together fail the curve equation check.
Common situations: Corrupted P-521 public key; adversarially crafted point for an invalid-curve attack; mixing coordinates from different points; endianness mismatch in the coordinate encoding.
Related errors
- P256 point not on curve
- P521 point is the point at infinity
- invalid {{ .Element }} encoding
- invalid {{.P}} compressed point encoding
- invalid {{.P}} point encoding
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/c66e0c4009d69d8a.
Report an issue: GitHub.