Hiding data so that only someone with the right key can read it, and proving it has not been tampered with along the way.
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.
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.
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.
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 Advanced Encryption Standard (AES) is the universally used block cipher. It supports key sizes of , , and 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.
AES always processes exactly 16 bytes at a time. Longer messages need a mode of operation to handle multiple blocks. Shorter messages need padding.
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.
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.
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.
CBC adds PKCS#7 padding so the ciphertext is always a multiple of 16 bytes. A 20-byte message produces 32 bytes of ciphertext.
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.
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.
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.
Identical plaintext blocks produce identical ciphertext in ECB mode. In CBC mode, chaining makes every block unique.
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.
Each plaintext block is XORed with the previous ciphertext block before encryption: . 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.
AES encrypts a counter (nonce combined with block number) to produce a keystream: . 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.
Step through each mode to see how plaintext blocks become ciphertext. Watch the connections between blocks.
Start with 3 plaintext blocks and a key.
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.
In CBC decryption, each plaintext block is produced by decrypting the ciphertext block and XORing with the previous ciphertext block: . If an attacker flips a bit in , the same bit flips in (predictably), while 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 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.
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.
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.
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.
The ciphertext reveals nothing about the plaintext without the key. Same guarantee as AES-CBC or AES-CTR, built into every AEAD operation.
Any modification to the ciphertext or authentication tag is detected. Decryption refuses to produce output. No corrupted plaintext, no silent failures.
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.
Type a message and watch AES-GCM produce both ciphertext and an authentication tag. One function call, two guarantees.
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.
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.
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.
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.
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.
The ciphertext and tag are fixed. Edit the "received" associated data below and watch authentication succeed or fail. The ciphertext itself is never touched.
The algorithm is the easy part. Getting the details right is where systems break.
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.
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.
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.
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.
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.
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.