cryptography.education / block ciphers / aes

AES, step by step — and what each step is for

A complete AES block cipher in plain client-side JavaScript, walking one 16-byte block through FIPS 197 exactly as the standard writes it: the byte-to-state mapping, all 4 (Nr+1) key-schedule words, the initial AddRoundKey, then every SubBytes, ShiftRows, MixColumns and AddRoundKey of all 10, 12 or 14 rounds. The S-box is derived on this page from GF(2⁸) inversion plus the affine map and checked byte for byte against the published FIPS 197 table. The final ciphertext is compared against your browser's own AES.

Modes of operation: why one block is never the whole story

Everything above encrypts one 16-byte block — that is what AES defines. To encrypt a message you need a mode of operation, and the naive choice (ECB: encrypt each block independently) leaks a great deal. Below, a 32-byte message made of two identical 16-byte blocks is encrypted with the page's own core: first block-by-block (ECB), then chained (CBC) with a fixed IV.

key: 000102…0f (AES-128)

What this demo does show: identical plaintext blocks give identical ciphertext blocks under ECB, and cease to do so under CBC. What it does not show: the rest of ECB's leakage (block reordering, prefix equality across messages, chosen-plaintext block surgery — see the glossary), any realistic message length, or any authentication. CBC here uses a fixed IV purely to keep the arithmetic reproducible; a real CBC implementation uses a fresh random IV, because a fixed IV still leaks where two messages share a prefix. Neither mode is authenticated: without a MAC, ECB/CBC ciphertexts can be modified in transit. Use AES-GCM or ChaCha20-Poly1305 for real work.

Diffusion, measured on this page

A block cipher is secure only if a one-bit input change scrambles the whole block. Below, each of the 16 plaintext bytes is flipped by one bit in turn, and the ciphertext is recomputed for step-reduced versions of AES (1 round, 2 rounds, … ) using the page's own core. The count is how many of the 16 ciphertext bytes change. Each of those rounds is the full round function — SubBytes, ShiftRows, MixColumns, AddRoundKey — because that is the unit "round" means in diffusion analysis; note that the shipped cipher omits MixColumns from its final round, so measuring it the same way would push every count out by one round. This is a diffusion measurement, not a break of AES: the reduced-round variants are illustrative only.

The S-box, derived and asserted

The page's SubBytes transformation uses an S-box that is computed at load time, not pasted in: for each byte x, take its multiplicative inverse in GF(2⁸) (x²⁵⁴, via square-and-multiply, so 0 → 0), then apply the affine map bi = ai ⊕ a(i+4) ⊕ a(i+5) ⊕ a(i+6) ⊕ a(i+7) ⊕ ci with c = 0x63 (indices mod 8). The right-hand column below compares the derived table with the published FIPS 197 Fig. 7 / Table 4, byte for byte. If any row mismatched, it would say so here.

Notation

S-box / S(x)The 256-byte substitution table. One row per high nibble, one column per low nibble: S(0x53) is row 5, column 3 = 0xed.
a b c d e f g hAES has no such registers — that is SHA-256. Here the working object is a 4×4 array of bytes.
StateA 4×4 array of bytes s[r,c]. Every transformation in the cipher is applied to it in place. FIPS 197 §3.4.
in₀ … in₁₅The 16 input bytes. They are copied into the State column by column: s[r,c] = in[r + 4c] (§3.4).
NbBlock length in 32-bit words. For AES this is always 4 (128-bit blocks).
NkKey length in 32-bit words: 4 (128-bit), 6 (192-bit) or 8 (256-bit).
NrNumber of rounds: Nr = Nk + 6, so 10, 12 or 14.
w[i] (words)The expanded key schedule: 4(Nr+1) words, so 44, 52 or 60. Each word is four bytes, drawn as a column of the State.
RotWord / SubWord / RconThe three operations of the key expansion: rotate a word's bytes left by one, apply the S-box to each byte, XOR a round constant (§5.2).
⊕Bitwise XOR = addition in GF(2⁸) (§4.1). Every "•" below is multiplication in GF(2⁸) (§4.2), with modulus m(x) = x⁸+x⁴+x³+x+1 = 0x11B.
{53}FIPS 197 writes bytes in braces as hex. {53} is the byte 0x53 = 83 decimal.
xtime(a)Multiply by {02} in GF(2⁸): left shift, XOR 0x1b if the top bit was set (§4.2.1). The atom of MixColumns.

Glossary

The state matrix — why the bytes go in sideways

The 16 input bytes in₀…in₁₅ are loaded column by column: s[r,c] = in[r + 4c] (FIPS 197 §3.4). So bytes 0,1,2,3 are column 0; bytes 4,5,6,7 are column 1. AES is specified on an array of 32-bit words, and words are columns here (§3.5). The convention matters because ShiftRows moves bytes between columns along each row, and MixColumns then mixes each column — the two together are what spread one byte across the block. Load a block row-by-row by mistake and you have written a different cipher that produces plausible-looking 16-byte output.

Why the S-box is an inverse plus an affine map

