{"record":{"id":"392481a809caa823","repo":"golang/go","slug":"crypto-ecdh-invalid-public-key","errorCode":null,"errorMessage":"crypto/ecdh: invalid public key","messagePattern":"crypto/ecdh: invalid public key","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/ecdh/nist.go","lineNumber":124,"sourceCode":"\t}\n\tk := &PrivateKey{\n\t\tcurve:      c,\n\t\tprivateKey: bytes.Clone(key),\n\t\tfips:       fk,\n\t\tpublicKey: &PublicKey{\n\t\t\tcurve:     c,\n\t\t\tpublicKey: fk.PublicKey().Bytes(),\n\t\t\tfips:      fk.PublicKey(),\n\t\t},\n\t}\n\treturn k, nil\n}\n\nfunc (c *nistCurve) NewPublicKey(key []byte) (*PublicKey, error) {\n\t// Reject the point at infinity and compressed encodings.\n\t// Note that boring.NewPublicKeyECDH would accept them.\n\tif len(key) == 0 || key[0] != 4 {\n\t\treturn nil, errors.New(\"crypto/ecdh: invalid public key\")\n\t}\n\tk := &PublicKey{\n\t\tcurve:     c,\n\t\tpublicKey: bytes.Clone(key),\n\t}\n\tif boring.Enabled {\n\t\tbk, err := boring.NewPublicKeyECDH(c.name, k.publicKey)\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"crypto/ecdh: invalid public key\")\n\t\t}\n\t\tk.boring = bk\n\t} else {\n\t\tfk, err := c.newPublicKey(key)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tk.fips = fk\n\t}","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/ecdh/nist.go#L106-L142","documentation":"NIST-curve ECDH public keys must be in SEC1 uncompressed form, which begins with the 0x04 prefix byte followed by the x and y coordinates. NewPublicKey rejects empty input and any key whose first byte is not 0x04 (i.e. compressed encodings starting with 0x02/0x03 and the point at infinity) before delegating to BoringCrypto. The comment notes BoringCrypto would otherwise accept these.","triggerScenarios":"Calling curve.NewPublicKey(key) where key is empty, or key[0] is 0x02/0x03 (compressed) or any non-0x04 value.","commonSituations":"Peer sends a compressed point; receiving an X9.62-encoded key with a different prefix; empty buffer from a failed read; serializing with a library that defaults to compressed form.","solutions":["Provide the uncompressed SEC1 encoding: 0x04 || X || Y.","If you only have a compressed point, decompress it first (e.g. via elliptic.UnmarshalCompressed) then re-encode uncompressed.","Reject peer keys that do not start with 0x04 at the protocol boundary."],"exampleFix":"// before\npub, err := curve.NewPublicKey(compressedKey) // compressedKey[0] == 0x02\n// after\nx, y := elliptic.UnmarshalCompressed(curve.Curve, compressedKey)\nuncompressed := append([]byte{0x04}, append(x, y...)...)\npub, err := curve.NewPublicKey(uncompressed)","handlingStrategy":"validation","validationCode":"func loadPub(curve ecdh.Curve, key []byte) (*ecdh.PublicKey, error) {\n    if len(key) == 0 || key[0] != 0x04 {\n        return nil, fmt.Errorf(\"public key must be uncompressed SEC1 (0x04 prefix), got %d bytes\", len(key))\n    }\n    return curve.NewPublicKey(key)\n}","typeGuard":"func isUncompressedSEC1(key []byte) bool {\n    return len(key) > 0 && key[0] == 0x04\n}","tryCatchPattern":"pub, err := curve.NewPublicKey(key)\nif err != nil && len(key) > 0 && key[0] != 0x04 {\n    // decompress first, then retry with 0x04 || X || Y\n}","preventionTips":["Require uncompressed (0x04) SEC1 encoding at the protocol boundary.","If peers send compressed points, decompress (elliptic.UnmarshalCompressed) before ECDH.","Reject empty public-key buffers early."],"tags":["crypto","ecdh","keys","encoding","validation","go","security"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}