Skip to main content
Modern Block Cipher Intermediate

The Complete Guide to AES Encryption

AES is the encryption algorithm that secures nearly everything: HTTPS traffic, encrypted disks, messaging apps, and government secrets alike. This comprehensive guide breaks down exactly how the Rijndael cipher transforms data, round by round.

PL
Pashalis Laoutaris
August 4, 2026
17 min read

Interactive AES Visualizer

🔐 AES Encryption

6
This is AES-128 (10 rounds). Defaults are the official FIPS-197 test vector.
Enter text and click a button to start!
Round: / 10
Click Encrypt to run all 10 rounds.

The Advanced Encryption Standard (AES) is, without exaggeration, the single most widely used encryption algorithm on Earth. It protects your HTTPS traffic, your encrypted phone storage, your Wi-Fi connection, your password manager’s vault, and classified government communications — all with the same publicly documented, thoroughly scrutinized algorithm. This guide walks through exactly how AES transforms sixteen bytes of plaintext into sixteen bytes of ciphertext, one round at a time.

Table of Contents

  1. Introduction
  2. Historical Context: The AES Competition
  3. Mathematical Foundations
  4. The AES State: Representing Data as a Matrix
  5. How AES Works: The Round Structure
  6. Key Expansion
  7. A Worked Example
  8. Modes of Operation
  9. Security Analysis and Threat Models
  10. Common Vulnerabilities and Mitigation Strategies
  11. Key Sizes and Performance
  12. Implementation Considerations
  13. Real-World Applications
  14. AES vs. Other Block Ciphers
  15. Quantum Computing and AES
  16. Practical Code Implementation
  17. Frequently Asked Questions
  18. Conclusion
  19. References and Further Reading

Introduction

Unlike RSA or Diffie-Hellman, AES is a symmetric algorithm: the same secret key both encrypts and decrypts. It’s also a block cipher — it operates on fixed-size 128-bit (16-byte) chunks of data at a time, applying a sequence of well-defined mathematical transformations repeatedly (“rounds”) until the plaintext block is thoroughly scrambled into ciphertext that reveals nothing about the original data without the key.

AES is fast (it has dedicated hardware instructions — AES-NI — on nearly every modern CPU), well-studied (it has survived over two decades of intense public cryptanalysis), and flexible (it supports 128, 192, or 256-bit keys depending on the security margin required). It is, by a wide margin, the default choice whenever symmetric encryption is needed.

Historical Context: The AES Competition

By the mid-1990s, the aging Data Encryption Standard (DES), with its 56-bit key, was clearly inadequate — it had been publicly broken by brute force in under 24 hours by 1999. Rather than have a single agency design a replacement behind closed doors, the U.S. National Institute of Standards and Technology (NIST) ran an open, public competition, starting in 1997.

Fifteen candidate algorithms were submitted by cryptographers from around the world. Over several years, the global cryptographic community publicly attacked, analyzed, and debated each candidate’s security and performance. In 2000, NIST selected the winner: Rijndael, designed by two Belgian cryptographers, Joan Daemen and Vincent Rijmen (the name is a portmanteau of their surnames). It was formally standardized as FIPS-197 in 2001, becoming the Advanced Encryption Standard.

This open, competitive design process — repeated later for SHA-3 and again for post-quantum cryptography — is now considered the gold standard for developing cryptographic algorithms, precisely because AES has held up so well against decades of subsequent cryptanalysis.

Mathematical Foundations

AES’s transformations operate within GF(2⁸), the Galois Field with 256 elements — essentially, arithmetic on single bytes where addition is XOR and multiplication follows special rules that “wrap around” using an irreducible polynomial (specifically, x⁸ + x⁴ + x³ + x + 1). This might sound abstract, but the practical upshot is simple: every byte-level operation inside AES (the S-box substitution, the MixColumns diffusion step) is really just carefully chosen finite-field arithmetic, chosen specifically because it resists the two classical attacks on block ciphers — linear and differential cryptanalysis — far better than ordinary arithmetic would.

