Glossary

AES-CBC

AES-CBC is a mode of operation that encrypts AES blocks in a chain, where each 16-byte plaintext block is XORed with the previous ciphertext block before encryption. CBC stands for cipher block chaining and is defined in NIST SP 800-38A. The first block uses a 16-byte initialization vector (IV). CBC gives confidentiality only, so tampering is not detected, and TLS 1.3 keeps only authenticated ciphers.

How it works

For block i, the encryptor computes C(i) = AES(P(i) XOR C(i-1)), with C(0) set to the IV. Decryption computes P(i) = AES-decrypt(C(i)) XOR C(i-1). Because of the chaining, equal plaintext blocks produce different ciphertext blocks, which fixes the main weakness of ECB.

The details developers must handle:

  • IV: 16 bytes, the AES block size. NIST SP 800-38A says it must be unpredictable, so use a random value from a secure generator for each message. It is not secret and is normally sent in front of the ciphertext.
  • Padding: CBC works on whole blocks. The usual scheme is PKCS#7, described for CMS in RFC 5652: add k - (length mod k) bytes, each holding that count, with k = 16. Input that already fills the blocks gets a full extra block of 16 bytes of 0x10.
  • Key: 128, 192 or 256 bits.
  • Output length: a multiple of 16 bytes, always at least one byte longer than the input.

The example encrypts "hello" with Python and OpenSSL. Both produced the same 16 bytes.

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
key = bytes(range(16)); iv = bytes(range(16, 32))
p = padding.PKCS7(128).padder()
d = p.update(b"hello") + p.finalize()
print("padded", d.hex())
e = Cipher(algorithms.AES(key), modes.CBC(iv)).encryptor()
c = e.update(d) + e.finalize()
print("cbc", len(c), c.hex())
padded 68656c6c6f0b0b0b0b0b0b0b0b0b0b0b
cbc 16 32f6d6ea1ec8872debccea8596d9c3c3

How big is AES-CBC output?

AES-CBC output is the input length rounded up to the next multiple of 16 bytes, plus one more block when the input is already a multiple of 16. Padded lengths measured from PKCS#7: 0, 1, 15 bytes give 16; 16, 17 and 31 give 32; 32 gives 48. Store the 16-byte IV as well, so a 5-byte message takes 32 bytes in total.

Is AES-CBC secure?

AES-CBC is secure for confidentiality when the IV is unpredictable and the key is strong, but it is not authenticated. Anyone who can change the data can flip bits. Changing one byte of the IV turned the decrypted first block amount=0000100.0 into amount=9000100.0, while the ciphertext still decrypted without any error. Add an HMAC over the IV and ciphertext, check it before decrypting, or use AES-GCM.

Common pitfalls

  • Skipping authentication: CBC accepts modified ciphertext. Use encrypt-then-MAC with HMAC, or switch to AES-GCM.
  • Leaking padding errors: if a server tells a client whether padding was valid, an attacker can decrypt data byte by byte. Python reports "Invalid padding bytes." for a damaged last block; never show that distinction to callers, and verify the MAC first.
  • Fixed IV: a constant IV makes identical messages produce identical ciphertext, which reveals repeats. Generate a fresh random IV for each message.
  • Counter as IV: NIST requires unpredictability, so a plain counter is not enough. SP 800-38A suggests encrypting the counter with the same key to form the IV.
  • Forgetting to send the IV: decryption fails without it. Prepend the 16 bytes to the ciphertext.

Related terms

  • AES — the block cipher that CBC chains.
  • AES-GCM — the authenticated mode that replaces CBC in most new designs.
  • AES-CTR — a parallel mode with no padding.
  • HMAC — adds the integrity check that CBC lacks.
  • PBKDF2 — derives the AES key from a password.

See also