07 — Symmetric

AES — One Round, Full Step-by-Step Math

Worked end-to-end using the FIPS-197 Appendix B test vector. Every byte accounted for.

Key sizeRoundsNote
AES-12810This page's vector
AES-19212Stronger key schedule
AES-25614Post-quantum margin (~128-bit vs Grover)

Setup — 128-bit block, 128-bit key, 10 rounds

Plaintext : 32 43 f6 a8 88 5a 30 8d 31 31 98 a2 e0 37 07 34 Cipher Key: 2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c // After Round 0 (initial AddRoundKey with K0 = cipher key): State matrix (column-major, displayed 4x4): 19 a0 9a e9 3d f4 c6 f8 e3 e2 8d 48 be 2b 2a 08

State is filled column-by-column. Each column of 4 bytes = 32 bits = one word.

Step 1 · SubBytes

Each byte replaced via the AES S-box (multiplicative inverse in GF(2⁸) + affine transform b = A·a⁻¹ ⊕ 0x63). The inverse gives non-linearity; the affine map removes algebraic simplicity.

Input S-box Output 19 → S[0x19] = d4 a0 → S[0xa0] = e0 9a → S[0x9a] = b8 e9 → S[0xe9] = 1e 3d → S[0x3d] = 27 f4 → S[0xf4] = bf c6 → S[0xc6] = b4 f8 → S[0xf8] = 41 e3 → S[0xe3] = 11 e2 → S[0xe2] = 98 8d → S[0x8d] = 5d 48 → S[0x48] = 52 be → S[0xbe] = ae 2b → S[0x2b] = f1 2a → S[0x2a] = e5 08 → S[0x08] = 30
State after SubBytes
d4 e0 b8 1e 27 bf b4 41 11 98 5d 52 ae f1 e5 30

Step 2 · ShiftRows

Row r is cyclically shifted left by r positions.

Row 0: d4 e0 b8 1e → d4 e0 b8 1e (no shift) Row 1: 27 bf b4 41 → bf b4 41 27 (shift 1 left) Row 2: 11 98 5d 52 → 5d 52 11 98 (shift 2 left) Row 3: ae f1 e5 30 → 30 ae f1 e5 (shift 3 left)
State after ShiftRows
d4 e0 b8 1e bf b4 41 27 5d 52 11 98 30 ae f1 e5

Step 3 · MixColumns

Each column is treated as a polynomial in GF(2⁸) and multiplied by the fixed matrix:

[ s'0 ] [ 2 3 1 1 ] [ s0 ] [ s'1 ] = [ 1 2 3 1 ] · [ s1 ] [ s'2 ] [ 1 1 2 3 ] [ s2 ] [ s'3 ] [ 3 1 1 2 ] [ s3 ] // GF(2^8), mod x^8+x^4+x^3+x+1 (0x11b) // 2·x : if MSB(x)=0, x<<1. If MSB=1, (x<<1) XOR 0x1b // 3·x = 2·x XOR x

Column 0 — input [d4, bf, 5d, 30]ᵀ

// Precompute 2·d4: d4=11010100 MSB=1 → a8⊕1b = b3 3·d4 = b3⊕d4 = 67 2·bf: bf=10111111 MSB=1 → 7e⊕1b = 65 3·bf = 65⊕bf = da 2·5d: 5d=01011101 MSB=0 → ba 3·5d = ba⊕5d = e7 2·30: 30=00110000 MSB=0 → 60 3·30 = 60⊕30 = 50 s'0 = b3⊕da⊕5d⊕30 = 04 s'1 = d4⊕65⊕e7⊕30 = 66 s'2 = d4⊕bf⊕ba⊕50 = 81 s'3 = 67⊕bf⊕5d⊕60 = e5 Column 0: 04 66 81 e5

Column 1 — [e0, b4, 52, ae]ᵀ

2·e0=db 3·e0=3b 2·b4=73 3·b4=c7 2·52=a4 3·52=f6 2·ae=47 3·ae=e9 s'0=db⊕c7⊕52⊕ae=e0 s'1=e0⊕73⊕f6⊕ae=cb s'2=e0⊕b4⊕a4⊕e9=19 s'3=3b⊕b4⊕52⊕47=9a Column 1: e0 cb 19 9a

Column 2 — [b8, 41, 11, f1]ᵀ

2·b8=6b 3·b8=d3 2·41=82 3·41=c3 2·11=22 3·11=33 2·f1=f9 3·f1=08 s'0=6b⊕c3⊕11⊕f1=48 s'1=b8⊕82⊕33⊕f1=f8 s'2=b8⊕41⊕22⊕08=d3 s'3=d3⊕41⊕11⊕f9=7a Column 2: 48 f8 d3 7a

Column 3 — [1e, 27, 98, e5]ᵀ

2·1e=3c 3·1e=22 2·27=4e 3·27=69 2·98=2b 3·98=b3 2·e5=d1 3·e5=34 s'0=3c⊕69⊕98⊕e5=28 s'1=1e⊕4e⊕b3⊕e5=06 s'2=1e⊕27⊕2b⊕34=26 s'3=22⊕27⊕98⊕d1=4c Column 3: 28 06 26 4c
State after MixColumns
04 e0 48 28 66 cb f8 06 81 19 d3 26 e5 9a 7a 4c

Step 4 · AddRoundKey (K₁) — columns built, then XORed

Round-1 subkey from key expansion: K₁ = a0fafe17 88542cb1 23a33939 2a6c7605

