gchq/CyberChef · error · OperationError

input must be 8n (n>=3) bytes (currently " + inputData.lengt

Error message

input must be 8n (n>=3) bytes (currently " + inputData.length + " bytes)

What it means

A wrapped key produced by RFC 3394 is always (n+1)·8 bytes where n≥2 is the number of key-data blocks; the unwrap therefore requires its input to be a multiple of 8 and at least 24 bytes (3 blocks: 1 IV block + ≥2 data blocks). AESKeyUnwrap throws this when inputData.length % 8 !== 0 or inputData.length < 24.

Source

Thrown at src/core/operations/AESKeyUnwrap.mjs:75

     * @param {string} input
     * @param {Object[]} args
     * @returns {string}
     */
    run(input, args) {
        const kek = Utils.convertToByteString(args[0].string, args[0].option),
            iv = Utils.convertToByteString(args[1].string, args[1].option),
            inputType = args[2],
            outputType = args[3];

        if (kek.length !== 16 && kek.length !== 24 && kek.length !== 32) {
            throw new OperationError("KEK must be either 16, 24, or 32 bytes (currently " + kek.length + " bytes)");
        }
        if (iv.length !== 8) {
            throw new OperationError("IV must be 8 bytes (currently " + iv.length + " bytes)");
        }
        const inputData = Utils.convertToByteString(input, inputType);
        if (inputData.length % 8 !== 0 || inputData.length < 24) {
            throw new OperationError("input must be 8n (n>=3) bytes (currently " + inputData.length + " bytes)");
        }

        const cipher = forge.cipher.createCipher("AES-ECB", kek);
        cipher.start();
        cipher.update(forge.util.createBuffer(""));
        cipher.finish();
        const paddingBlock = cipher.output.getBytes();

        const decipher = forge.cipher.createDecipher("AES-ECB", kek);

        let A = inputData.substring(0, 8);
        const R = [];
        for (let i = 8; i < inputData.length; i += 8) {
            R.push(inputData.substring(i, i + 8));
        }
        let cntLower = R.length >>> 0;
        let cntUpper = (R.length / ((1 << 30) * 4)) >>> 0;
        cntUpper = cntUpper * 6 + ((cntLower * 6 / ((1 << 30) * 4)) >>> 0);

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Confirm the input is the actual wrapped-key output of AESKeyWrap (or another RFC 3394 implementation).
  2. Verify the input format option (Hex/Base64/Latin1) and that decoding yields a multiple of 8 bytes ≥ 24.
  3. Re-wrap the key to regenerate a valid blob.

Example fix

// before: wrapped input 16 bytes (n=2, but unwrap needs ≥3 blocks) → throws
// after: use the full wrapped output of AESKeyWrap (≥24 bytes)
Defensive patterns

Strategy: validation

Validate before calling

function validateWrappedInput(bytes) {
  if (bytes.length % 8 !== 0 || bytes.length < 24) {
    throw new Error(`Wrapped input must be 8n (n>=3) bytes, got ${bytes.length}`);
  }
}

Type guard

function isUnwrappable(bytes) { return bytes.length >= 24 && bytes.length % 8 === 0; }

Try / catch

try { aesKeyUnwrap(...); } catch (e) { if (/input must be 8n/.test(e.message)) {/* supply full wrapped blob */} else throw e; }

Prevention

When it happens

Trigger: The wrapped-key input is not a multiple of 8 bytes, or is 8 or 16 bytes (too short). Often caused by truncation, an extra/missing byte from hex decoding, or feeding a raw key instead of a wrapped key.

Common situations: Hex/base64 decoding produced an odd byte count; user fed the unwrapped key material by mistake; wrapped blob was truncated during copy-paste; format option mismatch on the input.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/a3bf57a2810a36e5. Report an issue: GitHub.