Chapter 04

Encryption

Hiding data so that only someone with the right key can read it, and proving it has not been tampered with along the way.

Scroll to explore↓
01 // The Core Problem

Turning secrets into gibberish

Encryption transforms readable data into something indistinguishable from random noise. Symmetric encryption means both sender and receiver share the same key. It is the oldest and still the most widely used form of encryption.

What "symmetric" means

One key encrypts. The same key decrypts. Both parties must have it, and nobody else can. This is different from asymmetric cryptography (covered later), where encryption and decryption use different keys.

Symmetric encryption is what AES does. It protects your disk (FileVault, BitLocker), your HTTPS traffic (TLS), your messages (Signal, WhatsApp), and your VPN tunnels. Everything starts here.

The encryption contract

A good symmetric cipher gives you three guarantees. Confidentiality: when used correctly (with a proper mode of operation), the ciphertext reveals nothing about the plaintext without the key. Uniqueness: different plaintexts produce different ciphertexts. Reversibility: decrypting with the correct key always recovers the original.

Notice what is not promised. Encryption alone does not tell you whether someone tampered with the ciphertext. An attacker can flip bits, rearrange blocks, or truncate the message, and decryption will happily produce garbage without raising an alarm. We solve that problem later in this chapter.

02 // The Building Block

AES: one block at a time

A block cipher encrypts a fixed-size chunk of data using a key. AES operates on 128-bit (16-byte) blocks. It is a permutation: every possible input maps to a unique output, and the process reverses with the same key.

The standard block cipher

The Advanced Encryption Standard (AES) is the universally used block cipher. It supports key sizes of 128128, 192192, and 256256 bits. Modern CPUs have dedicated hardware instructions that make AES essentially free in terms of performance.

Internally, AES applies multiple rounds of substitution, shifting, and XOR mixing: 10 rounds for AES-128, 12 for AES-192, 14 for AES-256. After these rounds, changing a single input bit avalanches across every output bit. Same principle as hash functions, applied as a reversible transformation.

πŸ“¦

Fixed Block Size

AES always processes exactly 16 bytes at a time. Longer messages need a mode of operation to handle multiple blocks. Shorter messages need padding.

πŸ”€

Key-Dependent Scrambling

With one key, AES maps each possible 128-bit input to a unique 128-bit output. A different key produces a completely different mapping. The key is the secret.

🎯

Deterministic

Same key plus same plaintext block always equals the same ciphertext block. This is by design, but it creates a serious problem when encrypting multiple blocks independently.

AES Encryption Playground

Type a message, pick a mode, and watch the ciphertext update in real time. Change a single character and see how the entire output changes.

Key (256-bit)
Β 
IV (128-bit)
a4af7cdb11316666551f9757df63d0ac
Ciphertext (0 bytes)Β 

CBC adds PKCS#7 padding so the ciphertext is always a multiple of 16 bytes. A 20-byte message produces 32 bytes of ciphertext.

Inside One AES Round

AES encrypts by repeating four operations on a 4x4 byte grid. Each operation exists for a specific reason. Here is round 1 in full, with the actual bytes at every step.

03 // The ECB Disaster

Why encrypting blocks independently fails

AES encrypts one 16-byte block at a time. The simplest approach is to encrypt each block separately. This is called ECB mode (Electronic Codebook). It is catastrophically insecure for almost all real data.

Identical blocks, identical ciphertext

Because AES is deterministic, identical plaintext blocks produce identical ciphertext blocks. If your message contains repeated patterns (and real data almost always does), those patterns survive encryption unchanged.

The classic demonstration: encrypt a bitmap image in ECB mode. The colors are scrambled, but the shape is perfectly visible. The structure of the data leaks through because identical pixel blocks map to identical ciphertext blocks. This is not encryption in any meaningful sense.

ECB vs CBC: Pattern Leakage

Identical plaintext blocks produce identical ciphertext in ECB mode. In CBC mode, chaining makes every block unique.

Plaintext blocks (4 x 16 bytes)
P1
AAAAAAAAAAAAAAAA
P2
AAAAAAAAAAAAAAAA
P3
AAAAAAAAAAAAAAAA
P4
AAAAAAAAAAAAAAAA
ECB ciphertext
CBC ciphertextAll unique
04 // Modes That Work

CBC and CTR: solving the pattern problem

