{"record":{"id":"56041fb48f951697","repo":"denoland/deno","slug":"hkdf-keys-are-not-extractable","errorCode":null,"errorMessage":"HKDF keys are not extractable","messagePattern":"HKDF keys are not extractable","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/crypto/keys.ts","lineNumber":779,"sourceCode":"          \"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\") {\n      if (extractable) {\n        throw new DOMException(\n          \"HKDF 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 an HKDF key\",\n          \"SyntaxError\",\n        );\n      }\n    } else if (algName === \"HMAC\") {","sourceCodeStart":761,"sourceCodeEnd":797,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/crypto/keys.ts#L761-L797","documentation":"Thrown by SecretKeyObject.toCryptoKey() when a secret KeyObject is converted to a WebCrypto CryptoKey with algorithm name 'HKDF' and extractable=true. WebCrypto requires HKDF key material to be non-extractable, since the raw IKM (input keying material) must stay hidden inside the CryptoKey. Deno enforces the rule with a DOMException of type SyntaxError, consistent with Node.js and browsers.","triggerScenarios":"Calling createSecretKey(ikm).toCryptoKey('HKDF', true, ['deriveBits']) — extractable=true with algorithm name 'HKDF'.","commonSituations":"Shared 'import any secret' helpers that thread one extractable flag to every algorithm; copying PBKDF2-adjacent sample code; HKDF salt/IKM handling refactored from raw-buffer code that assumed the bytes stay readable.","solutions":["Pass extractable=false when the algorithm is HKDF","Keep the IKM bytes in your own buffer if you need to re-derive later, or export them via keyObject.export() before conversion","Force extractable=false for all derivation algorithms in shared wrappers"],"exampleFix":"// before\nconst key = createSecretKey(ikm).toCryptoKey('HKDF', true, ['deriveKey']); // throws\n\n// after\nconst key = createSecretKey(ikm).toCryptoKey('HKDF', false, ['deriveKey']);","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('HKDF', extractable, usages);\n} catch (e) {\n  if (e instanceof DOMException && e.name === 'SyntaxError' && e.message.includes('not extractable')) {\n    // log caller config and fall back to extractable=false\n  } else throw e;\n}","preventionTips":["Treat HKDF IKM like PBKDF2 password material: never request extraction","Store the IKM in your own secret store if re-derivation is needed","Centralize the derivation-algorithm rules so PBKDF2 and HKDF cannot drift apart"],"tags":["crypto","webcrypto","hkdf","node-compat","key-management"],"backgroundTag":"webcrypto-non-extractable-key","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}