denoland/deno · error · DOMException

HKDF keys are not extractable

Error message

HKDF keys are not extractable

What it means

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.

Source

Thrown at ext/node/polyfills/internal/crypto/keys.ts:779

          "SyntaxError",
        );
      }
      if (
        usages.length > 0 &&
        ArrayPrototypeSome(
          usages,
          (u: string) =>
            !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u),
        )
      ) {
        throw new DOMException(
          "Unsupported key usage for a PBKDF2 key",
          "SyntaxError",
        );
      }
    } else if (algName === "HKDF") {
      if (extractable) {
        throw new DOMException(
          "HKDF keys are not extractable",
          "SyntaxError",
        );
      }
      if (
        usages.length > 0 &&
        ArrayPrototypeSome(
          usages,
          (u: string) =>
            !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u),
        )
      ) {
        throw new DOMException(
          "Unsupported key usage for an HKDF key",
          "SyntaxError",
        );
      }
    } else if (algName === "HMAC") {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pass extractable=false when the algorithm is HKDF
  2. Keep the IKM bytes in your own buffer if you need to re-derive later, or export them via keyObject.export() before conversion
  3. Force extractable=false for all derivation algorithms in shared wrappers

Example fix

// before
const key = createSecretKey(ikm).toCryptoKey('HKDF', true, ['deriveKey']); // throws

// after
const key = createSecretKey(ikm).toCryptoKey('HKDF', false, ['deriveKey']);
Defensive patterns

Strategy: validation

Validate before calling

const isDerivationAlg = (name) => name === 'PBKDF2' || name === 'HKDF';
const algName = typeof algorithm === 'string' ? algorithm : algorithm.name;
const extractable = isDerivationAlg(algName) ? false : requestedExtractable;
const key = secretKeyObject.toCryptoKey(algorithm, extractable, usages);

Type guard

function isNonExtractableOnlyAlgorithm(name: string): boolean {
  return name === 'PBKDF2' || name === 'HKDF';
}

Try / catch

try {
  const key = secretKeyObject.toCryptoKey('HKDF', extractable, usages);
} catch (e) {
  if (e instanceof DOMException && e.name === 'SyntaxError' && e.message.includes('not extractable')) {
    // log caller config and fall back to extractable=false
  } else throw e;
}

Prevention

When it happens

Trigger: Calling createSecretKey(ikm).toCryptoKey('HKDF', true, ['deriveBits']) — extractable=true with algorithm name 'HKDF'.

Common situations: 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.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/56041fb48f951697. Report an issue: GitHub.