gchq/CyberChef · error · OperationError
Invalid ciphertext length: ${originalLength} bytes. Must be
Error message
Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of 16. What it means
Thrown by decryptTwofish() in Twofish.mjs when decrypting in ECB or CBC and the ciphertext length is not a multiple of the 16-byte Twofish block size. Block modes cannot operate on partial blocks, so a non-aligned length signals truncation, corruption, or a mode/cipher mismatch. Stream modes (CFB/OFB/CTR) zero-pad internally and slice back, so they never reach this check.
Source
Thrown at src/core/lib/Twofish.mjs:538
/**
* Decrypt using Twofish cipher with specified block mode
*
* @param {number[]} cipherText - Ciphertext as byte array
* @param {number[]} key - Key (16, 24, or 32 bytes)
* @param {number[]} iv - IV (16 bytes, not used for ECB)
* @param {string} mode - Block cipher mode ("ECB", "CBC", "CFB", "OFB", "CTR")
* @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT")
* @returns {number[]} - Plaintext as byte array
*/
export function decryptTwofish(cipherText, key, iv, mode = "ECB", padding = "PKCS5") {
const originalLength = cipherText.length;
if (originalLength === 0) return [];
const keyData = generateSubkeys(key);
if (mode === "ECB" || mode === "CBC") {
if ((originalLength % BLOCKSIZE) !== 0)
throw new OperationError(`Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of 16.`);
} else {
// Pad for stream modes
while ((cipherText.length % BLOCKSIZE) !== 0)
cipherText.push(0);
}
const plainText = [];
switch (mode) {
case "ECB":
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
const block = cipherText.slice(i, i + BLOCKSIZE);
plainText.push(...decryptBlock(block, keyData));
}
break;
case "CBC": {
let ivBlock = [...iv];View on GitHub (pinned to 4290ea7539)
Solutions
- Verify cipherText.length % 16 === 0; re-derive the bytes from the source (re-decode hex/base64) to rule out truncation.
- If the data was encrypted with a stream mode (CFB/OFB/CTR), decrypt with the matching mode instead of ECB/CBC.
- If the data is genuinely not block-aligned it is not valid Twofish ECB/CBC ciphertext — locate where it was truncated rather than padding it yourself.
Example fix
// before: 30-byte ciphertext declared as CBC decryptTwofish(ct, key, iv, "CBC", "PKCS5"); // throws // after: data was CTR-encrypted decryptTwofish(ct, key, iv, "CTR", "PKCS5");
Defensive patterns
Strategy: validation
Validate before calling
const BLOCK = 16; // Twofish
if ((mode === "ECB" || mode === "CBC") && cipherText.length % BLOCK !== 0) {
throw new Error(
`Ciphertext is ${cipherText.length} bytes; ECB/CBC require a multiple of ${BLOCK}. ` +
`Check for truncation or use the matching stream mode.`
);
}
plainText = decryptTwofish(cipherText, key, iv, mode, padding); Type guard
function isBlockAligned(bytes, blockSize = 16) {
return Array.isArray(bytes) && bytes.length % blockSize === 0;
} Try / catch
try {
pt = decryptTwofish(ct, key, iv, mode, padding);
} catch (e) {
if (e instanceof OperationError && /Invalid ciphertext length/.test(e.message)) {
return { error: `Ciphertext length invalid for ${mode}. Verify cipher/mode and re-derive bytes.` };
}
throw e;
} Prevention
- Decrypt with the same mode (and cipher) used to encrypt.
- Slice ciphertext by explicit length fields, not by buffer boundaries.
- Re-decode hex/base64 at the point of use to avoid stale/truncated slices.
When it happens
Trigger: Calling decryptTwofish() with mode "ECB" or "CBC" and cipherText.length % 16 !== 0. Typical: truncated base64/hex decode, mode mismatch (data was CTR-encrypted), wrong cipher (data is TEA/AES not Twofish), or a copy-paste that dropped trailing bytes.
Common situations: Encrypt/decrypt mode disagreement; hex string with odd character count; ArrayBuffer slicing with the wrong byteOffset/length; interop with a tool that stripped padding before storing ciphertext.
Related errors
- Invalid ciphertext length: ${originalLength} bytes. Must be
- No padding requested but input is not a ${blockSize}-byte mu
- Unknown padding type: ${padding}
- Invalid PKCS#5 padding.
- Invalid BIT padding.
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/b073aacf359720bf.
Report an issue: GitHub.