denoland/deno · error · DOMException

Unsupported key usage for an HKDF key

Error message

Unsupported key usage for an HKDF key

What it means

Thrown by SecretKeyObject.toCryptoKey() when the algorithm is 'HKDF' and the usages array is non-empty and contains anything other than 'deriveKey' or 'deriveBits'. HKDF keys exist solely as derivation inputs in WebCrypto, so any other usage entry is rejected with a SyntaxError DOMException. As with PBKDF2, an empty usages array is accepted by this branch.

Source

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

          "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") {
      if (usages.length === 0) {
        throw new DOMException(
          "Usages cannot be empty when importing a secret key.",
          "SyntaxError",
        );
      }
      const alg = algorithm as { length?: number };
      if (alg.length !== undefined && alg.length === 0) {
        throw new DOMException(
          "HmacImportParams.length cannot be 0",
          "DataError",
        );
      }

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Use only 'deriveKey' and/or 'deriveBits' for HKDF
  2. Pass [] if you have no specific usage to declare
  3. Derive with crypto.subtle.deriveBits/deriveKey and use the output key for the actual operation

Example fix

// before
const hkdfKey = createSecretKey(ikm).toCryptoKey('HKDF', false, ['sign']); // throws

// after
const hkdfKey = createSecretKey(ikm).toCryptoKey('HKDF', false, ['deriveBits']);
const okm = await crypto.subtle.deriveBits({ name: 'HKDF', hash: 'SHA-256', salt, info }, hkdfKey, 256);
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = ['deriveKey', 'deriveBits'];
const usages = requestedUsages.filter((u) => ALLOWED.includes(u));
const key = secretKeyObject.toCryptoKey('HKDF', false, usages);

Type guard

const isDerivationUsage = (u: string): boolean =>
  u === 'deriveKey' || u === 'deriveBits';

Try / catch

try {
  key = secretKeyObject.toCryptoKey('HKDF', false, usages);
} catch (e) {
  if (e instanceof DOMException && e.name === 'SyntaxError' && /Unsupported key usage/.test(e.message)) {
    key = secretKeyObject.toCryptoKey('HKDF', false, ['deriveBits']);
  } else throw e;
}

Prevention

When it happens

Trigger: createSecretKey(ikm).toCryptoKey('HKDF', false, ['deriveBits', 'encrypt']) — at least one usage outside ['deriveKey','deriveBits'].

Common situations: One generic usages array shared across HMAC, AES and HKDF imports; wrappers that copy the caller's requested operations straight through; upgrading key-derivation code from scrypt/pbkdf2 to HKDF while keeping old usages.

Related errors


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