embassy-rs/embassy · error

Input length must be a multiple of

Error message

Input length must be a multiple of {} bytes.

What it means

For non-final processing the driver requires input length to be a multiple of the cipher block size (16 bytes for AES), since hardware without padding cannot consume a partial block. A non-final chunk with a remainder panics, reporting the required block size via the {} format argument.

Solutions

  1. Buffer input into block-aligned (16-byte) chunks; only the final chunk with last_block=true may be shorter where the mode allows.
  2. Use a padding/CTS mode for arbitrary-length inputs, applying padding before the final call.
  3. Pad the input yourself to a block multiple and track the real length for verification.

Example fix

// before
for chunk in data.chunks(1024 + 7) { ctx.payload(chunk, &mut out, false); } // unaligned
// after
for chunk in data.chunks(1024) { ctx.payload(chunk, &mut out, false); } // block-aligned
Defensive patterns

Strategy: validation

Validate before calling

if !is_last && input.len() % 16 != 0 { /* stage bytes until a full 16-byte block is available */ }

Prevention

When it happens

Trigger: Calling payload() with last_block=false and input.len() % C::BLOCK_SIZE != 0 — e.g. a 10-byte chunk into AES.

Common situations: Reading files in arbitrary-size chunks and passing them straight to the cipher; protocol framing yielding non-aligned bodies; forgetting only the final block may be partial.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/50605980a3760ddf. Report an issue: GitHub.

Appendix: source

Thrown at embassy-stm32/src/cryp/mod.rs:1423

        } else if !ctx.aad_complete {
            #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]
            {
                ctx.aad_complete = true;
                T::regs().cr().modify(|w| w.set_crypen(false));
                T::regs().cr().modify(|w| w.set_gcm_ccmph(2));
                T::regs().cr().modify(|w| w.set_fflush(true));
                T::regs().cr().modify(|w| w.set_crypen(true));
            }
        }
        if ctx.last_block_processed {
            panic!("The last block has already been processed!");
        }
        if input.len() > output.len() {
            panic!("Output buffer length must match input length.");
        }
        if !last_block {
            if last_block_remainder != 0 {
                panic!("Input length must be a multiple of {} bytes.", C::BLOCK_SIZE);
            }
        }
        if C::REQUIRES_PADDING {
            if last_block_remainder != 0 {
                panic!(
                    "Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.",
                    C::BLOCK_SIZE
                );
            }
        }
        if last_block {
            ctx.last_block_processed = true;
        }

        // Load data into core, block by block.
        let num_full_blocks = input.len() / C::BLOCK_SIZE;
        for block in 0..num_full_blocks {
            let index = block * C::BLOCK_SIZE;

View on GitHub (pinned to 463a07b963)