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

  1. Reject the public key — it is not a valid P-521 point
  2. Re-obtain the key from an authenticated source
  3. 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

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


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