Luke a Pro

Luke Sun

Developer & Marketer

๐Ÿ‡บ๐Ÿ‡ฆ

How AES Works (No Math Required)

| , 9 minutes reading.

1. Why Should You Care?

Every time you:

  • Visit an HTTPS website
  • Send messages via WhatsApp or Signal
  • Store files on an encrypted drive
  • Connect to a server via SSH

Youโ€™re using AES.

But most developersโ€™ understanding of AES stops at โ€œcall the library function.โ€ This article aims to help you understand what AES does internallyโ€”no mathematical proofs required, just intuitive understanding.

Understanding how AES works helps you:

  • Choose the right mode of operation (ECB vs CBC vs GCM)
  • Understand why certain configurations are dangerous
  • Know where to look when debugging

2. Definition

AES (Advanced Encryption Standard) is a symmetric block cipher algorithm selected by NIST in 2001 as the replacement for DES.

Technical specifications:

  • Block size: Fixed at 128 bits (16 bytes)
  • Key length: 128, 192, or 256 bits
  • Rounds: 10, 12, or 14 (depending on key length)
  • Structure: SPN (Substitution-Permutation Network)

AES was originally called Rijndael (pronounced roughly like โ€œRain-dollโ€), designed by Belgian cryptographers Vincent Rijmen and Joan Daemen.

3. SPN vs Feistel: Structural Differences

Feistel (Used by DES)

Each round processes only half the data:
L' = R
R' = L โŠ• F(R, K)

Pros: Encryption and decryption share the same circuit
Cons: Slower diffusion, requires more rounds

SPN (Used by AES)

Each round processes all data:
State' = MixColumns(ShiftRows(SubBytes(State))) โŠ• RoundKey

Pros: Fast diffusion, fewer rounds for same security
Cons: Encryption and decryption need different operations (inverses)

4. AES State Matrix

AES organizes 16 bytes of input into a 4ร—4 byte matrix:

Input: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

State matrix:
โ”Œโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”
โ”‚ 00 โ”‚ 04 โ”‚ 08 โ”‚ 0C โ”‚
โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ค
โ”‚ 01 โ”‚ 05 โ”‚ 09 โ”‚ 0D โ”‚
โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ค
โ”‚ 02 โ”‚ 06 โ”‚ 0A โ”‚ 0E โ”‚
โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ค
โ”‚ 03 โ”‚ 07 โ”‚ 0B โ”‚ 0F โ”‚
โ””โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”˜

Note: Filled by columns, not rows!

All encryption operations work on this matrix.

5. The Four Steps of Each AES Round

Each round (except the last) performs these four operations:

Step 1: SubBytes (Byte Substitution)

Each byte is substituted through a lookup table called the S-Box.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Input byte โ†’ Lookup โ†’ Output byte           โ”‚
โ”‚                                             โ”‚
โ”‚ Example: 0x53 โ†’ S-Box[0x53] โ†’ 0xED          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Why is this step needed?

This is the only non-linear operation in AES. Without it, AES would be just linear operations (XOR, shifts, multiplication) that could be solved with linear algebra.

The S-Box is designed based on multiplicative inverses in finite fields, with good cryptographic properties:

  • No fixed points (no x where S(x) = x)
  • No opposite fixed points (no x where S(x) = x โŠ• 0xFF)
  • Highly non-linear

Step 2: ShiftRows (Row Shifting)

Each row of the matrix is cyclically shifted left by different amounts:

Row 0: No shift
Row 1: Shift left by 1
Row 2: Shift left by 2
Row 3: Shift left by 3

Before:                   After:
โ”Œโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”
โ”‚ 00 โ”‚ 04 โ”‚ 08 โ”‚ 0C โ”‚    โ”‚ 00 โ”‚ 04 โ”‚ 08 โ”‚ 0C โ”‚
โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ค    โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ค
โ”‚ 01 โ”‚ 05 โ”‚ 09 โ”‚ 0D โ”‚ โ†’  โ”‚ 05 โ”‚ 09 โ”‚ 0D โ”‚ 01 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ค    โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ค
โ”‚ 02 โ”‚ 06 โ”‚ 0A โ”‚ 0E โ”‚    โ”‚ 0A โ”‚ 0E โ”‚ 02 โ”‚ 06 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ค    โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ค
โ”‚ 03 โ”‚ 07 โ”‚ 0B โ”‚ 0F โ”‚    โ”‚ 0F โ”‚ 03 โ”‚ 07 โ”‚ 0B โ”‚
โ””โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”˜

Why is this step needed?

It ensures bytes from each column get distributed to different columns in the next round. This provides diffusionโ€”a change in one input bit affects the entire output.

Step 3: MixColumns (Column Mixing)

Each column is treated as a polynomial and multiplied by a fixed polynomial (in the GF(2โธ) finite field):

โ”Œโ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”
โ”‚ aโ‚€ โ”‚     โ”‚ 02 03 01 01 โ”‚     โ”‚ bโ‚€ โ”‚
โ”‚ aโ‚ โ”‚  ร—  โ”‚ 01 02 03 01 โ”‚  =  โ”‚ bโ‚ โ”‚
โ”‚ aโ‚‚ โ”‚     โ”‚ 01 01 02 03 โ”‚     โ”‚ bโ‚‚ โ”‚
โ”‚ aโ‚ƒ โ”‚     โ”‚ 03 01 01 02 โ”‚     โ”‚ bโ‚ƒ โ”‚
โ””โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”˜

Why is this step needed?

This is another source of diffusion. It ensures each byte in a column affects all bytes in that column. Combined with ShiftRows, after a few rounds every input bit affects every output bit.

Step 4: AddRoundKey (Round Key Addition)

The state matrix is XORed with the roundโ€™s subkey:

