{"record":{"id":"c66e0c4009d69d8a","repo":"golang/go","slug":"p521-point-not-on-curve","errorCode":null,"errorMessage":"P521 point not on curve","messagePattern":"P521 point not on curve","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/crypto/internal/fips140/nistec/p521.go","lineNumber":137,"sourceCode":"\n// p521Polynomial sets y2 to x³ - 3x + b, and returns y2.\nfunc p521Polynomial(y2, x *fiat.P521Element) *fiat.P521Element {\n\ty2.Square(x)\n\ty2.Mul(y2, x)\n\n\tthreeX := new(fiat.P521Element).Add(x, x)\n\tthreeX.Add(threeX, x)\n\ty2.Sub(y2, threeX)\n\n\treturn y2.Add(y2, p521B())\n}\n\nfunc p521CheckOnCurve(x, y *fiat.P521Element) error {\n\t// y² = x³ - 3x + b\n\trhs := p521Polynomial(new(fiat.P521Element), x)\n\tlhs := new(fiat.P521Element).Square(y)\n\tif rhs.Equal(lhs) != 1 {\n\t\treturn errors.New(\"P521 point not on curve\")\n\t}\n\treturn nil\n}\n\n// Bytes returns the uncompressed or infinity encoding of p, as specified in\n// SEC 1, Version 2.0, Section 2.3.3. Note that the encoding of the point at\n// infinity is shorter than all other encodings.\nfunc (p *P521Point) Bytes() []byte {\n\t// This function is outlined to make the allocations inline in the caller\n\t// rather than happen on the heap.\n\tvar out [1 + 2*p521ElementLength]byte\n\treturn p.bytes(&out)\n}\n\nfunc (p *P521Point) bytes(out *[1 + 2*p521ElementLength]byte) []byte {\n\tif p.z.IsZero() == 1 {\n\t\treturn append(out[:0], 0)\n\t}","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/nistec/p521.go#L119-L155","documentation":"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.","triggerScenarios":"Decoding an uncompressed P-521 point (133 bytes, 0x04 prefix) where both coordinates are valid field elements but together fail the curve equation check.","commonSituations":"Corrupted P-521 public key; adversarially crafted point for an invalid-curve attack; mixing coordinates from different points; endianness mismatch in the coordinate encoding.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"_, err := p521Point.SetBytes(b)\nif err != nil {\n    // Off-curve P-521 point — possible attack or corruption.\n    log.Printf(\"rejected off-curve P-521 point: %v\", err)\n    return fmt.Errorf(\"invalid P-521 point: %w\", err)\n}","preventionTips":["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"],"tags":["crypto","fips140","p521","elliptic-curve","on-curve-check","invalid-curve-attack"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}