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
- 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).
- 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.
- 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
- Always decrypt with the same mode used to encrypt; store the mode alongside the ciphertext.
- Re-derive bytes from hex/base64 immediately before decrypting rather than passing slices of larger buffers.
- When reading ciphertext from a frame/protocol, use the explicit length field to slice exactly the right bytes.
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
- Invalid ciphertext length: ${originalLength} bytes. Must be
- Invalid block cipher mode: ${mode}
- No padding requested but input is not a ${blockSize}-byte mu
- Invalid PKCS#5 padding.
- Invalid BIT padding.
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/831f35f56dae0b4a.
Report an issue: GitHub.