State' = State โŠ• RoundKey

Why is this step needed?

This is where key material is introduced. Without this, encryption would be key-independentโ€”anyone could โ€œdecrypt.โ€

6. Complete AES Flow

Plaintext (16 bytes)
    โ”‚
    โ–ผ
AddRoundKey (Initial round key)
    โ”‚
    โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Repeat N-1 rounds:      โ”‚
โ”‚   SubBytes              โ”‚
โ”‚   ShiftRows             โ”‚
โ”‚   MixColumns            โ”‚
โ”‚   AddRoundKey           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚
    โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Final round (no         โ”‚
โ”‚ MixColumns):            โ”‚
โ”‚   SubBytes              โ”‚
โ”‚   ShiftRows             โ”‚
โ”‚   AddRoundKey           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚
    โ–ผ
Ciphertext (16 bytes)

N = 10 (AES-128), 12 (AES-192), 14 (AES-256)

Why no MixColumns in the final round? This makes encryption and decryption more symmetricโ€”decryptionโ€™s first round also has no MixColumns.

7. Key Expansion

AES needs to generate a subkey for each round. This is done through the key expansion algorithm:

Original Key (128/192/256 bits)
    โ”‚
    โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Key expansion algorithm:                โ”‚
โ”‚   - Uses S-Box                          โ”‚
โ”‚   - Uses round constants (Rcon)         โ”‚
โ”‚   - Each round key depends on previous  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚
    โ–ผ
11/13/15 round keys (128 bits each)

Key expansion ensures:

  • Any bit change in the original key affects multiple round keys
  • Cannot derive other round keys from one round key (without knowing the original key)

8. Why 128-Bit Blocks?

Security Considerations

64-bit blocks (DES): Collision after 2ยณยฒ blocks (~32GB)
128-bit blocks (AES): Collision after 2โถโด blocks (~256 EB)

128-bit blocks let you safely process massive amounts of data without worrying about birthday attacks.

Performance Considerations

Modern CPU registers: 64 bits or larger
128 bits = 2 ร— 64-bit operations
256-bit blocks would be slower with diminishing returns

128 bits is the sweet spot between security and performance.

9. AES Security

Current Status

AES-128: Secure
AES-192: Secure
AES-256: Secure

Best known attacks:
- AES-128 complexity reduced from 2ยนยฒโธ to about 2ยนยฒโถยทยน
- This is still infeasible in practice
- No practical break exists
If attackers can encrypt with multiple related keys:
AES-256 may be more vulnerable than AES-128

But in real applications:
- Keys should be random
- "Related keys" don't exist
- AES-256 is still secure

Side-Channel Attacks

AES itself is secure, but implementations may leak information:
- Timing attacks: Different operations take different time
- Cache attacks: S-Box lookup cache behavior
- Power analysis: Power consumption correlates with data

Defenses:
- Use hardware acceleration (AES-NI)
- Constant-time implementations
- Don't implement AES yourself

10. Code Example: Basic AES Usage

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import os

def aes_encrypt_block(plaintext: bytes, key: bytes) -> bytes:
    """
    Encrypt a single block using AES-ECB
    (For understanding only, NEVER use ECB in production!)
    """
    if len(plaintext) != 16:
        raise ValueError("AES block must be 16 bytes")
    if len(key) not in (16, 24, 32):
        raise ValueError("AES key must be 16, 24, or 32 bytes")

    cipher = Cipher(algorithms.AES(key), modes.ECB())
    encryptor = cipher.encryptor()
    return encryptor.update(plaintext) + encryptor.finalize()

def aes_decrypt_block(ciphertext: bytes, key: bytes) -> bytes:
    """
    Decrypt a single block using AES-ECB
    """
    cipher = Cipher(algorithms.AES(key), modes.ECB())
    decryptor = cipher.decryptor()
    return decryptor.update(ciphertext) + decryptor.finalize()

# Demonstration
if __name__ == "__main__":
    # Generate random key
    key = os.urandom(32)  # AES-256

    # Plaintext must be 16 bytes
    plaintext = b"Hello, AES-256!!"

    # Encrypt and decrypt
    ciphertext = aes_encrypt_block(plaintext, key)
    decrypted = aes_decrypt_block(ciphertext, key)

    print(f"Plaintext:  {plaintext}")
    print(f"Ciphertext: {ciphertext.hex()}")
    print(f"Decrypted:  {decrypted}")

11. Common Misconceptions

MisconceptionReality
โ€AES-256 is twice as secure as AES-128โ€AES-256 has 2^256 keys, which is 2^128 times more than AES-128, but both are unbreakable in practice
โ€AES encryption is secureโ€AES block encryption is secure, but mode of operation matters equally (ECB is insecure)
โ€œLonger keys are always betterโ€For quantum computers, AES-256 is indeed better. But for classical computers, AES-128 is sufficient
โ€AES decryption is slower than encryptionโ€With hardware acceleration, theyโ€™re the same speed

12. Summary

Three things to remember:

  1. AES uses SPN structure. The four steps per round (SubBytes, ShiftRows, MixColumns, AddRoundKey) each serve a purpose: non-linearity, diffusion, more diffusion, introduce key.

  2. 128-bit blocks solve DESโ€™s birthday attack problem. You can safely encrypt EB-scale data with the same key without worrying about collisions.

  3. AES itself is secure, but how you use it matters. Choosing the right mode (GCM, CBC+HMAC) is more important than choosing AES-128 vs AES-256.

13. Whatโ€™s Next

We understand how AES encrypts a single block. But real-world data is rarely exactly 16 bytes. How do we encrypt arbitrary-length data?

In the next article, weโ€™ll explore: AES modes of operationโ€”why ECB is a disaster, what to watch for with CBC, and why GCM has become the modern default choice.