You don’t need to compute GF(2⁸) multiplication by hand to understand AES conceptually; what matters is why it’s used: it gives every transformation strong mathematical guarantees against known attack techniques, guarantees that were rigorously analyzed during the AES competition.

The AES State: Representing Data as a Matrix

AES treats each 128-bit (16-byte) block of data as a 4×4 matrix of bytes, called the state, filled in column-major order:

[ b0  b4  b8  b12 ]
[ b1  b5  b9  b13 ]
[ b2  b6  b10 b14 ]
[ b3  b7  b11 b15 ]

Every transformation in AES — substitution, row shifting, column mixing, key addition — operates on this 4×4 grid. This is exactly what the interactive visualizer above renders: watch the state matrix evolve as each transformation is applied.

How AES Works: The Round Structure

AES processes data through a fixed number of rounds, depending on key size:

  • AES-128: 10 rounds
  • AES-192: 12 rounds
  • AES-256: 14 rounds

Each round (except the last) consists of four transformations, applied in sequence:

1. SubBytes (Substitution)

Every byte in the state is replaced with a different byte, looked up in a fixed, publicly known 256-entry lookup table called the S-box. The S-box is constructed from the multiplicative inverse in GF(2⁸) followed by an affine transformation, specifically designed to have no simple algebraic relationship between input and output — this is what makes AES resistant to linear and differential cryptanalysis. This is the only non-linear step in AES, and it’s essential: without it, AES would just be a (breakable) linear function of the key.

2. ShiftRows

Each row of the state matrix is cyclically shifted left by an amount equal to its row index: row 0 isn’t shifted, row 1 shifts by 1 byte, row 2 by 2 bytes, row 3 by 3 bytes. This spreads byte values across columns, ensuring that the columns processed by MixColumns aren’t independent of each other from round to round.

3. MixColumns

Each column of the state is treated as a 4-term polynomial and multiplied (in GF(2⁸)) by a fixed polynomial, implemented as a matrix multiplication:

[ 02 03 01 01 ]   [ b0 ]
[ 01 02 03 01 ] × [ b1 ]
[ 01 01 02 03 ]   [ b2 ]
[ 03 01 01 02 ]   [ b3 ]

This step provides diffusion — it ensures that a change to a single byte spreads across an entire column, and combined with ShiftRows’ row-mixing, a single changed input bit affects nearly every output bit within a few rounds (the “avalanche effect”). MixColumns is skipped in the final round.

4. AddRoundKey

The state is XORed with a 128-bit round key, derived from the main encryption key via the key expansion process described below. This is the only step where the secret key actually enters the computation — every other transformation is public and identical for every AES operation regardless of key.

Putting It Together

A full AES-128 encryption looks like this:

  1. Initial round: AddRoundKey (using the original key, before any rounds)
  2. Rounds 1–9: SubBytes → ShiftRows → MixColumns → AddRoundKey
  3. Round 10 (final): SubBytes → ShiftRows → AddRoundKey (no MixColumns)

Decryption reverses this exact process using inverse transformations (InvSubBytes, InvShiftRows, InvMixColumns) and the round keys in reverse order.

Key Expansion

AES doesn’t reuse a single key for every round — it derives a unique round key for each round (plus one extra for the initial AddRoundKey) from the original cipher key, through a process called the Rijndael key schedule. For AES-128, this expands a single 16-byte key into eleven 16-byte round keys (176 bytes total), using a combination of byte rotation, S-box substitution, XOR with round constants, and XOR with previous key material. This ensures that even though the same S-box and matrix operations repeat every round, the actual data transformation is different each time, because the key material mixed in keeps changing.

A Worked Example

