Symmetric Encryption: AES, Block Ciphers, and Modes of Operation
Understanding how symmetric encryption works, why AES dominates modern cryptography, how block cipher modes shape security, and why ECB mode is fundamentally broken.
Terminology
| Term | Definition |
|---|---|
| Symmetric encryption | An encryption scheme where the same secret key is used for both encryption and decryption |
| Block cipher | An encryption algorithm that operates on fixed-size groups of bits (blocks), typically 128 bits, transforming each block independently using a key |
| Plaintext | The original unencrypted data before any cryptographic transformation is applied |
| Ciphertext | The encrypted output produced by applying an encryption algorithm to plaintext |
| AES (Advanced Encryption Standard) | A symmetric block cipher adopted as a federal standard in 2001, operating on 128-bit blocks with key sizes of 128, 192, or 256 bits |
| Mode of operation | A method for applying a block cipher repeatedly to encrypt data larger than a single block, such as ECB, CBC, or CTR |
| Initialization vector (IV) | A random or unique value combined with the key to ensure that identical plaintexts produce different ciphertexts across encryptions |
| Key schedule | The algorithm that expands a short encryption key into a series of round keys, one for each round of the cipher |
| Round | One iteration of the cipher's internal transformation; AES uses 10, 12, or 14 rounds depending on key size |
| Substitution-permutation network (SPN) | A cipher design that alternates substitution steps (replacing bytes via lookup tables) with permutation steps (rearranging byte positions) to achieve confusion and diffusion |
| S-box (substitution box) | A lookup table used in block ciphers to perform non-linear byte substitution, providing confusion |
| Confusion | A property of a cipher where each bit of the ciphertext depends on multiple bits of the key, making the relationship between key and ciphertext complex |
| Diffusion | A property of a cipher where changing one bit of plaintext changes approximately half the bits of the ciphertext, spreading influence across the entire block |
| Padding | Extra bytes added to the last plaintext block so it reaches the required block size; PKCS#7 is the most common scheme |
| XOR (exclusive or) | A bitwise operation where the output is 1 only when the two input bits differ; fundamental to most encryption operations because $a \oplus b \oplus b = a$ |
What & Why
Symmetric encryption is the workhorse of modern cryptography. When you connect to a website over HTTPS, stream a video, or send a message on an encrypted chat app, symmetric encryption does the heavy lifting of protecting the actual data. The word "symmetric" means both sides share the same secret key: the sender encrypts with it, and the receiver decrypts with the same key.
The dominant symmetric cipher today is AES (Advanced Encryption Standard). Selected by NIST in 2001 after a multi-year public competition, AES replaced the aging DES standard and has withstood over two decades of cryptanalysis with no practical attacks found. AES is a block cipher, meaning it encrypts data in fixed-size chunks of 128 bits (16 bytes). To encrypt messages longer than one block, you need a mode of operation that defines how blocks are chained together.
The choice of mode matters enormously. ECB (Electronic Codebook) mode encrypts each block independently, which leaks patterns in the plaintext. The famous "ECB penguin" demonstration shows that encrypting a bitmap image with ECB preserves the visual outline of the original image. CBC (Cipher Block Chaining) and CTR (Counter) modes solve this by introducing dependencies between blocks, ensuring identical plaintext blocks produce different ciphertext blocks.
Understanding symmetric encryption is essential because it sits at the foundation of nearly every secure system. Asymmetric encryption (RSA, Diffie-Hellman) is too slow for bulk data, so real protocols use asymmetric crypto only to exchange a symmetric key, then switch to AES for the actual data transfer. This hybrid approach powers TLS, SSH, VPNs, and disk encryption.
How It Works
Block Cipher Fundamentals
A block cipher takes two inputs: a fixed-size plaintext block and a secret key. It produces a fixed-size ciphertext block. AES operates on 128-bit (16-byte) blocks with key sizes of 128, 192, or 256 bits. The number of internal rounds depends on the key size: 10 rounds for AES-128, 12 for AES-192, and 14 for AES-256.
AES is built on a substitution-permutation network (SPN). Each round applies four operations to a 4x4 grid of bytes called the state matrix:
- SubBytes: each byte is replaced using a fixed lookup table (S-box), providing non-linearity (confusion)
- ShiftRows: rows of the state matrix are cyclically shifted by different offsets, spreading bytes across columns
- MixColumns: each column is multiplied by a fixed matrix in the Galois field $GF(2^8)$, mixing bytes within columns (diffusion)
- AddRoundKey: the state is XORed with the round key derived from the key schedule
The final round omits MixColumns (it would add no security and would complicate decryption). Before the first round, an initial AddRoundKey step XORs the plaintext with the original key.
Key Schedule
The key schedule takes the original key and expands it into a separate round key for each round (plus one for the initial AddRoundKey). For AES-128, the 16-byte key is expanded into 11 round keys (176 bytes total). Each new 4-byte word is derived from the previous word and the word four positions back, using S-box substitution and XOR with a round constant. This ensures that every round key is different and that a small change in the original key cascades into completely different round keys.
Modes of Operation
A block cipher alone only encrypts one block. Real messages are almost always longer than 16 bytes, so a mode of operation defines how to apply the cipher across multiple blocks.
ECB Mode (Electronic Codebook): The Broken Mode
ECB is the simplest mode: each plaintext block is encrypted independently with the same key. This means identical plaintext blocks always produce identical ciphertext blocks.
The problem is obvious: P1 and P3 are identical, so C1 and C3 are identical. An attacker who sees the ciphertext can detect repeated patterns without knowing the key. The "ECB penguin" is the classic demonstration: encrypting a bitmap image of a penguin with ECB preserves the silhouette because large regions of identical pixel values produce identical ciphertext blocks. The outline of the penguin is clearly visible in the encrypted image. ECB should never be used for data longer than a single block.
CBC Mode (Cipher Block Chaining)
CBC solves ECB's pattern leakage by XORing each plaintext block with the previous ciphertext block before encryption. The first block is XORed with a random initialization vector (IV). This means identical plaintext blocks produce different ciphertext blocks because each block's encryption depends on all previous blocks.
CBC encryption is inherently sequential because each block depends on the previous ciphertext. Decryption, however, can be parallelized because you only need the previous ciphertext block (which is already known) to decrypt any block. CBC requires padding the last block to the full block size, typically using PKCS#7 padding.
CTR Mode (Counter)
CTR mode turns a block cipher into a stream cipher. Instead of encrypting the plaintext directly, it encrypts a sequence of counter values and XORs the result with the plaintext. The counter is formed by concatenating a nonce (number used once) with a block counter that increments for each block.
CTR mode has several advantages over CBC. Both encryption and decryption are fully parallelizable because no block depends on any other. No padding is needed because you can simply truncate the last keystream block to match the plaintext length. Random access is possible: to decrypt block $i$, you just encrypt counter value $i$ and XOR. The nonce must never be reused with the same key, because if two messages share a nonce, XORing their ciphertexts cancels the keystream and reveals the XOR of the two plaintexts.
Complexity Analysis
The computational cost of AES is measured per block (128 bits). Each round involves fixed operations on a 16-byte state, and the number of rounds is constant for a given key size.
| Operation | Time per Block | Space | Parallelizable |
|---|---|---|---|
| AES-128 (single block) | $O(1)$, 10 rounds | $O(1)$, 176 bytes round keys | N/A |
| AES-256 (single block) | $O(1)$, 14 rounds | $O(1)$, 240 bytes round keys | N/A |
| ECB encrypt $n$ blocks | $O(n)$ | $O(1)$ extra | Yes (all blocks independent) |
| CBC encrypt $n$ blocks | $O(n)$ sequential | $O(1)$ extra | Encrypt: No, Decrypt: Yes |
| CTR encrypt $n$ blocks | $O(n)$ | $O(1)$ extra | Yes (both directions) |
| Key schedule (AES-128) | $O(1)$, 40 word expansions | $O(1)$, 44 words | No (sequential) |
For a message of $n$ blocks, the total encryption time is:
where $R$ is the number of rounds (10, 12, or 14) and $T_{\text{round}}$ is the time for one round's four operations. On modern CPUs with AES-NI hardware instructions, a single AES round executes in approximately 1 clock cycle, giving throughput of:
The brute-force search space for AES keys is:
Even at $10^{18}$ keys per second (far beyond current capability), brute-forcing AES-128 would take approximately $10^{13}$ years.
Implementation
ALGORITHM AES_Encrypt(plaintext, key)
INPUT: plaintext: 16-byte block, key: 16/24/32-byte key
OUTPUT: 16-byte ciphertext block
BEGIN
state <- COPY(plaintext) as 4x4 byte matrix (column-major)
roundKeys <- KeyExpansion(key)
numRounds <- 10 if keyLen=16, 12 if keyLen=24, 14 if keyLen=32
// Initial round key addition
state <- AddRoundKey(state, roundKeys[0])
// Main rounds
FOR round <- 1 TO numRounds - 1 DO
state <- SubBytes(state) // S-box substitution on each byte
state <- ShiftRows(state) // Cyclic left shift: row i shifts by i
state <- MixColumns(state) // Matrix multiply in GF(2^8)
state <- AddRoundKey(state, roundKeys[round])
END FOR
// Final round (no MixColumns)
state <- SubBytes(state)
state <- ShiftRows(state)
state <- AddRoundKey(state, roundKeys[numRounds])
RETURN state as 16-byte array
END
ALGORITHM KeyExpansion(key)
INPUT: key: original cipher key (16 bytes for AES-128)
OUTPUT: array of 11 round keys (each 16 bytes)
BEGIN
N <- keyLen / 4 // Number of 32-bit words in key (4 for AES-128)
R <- numRounds + 1 // Number of round keys needed (11 for AES-128)
W <- array of R * 4 words
// Copy original key into first N words
FOR i <- 0 TO N - 1 DO
W[i] <- key[4*i .. 4*i+3]
END FOR
// Expand remaining words
FOR i <- N TO (R * 4) - 1 DO
temp <- W[i - 1]
IF i MOD N = 0 THEN
temp <- RotateLeft(temp, 1 byte)
temp <- SubWord(temp) // Apply S-box to each byte
temp <- temp XOR RoundConstant[i / N]
END IF
W[i] <- W[i - N] XOR temp
END FOR
RETURN W grouped into R round keys of 4 words each
END
ALGORITHM CBC_Encrypt(plaintext, key, iv)
INPUT: plaintext: byte array, key: cipher key, iv: 16-byte IV
OUTPUT: ciphertext byte array
BEGIN
blocks <- PadAndSplit(plaintext, 16) // PKCS#7 pad, split into 16-byte blocks
ciphertext <- empty byte array
previous <- iv
FOR EACH block IN blocks DO
xored <- block XOR previous
encrypted <- AES_Encrypt(xored, key)
APPEND encrypted TO ciphertext
previous <- encrypted
END FOR
RETURN ciphertext
END
ALGORITHM CTR_Encrypt(plaintext, key, nonce)
INPUT: plaintext: byte array, key: cipher key, nonce: 8-byte nonce
OUTPUT: ciphertext byte array
BEGIN
numBlocks <- CEIL(LENGTH(plaintext) / 16)
ciphertext <- empty byte array
FOR counter <- 0 TO numBlocks - 1 DO
counterBlock <- nonce || counter as 16-byte value
keystream <- AES_Encrypt(counterBlock, key)
blockStart <- counter * 16
blockEnd <- MIN(blockStart + 16, LENGTH(plaintext))
plaintextBlock <- plaintext[blockStart .. blockEnd - 1]
encryptedBlock <- plaintextBlock XOR keystream[0 .. LENGTH(plaintextBlock) - 1]
APPEND encryptedBlock TO ciphertext
END FOR
RETURN ciphertext
END
ALGORITHM ECB_Encrypt(plaintext, key)
INPUT: plaintext: byte array, key: cipher key
OUTPUT: ciphertext byte array
// WARNING: ECB is insecure for multi-block data. Shown for educational purposes only.
BEGIN
blocks <- PadAndSplit(plaintext, 16)
ciphertext <- empty byte array
FOR EACH block IN blocks DO
encrypted <- AES_Encrypt(block, key)
APPEND encrypted TO ciphertext
END FOR
RETURN ciphertext
END
Real-World Applications
- TLS/HTTPS: after the TLS handshake negotiates a shared secret using asymmetric cryptography, all application data is encrypted with AES (typically AES-128-GCM or AES-256-GCM, which combines CTR mode with authentication). Every secure web connection relies on this.
- Full-disk encryption: tools like BitLocker (Windows), FileVault (macOS), and LUKS (Linux) use AES to encrypt entire disk volumes. They typically use XTS mode, a variant designed for storage where blocks must be independently accessible.
- Wi-Fi security: WPA2 uses AES-CCMP (Counter mode with CBC-MAC Protocol) to encrypt wireless traffic. WPA3 continues to use AES with improved key exchange.
- VPNs: IPsec and WireGuard use symmetric encryption (AES or ChaCha20) to protect tunneled traffic between endpoints.
- Database encryption: transparent data encryption (TDE) in databases like PostgreSQL and SQL Server uses AES to encrypt data at rest, protecting against physical theft of storage media.
- Messaging apps: Signal, WhatsApp, and iMessage use AES (within the Signal Protocol or similar) for encrypting message content after key agreement.
- File encryption: tools like GPG and 7-Zip use AES-256 to encrypt files and archives, protecting sensitive data in transit or storage.
Key Takeaways
- Symmetric encryption uses the same key for encryption and decryption, making it fast enough for bulk data protection; AES is the dominant standard with no known practical attacks after over two decades
- AES is a substitution-permutation network that processes 128-bit blocks through 10, 12, or 14 rounds of SubBytes, ShiftRows, MixColumns, and AddRoundKey operations
- ECB mode encrypts each block independently, which leaks plaintext patterns (the "ECB penguin" problem); it should never be used for data longer than one block
- CBC mode chains blocks together by XORing each plaintext block with the previous ciphertext, eliminating pattern leakage but making encryption sequential
- CTR mode turns AES into a parallelizable stream cipher by encrypting counter values and XORing with plaintext; it requires no padding and supports random access
- The key schedule expands the original key into unique round keys, ensuring that a small change in the key produces completely different encryption at every round
- Modern CPUs include AES-NI hardware instructions that execute AES rounds in a single clock cycle, achieving throughput of several gigabytes per second
Read More
2026-03-28
Complexity vs. Volume: Why GPUs Rule AI Math
Understanding the fundamental split between CPU and GPU architecture, explained through the lens of performance philosophy.
2025-10-25
Information Theory Basics: Entropy, Compression, and Shannon's Theorem
Shannon entropy, the source coding theorem, compression limits, Huffman coding, and why you can never compress truly random data.
2025-10-24
Randomized Algorithms and Probabilistic Analysis
Las Vegas vs Monte Carlo algorithms, expected running time, randomized quicksort, skip list analysis, and why adding randomness can make algorithms simpler and faster.