{"record":{"id":"4a7e6cd74ddf1650","repo":"denoland/deno","slug":"named-curve-mismatch","errorCode":null,"errorMessage":"Named curve mismatch","messagePattern":"Named curve mismatch","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/crypto/keys.ts","lineNumber":1063,"sourceCode":"\nfunction _validateEcNamedCurve(\n  keyObject: AsymmetricKeyObject,\n  algorithm: object,\n) {\n  const details = keyObject.asymmetricKeyDetails;\n  const alg = algorithm as { namedCurve?: string };\n  if (alg.namedCurve && details?.namedCurve) {\n    const curveMap: Record<string, string> = {\n      \"prime256v1\": \"P-256\",\n      \"secp384r1\": \"P-384\",\n      \"secp521r1\": \"P-521\",\n      \"P-256\": \"P-256\",\n      \"P-384\": \"P-384\",\n      \"P-521\": \"P-521\",\n    };\n    const keyCurve = curveMap[details.namedCurve] || details.namedCurve;\n    if (keyCurve !== alg.namedCurve) {\n      throw new DOMException(\"Named curve mismatch\", \"DataError\");\n    }\n  }\n}\n\nfunction createSecretKey(\n  key: string | ArrayBufferView | ArrayBuffer | KeyObject | CryptoKey,\n  encoding?: string,\n): KeyObject {\n  if (isCryptoKey(key)) {\n    if (key.type !== \"secret\") {\n      throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, \"secret\");\n    }\n    return KeyObject.from(key);\n  }\n  const preparedKey = prepareSecretKey(key, encoding, true);\n  if (isArrayBufferView(preparedKey) || isAnyArrayBuffer(preparedKey)) {\n    const handle = op_node_create_secret_key(preparedKey);\n    return new SecretKeyObject(handle);","sourceCodeStart":1045,"sourceCodeEnd":1081,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/crypto/keys.ts#L1045-L1081","documentation":"_validateEcNamedCurve throws DOMException DataError 'Named curve mismatch' when an EC KeyObject on one curve is converted via toCryptoKey() with an algorithm object whose namedCurve names a different curve. The polyfill first normalizes OpenSSL names (prime256v1/secp384r1/secp521r1) and WebCrypto names (P-256/P-384/P-521) to a common form, so only genuine mismatches throw — either naming style works if it is the right curve.","triggerScenarios":"generateKeyPairSync('ec', { namedCurve: 'prime256v1' }).privateKey.toCryptoKey({ name: 'ECDSA', namedCurve: 'P-384' }, true, ['sign']) — key is P-256, params say P-384.","commonSituations":"Curve configured in one place for key generation and in another for import/use; a shared default namedCurve drifting from the keys actually issued; compliance-mandated migration to P-384 while old P-256 keys are still converted by the same code.","solutions":["Set namedCurve to the key's actual curve: 'P-256', 'P-384' or 'P-521'","Read keyObject.asymmetricKeyDetails.namedCurve and feed that value into the algorithm object","Centralize the curve in one constant used by both generation and conversion code"],"exampleFix":"// before\nconst ck = privateKey.toCryptoKey({ name: 'ECDSA', namedCurve: 'P-384' }, true, ['sign']); // key is P-256 -> throws\n\n// after\nconst details = privateKey.asymmetricKeyDetails;\nconst ck = privateKey.toCryptoKey({ name: 'ECDSA', namedCurve: details.namedCurve }, true, ['sign']);","handlingStrategy":"validation","validationCode":"const NORMALIZE: Record<string, string> = {\n  prime256v1: 'P-256', secp384r1: 'P-384', secp521r1: 'P-521',\n};\nconst details = privateKey.asymmetricKeyDetails;\nif (details?.namedCurve && params.namedCurve) {\n  const keyCurve = NORMALIZE[details.namedCurve] ?? details.namedCurve;\n  if (keyCurve !== params.namedCurve) {\n    params = { ...params, namedCurve: keyCurve };\n  }\n}\nconst ck = privateKey.toCryptoKey(params, true, ['sign']);","typeGuard":"function namedCurveMatches(keyObject: KeyObject, namedCurve: string): boolean {\n  const map: Record<string, string> = {\n    prime256v1: 'P-256', secp384r1: 'P-384', secp521r1: 'P-521',\n  };\n  const d = keyObject.asymmetricKeyDetails?.namedCurve;\n  return !d || !namedCurve || (map[d] ?? d) === namedCurve;\n}","tryCatchPattern":"try {\n  ck = privateKey.toCryptoKey(params, true, usages);\n} catch (e) {\n  if (e instanceof DOMException && e.name === 'DataError' && e.message === 'Named curve mismatch') {\n    const actual = privateKey.asymmetricKeyDetails.namedCurve;\n    ck = privateKey.toCryptoKey({ ...params, namedCurve: actual }, true, usages);\n  } else throw e;\n}","preventionTips":["Read the curve from asymmetricKeyDetails instead of trusting config","Keep one curve constant used by both generation and import","Accept both OpenSSL and WebCrypto curve spellings in config, then normalize"],"tags":["crypto","webcrypto","ecdsa","ecc","key-management"],"backgroundTag":"ec-curve-mismatch","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}