AES (Advanced Encryption Standard)
Overview
AES (Advanced Encryption Standard) is a symmetric block cipher algorithm that has been the standard for encrypting data since 2001. It was selected by the U.S. National Institute of Standards and Technology (NIST) to replace DES and is now used worldwide for securing sensitive data.
AES operates on fixed-size blocks of data (128 bits) and supports key sizes of 128, 192, or 256 bits. It's fast, secure, and efficient, making it suitable for both software and hardware implementations.
AES is the most heavily-analysed symmetric cipher ever designed and the most widely deployed piece of cryptographic code in the world. If you use HTTPS, WPA2/WPA3 Wi-Fi, an SSD with hardware encryption, an iPhone or Android device with disk encryption, a modern VPN, Signal, WhatsApp, or almost any product with the word "encrypted" in its marketing, AES is running under the hood. Its selection as the US federal standard in 2001 was the result of a five-year open cryptographic competition, the largest such competition ever held, with 15 initial candidates, 5 finalists, and thousands of cryptographers worldwide scrutinising the designs. The eventual winner, Rijndael, has withstood 25 years of subsequent public analysis without any practical attack being found.
How AES Was Chosen: The NIST Competition
By the mid-1990s DES had become obviously inadequate, its 56-bit key was too small for modern attacks, as EFF's Deep Crack machine demonstrated in 1998 by breaking DES in 56 hours for $250,000. NIST needed a successor and made a decision that permanently shaped the field: rather than commission a design internally (as had happened with DES), NIST held an open, international competition. Anyone could submit a candidate, all submissions and cryptanalysis would be public, and the winner would be chosen on security and performance merits alone.
The Advanced Encryption Standard Development Effort was announced in January 1997. NIST received 15 candidate ciphers from cryptographers in 12 countries. After two rounds of public analysis at three international workshops, the field was narrowed to five finalists in August 1999: MARS (IBM), RC6 (RSA Laboratories), Rijndael (two Belgian cryptographers, Joan Daemen and Vincent Rijmen), Serpent (Ross Anderson, Eli Biham, Lars Knudsen), and Twofish (Bruce Schneier and team). All five were considered secure at the time of selection.
NIST chose Rijndael in October 2000 on the basis of a combination of security margin, software performance across a wide range of platforms (32-bit CPUs, 8-bit smart cards, hardware), and simplicity. Serpent was the most conservative design and had the largest security margin; Rijndael was faster and simpler and had adequate margin. In hindsight Rijndael was the correct choice, the intervening 25 years have not narrowed its security margin significantly, while its performance has proven exceptionally robust across hardware generations that did not exist at the time of the competition. On modern x86 processors with AES-NI hardware acceleration, AES encrypts data faster than memcpy on many systems, a level of performance nobody predicted in 2001.
Rijndael was standardised as FIPS 197 on 26 November 2001. The name "AES" formally refers to a subset of Rijndael: Rijndael supports variable block sizes and key sizes, but AES fixes the block size at 128 bits and permits only three key sizes (128, 192, 256). In practice everyone uses "AES" and "Rijndael" interchangeably.
The Structure: A Substitution-Permutation Network
AES is a substitution-permutation network (SPN), a well-established cipher design pattern that alternates two kinds of operation: substitutions that introduce non-linearity, and permutations that mix bits across the whole block. The intuition, dating back to Claude Shannon's 1949 paper "Communication Theory of Secrecy Systems," is that a good cipher needs both confusion (the relationship between the key and the ciphertext should be complex and non-linear) and diffusion (each ciphertext bit should depend on many key bits and many plaintext bits, so that small input changes propagate across the whole output). AES's four operations are designed to provide these two properties.
The internal state of AES is a 4×4 matrix of bytes, 128 bits arranged as 16 bytes in a two-dimensional grid. Every round applies four operations to this state:
- SubBytes replaces each byte with its output from a fixed substitution table called the S-box. The S-box is the algorithm's main source of non-linearity, and it is not a random table, it is defined by the multiplicative inverse in the Galois field GF(28) followed by an affine transformation. This algebraic structure was chosen specifically to resist differential and linear cryptanalysis, the two attack techniques that had broken DES.
- ShiftRows cyclically shifts the rows of the state matrix by different offsets. Row 0 is not shifted, row 1 is shifted by 1 byte, row 2 by 2, row 3 by 3. This spreads the effect of one input byte across all four columns of subsequent state, contributing to diffusion.
- MixColumns treats each column as a polynomial over GF(28) and multiplies it by a fixed polynomial. This mixes bits within each column and, combined with ShiftRows in the previous step, ensures that after two rounds every output bit depends on every input bit. This is the operation that gives AES its strong avalanche property.
- AddRoundKey XORs the state with the current round key. This is the only operation that involves the key material and is what actually makes the transformation depend on the key.
The final round omits MixColumns for a subtle reason: without this asymmetry, the last two rounds would be functionally equivalent to a single round, and the algorithm's security margin would be smaller than the round count suggests. The number of rounds, 10, 12, or 14 for 128, 192, 256-bit keys respectively, was chosen to give a comfortable security margin above the number of rounds that any known attack can break. Reduced-round variants of AES have been broken academically (7-round AES-128 has been attacked with impractical but non-brute-force complexity), but the full version has not.
The Key Schedule
AES's key schedule is the algorithm that expands the input key into a sequence of round keys, one per round plus one for the initial AddRoundKey. For AES-128, a single 128-bit input key expands into 11 128-bit round keys, or 1408 bits of round-key material total. The expansion uses the same S-box and a small "round constant" table to break symmetry between rounds.
Key schedules are often an under-appreciated part of a cipher's design. A bad key schedule can leak information about the key across rounds and enable related-key attacks. AES's key schedule is not perfect, academic related-key attacks exist against AES-192 and AES-256 (Biryukov and Khovratovich, 2009) with complexity around 2176 and 299.5 respectively. AES-128's key schedule is not known to be susceptible. Related-key attacks require an unrealistic capability: the attacker must be able to encrypt data under a chosen relationship between two unknown keys, a scenario no sane protocol permits. The attacks are theoretically important but do not represent a practical threat.
How It Works
AES uses a substitution-permutation network (SPN) structure:
- Key Expansion: Derives round keys from the original key
- Initial Round: AddRoundKey operation
- Main Rounds: Repeated application of:
- SubBytes - Substitution using S-box
- ShiftRows - Permutation of rows
- MixColumns - Mixing of columns
- AddRoundKey - XOR with round key
- Final Round: Same as main rounds but without MixColumns
AES Algorithm
AES_Encrypt(plaintext, key):
state = plaintext
expanded_key = KeyExpansion(key)
AddRoundKey(state, expanded_key[0])
for round = 1 to Nr - 1:
SubBytes(state)
ShiftRows(state)
MixColumns(state)
AddRoundKey(state, expanded_key[round])
SubBytes(state)
ShiftRows(state)
AddRoundKey(state, expanded_key[Nr])
return state
Implementation
Note: implementing AES yourself is a bad idea, constant-time implementation is genuinely hard, and getting it wrong leaks keys through cache timing. Use a vetted library. The example below uses AES-GCM, an authenticated mode (AEAD), which is what you should reach for by default:
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
def aes_encrypt(plaintext, key, associated_data=None):
"""AES-256-GCM: encrypts AND authenticates."""
nonce = os.urandom(12) # 96-bit nonce, NEVER reused with the same key
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
return nonce + ciphertext # tag is appended to ciphertext by the library
def aes_decrypt(blob, key, associated_data=None):
"""Raises InvalidTag if the ciphertext was modified. Do not catch and ignore it."""
nonce, ciphertext = blob[:12], blob[12:]
aesgcm = AESGCM(key)
return aesgcm.decrypt(nonce, ciphertext, associated_data)
# Example usage
key = AESGCM.generate_key(bit_length=256)
plaintext = b"Hello, AES Encryption!" # any length - GCM is a stream mode, no padding
blob = aes_encrypt(plaintext, key)
assert aes_decrypt(blob, key) == plaintext
Two things this example fixes that a naive CBC version gets wrong. First, GCM is a stream mode, so
plaintext of any length works, CBC requires block-aligned input and would raise
ValueError: The length of the provided data is not a multiple of the block length on the
22-byte string above unless you add PKCS#7 padding. Second, and more importantly, GCM
authenticates. Raw CBC does not: an attacker who cannot read your ciphertext can
still flip bits in it and change the decrypted plaintext in predictable ways, and if your
application reveals whether padding was valid, a padding oracle attack recovers the
plaintext outright.
The one rule for GCM: never reuse a nonce with the same key. Nonce reuse in GCM is catastrophic, it leaks the XOR of the two plaintexts and the authentication subkey, which lets an attacker forge arbitrary messages. Generate nonces randomly (96 bits gives ample margin) or use a strict counter. If you cannot guarantee nonce uniqueness, use AES-GCM-SIV, which degrades gracefully on reuse.
Specifications
| Key Size | Block Size | Number of Rounds | Security Level |
|---|---|---|---|
| 128 bits | 128 bits | 10 | High |
| 192 bits | 128 bits | 12 | Very High |
| 256 bits | 128 bits | 14 | Very High |
Security
AES is considered highly secure when properly implemented:
- No known practical attacks against full AES. The best published result is the 2011 biclique attack at roughly 2126.1 for AES-128, a factor of about four better than brute force, which is to say no threat at all.
- Related-key attacks exist against the full AES-192 and AES-256 key schedules (Biryukov & Khovratovich, 2009), but they require the attacker to control relationships between keys, which no sane protocol permits.
- Widely analyzed and tested. AES has had more cryptanalytic attention than any other cipher.
- Approved for classified information at AES-256 (CNSSP-15). CNSA 2.0 now mandates AES-256 alongside post-quantum algorithms.
- Implementation is the real risk. The algorithm is sound; the attacks that succeed in practice are side-channel attacks against cache-timing-vulnerable software implementations, and key-management failures.
Best practices:
- Use an AEAD mode. AES-GCM, AES-GCM-SIV, or ChaCha20-Poly1305. These encrypt and authenticate in one step. Unauthenticated modes (CBC, CTR) are not "less secure options", they are incomplete, and need a separate MAC applied correctly (encrypt-then-MAC) to be safe. Never use ECB: it encrypts identical plaintext blocks to identical ciphertext blocks, leaving the structure of your data plainly visible.
- AES-128 is not broken. AES-256 is the sensible default and is required by CNSA 2.0 for classified use, largely for post-quantum margin, Grover's algorithm notionally halves the effective key length, taking AES-256 to a still-comfortable 128 bits. But AES-128 remains secure against classical attack.
- Generate keys with a cryptographically secure RNG (
os.urandom,secrets), neverrandom. - Respect key wear-out limits. A single AES-GCM key should not encrypt more than about 232 messages.
- Use hardware AES (AES-NI) where available, it is both faster and constant-time, which naive software S-box implementations are not.
Applications
- Data Encryption: File encryption, database encryption
- Network Security: SSL/TLS, VPN protocols
- Disk Encryption: Full disk encryption systems
- Wireless Security: WPA2, WPA3
- Government Use: Classified information protection
Related Algorithms
Explore other encryption algorithms:
- RSA - Asymmetric encryption
- DES - Legacy symmetric encryption
- SHA - Hash functions
- Back to Encryption Algorithms Overview
☕ Buy me a coffee — $3