# K1 columns (words → columns): col0 = W4 = a0 fa fe 17 | col1 = W5 = 88 54 2c b1 col2 = W6 = 23 a3 39 39 | col3 = W7 = 2a 6c 76 05 # As 4x4 (column-major): a0 88 23 2a / fa 54 a3 6c / fe 2c 39 76 / 17 b1 39 05 Row 0: 04⊕a0 e0⊕88 48⊕23 28⊕2a = a4 68 6b 02 Row 1: 66⊕fa cb⊕54 f8⊕a3 06⊕6c = 9c 9f 5b 6a Row 2: 81⊕fe 19⊕2c d3⊕39 26⊕76 = 7f 35 ea 50 Row 3: e5⊕17 9a⊕b1 7a⊕39 4c⊕05 = f2 2b 43 49
State after Round 1 — verified vs FIPS-197 ✓
a4 68 6b 02 9c 9f 5b 6a 7f 35 ea 50 f2 2b 43 49

Setup detail — Round 0 (plaintext ⊕ K₀) byte-by-byte

# State is column-major; plaintext and K0 both laid column-wise, then XORed P (cols): 32 88 31 e0 | 43 5a 31 37 | f6 30 98 07 | a8 8d a2 34 K0 (cols): 2b 28 ab 09 | 7e ae f7 cf | 15 d2 15 4f | 16 a6 88 3c State0 : 19 a0 9a e9 | 3d f4 c6 f8 | e3 e2 8d 48 | be 2b 2a 08 # e.g. col0: 32⊕2b=19, 88⊕28=a0, 31⊕ab=9a, e0⊕09=e9 ✓

Theory — GF(2⁸) xtime in 30 seconds

Bytes are polynomials mod m(x)=x⁸+x⁴+x³+x+1 (0x11B). Multiplying by 2 ("xtime") is a left shift plus conditional XOR with 0x1B when the top bit overflows. Multiplying by 3 = xtime ⊕ original. That's every number in the MixColumns traces above.

# xtime examples from Column 0: 0xd4 = 11010100, MSB=1 → (0xd4<<1)=0x1a8, drop 9th bit → 0xa8 ⊕ 0x1b = 0xb3 0x5d = 01011101, MSB=0 → 0x5d<<1 = 0xba (no reduction) 0xbf = 10111111 → (0x17e & 0xff)=0x7e ⊕ 0x1b = 0x65 3·x = 2·x ⊕ x, e.g. 3·0xd4 = 0xb3 ⊕ 0xd4 = 0x67

Theory — S-box anatomy (why 0x19 → 0xd4)

# S(a) = A·a⁻¹ ⊕ 0x63, with inverse in GF(2^8) (0 maps to 0) a = 0x19, a⁻¹ mod m(x) = 0x4e # 0x19·0x4e ≡ 1 affine: b_i = a⁻¹_i ⊕ a⁻¹_(i+4) ⊕ a⁻¹_(i+5) ⊕ a⁻¹_(i+6) ⊕ a⁻¹_(i+7) ⊕ c_i, c=0x63 → b = 0xd4# Inverse kills linearity; affine kills algebraic simplicity (no fixed points).

Full AES shape — 10 rounds, last one special

Round 0: AddRoundKey(K0) Rounds 1–9: SubBytes → ShiftRows → MixColumns → AddRoundKey(K_r) Round 10 (final): SubBytes → ShiftRows → AddRoundKey(K10) # NO MixColumns # Decryption inverts each step in reverse with Inv- versions: InvShiftRows → InvSubBytes → AddRoundKey → InvMixColumns (Equivalent Inverse Cipher)

Theory — where does K₁ come from? (key expansion, full W₄–W₁₁)

# AES-128: 4 words → 44 words. W0..W3 = key columns: W0=2b7e1516 W1=28aed2a6 W2=abf71588 W3=09cf4f3c # Rule: if i mod 4==0, temp=SubWord(RotWord(W[i-1]))⊕Rcon; else temp=W[i-1]; W[i]=W[i-4]⊕temp # Rcon[1]=01, Rcon[2]=02 (powers of 2 in GF(2^8)) # --- W4 (Rcon[1]) --- Rot(09cf4f3c)=cf4f3c09 → Sub=8a84eb01 → ⊕01 = 8b84eb01 W4 = 2b7e1516⊕8b84eb01 = a0fafe17 # 2b⊕8b=a0 7e⊕84=fa 15⊕eb=fe 16⊕01=17 W5 = 28aed2a6⊕a0fafe17 = 88542cb1 W6 = abf71588⊕88542cb1 = 23a33939 W7 = 09cf4f3c⊕23a33939 = 2a6c7605 K1 = a0fafe17 88542cb1 23a33939 2a6c7605# --- W8 (Rcon[2]) --- Rot(2a6c7605)=6c76052a → Sub=50386be5 → ⊕02 = 52386be5 W8 =a0fafe17⊕52386be5 = f2c295f2 W9 =88542cb1⊕f2c295f2 = 7a96b943 W10=23a33939⊕7a96b943 = 5935807a W11=2a6c7605⊕5935807a = 7359f67f K2 = f2c295f2 7a96b943 5935807a 7359f67f (FIPS-197 ✓)
Deep-dive → 07B
Every byte of Round 1 (S-box coordinates, ShiftRows index map, MixColumns binary longhand + intermediate XORs, AddRoundKey per byte) is traced on 07B · AES Round 1 Every Step Expanded.
Design rationale
SubBytes = non-linearity (vs linear/differential cryptanalysis) · ShiftRows + MixColumns = diffusion (avalanche) · AddRoundKey = confusion (mixes key). Final round omits MixColumns (symmetry for decryption).
Exam one-liner: SubBytes confuses, ShiftRows+MixColumns diffuse, AddRoundKey keying — verify Round-1 state a4 68 6b 02….
← Prev
06 · Playfair Cipher
Next →
07B · AES Round 1