gchq/CyberChef · error · OperationError
Invalid PKCS#5 padding.
Error message
Invalid PKCS#5 padding.
What it means
TEA's removePadding PKCS#5 inner-loop check at TEA.mjs:239. After the trailing byte passes the range check (1..BLOCK_SIZE), the code verifies each of the trailing padByte bytes equals padByte; any mismatch means the pad is malformed.
Source
Thrown at src/core/lib/TEA.mjs:239
* @param {number[]} message
* @param {string} padding
* @returns {number[]}
*/
function removePadding(message, padding) {
if (message.length === 0) return message;
switch (padding) {
case "NO":
case "ZERO":
case "RANDOM":
return message;
case "PKCS5": {
const padByte = message[message.length - 1];
if (padByte > 0 && padByte <= BLOCK_SIZE) {
for (let i = 0; i < padByte; i++) {
if (message[message.length - 1 - i] !== padByte) {
throw new OperationError("Invalid PKCS#5 padding.");
}
}
return message.slice(0, message.length - padByte);
}
throw new OperationError("Invalid PKCS#5 padding.");
}
case "BIT": {
for (let i = message.length - 1; i >= 0; i--) {
if (message[i] === 0x80) return message.slice(0, i);
if (message[i] !== 0) throw new OperationError("Invalid BIT padding.");
}
throw new OperationError("Invalid BIT padding.");
}
default:
throw new OperationError(`Unknown padding type: ${padding}`);
}View on GitHub (pinned to 4290ea7539)
Solutions
- Verify key, IV, and mode match the encrypt side.
- Confirm the encrypt-time padding was 'PKCS5'; if it was NO/ZERO/RANDOM/BIT, decrypt with that matching value.
- As a diagnostic, decrypt with padding='NO' and inspect the raw tail bytes.
Example fix
// before const pt = decryptWithBlockMode(ct, key, iv, "CBC", "PKCS5"); // after: data was ZERO-padded const pt = decryptWithBlockMode(ct, key, iv, "CBC", "ZERO");
Defensive patterns
Strategy: validation
Validate before calling
const TEA_BLOCK_SIZE = 8;
function looksLikeValidPkcs5Pad(plain) {
if (plain.length === 0) return false;
const padByte = plain[plain.length - 1];
if (padByte < 1 || padByte > TEA_BLOCK_SIZE) return false;
for (let i = 0; i < padByte; i++)
if (plain[plain.length - 1 - i] !== padByte) return false;
return true;
} Type guard
function isConsistentPkcs5Pad(plain, blockSize = 8) {
const padByte = plain.length ? plain[plain.length - 1] : 0;
if (padByte < 1 || padByte > blockSize) return false;
return plain.slice(plain.length - padByte).every(b => b === padByte);
} Try / catch
import OperationError from "../errors/OperationError.mjs";
try {
const pt = decryptWithBlockMode(ct, key, iv, mode, "PKCS5");
} catch (e) {
if (e instanceof OperationError && /Invalid PKCS#5 padding/.test(e.message)) {
// wrong key/IV or padding-scheme mismatch; do not return partial plaintext
} else throw e;
} Prevention
- Decrypt with the same padding literal used to encrypt.
- Treat PKCS#5 errors as a wrong-key/padding-mismatch signal.
- Round-trip encrypt+decrypt in tests for every key/mode combination.
When it happens
Trigger: Decryption with padding='PKCS5' where the trailing byte is in range [1..8] but the preceding (padByte-1) bytes do not all equal padByte. Causes: wrong key producing plaintext whose final bytes look like a plausible but inconsistent pad; partial corruption of the last block; encryptor used a different padding scheme (ZERO/BIT) but decryptor expects PKCS5.
Common situations: Key/IV mismatch where the random tail happens to start with a byte in 1..8; encrypt/decrypt padding scheme mismatch; bit-flip in the last ciphertext block.
Related errors
- Invalid BIT padding.
- No padding requested but input length (${message.length} byt
- Unknown padding type: ${padding}
- Invalid PKCS#7 padding.
- Invalid PKCS#5 padding.
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/28328d73fe27416b.
Report an issue: GitHub.