{"record":{"id":"3469581ffe517ada","repo":"golang/go","slug":"ecdsa-private-key-does-not-match-curve","errorCode":null,"errorMessage":"ecdsa: private key does not match curve","messagePattern":"ecdsa: private key does not match curve","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/ecdsa/ecdsa.go","lineNumber":284,"sourceCode":"}\n\n// testingOnlyRejectionSamplingLooped is called when rejection sampling in\n// randomPoint rejects a candidate for being higher than the modulus.\nvar testingOnlyRejectionSamplingLooped func()\n\n// Signature is an ECDSA signature, where r and s are represented as big-endian\n// byte slices of the same length as the curve order.\ntype Signature struct {\n\tR, S []byte\n}\n\n// Sign signs a hash (which should be the result of hashing a larger message with\n// the hash function H) using the private key, priv. If the hash is longer than\n// the bit-length of the private key's curve order, the hash will be truncated\n// to that length.\nfunc Sign[P Point[P], H hash.Hash](c *Curve[P], h func() H, priv *PrivateKey, rand io.Reader, hash []byte) (*Signature, error) {\n\tif priv.pub.curve != c.curve {\n\t\treturn nil, errors.New(\"ecdsa: private key does not match curve\")\n\t}\n\tif len(hash) == 0 {\n\t\treturn nil, errors.New(\"ecdsa: hash cannot be empty\")\n\t}\n\tfips140.RecordApproved()\n\tfipsSelfTest()\n\n\t// Random ECDSA is dangerous, because a failure of the RNG would immediately\n\t// leak the private key. Instead, we use a \"hedged\" approach, as specified\n\t// in draft-irtf-cfrg-det-sigs-with-noise-04, Section 4. This has also the\n\t// advantage of closely resembling Deterministic ECDSA.\n\n\tZ := make([]byte, len(priv.d))\n\tif err := drbg.ReadWithReader(rand, Z); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// See https://github.com/cfrg/draft-irtf-cfrg-det-sigs-with-noise/issues/6","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/ecdsa/ecdsa.go#L266-L302","documentation":"Thrown by fips140/ecdsa.Sign when the supplied private key's curve does not match the operation context curve (priv.pub.curve != c.curve). The signing curve handle must match the key's curve before the hedged (draft-irtf-cfrg-det-sigs-with-noise) signing proceeds.","triggerScenarios":"Calling Sign(c, h, priv, rand, hash) where c and priv come from different curves.","commonSituations":"Refactoring that swaps a curve constant, multi-curve code that passes the wrong handle, or a key deserialized without recording its curve.","solutions":["Derive both c and priv from the same curve constant.","Assert priv.pub.curve matches c before calling Sign.","Store the curve with the key and select c from it."],"exampleFix":"// before\nsig, err := ecdsa.Sign(ecdsa.P256(), sha256.New, p384Priv, rand, hash)\n\n// after: match the key's curve\nsig, err := ecdsa.Sign(ecdsa.P384(), sha512.New, p384Priv, rand, hash)","handlingStrategy":"validation","validationCode":"if priv.PublicKey().Curve() != c.Curve() {\n    return errors.New(\"private key curve differs from signing context\")\n}\nreturn ecdsa.Sign(c, h, priv, rand, hash)","typeGuard":"func keyMatchesSignCurve(c *ecdsa.Curve, priv *ecdsa.PrivateKey) bool {\n    return priv.PublicKey().Curve() == c.Curve()\n}","tryCatchPattern":null,"preventionTips":["Derive c from the key's Curve().","Bind the curve to the key object.","Add a pre-sign assertion in multi-curve services."],"tags":["go","crypto","fips","ecdsa","key-validation","curve-mismatch"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}