The official FIPS-197 standard includes a canonical test vector that every correct AES-128 implementation must reproduce — and it’s exactly what the interactive visualizer above uses as its default input:

  • Key: 000102030405060708090a0b0c0d0e0f
  • Plaintext: 00112233445566778899aabbccddeeff
  • Ciphertext: 69c4e0d86a7b0430d8cdb78070b4c55a

Watch the visualizer step through all ten rounds — SubBytes scrambling each byte via the S-box, ShiftRows rotating rows, MixColumns diffusing columns, and AddRoundKey folding in each round key — to see exactly how those sixteen plaintext bytes transform into that specific ciphertext, deterministically and reproducibly, every single time.

Modes of Operation

AES itself only defines how to transform a single 16-byte block. To encrypt real messages (which are rarely exactly 16 bytes), AES is combined with a mode of operation that defines how successive blocks relate to each other:

  • ECB (Electronic Codebook): Encrypts each block independently. Never use this — identical plaintext blocks produce identical ciphertext blocks, leaking structural patterns (the infamous “ECB penguin” image is the classic demonstration).
  • CBC (Cipher Block Chaining): XORs each plaintext block with the previous ciphertext block before encrypting, using a random initialization vector (IV) for the first block. Widely used, but requires careful padding and doesn’t parallelize encryption.
  • CTR (Counter Mode): Turns AES into a stream cipher by encrypting a counter value and XORing the result with plaintext. Fully parallelizable and doesn’t require padding.
  • GCM (Galois/Counter Mode): CTR mode combined with a built-in authentication tag (see the dedicated AES-GCM guide for details) — the modern default for authenticated encryption, used throughout TLS.

Choosing the wrong mode (especially ECB) is one of the most common real-world AES implementation mistakes — the algorithm itself being secure doesn’t help if it’s used in an insecure mode.

Security Analysis and Threat Models

After more than two decades of public cryptanalysis by the world’s cryptographic community, no practical attack exists against full-round AES at any key size. The best known academic attacks (such as biclique cryptanalysis) reduce the effective security margin only marginally — from 2¹²⁸ to roughly 2¹²⁶·¹ operations for AES-128, for example — which remains astronomically infeasible and doesn’t represent a practical threat.

AES’s real-world security risks come almost entirely from implementation issues rather than the algorithm itself:

  • Side-channel attacks: Naive software implementations of the S-box lookup can leak information through CPU cache-timing differences, potentially allowing key recovery. This is why hardware AES-NI instructions (which perform S-box lookups without data-dependent memory access patterns) are strongly preferred.
  • Weak modes of operation: As covered above, using ECB mode or reusing an IV/nonce in CBC or CTR/GCM mode can catastrophically undermine security regardless of how strong AES itself is.
  • Key management failures: A perfectly implemented AES system is worthless if the key is weak, predictable, or improperly stored.

Common Vulnerabilities and Mitigation Strategies

Issue Description Mitigation
ECB mode usage Identical plaintext blocks produce identical ciphertext, leaking structure. Never use ECB for anything beyond single, independent, high-entropy blocks; use CBC, CTR, or GCM instead.
IV/nonce reuse Reusing an initialization vector or nonce in CBC/CTR/GCM catastrophically weakens (or completely breaks) confidentiality and, for GCM, authenticity. Always generate a fresh, unique IV/nonce per encryption operation; use random or properly managed counters.
Cache-timing side channels Table-lookup-based software S-box implementations can leak key bits via CPU cache access patterns. Use hardware AES-NI instructions or constant-time / bitsliced software implementations.
Padding oracle attacks (CBC mode) If a system reveals whether decrypted padding was valid, attackers can decrypt CBC ciphertext without the key (e.g., the POODLE and Lucky 13 attacks against TLS). Use authenticated encryption (AES-GCM) instead of CBC-with-padding wherever possible; if CBC is required, use constant-time padding checks combined with a MAC (encrypt-then-MAC).
Weak key derivation Deriving an AES key directly from a low-entropy password without a proper KDF makes brute-forcing feasible regardless of AES’s strength. Use a dedicated password-based KDF like Argon2 or PBKDF2 to derive keys from passwords.