A block cipher alone can only encrypt 16 bytes. A mode of operation defines how to encrypt messages of any length. The choice of mode determines whether patterns leak, whether you need padding, and what happens when things go wrong.

CBC (Cipher Block Chaining)

Each plaintext block is XORed with the previous ciphertext block before encryption: Ci=EK(PiβŠ•Ciβˆ’1)C_i = E_K(P_i \oplus C_{i-1}). The first block is XORed with a random IV (initialization vector). This chains blocks together: even identical plaintext blocks produce different ciphertext because each one depends on everything before it.

CBC requires padding (extra bytes added to the last block to fill it to 16 bytes, which the receiver knows how to strip). The IV must be random and unpredictable for each encryption. It does not need to be secret, just unique. CBC is sequential: you cannot encrypt block N without first encrypting block N-1.

CTR (Counter)

AES encrypts a counter (nonce combined with block number) to produce a keystream: Ci=PiβŠ•EK(nonceβˆ₯i)C_i = P_i \oplus E_K(\text{nonce} \| i). This turns AES into a stream cipher. No padding needed. Ciphertext is the same length as plaintext.

CTR can be parallelized because each block uses an independent counter. It also allows random access: you can decrypt block 47 without touching blocks 0-46. The nonce must never repeat with the same key. Encryption and decryption are the same operation, since both just XOR with the keystream.

Block Cipher Modes Visualizer

Step through each mode to see how plaintext blocks become ciphertext. Watch the connections between blocks.

Key
P1
↓
AES
↓
C1
P2
↓
AES
↓
C2
P3
↓
AES
↓
C3
Step 1 of 8

Start with 3 plaintext blocks and a key.

05 // The Missing Piece

Encryption without integrity is dangerous

Symmetric encryption hides your data. But it does not prevent an attacker from modifying the ciphertext. With CBC, an attacker can flip specific bits in the decrypted plaintext without knowing the key.

How CBC bit-flipping works

In CBC decryption, each plaintext block is produced by decrypting the ciphertext block and XORing with the previous ciphertext block: Pi=DK(Ci)βŠ•Ciβˆ’1P_i = D_K(C_i) \oplus C_{i-1}. If an attacker flips a bit in Ciβˆ’1C_{i-1}, the same bit flips in PiP_i (predictably), while Piβˆ’1P_{i-1} becomes completely garbled (unpredictably).

An attacker who knows the plaintext of block K+1 can calculate exactly which bits to flip to produce any desired modification. Changing "admin=false" to "admin=true" is a matter of XORing the right bytes. No key required.

Real-world consequences

Real attacks have exploited this. If a server reveals whether decrypted padding is valid (through error messages or timing differences), an attacker can use that feedback to decrypt traffic one byte at a time, without ever knowing the key.

The fundamental issue: encryption alone does not tell you whether the ciphertext was modified. The decryption function will happily process tampered data and return corrupted plaintext without complaint.

CBC Bit-Flipping Attack

Click a byte in the ciphertext to flip it. Block 0 will be garbled, but block 1 gets a precise, targeted modification. The attacker needs no key.

Original plaintext
role=user;admin=false;id=123456789;session=valid
Ciphertext (click a byte to flip it)
0x00XOR 0x15= 0x15
0x000x150xFF
Original decryption
Modified decryption
06 // The Complete Solution

AEAD: encryption and authentication in one step

Manually combining a cipher with a MAC is error-prone. The ordering matters, you need separate keys, and every step is an opportunity for a subtle bug. AEAD (Authenticated Encryption with Associated Data) solves this by combining both into a single construction. One key, one function call, both confidentiality and integrity.

How AEAD works

An AEAD algorithm takes four inputs: a key, a nonce (a unique value per encryption), the plaintext, and optional associated data. It produces two outputs: the ciphertext and an authentication tag.

During decryption, if anything has been modified (the ciphertext, the tag, or the associated data), the algorithm rejects the input with an explicit error. It does not return corrupted data. It does not attempt partial decryption. The answer is either the original plaintext or nothing at all.

AEAD encryption flow
Plaintext
+
Key
+
Nonce
+
AAD
β†’
AEAD
β†’
Ciphertext
+
Tag
AEAD decryption flow
Ciphertext
+
Tag
+
Key
+
Nonce
+
AAD
β†’
Verify
β†’
Plaintextif tag matches
REJECTif tampered
πŸ”’

