symmetric · level 9

TLS 1.3 Symmetric Layer

AES-GCM vs ChaCha20-Poly1305 — when each wins.

200 XP

TLS 1.3 Symmetric Layer

By the time the TLS handshake finishes, two parties have agreed on a shared secret and a cipher suite. Everything after is encrypted bulk data — and "bulk data" is where symmetric ciphers earn their keep. TLS 1.3's bulk-data layer is a study in aggressive simplification: take the lessons of two decades of TLS attacks, throw out everything that isn't AEAD, and ship five cipher suites.

The TLS 1.3 cipher suite list (in full)

TLS_AES_128_GCM_SHA256        ← mandatory
TLS_AES_256_GCM_SHA384        ← mandatory
TLS_CHACHA20_POLY1305_SHA256  ← mandatory
TLS_AES_128_CCM_SHA256        ← optional
TLS_AES_128_CCM_8_SHA256      ← optional

That's the entire menu. Compared to TLS 1.2 — which had over 300 registered suites including RC4, 3DES, and DES-CBC — this is a rounding error. The lesson of 1.0/1.1/1.2: every cipher you don't ship is one fewer downgrade attack you have to defend against.

What the suite name actually says

TLS_AES_128_GCM_SHA256 decomposes into:

  • TLS_ — the protocol family.
  • AES_128_GCM — the bulk encryption AEAD: AES-128 in GCM mode.
  • SHA256 — the hash for HKDF, used to derive every key from the shared secret. Not used for any HMAC or signature in the normal data flow.

That's it. TLS 1.2 cipher suites encoded the key exchange (ECDHE), the signature algorithm (RSA), the bulk cipher (AES_128_GCM), and a MAC. TLS 1.3 negotiates key exchange, signature, and AEAD+hash separately. So fewer suite names, more orthogonal choices.

AES-GCM vs ChaCha20-Poly1305 — when each wins

The only realistic pick in 2024 is between AES-GCM (any size) and ChaCha20-Poly1305. The trade-off is hardware acceleration.

AES-GCM wins when AES-NI is available

AES on x86 has had AES-NI instructions since Westmere (2010). Modern Intel/AMD CPUs do AES-128 at ~10 GB/s per core. GCM's GHASH (the "carry-less multiply" part) has its own instruction (PCLMULQDQ). Combined: GCM throughput is bandwidth-limited, not compute-limited.

ARM has had AES instructions since v8.0 (mostly server / late-model phones).

ChaCha20-Poly1305 wins when AES-NI isn't

ChaCha20 is pure ARX: addition, rotation, XOR. No S-boxes, no Galois fields, no special hardware. On a CPU without AES instructions (older phones, embedded, IoT), ChaCha20 destroys AES — often by 3-5×.

It's also constant-time everywhere, while software AES has timing leaks via cache lookups.

What modern stacks do

Browsers and servers hedge:

  • Chrome / Firefox advertise both, with ChaCha20-Poly1305 first when the client is on a phone (no AES-NI), AES-128-GCM first when on x86.
  • NGINX / OpenSSL default to AES-256-GCM first (since most production servers have AES-NI) but accept ChaCha20-Poly1305 if the client prefers it.
  • Cloudflare famously switched to "follow the client's preference for mobile clients" in 2015 — saving battery on millions of phones.

In TLS 1.3 the server picks the cipher (RFC 8446 §4.1.1). Server preference rules. Client preference is just a hint expressed by the order it offers suites.

# NGINX TLS 1.3 — server prefers AES-128-GCM, falls back to ChaCha20.
ssl_protocols TLSv1.3;
ssl_ciphers   "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384";
ssl_prefer_server_ciphers on;

What was removed

TLS 1.2 had a chronic "death of a thousand modes" problem. TLS 1.3 axed the entire family that caused it:

  • CBC mode + HMAC. Source of POODLE (SSLv3), Lucky13, BEAST. The MAC-then-encrypt construction was provably wrong; removing it ends the problem class.
  • RC4. Biased keystream + WEP-style attacks. Banned by RFC 7465.
  • 3DES. 64-bit blocks; Sweet32 broke it for long-lived sessions. Removed.
  • MD5, SHA-1 in handshake. Replaced with SHA-256 / SHA-384 in HKDF.
  • RSA key transport. Dropped in favor of mandatory ephemeral (EC)DH — gives forward secrecy by default.
  • Custom DH groups, custom curves. Replaced by a fixed registry of named groups (X25519, secp256r1, secp384r1, x448).

This is the most aggressive "remove, don't fix" revision the IETF has shipped in any standard. It's also why TLS 1.3 is the symmetric layer you should reach for in any new design.

Forward secrecy and key separation

TLS 1.3 derives separate keys for each direction (client→server and server→client) and re-derives keys for resumption. The bulk cipher in each direction has:

  • A unique key (from HKDF).
  • A 96-bit IV that's XORed with the record sequence number — so the actual nonce per record is iv ⊕ seqnum. Counter-based nonces, no random nonces, no nonce reuse possible across records.
  • A finite lifetime: the spec recommends rekeying after 2^24 records to stay well below GCM's 2^32 bound.

The key insight: TLS 1.3's nonce is the sequence number. There's no separate IV negotiation. Counter-management is trivial because the record layer already has counters.

What this means in practice

If you're building a new system that does its own AEAD over TCP/UDP/QUIC, copy TLS 1.3's pattern: derive a key per direction with HKDF, use the record sequence number as the GCM/ChaCha20 nonce, rekey before the bound. WireGuard, Noise, and QUIC all do exactly this. There is no good reason to invent a different pattern.

If you're an operator: enable TLS 1.3, list both AES-GCM and ChaCha20-Poly1305, set server cipher preference, and you're done. The cipher-suite-tuning era is over.

Diagnostic snippets

# What did this connection negotiate?
openssl s_client -connect example.com:443 -tls1_3 -servername example.com 2>&1 | grep -E "Cipher|Protocol"

# Probe what a server supports without negotiating:
nmap --script ssl-enum-ciphers -p 443 example.com

# Comprehensive online test:
# https://www.ssllabs.com/ssltest/

If a TLS-related performance bug crosses your desk, the first move is figuring out which AEAD got picked. Mobile client + AES-GCM-only server = battery drain. AES-NI server + ChaCha20-Poly1305-only client = wasted CPU. The fix in both cases is "list both".

Tools in the wild

4 tools
  • Inspect any TLS connection. `openssl s_client -connect host:443 -tls1_3` shows the negotiated suite.

    cli
  • ssllabs.comfree tier

    Qualys SSL Labs Server Test — comprehensive analysis of any public HTTPS site.

    service
  • testssl.shfree tier

    Self-hosted TLS scanner. Walks the suite list, tests for known attacks, prints negotiation details.

    cli
  • Wiresharkfree tier

    Decrypt TLS traffic with the SSLKEYLOGFILE env var. Inspect the handshake including key shares.

    cli