gchq/CyberChef · error · OperationError
Invalid ciphertext length: ${cipherText.length} bytes. Must
Error message
Invalid ciphertext length: ${cipherText.length} bytes. Must be a multiple of 8. What it means
PRESENT is a 64-bit block cipher (8 bytes). decryptPRESENT requires the ciphertext length to be an exact multiple of BLOCKSIZE so it can slice the input into whole blocks; a non-aligned length means the data is truncated or otherwise invalid before decryption can begin.
Source
Thrown at src/core/lib/Present.mjs:385
return cipherText;
}
/**
* Decrypt using PRESENT cipher with specified block mode
*
* @param {number[]} cipherText - Ciphertext as byte array
* @param {number[]} key - Key (10 bytes for 80-bit or 16 bytes for 128-bit)
* @param {number[]} iv - IV (8 bytes, not used for ECB)
* @param {string} mode - Block cipher mode ("ECB" or "CBC")
* @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT")
* @returns {number[]} - Plaintext as byte array
*/
export function decryptPRESENT(cipherText, key, iv, mode = "ECB", padding = "PKCS5") {
if (cipherText.length === 0) return [];
if (cipherText.length % BLOCKSIZE !== 0) {
throw new OperationError(`Invalid ciphertext length: ${cipherText.length} bytes. Must be a multiple of 8.`);
}
// Generate round keys based on key length
const roundKeys = key.length === 10 ?
generateRoundKeys80(key) :
generateRoundKeys128(key);
const plainText = [];
switch (mode) {
case "ECB":
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
const block = bytesToBigInt(cipherText.slice(i, i + BLOCKSIZE));
const decrypted = decryptBlock(block, roundKeys);
plainText.push(...bigIntToBytes(decrypted, BLOCKSIZE));
}
break;
View on GitHub (pinned to 4290ea7539)
Solutions
- Verify the source produced 8-byte-aligned output (PRESENT) and not 16-byte (AES).
- Re-acquire the full ciphertext and re-decode any hex/base64 encoding cleanly.
- Check for and strip stray delimiters, whitespace, or length prefixes before slicing into bytes.
- If truncation is expected, pad the ciphertext with zeros to a multiple of 8 and accept that the last block will decrypt to garbage (wrap in try/catch for padding errors).
Example fix
// before
const pt = decryptPRESENT(bytes, key, iv, 'ECB', 'PKCS5'); // 13 bytes -> error
// after
if (bytes.length % 8 !== 0) {
// either re-acquire or pad-and-accept
while (bytes.length % 8 !== 0) bytes.push(0);
}
const pt = decryptPRESENT(bytes, key, iv, 'ECB', 'NO'); // skip PKCS5 since tail is synthetic Defensive patterns
Strategy: validation
Validate before calling
function isPresentBlockAligned(bytes) {
return bytes.length % 8 === 0;
}
if (!isPresentBlockAligned(cipherText)) {
throw new Error('Ciphertext length ' + cipherText.length + ' is not a multiple of 8 bytes');
}
decryptPRESENT(cipherText, key, iv, mode, padding); Try / catch
try {
return decryptPRESENT(cipherText, key, iv, mode, 'PKCS5');
} catch (e) {
if (/multiple of 8/.test(e.message)) {
// re-acquire or pad-and-accept
const padded = [...cipherText];
while (padded.length % 8 !== 0) padded.push(0);
return decryptPRESENT(padded, key, iv, mode, 'NO');
}
throw e;
} Prevention
- Confirm the cipher is PRESENT (8-byte blocks) and not AES (16-byte).
- Re-decode hex/base64 cleanly so no bytes are dropped.
- Strip delimiters/whitespace before slicing into bytes.
- Validate cipherText.length % 8 === 0 before calling decryptPRESENT.
When it happens
Trigger: Calling decryptPRESENT(cipherText, ...) where cipherText.length % 8 !== 0. Happens when bytes were dropped in transit, the input was sliced at the wrong offset, or a non-PRESENT ciphertext (e.g. AES, 16-byte blocks) is fed in.
Common situations: Ciphertext truncated by a transport/storage layer; copy-paste dropped a byte; wrong cipher selected (AES-128 uses 16-byte blocks); hex/base64 decoding produced an odd number of bytes; extra delimiter or whitespace byte appended.
Related errors
- Invalid block cipher mode: ${mode}
- No padding requested but input is not a ${blockSize}-byte mu
- Unknown padding type: ${padding}
- Wrong key size
- Letter ${elem} not in LS47
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/1179cc86cfffa095.
Report an issue: GitHub.