An Interactive Guide to AES

August 14, 202610 min read
computer-sciencecryptographyinteractive

Table Of Contents

  1. The state
  2. The 4 operations
  3. Key Expansion
  4. Putting it together
  5. Avalanche Effect
  6. Why it is hard to break

I’ve written before about how 2 strangers can agree on a secret over a public channel (RSA, Diffie-Hellman). But that’s only half the story. Modular exponentiation is beautiful math, but it’s way too slow to encrypt a video call or a file transfer byte by byte. Once both sides have a shared secret, they need something fast to actually use it. That something is AES, a symmetric key algorithm, meaning both sides use the same key to encrypt and decrypt.

The Advanced Encryption Standard is the cipher almost everything reaches for once the handshake is done. It replaced DES, a 1977 standard with a 56-bit key. By the late 1990s, DES was breakable by brute force in hours. A machine called Deep Crack did it in under a day in 1998.

NIST needed a real successor, so in 1997 it did something unusual. It ran an open competition instead of picking a design behind closed doors. 15 teams submitted candidates, and cryptographers worldwide spent 3 years publicly trying to break them. 5 finalists remained by 1999. In 2000, NIST picked Rijndael, designed by 2 Belgian cryptographers, Joan Daemen and Vincent Rijmen. It won on a mix of security, speed, and how well it ran on everything from servers to smart cards. AES was standardized in 2001, and now it’s everywhere, TLS, disk encryption, Wi-Fi, password managers.

I want to show you what’s actually happening inside it. Below, you’ll walk through the 4 operations AES runs on 16-byte blocks and why each one earns its place. By the end, you’ll see why flipping a single input bit changes roughly half the output, and why doing the same 4 simple things 10 times in a row is enough to make a 128-bit key unbreakable with any computer we have now.

To be specific, we will be going through the AES-128 variant. Other variants, AES-192 and AES-256, differ in key size and number of rounds only.

VariantKey Size (bits)Number of Rounds
AES-12812810 rounds
AES-19219212 rounds
AES-25625614 rounds

# The state

All variants of AES encrypt data 16 bytes at a time. Those 16 bytes get arranged into a 4x4 grid called the state.

The order matters, and it isn’t left to right. The first 4 input bytes fill column 0 top to bottom, the next 4 fill column 1, and so on.

The grid design is an important part. A column is 4 bytes and can be treated as a single 32-bit value. That means CPUs can process it in 1 step, unlike a flat 16-byte line. AES software implementations take advantage of this to run fast. A column is 4 bytes, which fits in a single 32-bit value a CPU can handle in 1 step. So instead of looping over 16 bytes 1 at a time, an implementation can process a whole column at once, turning SubBytes, ShiftRows, MixColumns, and AddRoundKey into a handful of table lookups and XORs instead of 16 separate byte-by-byte operations.

Try it yourself. Enter a plaintext block below, as hex.

Plaintext

Plaintext state
004488cc115599dd2266aaee3377bbff

Keys are also arranged into states. For bigger keys (as in AES-192 and AES-256), the key is expanded into a larger state grid with 4 rows and more columns.

# The 4 operations

Every round but the last applies the same 4 operations in sequence. The plaintext block you entered above is the starting state. Each operation below transforms that state and hands it to the next one, and the state that comes out of round 1 is exactly what round 2 starts with. The sections below show each operation in isolation so you can see what it does on its own, then the “Putting it together” section further down chains all 4 across all 10 rounds so you can watch the full pipeline run.

# SubBytes

In this operation, each cell (or byte) in the state is replaced with a value that has no linear relationship with the original value. A S-box defines this mapping, using a 16x16 fixed lookup table each cell holding 2 bytes. Cryptographers call this confusion. It hides the relationship between key and ciphertext. Click a byte below to see it move through the table.

AES defines the S-box and it is constant.

SubBytes operation reduces the risk of pattern-hunting in the ciphertext.

SubBytes, nonlinear substitution via the S-box

