{"record":{"id":"25bae841ec9e4745","repo":"golang/go","slug":"crypto-ecdh-private-key-and-public-key-curves-do","errorCode":null,"errorMessage":"crypto/ecdh: private key and public key curves do not match","messagePattern":"crypto/ecdh: private key and public key curves do not match","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/ecdh/ecdh.go","lineNumber":133,"sourceCode":"\tpublicKey  *PublicKey\n\tboring     *boring.PrivateKeyECDH\n\tfips       *ecdh.PrivateKey\n}\n\n// ECDH performs an ECDH exchange and returns the shared secret. The [PrivateKey]\n// and [PublicKey] must use the same curve.\n//\n// For NIST curves, this performs ECDH as specified in SEC 1, Version 2.0,\n// Section 3.3.1, and returns the x-coordinate encoded according to SEC 1,\n// Version 2.0, Section 2.3.5. The result is never the point at infinity.\n// This is also known as the Shared Secret Computation of the Ephemeral Unified\n// Model scheme specified in NIST SP 800-56A Rev. 3, Section 6.1.2.2.\n//\n// For [X25519], this performs ECDH as specified in RFC 7748, Section 6.1. If\n// the result is the all-zero value, ECDH returns an error.\nfunc (k *PrivateKey) ECDH(remote *PublicKey) ([]byte, error) {\n\tif k.curve != remote.curve {\n\t\treturn nil, errors.New(\"crypto/ecdh: private key and public key curves do not match\")\n\t}\n\treturn k.curve.ecdh(k, remote)\n}\n\n// Bytes returns a copy of the encoding of the private key.\nfunc (k *PrivateKey) Bytes() []byte {\n\t// Copy the private key to a fixed size buffer that can get allocated on the\n\t// caller's stack after inlining.\n\tvar buf [66]byte\n\treturn append(buf[:0], k.privateKey...)\n}\n\n// Equal returns whether x represents the same private key as k.\n//\n// Note that there can be equivalent private keys with different encodings which\n// would return false from this check but behave the same way as inputs to [ECDH].\n//\n// This check is performed in constant time as long as the key types and their","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/ecdh/ecdh.go#L115-L151","documentation":"ECDH key agreement requires both parties' keys to be on the same curve. PrivateKey and PublicKey each carry an opaque, comparable curve field; if k.curve != remote.curve the operation is refused before any scalar multiplication, because cross-curve ECDH is undefined.","triggerScenarios":"Calling priv.ECDH(remotePub) where priv was created with e.g. ecdh.P256() and remotePub with ecdh.P384(), or where one side is an X25519 key and the other a NIST key.","commonSituations":"Mixing key pairs of different curves in a handshake; deserializing a public key without recording which curve it belongs to; configuration drift between client and server curve selection; peer sends a key for a curve you did not negotiate.","solutions":["Ensure both keys are produced/loaded via the same Curve instance (e.g. both ecdh.P256()).","Compare curves before calling ECDH: if priv.PublicKey().Curve() != remotePub.Curve() handle the mismatch.","Persist the curve name alongside serialized keys and reconstruct keys through the matching Curve accessor.","Negotiate a single curve up front in the protocol and reject peer keys for other curves."],"exampleFix":"// before\nsecret, err := privP256.ECDH(pubP384) // different curves\n// after\nsecret, err := privP256.ECDH(pubP256) // same curve","handlingStrategy":"type-guard","validationCode":"func ecdhOrErr(priv *ecdh.PrivateKey, remote *ecdh.PublicKey) ([]byte, error) {\n    if !sameCurve(priv, remote) {\n        return nil, fmt.Errorf(\"curve mismatch: local=%s remote=%s\",\n            priv.Curve(), remote.Curve())\n    }\n    return priv.ECDH(remote)\n}","typeGuard":"func sameCurve(priv *ecdh.PrivateKey, pub *ecdh.PublicKey) bool {\n    return priv != nil && pub != nil &&\n        priv.PublicKey().Curve() == pub.Curve()\n}","tryCatchPattern":"secret, err := priv.ECDH(remote)\nif err != nil {\n    if errors.Is(err, errCurveMismatch) /* compare via message if needed */ {\n        // negotiate the correct curve and retry with a fresh peer key\n    }\n    return err\n}","preventionTips":["Store the curve name with serialized keys and reconstruct via the matching Curve accessor.","Negotiate a single curve in the protocol and reject peer keys for other curves.","Unit-test ECDH with mismatched curves to confirm it errors cleanly."],"tags":["crypto","ecdh","keys","validation","go","security"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}