← Back to Table of Contents

Bulk Encryption: AES-GCM and ChaCha20

After the handshake, every byte of data between client and server is encrypted with symmetric encryption. The bulk encryption algorithm in the cipher suite determines which one. Think of the handshake as two diplomats agreeing on which language to speak in private. Once they’ve agreed, all the actual conversation happens in that language. The bulk cipher is that language.

AES-GCM

AES in Galois/Counter Mode is the dominant choice. GCM is an AEAD mode (Authenticated Encryption with Associated Data), which means it provides both encryption and integrity in a single operation.

Why AEAD matters: older modes like CBC required a separate MAC (message authentication code) step after encryption. It’s like locking a box and then separately putting a tamper-evident seal on it. Two steps, two chances for something to go wrong. AEAD is like a lock that also detects tampering in one operation. This two-step process led to subtle vulnerabilities. Padding oracle attacks exploited the gap between decryption and MAC verification. AEAD eliminates this class of attacks by combining both operations.

How GCM Actually Works

GCM combines two operations: Counter mode (CTR) for encryption and Galois field multiplication for authentication.

The encryption side works like this. GCM takes a counter value (starting at 1 and incrementing for each block), encrypts the counter with AES, and XORs the result with the plaintext block. The plaintext never goes through AES directly. Instead, AES encrypts the counter, and the output is used as a keystream that gets XORed with the data. This is important because it means blocks can be encrypted in parallel. Each block’s counter is independent, so you don’t have to wait for the previous block to finish (unlike CBC, where each block depends on the previous one).

The authentication side runs alongside the encryption. As each ciphertext block is produced, it’s fed into a Galois field multiplication that accumulates a running authentication tag. Any associated data (like TLS record headers that need integrity protection but not encryption) is also fed into this computation. At the end, the final tag is appended to the ciphertext.

The receiver performs the same process in reverse. It decrypts using the same counter sequence and simultaneously recomputes the authentication tag. If the recomputed tag doesn’t match the one appended to the ciphertext, the data was tampered with, and the entire record is rejected. The receiver never releases any decrypted data until the tag is verified.

The key insight: encryption and authentication happen in a single pass through the data. There’s no gap between “decrypt” and “check integrity” that an attacker could exploit. This is what makes GCM immune to the padding oracle attacks that plagued CBC.

AES-GCM is hardware-accelerated on most modern CPUs through AES-NI instructions. On a typical server, AES-256-GCM can encrypt at several gigabytes per second. This is why TLS adds negligible overhead to data transfer.

TLS uses two variants:

ChaCha20-Poly1305

ChaCha20 is a stream cipher designed by Daniel Bernstein. Poly1305 is the accompanying MAC. Together, they form an AEAD construction, just like AES-GCM, but built on completely different math.

How ChaCha20 Works

ChaCha20 is a stream cipher. Unlike AES, which encrypts fixed-size blocks, a stream cipher generates a continuous stream of pseudorandom bytes called a keystream. You take the keystream and XOR it with the plaintext to get ciphertext. To decrypt, you generate the same keystream and XOR it with the ciphertext to get the plaintext back.

The keystream is generated from three inputs: the encryption key, a nonce (a unique number for each message), and a block counter. ChaCha20 mixes these inputs through 20 rounds of addition, XOR, and rotation operations. No lookup tables, no complex substitution boxes. Just simple arithmetic that any CPU can do quickly.

Because there’s no block structure, there’s no padding needed. You generate exactly as many keystream bytes as you have plaintext bytes. No padding means no padding oracle attacks.

How Poly1305 Works

Poly1305 is a one-time message authentication code. It takes the ciphertext and produces a 128-bit authentication tag. The tag is computed using a one-time key that’s derived from the ChaCha20 keystream itself. This tight coupling means you can’t accidentally use ChaCha20 without Poly1305 or vice versa. They’re designed as a pair.

The receiver recomputes the Poly1305 tag over the received ciphertext. If the tags match, the data is authentic and unmodified. If they don’t match, the data was tampered with.

Why ChaCha20-Poly1305 Exists

On devices with AES hardware acceleration (AES-NI), AES-GCM is faster. But not every device has AES-NI. Older mobile phones, some ARM processors, and embedded devices do AES in software, which is slow.

ChaCha20 was designed to be fast in pure software. It uses only addition, XOR, and rotation, operations that every CPU handles natively without special instructions. On a phone without AES-NI, ChaCha20-Poly1305 can be three times faster than AES-GCM.

Many servers are configured to let the client choose. If the client prefers ChaCha20 (indicating it lacks AES hardware), the server uses ChaCha20. If the client prefers AES-GCM, the server uses AES-GCM. This way each client gets the fastest AEAD cipher for its hardware.

Why CBC Is Being Phased Out

AES-CBC (Cipher Block Chaining) was the standard in TLS 1.0 and 1.1, and was still common in TLS 1.2. It works, but it’s not AEAD. The separate MAC step created vulnerabilities:

TLS 1.3 removed CBC entirely. Only AEAD ciphers (GCM and ChaCha20-Poly1305) are allowed.


Next: Hashing in Cipher Suites

← Previous ChapterNext Chapter →