{"record":{"id":"f3410798741e9a75","repo":"gchq/CyberChef","slug":"with-ecb-or-cbc-modes-the-input-must-be-divisible","errorCode":null,"errorMessage":"With ECB or CBC modes, the input must be divisible into 16 byte blocks. (${cipherText.length & 0xF} bytes extra)","messagePattern":"With ECB or CBC modes, the input must be divisible into 16 byte blocks\\. \\((.+?) bytes extra\\)","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/SM4.mjs","lineNumber":259,"sourceCode":" *\n * @param {byteArray} cipherText - The ciphertext\n * @param {byteArray} key - The cipher key, 16 bytes.\n * @param {byteArray} iv - The IV or nonce, 16 bytes (not used with ECB mode)\n * @param {string} mode - The block cipher mode \"CBC\", \"ECB\", \"CFB\", \"OFB\", \"CTR\"\n * @param {boolean] ignorePadding - If true, ignore padding issues in ECB/CBC mode.\n * @returns {byteArray} - The cipher text.\n */\nexport function decryptSM4(cipherText, key, iv, mode=\"ECB\", ignorePadding=false) {\n    const originalLength = cipherText.length;\n    if (originalLength === 0)\n        return [];\n    let roundKey = initSM4RoundKey(bytesToInts(key, 0));\n\n    if (mode === \"ECB\" || mode === \"CBC\") {\n        /* Init decryption key */\n        roundKey = roundKey.reverse();\n        if ((originalLength & 0xF) !== 0 && !ignorePadding)\n            throw new OperationError(`With ECB or CBC modes, the input must be divisible into 16 byte blocks. (${cipherText.length & 0xF} bytes extra)`);\n    } else { /* Pad dummy bytes for other modes, chop them off at the end */\n        while ((cipherText.length & 0xF) !== 0)\n            cipherText.push(0);\n    }\n\n    const clearText = [];\n    switch (mode) {\n        case \"ECB\":\n            for (let i = 0; i < cipherText.length; i += BLOCKSIZE)\n                Array.prototype.push.apply(clearText, intsToBytes(encryptBlockSM4(bytesToInts(cipherText, i), roundKey)));\n            break;\n        case \"CBC\":\n            iv = bytesToInts(iv, 0);\n            for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {\n                const block = encryptBlockSM4(bytesToInts(cipherText, i), roundKey);\n                block[0] ^= iv[0]; block[1] ^= iv[1];\n                block[2] ^= iv[2]; block[3] ^= iv[3];\n                Array.prototype.push.apply(clearText, intsToBytes(block));","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/SM4.mjs#L241-L277","documentation":"decryptSM4 at SM4.mjs:259 enforces block alignment for ECB/CBC unless ignorePadding is set. SM4's block size is 16 bytes; a ciphertext whose length is not a multiple of 16 cannot be the output of a correct ECB/CBC encrypt and is rejected.","triggerScenarios":"decryptSM4(cipherText, key, iv, mode='ECB'|'CBC', ignorePadding=false) with (cipherText.length & 0xF) !== 0. Caused by truncated/corrupted ciphertext, encoding/decoding error in the hex/base64 path, or feeding a stream-mode ciphertext to an ECB/CBC decryptor.","commonSituations":"Hex string of odd length producing a short byte array; base64 padding lost in transit; mode mismatch where CFB/CTR output is decrypted as ECB/CBC; manual copy of ciphertext dropping a byte.","solutions":["Verify the ciphertext byte length is a multiple of 16 before calling decryptSM4.","Re-derive the byte array from its hex/base64 encoding and check for truncation.","Confirm the decrypt mode matches the encrypt mode.","If you knowingly want to skip alignment/padding checks, pass ignorePadding=true (but the result will be unreliable for mis-sized input)."],"exampleFix":"// before\nconst pt = decryptSM4(ct, key, iv, \"CBC\"); // ct.length === 17\n// after\nif (ct.length & 0xF) throw new Error(`ciphertext not block-aligned: ${ct.length} bytes`);\nconst pt = decryptSM4(ct, key, iv, \"CBC\");","handlingStrategy":"validation","validationCode":"function assertSm4CipherAligned(cipherText, ignorePadding, mode) {\n  if (!ignorePadding && (mode === \"ECB\" || mode === \"CBC\")) {\n    if (cipherText.length & 0xF)\n      throw new TypeError(\n        `ECB/CBC ciphertext must be a 16-byte multiple; got ${cipherText.length} bytes`);\n  }\n}","typeGuard":"function isSm4CipherBlockAligned(bytes) {\n  return Number.isInteger(bytes.length) && (bytes.length & 0xF) === 0;\n}","tryCatchPattern":"import OperationError from \"../errors/OperationError.mjs\";\ntry {\n  assertSm4CipherAligned(ct, ignorePadding, mode);\n  const pt = decryptSM4(ct, key, iv, mode, ignorePadding);\n} catch (e) {\n  if (e instanceof OperationError && /divisible into 16 byte blocks/.test(e.message)) {\n    // re-derive ct from hex/base64 and check for truncation\n  } else throw e;\n}","preventionTips":["Validate ciphertext length is a multiple of 16 before decrypting ECB/CBC.","Decode hex/base64 through a single tested helper.","Ensure decrypt mode matches encrypt mode."],"tags":["sm4","cipher","block-mode","validation","decryption"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}