{"record":{"id":"29ebf1a5b005f9e0","repo":"golang/go","slug":"crypto-ecdh-mismatched-curves","errorCode":null,"errorMessage":"crypto/ecdh: mismatched curves","messagePattern":"crypto/ecdh: mismatched curves","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/ecdh/ecdh.go","lineNumber":241,"sourceCode":"\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}\n\nfunc ecdh[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {\n\tif c.curve != k.pub.curve {\n\t\treturn nil, errors.New(\"crypto/ecdh: mismatched curves\")\n\t}\n\tif k.pub.curve != peer.curve {\n\t\treturn nil, errors.New(\"crypto/ecdh: mismatched curves\")\n\t}\n\n\t// This applies the Shared Secret Computation of the Ephemeral Unified Model\n\t// scheme specified in NIST SP 800-56A Rev. 3, Section 6.1.2.2.\n\n\t// Per Section 5.6.2.3.4, Step 1, reject the identity element (0x00).\n\tif len(k.pub.q) == 1 {\n\t\treturn nil, errors.New(\"crypto/ecdh: public key is the identity element\")\n\t}\n\n\t// SetBytes checks that (x, y) are reduced modulo p, and that they are on\n\t// the curve, performing Steps 2-3 of Section 5.6.2.3.4.\n\tp, err := c.newPoint().SetBytes(peer.q)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/ecdh/ecdh.go#L223-L259","documentation":"Thrown by fips140/ecdh.ecdh when the curve passed as the operation context c does not match the curve of the supplied PrivateKey (c.curve != k.pub.curve). The context curve, the private key's curve, and the peer's public key curve must all agree before any scalar multiplication.","triggerScenarios":"Calling ECDH(c, priv, peer) where c was obtained for a different curve than the one priv was generated on.","commonSituations":"Mixing a P-256 context with a P-384 private key, or refactoring that passes the wrong curve handle into ECDH.","solutions":["Derive c from the same curve constant used to generate priv (e.g. both ecdh.P256()).","Add a guard comparing the context curve to the key's curve before calling ECDH.","Centralize curve selection so a single constant flows to all key ops."],"exampleFix":"// before\nsecret, err := ecdh.ECDH(ecdh.P256(), p384Priv, peer) // mismatch\n\n// after: use the curve the key belongs to\nsecret, err := ecdh.ECDH(ecdh.P384(), p384Priv, peer)","handlingStrategy":"validation","validationCode":"// Ensure the context curve matches the key's curve before ECDH.\nif c.Curve() != priv.Curve() {\n    return errors.New(\"context curve does not match private key curve\")\n}\nreturn ecdh.ECDH(c, priv, peer)","typeGuard":"func sameCurveContext(c *ecdh.Curve, priv *ecdh.PrivateKey) bool {\n    return c.Curve() == priv.Curve()\n}","tryCatchPattern":null,"preventionTips":["Derive the context curve from the key's own Curve() rather than a separate constant.","Centralize curve selection in one place.","Add assertions in multi-curve code paths."],"tags":["go","crypto","fips","ecdh","key-validation","curve-mismatch"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}