Symmetric Encryption

AES

The AES block cipher and why you never invent your own.

The Advanced Encryption Standard is the most widely deployed cipher on the planet. It protects TLS sessions, disk volumes, VPN traffic, and password vaults. Understanding what AES is (and, just as importantly, what it is not) keeps you from the classic mistakes: using it in a broken mode, inventing your own alternative, or assuming the cipher alone gives you security.

Block ciphers as keyed permutations

A block cipher is a keyed, invertible function on fixed-size blocks. For AES the block is exactly 128 bits (16 bytes). Given a key, encryption maps each possible 16-byte input to a unique 16-byte output, and decryption reverses it exactly. Because the mapping is a permutation (a one-to-one shuffle of all possible blocks), no information is lost and every ciphertext decrypts to exactly one plaintext.

The security property is that without the key, the permutation looks random: flipping one input bit should change roughly half the output bits (the avalanche effect), and an attacker who sees many input/output pairs still cannot predict the key or new pairs. AES achieves this through repeated rounds of substitution and permutation (SubBytes, ShiftRows, MixColumns, AddRoundKey), 10 rounds for a 128-bit key, 12 for 192, and 14 for 256.

A crucial limitation: a block cipher only handles one block. Real messages are longer, so you need a mode of operation (see Modes Of Operation) to chain blocks together safely. AES by itself is not an encryption scheme, it is the engine inside one.

AES key sizes and hardware acceleration (AES-NI)

AES supports three key sizes. All three are considered secure against classical attacks; the difference is mostly margin.

Key sizeRoundsTypical useNotes
AES-12810General purposeFast, ample security for most needs
AES-19212Rarely chosen explicitlyMiddle ground, uncommon
AES-25614Compliance, long-term, high-valueDefault for regulated and post-quantum-hedged systems

For new systems, AES-256 is the common default: the extra cost is small and it gives a larger margin, including a better position against future quantum attacks (Grover’s algorithm halves the effective key strength, so 256 bits still leaves 128 bits of margin).

Modern CPUs include AES-NI, dedicated instructions that make AES extremely fast and, importantly, constant-time (resistant to cache-timing side channels that plague software table lookups). This is why AES is usually the right pick on server and desktop hardware. On platforms without AES hardware (some embedded, mobile, or older devices), a software AES can be slow and harder to keep constant-time, which is exactly where ChaCha20-Poly1305 shines.

Check whether your CPU advertises AES-NI:

sysctl -a 2>/dev/null | grep -i aes || grep -o 'aes' /proc/cpuinfo | head -1

Expected output:

machdep.cpu.features: ... AES ...

Why you never invent your own cipher

AES survived a multi-year open competition and two decades of relentless public cryptanalysis. That scrutiny is the source of the confidence, not the cleverness of the algorithm. A homemade cipher has zero scrutiny, and a broken cipher does not look broken from the outside: it still produces random-looking bytes. This is rule zero of the foundation. Use AES (or ChaCha20) through an audited library. The only defensible reason to read the AES internals is to understand them, never to reimplement them for production.

Hands-on: encrypting with openssl enc

The openssl enc command lets you drive AES directly. This is a teaching tool, not a production pattern; the warnings below matter.

mkdir -p ~/crypto-lab && cd ~/crypto-lab
echo "the launch codes are hunter2" > secret.txt

# Generate a random 256-bit key and a 128-bit IV as hex
KEY=$(openssl rand -hex 32)
IV=$(openssl rand -hex 16)

# Encrypt with AES-256 in CBC mode (used here only to show the mechanics)
openssl enc -aes-256-cbc -K $KEY -iv $IV -in secret.txt -out secret.enc
echo "ciphertext bytes: $(wc -c < secret.enc)"

# Decrypt back
openssl enc -d -aes-256-cbc -K $KEY -iv $IV -in secret.enc

Expected output:

ciphertext bytes: 32
the launch codes are hunter2

Two things to internalize from this exercise:

  • CBC here has no authentication. If an attacker flips bytes in secret.enc, decryption may still succeed and return altered plaintext, or fail in ways that leak information (padding oracle attacks). Never ship raw CBC. Use an AEAD, covered in Authenticated Encryption.
  • If you had used -aes-256-ecb, identical 16-byte plaintext blocks would produce identical ciphertext blocks, leaking structure. The Modes Of Operation page demonstrates this with the ECB penguin.

A safer one-shot for the CLI is GCM, though real code should use a library binding that handles the authentication tag for you:

cd ~/crypto-lab
KEY=$(openssl rand -hex 32); IV=$(openssl rand -hex 12)
openssl enc -aes-256-gcm -K $KEY -iv $IV -in secret.txt -out secret.gcm 2>/dev/null
echo "gcm ciphertext bytes: $(wc -c < secret.gcm)"

Expected output:

gcm ciphertext bytes: 29

Practical Guidance

  1. Choose AES-256 for new systems unless a specific constraint says otherwise. The performance cost over AES-128 is negligible on AES-NI hardware.
  2. Never use AES in ECB mode, and never use CBC or CTR without a separate MAC. Reach for AES-GCM (an AEAD) instead.
  3. Prefer AES where AES-NI hardware exists; prefer ChaCha20-Poly1305 on devices without it for speed and side-channel safety.
  4. Generate keys and IVs from a CSPRNG (openssl rand), never from timestamps, counters, or passwords directly.
  5. Treat openssl enc as a learning tool. In production, call AES through an audited library that manages nonces and authentication tags for you.
  6. Remember AES is only the engine. Confidentiality and integrity come from the mode; pick an AEAD and you get both.