gchq/CyberChef · error · OperationError

Invalid ciphertext length: ${originalLength} bytes. Must be

Error message

Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of ${BLOCK_SIZE}.

What it means

Thrown by decryptWithMode() in TEA.mjs when decrypting in ECB or CBC mode and the ciphertext byte length is not an exact multiple of the 8-byte TEA block size. ECB/CBC are block modes that cannot stream, so any remainder indicates truncation, corruption, or that the data was produced with a different cipher/mode. Stream modes (CFB/OFB/CTR) instead zero-pad internally and slice back to original length, so they never hit this check.

Source

Thrown at src/core/lib/TEA.mjs:362

/**
 * Decrypt with block cipher modes
 *
 * @param {number[]} cipherText - Ciphertext bytes
 * @param {number[]} key - 16-byte key
 * @param {number[]} iv - 8-byte IV (ignored for ECB)
 * @param {string} mode - "ECB", "CBC", "CFB", "OFB", "CTR"
 * @param {string} padding - "PKCS5", "NO", "ZERO", "RANDOM", "BIT"
 * @param {Function} encryptBlockFn - Block encrypt function (used for stream modes)
 * @param {Function} decryptBlockFn - Block decrypt function (used for ECB/CBC)
 * @returns {number[]} - Plaintext bytes
 */
function decryptWithMode(cipherText, key, iv, mode, padding, encryptBlockFn, decryptBlockFn) {
    const originalLength = cipherText.length;
    if (originalLength === 0) return [];

    if (mode === "ECB" || mode === "CBC") {
        if ((originalLength % BLOCK_SIZE) !== 0)
            throw new OperationError(
                `Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of ${BLOCK_SIZE}.`
            );
    } else {
        while ((cipherText.length % BLOCK_SIZE) !== 0)
            cipherText.push(0);
    }

    const plainText = [];

    switch (mode) {
        case "ECB":
            for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
                plainText.push(...decryptBlockFn(cipherText.slice(i, i + BLOCK_SIZE), key));
            }
            break;

        case "CBC": {
            let ivBlock = [...iv];

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Verify the ciphertext was encrypted with ECB/CBC and a TEA-family 8-byte block; if it was a stream mode, decrypt with the matching mode (CFB/OFB/CTR).
  2. Check the byte length: console.log(cipherText.length, cipherText.length % 8). Re-derive the bytes from the source (re-decode hex/base64) to rule out truncation.
  3. If the source is genuinely not block-aligned, it is not valid TEA ECB/CBC ciphertext — do not pad it yourself; locate where the data was truncated.

Example fix

// before: ciphertext is 13 bytes, mode declared as CBC
decryptTEA(cipherText, key, iv, "CBC", "PKCS5"); // throws
// after: it was actually produced with CTR
decryptTEA(cipherText, key, iv, "CTR", "PKCS5");
Defensive patterns

Strategy: validation

Validate before calling

const BLOCK_SIZE = 8; // TEA
if ((mode === "ECB" || mode === "CBC") && cipherText.length % BLOCK_SIZE !== 0) {
    throw new Error(
        `Ciphertext is ${cipherText.length} bytes; ECB/CBC require a multiple of ${BLOCK_SIZE}. ` +
        `Check for truncation or use the matching stream mode.`
    );
}
plainText = decryptTEA(cipherText, key, iv, mode, padding);

Type guard

function isBlockAligned(bytes, blockSize = 8) {
    return Array.isArray(bytes) && bytes.length % blockSize === 0;
}

Try / catch

try {
    plainText = decryptTEA(cipherText, key, iv, mode, padding);
} catch (e) {
    if (e instanceof OperationError && /Invalid ciphertext length/.test(e.message)) {
        return { error: `Ciphertext length is invalid for ${mode}. Verify the data was encrypted with this cipher and mode.` };
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling decryptTEA()/decryptXTEA() with mode "ECB" or "CBC" and a ciphertext whose .length % 8 !== 0. Typical causes: truncating base64/hex decode output, feeding a ciphertext encrypted with a stream mode but declaring ECB/CBC on decrypt, off-by-one slicing of an ArrayBuffer, or concatenating two partial ciphertext buffers.

Common situations: Key/mode mismatch between encrypt and decrypt sides; wrong endianness or wrong byte offset when extracting ciphertext from a larger protocol frame; copy-paste dropping the final block; hex string with odd number of characters decoding to a non-multiple length.

Related errors


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