Before
After
631bc44b82fceec19333ac28c3f5ea16

Click a byte. 0x00 splits into row 0 and column 0 of the S-box, giving 0x63.

As the S-box is constant, anyone can reverse it to find the original byte.

# ShiftRows

ShiftRows rotates row r[0,1,2,3]r \in [0, 1, 2, 3] of the state left by rr bytes.

ShiftRows, row r shifts left by r bytes

Before
631bc44b82fceec19333ac28c3f5ea16
After
631bc44bfceec182ac28933316c3f5ea

Even now, byte changes in one row does not affect the other rows.

# MixColumns

MixColumns makes every output byte in a column depend on all 4 input bytes of that column, not just 1.

Suppose a column with bytes a0,a1,a2,a3a_0, a_1, a_2, a_3. The 4 output bytes are each a different weighted combination of all 4 inputs.

o0=(2×a0)(3×a1)a2a3o1=a0(2×a1)(3×a2)a3o2=a0a1(2×a2)(3×a3)o3=(3×a0)a1a2(2×a3)\begin{align} o_0 &= (2 \times a_0) \oplus (3 \times a_1) \oplus a_2 \oplus a_3 \\ o_1 &= a_0 \oplus (2 \times a_1) \oplus (3 \times a_2) \oplus a_3 \\ o_2 &= a_0 \oplus a_1 \oplus (2 \times a_2) \oplus (3 \times a_3) \\ o_3 &= (3 \times a_0) \oplus a_1 \oplus a_2 \oplus (2 \times a_3) \end{align}

\oplus is the XOR operator. ×\times is defined over the finite field GF(2⁸), where addition is XOR and results wrap around using a fixed reduction rule instead of overflowing, but the important part is simpler than the math looks. Every output byte mixes in a piece of every input byte in that column.

That’s why changing 1 input byte changes all 4 output bytes in its column, not just 1. Try it below. ShiftRows and MixColumns together give AES diffusion, where a single flipped input bit spreads across the entire state within 2-3 rounds.

MixColumns, each column multiplied by a fixed matrix in GF(2⁸)

Before
631bc44bfceec182ac28933316c3f5ea
After
63f4add2796706ebe6fb3c8ad976f4a3

Column 0, [0x63, 0xfc, 0xac, 0x16] becomes [0x63, 0x79, 0xe6, 0xd9].

# AddRoundKey

Here’s where it gets interesting. The first 3 operations are deterministic and public. Anyone with the S-box and the shift pattern can compute them, no secret required. AddRoundKey is where the actual secret enters the picture and it’s the only operation that touches the secret. The state is XORed with that round’s key.

As XOR is its own inverse, decryption uses the exact same step, only the round key changes. The same initial key is reused for decryption, where the round schedule is ran in reverse.

AddRoundKey, XOR with round key 1

State
63f4add2796706ebe6fb3c8ad976f4a3
Round key
d6d2dad6aaafa6ab74727876fdfaf1fe
=
After
b5267704d3c8a040928944fc248c055d

Round key 1 came from somewhere. Where the other 10 round keys come from is next.

# Key Expansion

AES doesn’t reuse the same key for every round. The single 128-bit key is expanded into 11 round keys, 1 for the start and 1 for each of the 10 rounds that follow.

The expansion works on each column of the state. AES-128 has 4 columns in the state. And suppose the initial key has the columns: w0,w1,w2,w3w_0, w_1, w_2, w_3. Remaining 10 rounds’ 40 words are generated incrementally.

Each new word wiw_i comes from the word 4 positions back, wi4w_{i-4}, XORed with something built from the word right before it, wi1w_{i-1}. What gets built depends on whether ii is a multiple of 4.

When ii is not a multiple of 4, it’s a plain XOR.

wi=wi4wi1w_i = w_{i-4} \oplus w_{i-1}

When ii is a multiple of 4, wi1w_{i-1} passes through 3 steps first. This is the part that keeps the schedule from repeating.

# RotWord