Key Sizes and Performance

  • AES-128: 10 rounds, offers ~128 bits of security — sufficient for essentially all practical purposes and the fastest of the three variants.
  • AES-192: 12 rounds, offers ~192 bits of security — used less commonly, mostly where standards mandate a specific intermediate security level.
  • AES-256: 14 rounds, offers ~256 bits of security — the choice for long-term, high-assurance, or government/classified use (approved by the NSA for TOP SECRET information).

Because of dedicated AES-NI hardware instructions built into virtually every x86 and ARM processor manufactured in the last decade, AES is extraordinarily fast in practice — often encrypting several gigabytes per second on a single CPU core — which is a major reason it’s used to encrypt bulk data in hybrid systems like TLS and PGP, while slower asymmetric algorithms like RSA or ECC handle only the key exchange.

Implementation Considerations

  • Never implement AES’s math yourself for production use. Use audited libraries (OpenSSL, libsodium, or your platform’s built-in crypto API) that use hardware acceleration and constant-time implementations.
  • Always use an authenticated mode (AES-GCM, or AES-CBC combined with a separate MAC) rather than plain CBC or CTR alone — encryption without authentication is vulnerable to ciphertext manipulation.
  • Never reuse an IV/nonce with the same key, especially in CTR or GCM mode, where reuse can be catastrophic.
  • Derive keys properly from passwords using Argon2 or PBKDF2, never a raw hash of user input.
  • Prefer AES-256 for new systems needing long-term security margins, though AES-128 remains entirely sound for the vast majority of use cases.

Real-World Applications

  • TLS/HTTPS: AES (typically in GCM mode) encrypts the actual data of virtually every secure web connection, after asymmetric algorithms establish the session key.
  • Disk and file encryption: BitLocker, FileVault, LUKS, and VeraCrypt all use AES to encrypt entire drives or containers.
  • Wi-Fi security: WPA2 and WPA3 both use AES (in CCMP mode) to encrypt wireless traffic.
  • VPNs: Protocols like IPsec and OpenVPN use AES to encrypt tunneled traffic.
  • Messaging apps: The Signal Protocol uses AES-GCM (or ChaCha20-Poly1305) for actual message encryption after the Double Ratchet establishes per-message keys.
  • Government and military: AES-256 is approved by the U.S. NSA for encrypting classified information at all levels.

AES vs. Other Block Ciphers

  • DES / 3DES: AES’s predecessors, both now considered inadequate — DES’s 56-bit key is trivially brute-forceable today, and 3DES, while stronger, is far slower and has a small 64-bit block size that creates its own vulnerabilities (the Sweet32 attack) in long-lived connections.
  • Blowfish / Twofish: Bruce Schneier’s block cipher designs. Twofish was an AES competition finalist and remains considered secure, but never achieved AES’s ubiquity or hardware acceleration support.
  • IDEA: Used historically in some PGP implementations, but largely superseded by AES in modern deployments.
  • ChaCha20: Not a block cipher but a stream cipher, and AES’s main modern rival — preferred on devices lacking AES-NI hardware acceleration (older mobile chips), since ChaCha20 is fast in pure software without needing dedicated instructions.

Quantum Computing and AES

Unlike RSA, Diffie-Hellman, and ECC — all of which would be completely broken by a sufficiently powerful quantum computer running Shor’s algorithm — AES is only weakened, not broken, by the best known quantum attack, Grover’s algorithm. Grover’s algorithm provides a quadratic speedup for brute-force key search, effectively halving the security level: AES-128 would offer roughly 64 bits of quantum-adjusted security (weak), while AES-256 would still offer roughly 128 bits — comfortably secure even against quantum adversaries.

