{"record":{"id":"1dd9453627e3b167","repo":"embassy-rs/embassy","slug":"input-must-be-a-multiple-of-bytes-in-ecb-and-cb","errorCode":null,"errorMessage":"Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.","messagePattern":"Input must be a multiple of (.+?) bytes in ECB and CBC modes\\. Consider padding or ciphertext stealing\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-stm32/src/cryp/mod.rs","lineNumber":1428,"sourceCode":"                T::regs().cr().modify(|w| w.set_gcm_ccmph(2));\n                T::regs().cr().modify(|w| w.set_fflush(true));\n                T::regs().cr().modify(|w| w.set_crypen(true));\n            }\n        }\n        if ctx.last_block_processed {\n            panic!(\"The last block has already been processed!\");\n        }\n        if input.len() > output.len() {\n            panic!(\"Output buffer length must match input length.\");\n        }\n        if !last_block {\n            if last_block_remainder != 0 {\n                panic!(\"Input length must be a multiple of {} bytes.\", C::BLOCK_SIZE);\n            }\n        }\n        if C::REQUIRES_PADDING {\n            if last_block_remainder != 0 {\n                panic!(\n                    \"Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.\",\n                    C::BLOCK_SIZE\n                );\n            }\n        }\n        if last_block {\n            ctx.last_block_processed = true;\n        }\n\n        // Load data into core, block by block.\n        let num_full_blocks = input.len() / C::BLOCK_SIZE;\n        for block in 0..num_full_blocks {\n            let index = block * C::BLOCK_SIZE;\n            // Write block in\n            self.write_bytes_blocking(C::BLOCK_SIZE, &input[index..index + C::BLOCK_SIZE]);\n            // Read block out\n            self.read_bytes_blocking(C::BLOCK_SIZE, &mut output[index..index + C::BLOCK_SIZE]);\n        }","sourceCodeStart":1410,"sourceCodeEnd":1446,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-stm32/src/cryp/mod.rs#L1410-L1446","documentation":"For ECB and CBC the CRYP hardware applies no automatic padding, so the entire input — including the final block — must be an exact multiple of the AES block size. The driver panics with the block size in the {} slot, suggesting software padding or ciphertext stealing.","triggerScenarios":"Calling encrypt/decrypt in ECB or CBC mode with input length not divisible by 16 bytes (e.g. 30-byte plaintext with last_block=true).","commonSituations":"Encrypting raw records whose lengths aren't block-aligned; switching from a software AES-CBC that pads to this hardware driver; padding handled at an unexpected layer.","solutions":["Apply PKCS#7 (or other) padding in software before the final ECB/CBC block; strip it after decrypt.","Implement ciphertext stealing (CBC-CTS) for the tail blocks, as the message suggests.","Switch to CTR or GCM, which handle arbitrary lengths natively."],"exampleFix":"// before\nlet pt = *b\"hello world\"; // 11 bytes\nctx.payload(&pt, &mut out, true); // ECB/CBC panics\n// after\nlet mut pt = *b\"hello world\\u{5}\\u{5}\\u{5}\\u{5}\\u{5}\"; // PKCS#7 pad to 16\nctx.payload(&pt, &mut out, true);","handlingStrategy":"validation","validationCode":"if input.len() % 16 != 0 { /* apply PKCS#7 padding or use CTR/GCM instead */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember hardware ECB/CBC applies no padding","Centralize pad/unpad helpers for ECB/CBC use","Prefer CTR/GCM when plaintext lengths are arbitrary"],"tags":["embedded","crypto","aes","cbc","ecb","padding","panic"],"backgroundTag":"invalid-argument-format","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}