Confidentiality

The ciphertext reveals nothing about the plaintext without the key. Same guarantee as AES-CBC or AES-CTR, built into every AEAD operation.

πŸ›‘οΈ

Integrity

Any modification to the ciphertext or authentication tag is detected. Decryption refuses to produce output. No corrupted plaintext, no silent failures.

βœ…

Authenticity

Only someone with the key could have produced the ciphertext and tag pair. This proves the data came from the key holder, not an impersonator.

AES-GCM Encryption Playground

Type a message and watch AES-GCM produce both ciphertext and an authentication tag. One function call, two guarantees.

Key (256-bit)
Β 
Nonce (96-bit)
5ec7cc8c4e3807a8a7805e98
Ciphertext (0 bytes)Β 
Authentication Tag (128-bit)Β 

The ciphertext is the same length as the plaintext (no padding needed). The 16-byte authentication tag is computed over the ciphertext and binds everything together. Modify either one, and decryption will fail.

07 // Tamper Detection

Tamper with the ciphertext, get caught

In Section 05, you flipped bits in CBC ciphertext and the attacker got away with it. Try the same thing with AES-GCM. Flip any byte in the ciphertext or the authentication tag. Every modification is detected and decryption is refused.

AES-GCM Tamper Detection

Click any byte in the ciphertext or authentication tag to flip it. In Section 05, CBC silently produced corrupted output. With AES-GCM, every modification is caught.

Original plaintext
role=user;admin=false;session=ok!
Ciphertext + Tag (click a byte to flip it)
Ciphertext (0 bytes)
Auth Tag (0 bytes)
0x00XOR 0x15= 0x15
0x000x150xFF
08 // Associated Data

Authenticating data you don't encrypt

Sometimes metadata must travel in plaintext. Routing headers, protocol versions, sequence numbers. You need this data readable by network infrastructure, but you also need to guarantee nobody tampered with it. That is what associated data is for.

Why not just encrypt everything?

In TLS, the record header contains the content type, protocol version, and payload length. Network middleboxes and load balancers need to read this data to route traffic correctly. If you encrypt the header, the infrastructure breaks.

In a database, the row ID and table name might be associated data while the row contents are encrypted. The database engine needs the ID to locate the row, but the contents should be hidden from anyone without the key. Associated data lets you authenticate both parts while only encrypting the sensitive part.

Associated Data Explorer

The ciphertext and tag are fixed. Edit the "received" associated data below and watch authentication succeed or fail. The ciphertext itself is never touched.

Plaintext (encrypted)Top secret message content
Associated Data (not encrypted, authenticated)from=alice;to=bob;type=message
Ciphertext (0 bytes)Β 
Authentication TagΒ 
09 // Best Practices

Using encryption correctly

The algorithm is the easy part. Getting the details right is where systems break.

πŸ”

Use AES-GCM or ChaCha20-Poly1305

Never use AES-CBC or AES-CTR without a separate MAC. AEAD constructions handle everything in one operation. These are the default choices in TLS 1.3.

πŸ”’

Never Reuse (Key, Nonce) Pairs

Generate a random 12-byte nonce per encryption. With random nonces, you can safely encrypt about 4 billion messages under one key before collisions become likely. For higher volumes, rotate keys.

πŸ“Ž

Always Include Context in AAD

Include protocol version, message type, sender ID, or any context that should be bound to the ciphertext. Without AAD, an attacker can take a valid ciphertext from one context and replay it in another.

πŸ”‘

Use 256-Bit Keys

AES-128 is still considered secure, but AES-256 provides a larger safety margin. There is no meaningful performance difference on modern hardware with built-in AES acceleration.

πŸ“

Use the Full 128-Bit Tag

AES-GCM supports shorter tags (64 or 96 bits) but always use the full 128-bit tag. Shorter tags make forgery exponentially easier for an attacker.

🚫

Do Not Implement Your Own Crypto

Use your platform's built-in crypto library. Correct implementations must protect against timing attacks and other subtle leaks, which is extremely difficult to get right from scratch.

The single most important takeaway: never use a cipher without authentication. AES-GCM and ChaCha20-Poly1305 exist precisely so you never have to think about manually composing encryption and MACs, managing separate keys, or worrying about padding oracles. Use them.

Up Next

Chapter 05 // Mathematical Foundations

Continue Learning→