denoland/deno · error · ERR_CRYPTO_INVALID_KEYLEN

ERR_CRYPTO_INVALID_KEYLEN

ERR_CRYPTO_INVALID_KEYLEN

Error message

Invalid key length

What it means

HKDF (RFC 5869) can output at most 255 times the hash digest length, because HKDF-Expand runs at most 255 iterations of the HMAC loop. The polyfill enforces this in validateParameters (ext/node/polyfills/internal/crypto/hkdf.ts:95-98): if hashSize * 255 < length it throws ERR_CRYPTO_INVALID_KEYLEN ('Invalid key length') before any native op runs.

Source

Thrown at ext/node/polyfills/internal/crypto/hkdf.ts:97

    salt = toRawBytes(toBuf(salt));
    info = toRawBytes(toBuf(info));

    validateInteger(length, "length", 0, kMaxLength);

    if (TypedArrayPrototypeGetByteLength(info) > 1024) {
      throw new ERR_OUT_OF_RANGE(
        "info",
        "must not contain more than 1024 bytes",
        TypedArrayPrototypeGetByteLength(info),
      );
    }

    validateAlgorithm(hash);

    const size = op_node_get_hash_size(hash);
    if (typeof size === "number" && size * 255 < length) {
      throw new ERR_CRYPTO_INVALID_KEYLEN();
    }

    return {
      hash,
      key,
      salt,
      info,
      length,
    };
  },
);

function prepareKey(key: any) {
  if (isKeyObject(key)) {
    return key;
  }

  if (isAnyArrayBuffer(key)) {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Cap length at 255 * hashSize: 8160 for sha256, 5100 for sha1, 10200 for sha384, 16320 for sha512.
  2. Derive a master key once, then derive independent subkeys with distinct short info labels.
  3. If you need long random material, derive a 32-byte key and use it to seed a stream/XOF (e.g. ChaCha, shake256) instead.
  4. Validate/clamp the length parameter at your API boundary before it reaches hkdf.

Example fix

// before
crypto.hkdfSync('sha256', ikm, salt, info, 10000); // > 255*32 -> ERR_CRYPTO_INVALID_KEYLEN

// after
const MAX = 255 * crypto.createHash('sha256').digest().length; // 8160
crypto.hkdfSync('sha256', ikm, salt, info, Math.min(10000, MAX));
// or split into labeled subkeys:
const enc = crypto.hkdfSync('sha256', ikm, salt, Buffer.from('enc'), 32);
const mac = crypto.hkdfSync('sha256', ikm, salt, Buffer.from('mac'), 32);
Defensive patterns

Strategy: validation

Validate before calling

const HKDF_MAX = { sha1: 5100, sha256: 8160, sha384: 10200, sha512: 16320 };
function assertHkdfLength(digest, length) {
  const max = 255 * crypto.createHash(digest).digest().length;
  if (!(Number.isInteger(length) && length >= 0 && length <= max)) {
    throw new RangeError(`hkdf length for ${digest} must be 0..${max}, got ${length}`);
  }
}
assertHkdfLength('sha256', wanted);
crypto.hkdfSync('sha256', ikm, salt, info, wanted);

Try / catch

try {
  okm = crypto.hkdfSync(digest, ikm, salt, info, length);
} catch (e) {
  if (e.code === 'ERR_CRYPTO_INVALID_KEYLEN') {
    throw new Error(`HKDF output capped at ${255 * hashBytes} bytes; derive subkeys with distinct info labels instead`);
  }
  throw e;
}

Prevention

When it happens

Trigger: crypto.hkdfSync('sha256', ikm, salt, info, 8161) (255*32 = 8160 for SHA-256); requesting 64 KiB or more of output material from hkdf; hkdf with sha1 (max 5100 bytes) or sha512 (max 16320 bytes) and a length above that bound.

Common situations: Deriving many subkeys or a whole file-encryption key bundle in one hkdf call; porting code from libraries that chunk HKDF output automatically; parameterizing output length from user input or config without an upper bound.

Related errors


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