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.
| Variant | Key Size (bits) | Number of Rounds |
|---|---|---|
| AES-128 | 128 | 10 rounds |
| AES-192 | 192 | 12 rounds |
| AES-256 | 256 | 14 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
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
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 of the state left by bytes.
ShiftRows, row r shifts left by r bytes
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 . The 4 output bytes are each a different weighted combination of all 4 inputs.
is the XOR operator. 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⁸)
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
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: . Remaining 10 rounds’ 40 words are generated incrementally.
Each new word comes from the word 4 positions back, , XORed with something built from the word right before it, . What gets built depends on whether is a multiple of 4.
When is not a multiple of 4, it’s a plain XOR.
When is a multiple of 4, passes through 3 steps first. This is the part that keeps the schedule from repeating.
# RotWord
Cycle the 4 bytes of left by 1. becomes .
# 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 to produce . 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
# 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 / 41This 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.
Flipping bit 0 of byte 0 turns 0x00 into 0x01 in the plaintext.
# Why it is hard to break
There’s no known attack faster than trying keys directly. A 128-bit key means possibilities.
That’s in the order of 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.