{"record":{"id":"2e3b4a0362948b29","repo":"golang/go","slug":"crypto-ecdh-invalid-public-key-2e3b4a","errorCode":null,"errorMessage":"crypto/ecdh: invalid public key","messagePattern":"crypto/ecdh: invalid public key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/ecdh/ecdh.go","lineNumber":219,"sourceCode":"\t\tpanic(\"crypto/ecdh: internal error: nistec ScalarBaseMult failed for a fixed-size input\")\n\t}\n\n\tpublicKey := p.Bytes()\n\tif len(publicKey) == 1 {\n\t\t// The encoding of the identity is a single 0x00 byte. This is\n\t\t// unreachable because the only scalar that generates the identity is\n\t\t// zero, which is rejected above.\n\t\tpanic(\"crypto/ecdh: internal error: public key is the identity element\")\n\t}\n\n\tk := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}\n\treturn k, nil\n}\n\nfunc NewPublicKey[P Point[P]](c *Curve[P], key []byte) (*PublicKey, error) {\n\t// Reject the point at infinity and compressed encodings.\n\tif len(key) == 0 || key[0] != 4 {\n\t\treturn nil, errors.New(\"crypto/ecdh: invalid public key\")\n\t}\n\n\t// SetBytes checks that x and y are in the interval [0, p - 1], and that\n\t// the point is on the curve. Along with the rejection of the point at\n\t// infinity (the identity element) above, this fulfills the requirements\n\t// of NIST SP 800-56A Rev. 3, Section 5.6.2.3.4.\n\tif _, err := c.newPoint().SetBytes(key); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &PublicKey{curve: c.curve, q: bytes.Clone(key)}, nil\n}\n\nfunc ECDH[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {\n\tfipsSelfTest()\n\tfips140.RecordApproved()\n\treturn ecdh(c, k, peer)\n}","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/ecdh/ecdh.go#L201-L237","documentation":"Thrown by fips140/ecdh.NewPublicKey when the key is empty or its first byte is not 0x04. This package only accepts the uncompressed point encoding (prefix 0x04); it explicitly rejects the point-at-infinity, compressed encodings (0x02/0x03), and any malformed leading byte. Subsequent SetBytes then validates that (x,y) are in [0,p-1] and on the curve.","triggerScenarios":"Constructing an ECDH public key from a compressed encoding, an empty slice, or a buffer that does not begin with the 0x04 uncompressed prefix.","commonSituations":"Interoperating with libraries or wire formats (TLS, COSE, JWT) that use compressed points; parsing a malformed/corrupted key; feeding a raw coordinate pair without the prefix.","solutions":["Convert the point to uncompressed (0x04 || X || Y) form before passing it in.","Validate len(key) > 0 and key[0] == 4 before calling NewPublicKey.","If the source genuinely uses compressed points, decompress them on the curve first."],"exampleFix":"// before\npub, err := ecdh.NewPublicKey(curve, compressed) // compressed[0] == 0x02 or 0x03\n\n// after: supply uncompressed encoding\nuncompressed := append([]byte{0x04}, append(x, y...)...)\npub, err := ecdh.NewPublicKey(curve, uncompressed)","handlingStrategy":"validation","validationCode":"// Require uncompressed (0x04) encoding before NewPublicKey.\nif len(key) == 0 || key[0] != 4 {\n    return errors.New(\"ecdh public key must be uncompressed (0x04 prefix)\")\n}\nreturn ecdh.NewPublicKey(curve, key)","typeGuard":"func isUncompressedPoint(key []byte) bool {\n    return len(key) > 0 && key[0] == 0x04\n}","tryCatchPattern":null,"preventionTips":["Convert compressed points to uncompressed at the ingestion boundary.","Reject empty/malformed key buffers early.","Document the 0x04 requirement in your key-import API."],"tags":["go","crypto","fips","ecdh","key-validation","encoding"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}