Part 1: Foundations of Cryptography
Part 2: Classical Ciphers
Part 3: Mechanized Cryptography
Part 4: Theoretical Breakthroughs
Part 5: Modern Symmetric Cryptography
Part 6: Principles of Good Cryptosystems
Part 7: Introduction to Cryptanalysis
Overview
Shannon's theoretical framework showed what secure ciphers needed to achieve: destroy the patterns in language through confusion and diffusion, repeated in multiple rounds. The mechanized era demonstrated that machines could apply complex transformations consistently and continuously. Modern symmetric cryptography combines these insights to create practical systems that implement Shannon's principles at the scale and speed that digital communications require.
This section covers the tools you'll actually encounter in protocols and libraries. We examine how modern block ciphers implement confusion and diffusion, look at specific systems like AES and ChaCha20, and address the practical question of how to use these primitives safely on real data that's longer than a single block.
Block ciphers in practice
A block cipher is a keyed permutation on \(n\)-bit strings. Given a secret key, it produces one permutation of the \(2^n\) possible blocks; change the key and you get another.
Modern block ciphers don't create complex permutations in one step. Instead, they use a round-based structure: apply a simple transformation repeatedly, mixing in key material each time.
The state is a small array of bytes that holds the intermediate values. Each round reads the current state, applies the round transformation, and produces the next state. A key schedule expands the master key into separate round keys so each round can mix in fresh key material.
After enough rounds, simple relationships between input, key, and output bits are destroyed. The avalanche effect ensures that changing one input bit affects many output bits in unpredictable ways.
Two architectural patterns
Two structural patterns dominate block cipher design: substitution–permutation networks and Feistel networks.
Substitution–permutation networks (SPNs)
A modern block cipher like AES is a substitution–permutation network (SPN).
We treat the input block as a state: a small array of bytes holding the work in progress.
Encryption runs in rounds. Each round repeats the same set of operations on the state: substitute, mix, and add key information:
-
Substitution (confusion): Replace each byte using a fixed lookup table (an S-box). Each table maps a byte to another byte via a nonlinear rule, so simple XOR-based relationships do not survive.
-
Permutation (diffusion): Rearrange the bytes to spread local changes.
-
Key addition: Each round uses a round key. The key schedule expands the master key into a sequence of per-round keys. We combine the round key with the state by taking an exclusive-or of the current result to produce the state for the next round, written \(\text{state} \leftarrow \text{state} \oplus K_i\).
After enough rounds, small differences in the input or in the key affect many bytes of the state. That is Shannon’s confusion and diffusion in practice.
Decryption applies the inverse substitution and the inverse mixing, using the round keys in reverse order.
Feistel networks
Feistel networks solve a different problem: how do we build an invertible cipher even if the round computation itself is not invertible? This means that we don't need to design a function that performs an inverse operation for decrypting data.
As with SPNs, encryption uses multiple rounds, and each round gets a different round key derived from the master key. It applies the following steps:
- Split the block into two halves, left and right. In each round, compute a keyed function \(F\) on the right half: \(F(R_i, K_i)\).
- XOR the result into the left half, then swap halves: \[L_{i+1} = R_i,\qquad R_{i+1} = L_i \oplus F(R_i, K_i).\]
Repeat for several rounds. Here, \(F\) is any fast round function that takes the right half and a round key as parameters. It does not need to be invertible.
It is easy to see why this always reverses. After a round, the new left half is the old right half. During decryption, you already have it, so you recompute the same \(F\), undo the XOR, and swap back.
You never need to invert the round function. Decryption runs the same code with the round keys in reverse order.
DES is the classic Feistel cipher. It applies this structure for 16 rounds. Each round expands the right half, combines it with a round key, substitutes through small tables, permutes the result, XORs it into the left half, and swaps.
DES: commercial origins, structure, and modern limits
In the early 1970s, banks and companies needed a practical way to protect data as ATMs, electronic funds transfer, and shared computing grew. IBM built a family of ciphers under Horst Feistel for this market.
One of those designs, refined with feedback from the U.S. National Bureau of Standards (now NIST), became the Data Encryption Standard (DES) in 1977. The aim was a published, efficient standard that industry could implement.
Although it's most often implemented in software, DES was designed with hardware implementations in mind: the bit permutations and S-box lookups fit the chips of the day. The speed of computers of that time would otherwise use a significant chunk of computing time simply encrypting or decrypting data.
DES is a Feistel cipher that has the following structure:
- 64-bit blocks
- 56-bit key
- 16 rounds
Each round's \(F\) function:
- Expands 32 bits to 48 bits by duplication
- XORs with 48-bit round key
- Substitutes through eight 6→4 S-boxes
- Permutes the 32-bit result
The S-boxes were carefully engineered to resist differential cryptanalysis, which was not public knowledge until the 1990s, showing that DES designers understood attacks that wouldn't be published for another decade.
It is fair to say that DES is not fundamentally weak except for its key length. The 16-round structure and S-boxes still stand up well against known cryptanalysis. The problem is that a 56-bit key can be brute-forced; that was demonstrated in the late 1990s with purpose-built hardware. Brute-forcing simply means iterating through every possible key until you achieve a successful decryption. By the late 1990s, specialized hardware could break DES keys in days or hours.
The 64-bit block also runs into the birthday bound: collisions appear after about \(2^{n/2}\) samples. With \(n=64\), repeats are likely after roughly \(2^{32}\) blocks under one key (on the order of a few-hundred gigabytes), which weakens several modes.
Triple DES (3DES)**
To extend DES’s life, industry created triple-DES: using the DES algorithm in an encrypt–decrypt–encrypt (EDE) sequence:
- One-key 3DES (EDE1). Use the same 56-bit key three times: \(E_K(D_K(E_K(\cdot)))\). The decryption cancels out the first encryption. This is for backward compatibility; it does not raise security beyond single DES.
- Two-key 3DES (EDE2). Use \(K_1, K_2\) and set \(K_3=K_1\): \(E_{K_1}(D_{K_2}(E_{K_1}(\cdot)))\). This effectively applies a 112-bit key.
- Three-key 3DES (EDE3). Use three independent keys: \(E_{K_1}(D_{K_2}(E_{K_3}(\cdot)))\). Applies a 168-bit key.
3DES provided adequate key length but inherited the 64-bit block limitation and was slow on general-purpose processors. It served as a bridge until AES became available. Treat DES and 3DES as legacy.
AES: what Rijndael is and why it replaced DES
In 1997, NIST ran an open, international competition to replace DES. The winner, standardized as the Advanced Encryption Standard (AES) in 2001, is a cipher named Rijndael after its designers, Joan Daemen and Vincent Rijmen. “Rijndael” is part of a broader family of ciphers. It supports several block and key sizes. AES is the specific profile with a 128-bit block and key sizes of 128, 192, or 256 bits.
AES has this structure:
- Substitution-Permutation Network (software-friendly design)
- 128-bit blocks (larger than DES, avoiding birthday problems)
- 128/192/256-bit keys (adequate for long-term security)
- 10/12/14 rounds respectively
It represents the 128-bit state as a \(4\times 4\) array of bytes and applies a fixed round structure. Each round contains the following sequence of operations:
- SubBytes: Replace each byte using fixed 8×8 S-box. This is a lookup table with 256 entries. You give it one byte (0–255); it returns a different byte. The table is chosen so the mapping is nonlinear (no simple XOR rules apply). There is also an inverse table used during decryption.
- ShiftRows: Rotate each row by a different offset. This moves bytes to new columns. Row 0 is not rotated; row 1 is rotated by one position; row 2 by two positions; and row 3 by three positions.
- MixColumns: Linearly mix the four bytes in each column. This spreads local changes.
- AddRoundKey: XOR the round key into the state.
The final round omits MixColumns (a technicality needed for clean decryption).
A key schedule expands the master key into round keys and avoids simple relations that would leak structure. AES-128, AES-192, and AES-256 use 10, 12, and 14 rounds.
Why AES Succeeded
AES was chosen as a standard after much analysis:
-
Security: Extensive cryptanalysis found no practical attacks on full AES. Some published attacks can break variants with a reduced number of rounds, but full AES is far beyond reach.
-
Performance: Unlike DES's bit-twiddling, AES operations work naturally on bytes and 32-bit words. This made software implementations fast on general-purpose computers.
-
Hardware support: Modern processors include AES instructions (AES-NI on x86, similar on ARM) that implement AES rounds in hardware, providing both speed and resistance to timing attacks.
-
Versatility: The Rijndael family supports various block and key sizes, though AES standardized specific combinations.
-
Safety for large data: The 128-bit block keeps birthday concerns far away for normal per-key volumes.
If you need a block cipher, choose AES unless you have a specific, defensible reason not to.
Modes of operation: using block ciphers on real data
Block ciphers operate on one fixed-size block at a time. Files, packets, and records tend to be longer and typically span many such blocks.
A mode of operation defines how to apply a block cipher to whole messages. We will cover message authentication later, so we keep integrity details light here.
We will cover integrity later, but you will see one term now. AEAD (Authenticated Encryption with Associated Data) means “encrypt and also output a short tag to detect tampering.” Read AEAD as “encryption with a built-in tamper check.” We will return to the details when we discuss authentication.
- Electronic Codebook (ECB)
- ECB encrypts each block independently, applying the same mapping to every block under the same key. That makes it deterministic at the block level.
- The consequence is leakage. Identical plaintext blocks produce identical ciphertext blocks. Repeated fields, aligned structures, and low-variation regions show up as repeated ciphertext blocks. Encrypting an image with ECB preserves visible outlines; encrypting a database column reveals which records share the same value.
- ECB also enables simple cut-and-paste manipulations: an attacker can rearrange ciphertext blocks and produce a rearranged plaintext after decryption because each block is handled in isolation.
- ECB is fine for test vectors and toy examples. Do not use it for data; prefer a mode that hides patterns (CBC, CTR) or, better, an AEAD (AES-GCM or ChaCha20-Poly1305).
- Cipher Block Chaining (CBC)
- CBC hides repeats by XORing each plaintext block with the previous ciphertext block before encryption. For the first block, use an initialization vector (IV): a per-message, random, unpredictable block XORed with the first plaintext so the same data under the same key encrypts differently each time. The initialization vector is not secret -- it is typically sent immediately before the ciphertext. It ensures that even identical plaintexts encrypted with the same key produce different ciphertext.
- Counter mode (CTR)
- CTR turns a block cipher into a keystream generator. Encrypt a sequence of per-message inputs and counters to create a keystream, then XOR with the plaintext. There is no padding. Both directions are parallelizable. Random access is easy. The per-message input must be unique for a given key. If you reuse it with the same key, you reuse the keystream, and XORing two ciphertexts reveals \(P_1\oplus P_2\).
- Galois/Counter Mode (GCM)
- GCM builds on CTR and also produces an authentication tag. For now, know that it gives you encryption plus a tag to detect tampering. GCM prefers a 96-bit per-message value (called a nonce) and requires uniqueness for a given key.
“If the message length isn’t a multiple of the block size, ECB and CBC require padding (e.g., defined by a PKCS#7 standard).. CTR, GCM, CFB/OFB, and stream ciphers like ChaCha20 don’t need padding because they encrypt a keystream and XOR it with the plaintext.”
To keep terminology manageable, we will skip storage-specific modes here, but note that more modes have been designed.
Practical rule. Choose an AEAD in real-world systems. If you have AES hardware, AES-GCM is usually the right choice. If you do not, use ChaCha20-Poly1305 in the next section.
Stream ciphers and ChaCha20
While block ciphers work on fixed-size chunks, stream ciphers generate a continuous keystream that's XORed with the plaintext. They're conceptually similar to the one-time pad, but with the pad (keystream) generated algorithmically from a key and per-message value using a CSPRNG (cryptographically-secure pseudorandom number generator).
Advantages of a stream cipher
Stream ciphers have a few attractive features:
- No padding needed: Work with any message length
- Random access: Can encrypt/decrypt at any position (but you have to run the keystream generator to get to the proper position)
- Simple operation: Just XOR keystream with data
- Efficiency: Often faster than block ciphers, especially on limited hardware
Critical requirements
Stream ciphers simulate a one-time pad and have the same operational issue. You can never reuse keystreams, meaning that you cannot use the same key for two different messages since that will reveal the relationship between plaintexts: \(C_1 \oplus C_2 = P_1 \oplus P_2\).
This restriction would render stream ciphers useless, as you would need to generate a new key for each piece of data to be encrypted.
Different designs call it a nonce ("number used once") or an initialization vector (IV), and it is an extra parameter that is passed to the keystream generator along with the secret key. This ensures that the keystream will be different even with the same key since the nonce is randomly generated for each message. As with block modes, the nonce or initialization vector is not secret.
ChaCha20: A modern stream cipher
RC4 was once a common stream cipher on the web. Statistical biases and practical attacks ended its run.
ChaCha20 is the modern choice on general CPUs and on embedded systems.
ChaCha20's keystream generator is built from simple operations on 32-bit words: adds, rotates, and XORs.
These operations are fast on general-purpose processors and easy to implement in constant time, making ChaCha20 resistant to timing attacks.
The structure of ChaCha20 is:
- 256-bit key
- 96-bit nonce (unique per message)
- 32-bit internal counter (allows up to 256 GB of data per nonce)
- 20 rounds of the core transformation
ChaCha20 initializes a 512-bit state with key, nonce, counter, and constants, then applies 20 rounds to produce 512 bits of keystream. For longer messages, the counter increments to produce additional keystream blocks. That makes ChaCha20 a high-quality pseudorandom generator keyed by the secret and the nonce.
You then compute \[C = P \oplus \text{keystream}\] To decrypt, you compute \[P = C \oplus \text{keystream}\]
ChaCha20-Poly1305 AEAD
ChaCha20 almost always appears combined with Poly1305 authentication to create an AEAD (Authenticated Encryption with Associated Data) system called ChaCha20-Poly1305, which provides both confidentiality and integrity in one operation.
ChaCha20 is popular because it is fast without lookup tables, easy to implement in constant time, and resilient against cache-timing pitfalls. On some phones, small cores, and many embedded systems, it outperforms AES software. As with CTR and GCM, the nonce must be unique when using the same key.
When you have AES hardware, AES-GCM is usually the fastest safe choice. When you do not, ChaCha20-Poly1305 is typically faster and simpler to implement correctly. Both are solid when used correctly.
Reference: common ciphers and sizes
Core set you will actually choose:
Name | Type | Block/stream | Typical key sizes | Notes |
---|---|---|---|---|
AES-128/192/256 | SPN block cipher | 128-bit block | 128/192/256-bit | Default choice; broad hardware support |
ChaCha20-Poly1305 | Stream + authenticator (AEAD) | Stream | 256-bit (cipher) | Fast without AES-NI; strong side-channel profile |
DES | Feistel block cipher | 64-bit block | 56-bit (effective) | Legacy; brute-forceable; small block |
3DES (EDE2/EDE3) | Feistel block cipher | 64-bit block | 56, 112, or 168-bit (nominal) | Legacy; slow |
Optional ciphers you may see in standards/libraries:
Name | Type | Block/stream | Typical key sizes | Notes |
---|---|---|---|---|
Camellia | Feistel-style block cipher | 128-bit block | 128/192/256-bit | ISO standard; common in OpenSSL |
SM4 | Feistel-style block cipher | 128-bit block | 128-bit | Chinese national standard; 32 rounds |
ARIA | SPN block cipher | 128-bit block | 128/192/256-bit | Korean standard |
SEED | Feistel block cipher | 128-bit block | 128-bit | Korean standard; legacy in some stacks |
IDEA (legacy) | Mixed-arithmetic block cipher | 64-bit block | 128-bit | Early PGP; legacy only |
GOST 28147-89 (legacy) | Feistel cipher | 64-bit block | 256-bit | Former Russian standard; lagacy |
Takeaways
Pick AES for block-cipher deployments and ChaCha20-Poly1305 when AES hardware is weak.
Use an AEAD by default to ensure both confidentiality and integrity. We will cover authentication details later.
Treat the per-message input as a first-class parameter. In CBC, this is an IV (initialization vector): a fresh, unpredictable block used with the first plaintext block. In CTR, GCM, and ChaCha20, it is a per-message value (often called a nonce) that must be unique under a key. We will revisit “nonce” terminology when we cover authentication protocols.
Respect any per-key data limits that a standard calls out. Use constant-time implementations or hardware instructions to avoid timing leaks.
Most real failures are the same two mistakes: the wrong mode for the job, or reuse of a nonce/IV. Avoid those, and these ciphers will serve you well.