{"record":{"id":"4c28837caacd5475","repo":"slackhq/nebula","slug":"curve-in-cert-and-private-key-supplied-don-t-match","errorCode":null,"errorMessage":"curve in cert and private key supplied don't match","messagePattern":"curve in cert and private key supplied don't match","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cert/cert_v1.go","lineNumber":137,"sourceCode":"\tcase Curve_P256:\n\t\tpubKey, err := ecdsa.ParseUncompressedPublicKey(elliptic.P256(), key)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\thashed := sha256.Sum256(b)\n\t\treturn ecdsa.VerifyASN1(pubKey, hashed[:], c.signature)\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (c *certificateV1) Expired(t time.Time) bool {\n\treturn c.details.notBefore.After(t) || c.details.notAfter.Before(t)\n}\n\nfunc (c *certificateV1) VerifyPrivateKey(curve Curve, key []byte) error {\n\tif curve != c.details.curve {\n\t\treturn fmt.Errorf(\"curve in cert and private key supplied don't match\")\n\t}\n\tif c.details.isCA {\n\t\tswitch curve {\n\t\tcase Curve_CURVE25519:\n\t\t\t// the call to PublicKey below will panic slice bounds out of range otherwise\n\t\t\tif len(key) != ed25519.PrivateKeySize {\n\t\t\t\treturn fmt.Errorf(\"key was not 64 bytes, is invalid ed25519 private key\")\n\t\t\t}\n\n\t\t\tif !ed25519.PublicKey(c.details.publicKey).Equal(ed25519.PrivateKey(key).Public()) {\n\t\t\t\treturn fmt.Errorf(\"public key in cert and private key supplied don't match\")\n\t\t\t}\n\t\tcase Curve_P256:\n\t\t\tprivkey, err := ecdh.P256().NewPrivateKey(key)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"cannot parse private key as P256: %w\", err)\n\t\t\t}\n\t\t\tpub := privkey.PublicKey().Bytes()","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cert/cert_v1.go#L119-L155","documentation":"certificateV1.VerifyPrivateKey first checks that the curve argument matches the curve recorded in the certificate details. This error means the caller supplied a curve that differs from the certificate's own curve, so the private key cannot be meaningfully validated against the cert.","triggerScenarios":"Calling cert.VerifyPrivateKey(curve, key) on a certificateV1 where curve != c.details.curve — e.g. passing Curve_CURVE25519 for a P256 cert or vice versa.","commonSituations":"Hardcoded curve constant that doesn't match the CA/cert generation settings; migration from Curve25519 to P256 (or the reverse) where certs and keys were regenerated inconsistently; copying example code with the wrong Curve enum.","solutions":["Pass the same curve the certificate was issued with","Regenerate the keypair and certificate on the intended curve so they match","Log/compare curve values to confirm which side is wrong before calling VerifyPrivateKey"],"exampleFix":"// before\nerr := cert.VerifyPrivateKey(cert.Curve_CURVE25519, myKey) // cert is P256\n// after\nerr := cert.VerifyPrivateKey(cert.Curve(), myKey) // derive from the cert itself","handlingStrategy":"validation","validationCode":"if curve != cert.Curve() {\n    return fmt.Errorf(\"supplied curve %s does not match cert curve %s\", curve, cert.Curve())\n}\nerr := cert.VerifyPrivateKey(curve, key)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass cert.Curve() rather than a hardcoded constant","Regenerate keys and certs together whenever the curve choice changes","Add a startup check that key/cert curves agree","Avoid copy-pasted curve constants from examples"],"tags":["certificate","private-key","curve","mismatch"],"backgroundTag":"key-cert-curve-mismatch","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}