gchq/CyberChef · error · OperationError

Invalid PKCS#7 padding.

Error message

Invalid PKCS#7 padding.

What it means

decryptSM4's PKCS#7 check at SM4.mjs:323. After decrypting an ECB/CBC buffer it reads the trailing byte as the pad length; if that value exceeds 16 (the SM4 block size) it cannot be a valid PKCS#7 pad and is rejected. This almost always means the plaintext is garbage — wrong key, wrong IV, or wrong mode.

Source

Thrown at src/core/lib/SM4.mjs:323

                let iv2 = [...iv]; /* containing the IV + counter */
                iv2[3] += (i >> 4);/* Using a 32 bit counter here. 64 Gb encrypts should be enough for everyone. */
                iv2 = encryptBlockSM4(iv2, roundKey);
                const block = bytesToInts(cipherText, i);
                block[0] ^= iv2[0]; block[1] ^= iv2[1];
                block[2] ^= iv2[2]; block[3] ^= iv2[3];
                Array.prototype.push.apply(clearText, intsToBytes(block));
            }
            break;
        default:
            throw new OperationError(`Invalid block cipher mode: ${mode}`);
    }
    /* Check PKCS#7 padding */
    if (mode === "ECB" || mode === "CBC") {
        if (ignorePadding)
            return clearText;
        const padByte = clearText[clearText.length - 1];
        if (padByte > 16)
            throw new OperationError("Invalid PKCS#7 padding.");
        for (let i = 0; i < padByte; i++)
            if (clearText[clearText.length -i - 1] !== padByte)
                throw new OperationError("Invalid PKCS#7 padding.");
        return clearText.slice(0, clearText.length - padByte);
    }
    return clearText.slice(0, originalLength);
}

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Confirm the key and IV are identical to those used for encryption.
  2. Verify the encrypt mode matches the decrypt mode.
  3. If the data was encrypted without PKCS#7 padding, decrypt with ignorePadding=true.
  4. As a diagnostic, decrypt with ignorePadding=true and inspect the raw plaintext to see if it looks correct.

Example fix

// before
const pt = decryptSM4(ct, wrongKey, iv, "CBC");
// after
const pt = decryptSM4(ct, correctKey, iv, "CBC");
Defensive patterns

Strategy: validation

Validate before calling

// After a trial decrypt with ignorePadding=true, sanity-check the trailing byte.
function looksLikeValidPkcs7Pad(plain, blockSize = 16) {
  if (plain.length === 0) return false;
  const padByte = plain[plain.length - 1];
  if (padByte < 1 || padByte > blockSize) return false;
  for (let i = 0; i < padByte; i++)
    if (plain[plain.length - 1 - i] !== padByte) return false;
  return true;
}

Type guard

function hasPlausiblePkcs7TrailingByte(plain, blockSize = 16) {
  return plain.length > 0 && plain[plain.length - 1] >= 1 && plain[plain.length - 1] <= blockSize;
}

Try / catch

import OperationError from "../errors/OperationError.mjs";
try {
  const pt = decryptSM4(ct, key, iv, "CBC");
} catch (e) {
  if (e instanceof OperationError && /Invalid PKCS#7 padding/.test(e.message)) {
    // retry with ignorePadding=true to inspect raw plaintext; treat as wrong-key/mode signal
  } else throw e;
}

Prevention

When it happens

Trigger: decryptSM4(cipherText, key, iv, mode='ECB'|'CBC', ignorePadding=false) where the last decrypted byte is greater than 16. Typical cause: decrypting with the wrong key/IV produces random plaintext whose final byte happens to be > 16.

Common situations: Key/IV mismatch between encrypt and decrypt; mode mismatch (encrypt was CTR but decrypt is CBC); ciphertext from a different cipher entirely; encrypt side did not apply PKCS#7 (e.g. used noPadding) but decrypt expects it.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/2949abdec1e34700. Report an issue: GitHub.