This is precisely why security guidance for the post-quantum era recommends migrating to AES-256 for symmetric encryption while asymmetric algorithms are replaced entirely with lattice-based or hash-based post-quantum schemes like CRYSTALS-Kyber and CRYSTALS-Dilithium.

Practical Code Implementation

Implementing AES correctly from scratch (S-box generation, GF(2⁸) arithmetic, key schedule) is a substantial undertaking well outside the scope of “roll your own crypto” — always use audited libraries in production. Here’s how straightforward it looks using Python’s cryptography library with AES-GCM, the recommended mode for new systems:

import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

# Generate a random 256-bit key (do this once, store it securely)
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)

# A nonce MUST be unique for every encryption with the same key — never reuse it
nonce = os.urandom(12)

plaintext = b"This message is encrypted and authenticated."
associated_data = b"optional-metadata-not-encrypted-but-authenticated"

ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
print(f"Ciphertext: {ciphertext.hex()}")

decrypted = aesgcm.decrypt(nonce, ciphertext, associated_data)
print(f"Decrypted:  {decrypted.decode()}")
assert decrypted == plaintext

For a from-scratch educational reference (not for production), Python’s standard library has no built-in AES — but the algorithm’s core round transformation, in simplified pseudocode, looks like this:

def aes_encrypt_block(state, round_keys, num_rounds):
    state = add_round_key(state, round_keys[0])

    for round_num in range(1, num_rounds):
        state = sub_bytes(state)      # S-box substitution
        state = shift_rows(state)     # cyclic row shifts
        state = mix_columns(state)    # GF(2^8) column mixing
        state = add_round_key(state, round_keys[round_num])

    # Final round has no MixColumns
    state = sub_bytes(state)
    state = shift_rows(state)
    state = add_round_key(state, round_keys[num_rounds])

    return state

Frequently Asked Questions

Is AES the same thing as Rijndael?

Almost, but not quite. Rijndael is the original cipher design, which supports a wider range of block and key sizes. AES is the NIST-standardized subset of Rijndael that fixes the block size at 128 bits, with only 128, 192, or 256-bit keys allowed.

Which AES key size should I use?

AES-128 is fast, secure, and sufficient for the overwhelming majority of applications — it has no known practical weaknesses. AES-256 is recommended for long-term data protection, regulatory requirements, or defense against future quantum attacks on symmetric encryption (which, per Grover’s algorithm, only affects security margin, not full breakage).

What’s the difference between AES-CBC and AES-GCM?

CBC provides only confidentiality and requires a separate mechanism (like HMAC) to detect tampering. GCM provides both confidentiality and built-in authentication in a single, efficient pass, which is why it’s the recommended default for new systems — see the dedicated AES-GCM guide.

Can AES be broken by brute force?

Not with any technology that exists or is foreseeable. AES-128 has 2¹²⁸ possible keys — even if every computer on Earth searched keys in parallel at billions of attempts per second, exhausting that keyspace would take vastly longer than the age of the universe.

Why does AES use a 4×4 byte matrix instead of processing data as a simple string of bits?

Arranging data as a matrix lets ShiftRows and MixColumns spread (diffuse) the influence of each byte across the entire block efficiently, which is central to achieving the avalanche effect — small input changes causing large, unpredictable output changes — within just a handful of rounds.

Is AES vulnerable to quantum computers?

Not in the way RSA or ECC are. Grover’s algorithm only halves AES’s effective key strength, so AES-256 remains secure (~128-bit quantum security) even against large-scale quantum computers, unlike RSA/DH/ECC, which Shor’s algorithm would break entirely.

Conclusion

AES represents cryptographic engineering at its finest: a design born from open competition and unprecedented public scrutiny, built on solid mathematical foundations, and battle-tested for over two decades without a practical break. Its combination of security, speed (especially with hardware acceleration), and flexibility across key sizes explains why it quietly secures an enormous fraction of the world’s digital communication and storage — almost always invisibly, exactly as good cryptography should.

References and Further Reading