{"record":{"id":"a1fdf489142ba6b7","repo":"denoland/deno","slug":"pbkdf2-keys-are-not-extractable","errorCode":null,"errorMessage":"PBKDF2 keys are not extractable","messagePattern":"PBKDF2 keys are not extractable","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/crypto/keys.ts","lineNumber":759,"sourceCode":"    extractable: boolean,\n    usages: string[],\n  ): CryptoKey {\n    const algName = typeof algorithm === \"string\"\n      ? algorithm\n      : (algorithm as { name: string }).name;\n\n    const rawData = new Uint8Array(op_node_export_secret_key(this[kHandle]));\n\n    if (TypedArrayPrototypeGetByteLength(rawData) === 0) {\n      throw new DOMException(\n        \"Zero-length key is not supported\",\n        \"DataError\",\n      );\n    }\n\n    if (algName === \"PBKDF2\") {\n      if (extractable) {\n        throw new DOMException(\n          \"PBKDF2 keys are not extractable\",\n          \"SyntaxError\",\n        );\n      }\n      if (\n        usages.length > 0 &&\n        ArrayPrototypeSome(\n          usages,\n          (u: string) =>\n            !ArrayPrototypeIncludes([\"deriveKey\", \"deriveBits\"], u),\n        )\n      ) {\n        throw new DOMException(\n          \"Unsupported key usage for a PBKDF2 key\",\n          \"SyntaxError\",\n        );\n      }\n    } else if (algName === \"HKDF\") {","sourceCodeStart":741,"sourceCodeEnd":777,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/crypto/keys.ts#L741-L777","documentation":"Thrown by SecretKeyObject.toCryptoKey() in Deno's node:crypto polyfill when a secret KeyObject is converted to a WebCrypto CryptoKey with algorithm name 'PBKDF2' and extractable=true. The WebCrypto specification requires PBKDF2 password material to be non-extractable, because the raw password must never be read back out of the CryptoKey. Deno enforces this with a DOMException of type SyntaxError, matching Node.js and browsers. It is a usage error, not a problem with the key itself.","triggerScenarios":"Calling createSecretKey(password).toCryptoKey('PBKDF2', true, ['deriveBits']) — or any KeyObject-to-CryptoKey interop path that forwards extractable=true while the algorithm name is 'PBKDF2'.","commonSituations":"Copy-pasting a subtle.importKey('raw', ..., true, ...) example written for HMAC/AES into PBKDF2 code; generic key wrappers that always request extractable keys; porting browser snippets where the extractable flag comes from a variable the user controls.","solutions":["Pass extractable=false when the algorithm is PBKDF2 (or HKDF)","If you need the raw password bytes, keep them in your own buffer or call keyObject.export() on the KeyObject instead of extracting a CryptoKey","In generic wrappers, force extractable=false for derivation algorithms before calling toCryptoKey/importKey"],"exampleFix":"// before\nconst key = createSecretKey(pw).toCryptoKey('PBKDF2', true, ['deriveBits']); // DOMException SyntaxError\n\n// after\nconst key = createSecretKey(pw).toCryptoKey('PBKDF2', false, ['deriveBits']);","handlingStrategy":"validation","validationCode":"const isDerivationAlg = (name) => name === 'PBKDF2' || name === 'HKDF';\nconst algName = typeof algorithm === 'string' ? algorithm : algorithm.name;\nconst extractable = isDerivationAlg(algName) ? false : requestedExtractable;\nconst key = secretKeyObject.toCryptoKey(algorithm, extractable, usages);","typeGuard":"function isNonExtractableOnlyAlgorithm(name: string): boolean {\n  return name === 'PBKDF2' || name === 'HKDF';\n}","tryCatchPattern":"try {\n  const key = secretKeyObject.toCryptoKey(algorithm, extractable, usages);\n} catch (e) {\n  if (e instanceof DOMException && e.name === 'SyntaxError' && e.message.includes('not extractable')) {\n    // retry once with extractable=false, or surface a config error\n  } else throw e;\n}","preventionTips":["Default extractable to false; only opt in for algorithms that actually support extraction","Keep one algorithm->(extractable, allowedUsages) rules table instead of hand-passing flags","Remember PBKDF2/HKDF raw material must live in your own buffer if you need it again"],"tags":["crypto","webcrypto","pbkdf2","node-compat","key-management"],"backgroundTag":"webcrypto-non-extractable-key","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}