The S-box is the composition of two maps. First x ↦ x⁻¹ in GF(2⁸), which is highly nonlinear (degree-7 algebraic normal form) — exactly what a substitution layer needs to resist linear and differential cryptanalysis. Then a fixed affine map over GF(2), which is linear and adds no security by itself. Why bother with the second step, if it is linear? Because the plain inverse has fixed points (1 ↦ 1) and a rigid algebraic structure that makes attacks like interpolation and the algebraic "XSL" family easier to formulate; composing it with an affine map chosen so that the S-box has no fixed points and no inverse-fixed points, and a maximum-difference/maximum-linear-bias profile of 4/32, removes that structure at negligible cost. Security comes from the inverse; hygiene comes from the affine map.

Why MixColumns is not a permutation of bytes

A permutation of the 16 bytes (as ShiftRows is) rearranges information without ever creating new values: a single nonzero byte stays a single nonzero byte, and after any number of rounds only 16 distinct values have appeared. MixColumns instead treats each 4-byte column as a polynomial over GF(2⁸) and multiplies it by the fixed matrix [[02,03,01,01],[01,02,03,01],[01,01,02,03],[03,01,01,02]] (§5.1.3, §4.3). One input byte becomes four different output bytes, so a difference in one byte is spread across the whole column. Combined with ShiftRows, which moves the resulting column's bytes into four different columns, one byte reaches all 16 bytes after two rounds. MixColumns is invertible (that matrix has an inverse over GF(2⁸) — the decryption matrix [[0e,0b,0d,09],…]), so it is a bijection on the 128-bit block, but it is not a permutation of the individual bytes.

Diffusion and confusion, concretely

Confusion means the relationship between the key and the ciphertext is complicated; AES gets it from the S-box (a key-XORed byte then jumps to an unrelated value) and from the key schedule's RotWord/SubWord/Rcon. Diffusion means a change to one input bit spreads everywhere; AES gets it from ShiftRows + MixColumns. The exact spread, and the reason we can state it precisely, is that both layers are linear over the byte-group structure (counting a round as the full round function, MixColumns included): after 1 round a single flipped input byte has become 4 differing bytes (its column, after MixColumns); after 2 rounds all 16 bytes differ, because ShiftRows has moved that column's bytes into four distinct columns and MixColumns has re-expanded each of them. So "every input byte influences every output byte after 2 rounds" — the panel above measures this on the page's own core. The remaining 8–12 rounds are the margin that turns a linear spreading result into resistance to differential, linear and integral cryptanalysis.

What an attack on AES actually looks like

Nobody has found a practical attack on the cipher itself. The exhaustive key search is 2¹²⁸, 2¹⁹² or 2²⁵⁶, and the best known cryptanalysis — biclique key recovery against all 14 rounds of AES-256, but at 2²⁵⁴·⁴ work, and integral/square attacks on step-reduced variants — does not threaten any real key size. Related-key attacks (Biryukov–Khovratovich, 2009) break AES-256 badly, but only under a key-derivation model that well-designed protocols do not use. Related-key and "chosen-key" results are discussed in the literature because they say something about the design's margin, not because they apply to TLS. What actually breaks deployed AES is never the round function:

  • Side channels. A table lookup indexed by a secret-dependent byte leaks through the cache. Bernstein's 2005 attack recovered an AES key from a network server by timing cache misses; the "T-table" implementations that dominated the 2000s are exactly what make this possible. Constant-time bitslicing and AES-NI exist for this reason.
  • Fault attacks. Corrupt one byte of the state during round 8 or 9 with a voltage glitch or a laser, and the resulting faulty/valid ciphertext pair reveals key bytes with a handful of tries. Relevant mainly to smart cards and embedded devices.
  • Bad modes and protocols. ECB leaks structure; CBC without a MAC is malleable by an active attacker; a padding oracle turns a CBC decryption error into a plaintext-recovery oracle (BEAST, Lucky 13, POODLE). These are the attacks behind most real-world AES failures.
  • Bad key management. A 128-bit key derived from a password is a password. Weak entropy, key reuse across sessions, and nonce reuse in GCM are all more common than any of the above.
Why Rcon starts at 1 and why 0x1b appears in it

Rcon[i] = [xi−1, {00}, {00}, {00}] in GF(2⁸) (§5.2). Since x is the byte {02}, xi−1 is just xtime applied i−1 times: {01}, {02}, {04}, {08}, {10}, {20}, {40}, {80}, {1b}, {36}. The ninth one is the giveaway — {80} doubled is {100}, which reduces modulo 0x11B to {1b}. The index starts at 1 because Rcon[0] would be {01}, the same as the identity, and the round constants only ever enter at i mod Nk = 0 — there are at most ⌈4(Nr+1)/Nk⌉ − 1 = Nr of them, so ten values suffice for the 256-bit case and seven for 128-bit.

Why not just run AES more times?

You can — that is what a substitution-permutation network buys you, and it is roughly what AES with 14 rounds already is. But iterating a cipher with the same key (a "cascade") does not multiply security the way you would hope: the composition of two ciphers over the same key space is still a map over that key space, so double-AES has a meet-in-the-middle attack at 2¹²⁸ instead of the 2²⁵⁶ you asked for, and triple-DES (2×56 = 112-bit effective) is the textbook demonstration. Running AES-256 twice does not give you 512-bit security; the practical answer is AES-256 with a good mode.