Cycle the 4 bytes of wi1w_{i-1} left by 1. b0,b1,b2,b3b_0, b_1, b_2, b_3 becomes b1,b2,b3,b0b_1, b_2, b_3, b_0.

# SubWord

Run each of the 4 bytes from RotWord through the same S-box SubBytes uses.

# XOR with Rcon

XOR only the first byte of the SubWord output with a round constant that changes each time this step runs, so word 4, word 8, word 12, and every multiple of 4 after that gets a different constant.

The result of those 3 steps gets XORed with wi4w_{i-4} to produce wiw_i. Every 4 consecutive words form 1 round key.

# Example

Enter a key below, as hex, and watch the 11 round keys it generates.

Key

Key expansion, 11 round keys

Round 0
0004080c0105090d02060a0e03070b0f
Round 1
d6d2dad6aaafa6ab74727876fdfaf1fe
Round 2
b664be68923d9b30cfbdc5b30bf100fe
Round 3
b6d26c04ffc2596974c90cbf4ebfbf41
Round 4
4795f9fdf7356c05f73e328dbc03bcfd
Round 5
3ca950adaa9ff3f6a39daf22e8eb57aa
Round 6
5ef7a70a39a655a30f923d1f7d96c16b
Round 7
14e3444ef95f0aa970e2dfc01a8c4d26
Round 8
47a4e0ae431c16bf8765ba7a35b9f4d2
Round 9
54f010be9985932c3257ed97d1689c4e
Round 10
13e3f34d1194072b1d4aa7307f178bc5

# Putting it together

Now run all 4 operations in sequence. AES-128 starts with AddRoundKey using round key 0, then runs 9 full rounds of SubBytes, ShiftRows, MixColumns, AddRoundKey. Round 10, the final round, skips MixColumns (an optimization, not something the last round needs for security). Watch below as 16 bytes of plaintext turn into unrecognizable ciphertext.

Start

1 / 41
SubBytesShiftRowsMixColumnsAddRoundKey
004488cc115599dd2266aaee3377bbff

This is the plaintext, before any round runs.

# Avalanche Effect

In AES, flipping 1 input bit changes roughly half the output bits. This is the avalanche effect, and it’s the reason SubBytes, ShiftRows, and MixColumns exist. If a cipher does not have the avalanche effect, it is vulnerable to differential cryptanalysis, a technique that can break the cipher by analyzing the differences between input and output.

Below you can see the avalanche effect in action. Try flipping 1 input bit.

Avalanche effect

Click a plaintext byte, then a bit within it, to flip that 1 bit and see how far the change spreads through the ciphertext.

Plaintext

Flipping bit 0 of byte 0 turns 0x00 into 0x01 in the plaintext.

All 128 ciphertext bits, colored where they differ
62 / 128 bits changed

# Why it is hard to break

There’s no known attack faster than trying keys directly. A 128-bit key means 21282^{128} possibilities.

2128=340,282,366,920,938,463,463,374,607,431,768,211,4562^{128} = 340,282,366,920,938,463,463,374,607,431,768,211,456

That’s in the order of 103610^{36} possibilities. Even at a 100 trillion attempts per second, exhausting them takes about 100 quintillion years. To put that into perspective, the estimated age of the universe is about 13.8 billion years.

Mind it that we are talking about AES-128 here. The other variants, AES-192 and AES-256 have an increased safety margin.

We check each possibility one at a time on a classical computer. On a quantum computer, Grover’s algorithm gives a quadratic speedup, so a 128-bit key ends up behaving more like a 64-bit key against a quantum attacker. That’s a real weakening, but nowhere near a collapse. AES-256 stays out of reach even then, which is why it’s the choice for anything that has to stay secret for decades.

That’s the whole cipher. 4 simple operations, repeated 10 times, with a key schedule that makes sure no 2 rounds use the same key. None of the individual steps are exotic on their own. What makes AES hold up is running all 4 together, round after round, until brute force is the only option left.