{"record":{"id":"8d25e1f2217bd6ca","repo":"golang/go","slug":"invalid-p-compressed-point-encoding","errorCode":null,"errorMessage":"invalid {{.P}} compressed point encoding","messagePattern":"invalid (.+?)\\} compressed point encoding","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/nistec/generate.go","lineNumber":223,"sourceCode":"\t\tif err := {{.p}}CheckOnCurve(x, y); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tp.x.Set(x)\n\t\tp.y.Set(y)\n\t\tp.z.One()\n\t\treturn p, nil\n\n\t// Compressed form.\n\tcase len(b) == 1+{{.p}}ElementLength && (b[0] == 2 || b[0] == 3):\n\t\tx, err := new({{.Element}}).SetBytes(b[1:])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// y² = x³ - 3x + b\n\t\ty := {{.p}}Polynomial(new({{.Element}}), x)\n\t\tif !{{.p}}Sqrt(y, y) {\n\t\t\treturn nil, errors.New(\"invalid {{.P}} compressed point encoding\")\n\t\t}\n\n\t\t// Select the positive or negative root, as indicated by the least\n\t\t// significant bit, based on the encoding type byte.\n\t\totherRoot := new({{.Element}})\n\t\totherRoot.Sub(otherRoot, y)\n\t\tcond := y.Bytes()[{{.p}}ElementLength-1]&1 ^ b[0]&1\n\t\ty.Select(otherRoot, y, int(cond))\n\n\t\tp.x.Set(x)\n\t\tp.y.Set(y)\n\t\tp.z.One()\n\t\treturn p, nil\n\n\tdefault:\n\t\treturn nil, errors.New(\"invalid {{.P}} point encoding\")\n\t}\n}","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/nistec/generate.go#L205-L241","documentation":"Generated into each NIST curve from generate.go:223. Thrown when parsing a compressed SEC1 point (type byte 0x02/0x03, length 1+ElementLength) where the x-coordinate yields no valid y, i.e. x^3-3x+b is not a quadratic residue. The point is not on the curve.","triggerScenarios":"Compressed point with an x that is not on the curve, an x that was corrupted/truncated, or a compressed encoding from a different curve (e.g. P-384 point fed to a P-256 parser).","commonSituations":"Fuzzed/adversarial public keys, byte-swap or endian errors, cross-curve confusion, or a stale encoding from a draft curve parameter.","solutions":["Validate the source of the point; reject compressed points from untrusted peers with explicit error handling.","Confirm the type byte (0x02/0x03) and length match the target curve (1+ElementLength).","Use the curve-specific point parser rather than re-implementing decompression.","Re-serialize the point via BytesCompressed from a known-good point and diff against the input."],"exampleFix":"// before\np, err := nistec.NewP256Point().SetBytes(b) // b is a corrupted compressed point\n// after\nif !(len(b) == 1+p256ElementLength && (b[0] == 2 || b[0] == 3)) {\n    return errors.New(\"not a P-256 compressed point\")\n}\np, err := nistec.NewP256Point().SetBytes(b)\nif err != nil { return fmt.Errorf(\"point off curve: %w\", err) }","handlingStrategy":"validation","validationCode":"// Validate SEC1 compressed shape before decompression.\nif len(b) != 1+elementLength || (b[0] != 2 && b[0] != 3) {\n    return errors.New(\"not a compressed point\")\n}","typeGuard":"func isCompressedSEC1(b []byte, elLen int) bool {\n    return len(b) == 1+elLen && (b[0] == 2 || b[0] == 3)\n}","tryCatchPattern":"p, err := curve.NewPoint().SetBytes(b)\nif err != nil {\n    return fmt.Errorf(\"point decompression failed (len=%d): %w\", len(b), err)\n}","preventionTips":["Reject compressed points from untrusted peers with explicit error handling.","Match the curve before calling the curve-specific parser.","Re-encode from a known-good point and diff if a point is suspect."],"tags":["elliptic-curve","fips140","crypto","nistec","point-parsing","sec1","code-generation"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}