{"record":{"id":"b5404d07635e021a","repo":"gchq/CyberChef","slug":"invalid-pkcs-5-padding-b5404d","errorCode":null,"errorMessage":"Invalid PKCS#5 padding.","messagePattern":"Invalid PKCS#5 padding\\.","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/Twofish.mjs","lineNumber":410,"sourceCode":" * @returns {number[]} - Unpadded message\n */\nfunction removePadding(message, padding, blockSize) {\n    if (message.length === 0) return message;\n\n    switch (padding) {\n        case \"NO\":\n        case \"ZERO\":\n        case \"RANDOM\":\n            // These padding types cannot be reliably removed\n            return message;\n\n        case \"PKCS5\": {\n            const padByte = message[message.length - 1];\n            if (padByte > 0 && padByte <= blockSize) {\n                // Verify padding\n                for (let i = 0; i < padByte; i++) {\n                    if (message[message.length - 1 - i] !== padByte) {\n                        throw new OperationError(\"Invalid PKCS#5 padding.\");\n                    }\n                }\n                return message.slice(0, message.length - padByte);\n            }\n            throw new OperationError(\"Invalid PKCS#5 padding.\");\n        }\n\n        case \"BIT\": {\n            // Find 0x80 byte working backwards, skipping zeros\n            for (let i = message.length - 1; i >= 0; i--) {\n                if (message[i] === 0x80) {\n                    return message.slice(0, i);\n                } else if (message[i] !== 0) {\n                    throw new OperationError(\"Invalid BIT padding.\");\n                }\n            }\n            throw new OperationError(\"Invalid BIT padding.\");\n        }","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/Twofish.mjs#L392-L428","documentation":"Thrown by removePadding() in Twofish.mjs during PKCS#5 unpadding when at least one of the trailing pad bytes does not equal the declared pad value. PKCS#5 padding requires the last N bytes to all equal N; a mismatch means the ciphertext was altered, decrypted with the wrong key/IV, or was not PKCS#5-padded to begin with. This is the inner verification loop failing.","triggerScenarios":"Calling decryptTwofish() in ECB/CBC with padding \"PKCS5\" where the decrypted final block's tail bytes are inconsistent. Causes: wrong key, wrong IV, truncated/swapped ciphertext blocks, mode mismatch (decrypting CTR ciphertext as CBC), or the data was never PKCS#5-padded (it used ZERO/RANDOM/NO padding but declared PKCS5).","commonSituations":"Key/IV mismatch between encrypt and decrypt; bit flip in transit or storage; decrypting data encrypted by another tool that used a different padding; off-by-one block slicing.","solutions":["Verify the key and IV byte-for-byte against the values used at encryption time.","Confirm the encrypt side used PKCS5 padding and the same mode (ECB/CBC); if it used no padding or a stream mode, change the decrypt call accordingly.","If interoperating with PKCS#7 from another library, note that is compatible — the issue is data integrity, not the padding name."],"exampleFix":"// before: decrypting with wrong IV produces invalid padding\ndecryptTwofish(ct, key, wrongIv, \"CBC\", \"PKCS5\"); // throws\n// after\ndecryptTwofish(ct, key, correctIv, \"CBC\", \"PKCS5\");","handlingStrategy":"try-catch","validationCode":"function isValidPkcs5Tail(bytes, blockSize = 16) {\n    if (bytes.length === 0) return false;\n    const n = bytes[bytes.length - 1];\n    if (n < 1 || n > blockSize || n > bytes.length) return false;\n    for (let i = 0; i < n; i++) {\n        if (bytes[bytes.length - 1 - i] !== n) return false;\n    }\n    return true;\n}\n// decrypt without padding, inspect, then strip manually if valid\nconst probe = decryptTwofish(ct, key, iv, mode, \"NO\");\nif (!isValidPkcs5Tail(probe)) {\n    throw new Error(\"Decrypted output has invalid PKCS#5 padding — check key/IV/mode.\");\n}","typeGuard":"function isValidPkcs5Tail(bytes, blockSize = 16) {\n    if (!bytes || bytes.length === 0) return false;\n    const n = bytes[bytes.length - 1];\n    if (n < 1 || n > blockSize || n > bytes.length) return false;\n    return bytes.slice(bytes.length - n).every(b => b === n);\n}","tryCatchPattern":"try {\n    pt = decryptTwofish(ct, key, iv, \"CBC\", \"PKCS5\");\n} catch (e) {\n    if (e instanceof OperationError && /Invalid PKCS#5 padding/.test(e.message)) {\n        // almost always key/IV/mode mismatch; do NOT strip bytes blindly\n        return { error: \"Wrong key/IV/mode — decryption produced invalid padding.\" };\n    }\n    throw e;\n}","preventionTips":["Treat invalid PKCS#5 padding as a strong signal of wrong key/IV/mode, not as a padding bug.","Store the mode and padding alongside the ciphertext so decrypt uses the same parameters.","Add an HMAC or length check over the ciphertext to detect corruption before decrypting."],"tags":["crypto","twofish","padding","decryption","data-integrity"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}