{"record":{"id":"1179cc86cfffa095","repo":"gchq/CyberChef","slug":"invalid-ciphertext-length-ciphertext-length-by","errorCode":null,"errorMessage":"Invalid ciphertext length: ${cipherText.length} bytes. Must be a multiple of 8.","messagePattern":"Invalid ciphertext length: (.+?) bytes\\. Must be a multiple of 8\\.","errorType":"validation","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/Present.mjs","lineNumber":385,"sourceCode":"\n    return cipherText;\n}\n\n/**\n * Decrypt using PRESENT cipher with specified block mode\n *\n * @param {number[]} cipherText - Ciphertext as byte array\n * @param {number[]} key - Key (10 bytes for 80-bit or 16 bytes for 128-bit)\n * @param {number[]} iv - IV (8 bytes, not used for ECB)\n * @param {string} mode - Block cipher mode (\"ECB\" or \"CBC\")\n * @param {string} padding - Padding type (\"NO\", \"PKCS5\", \"ZERO\", \"RANDOM\", \"BIT\")\n * @returns {number[]} - Plaintext as byte array\n */\nexport function decryptPRESENT(cipherText, key, iv, mode = \"ECB\", padding = \"PKCS5\") {\n    if (cipherText.length === 0) return [];\n\n    if (cipherText.length % BLOCKSIZE !== 0) {\n        throw new OperationError(`Invalid ciphertext length: ${cipherText.length} bytes. Must be a multiple of 8.`);\n    }\n\n    // Generate round keys based on key length\n    const roundKeys = key.length === 10 ?\n        generateRoundKeys80(key) :\n        generateRoundKeys128(key);\n\n    const plainText = [];\n\n    switch (mode) {\n        case \"ECB\":\n            for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {\n                const block = bytesToBigInt(cipherText.slice(i, i + BLOCKSIZE));\n                const decrypted = decryptBlock(block, roundKeys);\n                plainText.push(...bigIntToBytes(decrypted, BLOCKSIZE));\n            }\n            break;\n","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/Present.mjs#L367-L403","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nconst pt = decryptPRESENT(bytes, key, iv, 'ECB', 'PKCS5'); // 13 bytes -> error\n\n// after\nif (bytes.length % 8 !== 0) {\n  // either re-acquire or pad-and-accept\n  while (bytes.length % 8 !== 0) bytes.push(0);\n}\nconst pt = decryptPRESENT(bytes, key, iv, 'ECB', 'NO'); // skip PKCS5 since tail is synthetic","handlingStrategy":"validation","validationCode":"function isPresentBlockAligned(bytes) {\n  return bytes.length % 8 === 0;\n}\n\nif (!isPresentBlockAligned(cipherText)) {\n  throw new Error('Ciphertext length ' + cipherText.length + ' is not a multiple of 8 bytes');\n}\ndecryptPRESENT(cipherText, key, iv, mode, padding);","typeGuard":null,"tryCatchPattern":"try {\n  return decryptPRESENT(cipherText, key, iv, mode, 'PKCS5');\n} catch (e) {\n  if (/multiple of 8/.test(e.message)) {\n    // re-acquire or pad-and-accept\n    const padded = [...cipherText];\n    while (padded.length % 8 !== 0) padded.push(0);\n    return decryptPRESENT(padded, key, iv, mode, 'NO');\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["cryptography","present","block-mode","data-corruption","validation"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}