Thread

🛡️
Article header

The Cypher Wars: Choose Your Weapon Wisely

Use ChaCha20-Poly1305 everywhere without hardware AES. Use AES-256-GCM with AES-NI. Never reuse nonces. Prefer AEAD always.

Introduction: What Are We Actually Talking About?

When you send an encrypted message, two things need to happen. First, the message needs to be scrambled so only the intended recipient can read it, which is encryption. Second, the recipient needs to verify the message wasn't tampered with along the way, which is authentication. Modern systems do both at once using something called AEAD (Authenticated Encryption with Associated Data), a fancy way of saying "encrypt and authenticate in one step."

The encryption world has settled on two main approaches: ChaCha20 and AES (Advanced Encryption Standard). Think of them as two different recipes for scrambling data. Both work well, but they shine in different situations, kind of like how a hand mixer and a stand mixer both make cake batter, but you'd choose differently depending on your kitchen setup.

This guide will help you understand how these tools work, when to use each one, and critically, how to avoid the mistakes that turn "encrypted" into "false sense of security."


Part 1: The Basics of Symmetric Encryption

Before diving into specific algorithms, let's establish some fundamentals.

Keys, Nonces, and Why They Matter

Symmetric encryption means the same key both encrypts and decrypts data. If Alice encrypts a message with key K, Bob needs that same key K to decrypt it. This differs from public-key cryptography where you have separate keys for each direction.

A nonce (number used once) is a random or sequential value that makes each encryption unique. Even if you encrypt the same message twice with the same key, different nonces produce completely different ciphertext. Think of it like adding a unique serial number to each message.

Why nonces matter so much: If you ever use the same key and nonce combination twice, bad things happen. How bad? We'll get there. For now, just know that you must never reuse nonces.

Stream Ciphers vs Block Ciphers

There are two fundamental approaches to encryption:

Stream ciphers generate a continuous stream of random-looking bytes, then combine them with your message using XOR (a simple bit-flipping operation). It's like generating a one-time pad on the fly. ChaCha20 is a stream cipher.

Block ciphers encrypt fixed-size chunks of data (typically 128 bits) at a time. To encrypt longer messages, you need a "mode of operation" that chains the blocks together safely. AES is a block cipher.

Neither approach is inherently better. They're just different tools.


Part 2: ChaCha20, the Software-Friendly Option

ChaCha20 was designed by cryptographer Daniel Bernstein as a more robust version of his earlier Salsa20 cipher. Its big selling point is that it's fast on any computer, even ones without special encryption hardware.

How It Works (The Intuitive Version)

Imagine you have a box with 16 slots, each holding a 32-bit number. You fill this box with some fixed constants (so the algorithm has a known starting point), your 256-bit secret key (split into 8 pieces), a counter (which slot in the message you're encrypting), and your nonce (to make this encryption unique).

ChaCha20 then stirs this box through 20 rounds of mixing. Each round performs simple operations: add numbers together, flip bits with XOR, and rotate bits left, all repeated in careful patterns. After all the mixing, you XOR the stirred output with your message. Done.

The beauty is that these operations (add, XOR, rotate) are fast on literally every CPU ever made. No special instructions needed.

Why "20" in ChaCha20?

The number refers to rounds of mixing. More rounds means more security margin, but slower speed. ChaCha20 uses 20 rounds, which provides substantial security margin since the best known attack only breaks 7.5 rounds, leaving plenty of headroom.

Variants exist: ChaCha8 and ChaCha12 use fewer rounds for more speed. Unless you have extreme performance constraints and deeply understand the tradeoffs, stick with ChaCha20.

The Nonce Size Problem and XChaCha20

Standard ChaCha20 uses a 96-bit nonce. That's big enough if you're carefully counting messages, but too small for random generation. You'd risk collisions after roughly 2^48 messages due to birthday paradox math.

XChaCha20 extends the nonce to 192 bits. With a nonce that large, you can safely generate random nonces without ever worrying about collisions. The collision probability only reaches 50% after 2^96 messages, which is more than you'll ever send.

| Variant | Nonce Size | Best For | |---------|------------|----------| | ChaCha20 (IETF) | 96 bits | Counter-based systems | | XChaCha20 | 192 bits | Random nonce generation |

Rule of thumb: If you're managing nonces as counters, standard ChaCha20 is fine. If you want to generate random nonces without thinking, use XChaCha20.


Part 3: AES, the Hardware-Accelerated Standard

AES (Advanced Encryption Standard) won a NIST competition in 2001 and became the global standard. It's everywhere: your phone, your laptop, your credit card, government systems, bank vaults.

How It Works (The Intuitive Version)

AES operates on 128-bit blocks (16 bytes). It treats each block as a 4×4 grid of bytes and transforms it through multiple rounds.

First it substitutes, replacing each byte using a lookup table called the S-box. Then it shifts rows, sliding each row sideways by different amounts. Then it mixes columns, mathematically blending each column. Finally it adds the round key by XORing with key material.

This sequence repeats 10, 12, or 14 times depending on key size (128, 192, or 256 bits). Each round further scrambles the data until the output looks completely random.

The Hardware Advantage: AES-NI

Here's where AES gets interesting. Intel and AMD chips (since around 2010) include special instructions called AES-NI (AES New Instructions) that perform entire AES rounds in hardware. This makes AES incredibly fast at about 1.3 CPU cycles per byte, and also side-channel resistant with no timing variations to leak secrets.

Without AES-NI, software implementations run 10x slower AND may leak information through timing. This is why hardware detection matters.

Modes of Operation: How to Use a Block Cipher

Since AES only encrypts 16 bytes at a time, you need a "mode" that handles arbitrary-length messages. This is where things get interesting, and dangerous.

GCM (Galois/Counter Mode), the Modern Default

GCM turns AES into a stream cipher (using counter mode) and adds authentication in one efficient pass. It's the default choice for TLS, IPsec, and most modern protocols.

The catch: GCM fails catastrophically if you reuse a nonce. Not "some information leaks" but "attacker can forge any message they want." We'll discuss this more in the pitfalls section.

GCM-SIV, the Safety Net

GCM-SIV (Synthetic Initialization Vector) provides "nonce-misuse resistance." If you accidentally reuse a nonce, the damage is limited. An attacker learns only whether two messages are identical, not the actual content or authentication keys.

The tradeoff: GCM-SIV requires two passes over the data, roughly halving throughput.

Other Modes You'll Encounter

CBC (Cipher Block Chaining) is a legacy mode that requires separate authentication and careful padding. It's vulnerable to subtle attacks like Lucky13 and POODLE. Avoid it in new designs.

XTS is designed for disk encryption. It provides no authentication, but also no data expansion. It's good for storage, not for network protocols.

CTR (Counter Mode) is a simple stream cipher mode that's fast and parallelizable, but provides no authentication on its own.


Part 4: Authentication with Poly1305 and the SHA Family

Encryption hides your message. Authentication proves it wasn't modified. You need both.

Poly1305: ChaCha's Partner

Poly1305 is a "message authentication code" (MAC) designed to pair with ChaCha20. Together they form ChaCha20-Poly1305, an AEAD construction.

How Poly1305 Works (Simplified)

Poly1305 treats your message as a polynomial (a mathematical expression with multiple terms) and evaluates it at a secret point. The result is a 128-bit "tag" that's effectively impossible to forge without the key.

The key insight: Poly1305 uses a one-time key for each message. In ChaCha20-Poly1305, this key is derived from the first 32 bytes of ChaCha20's output for that specific key and nonce combination. Different nonce means different Poly1305 key, which means safe.

Why Key Reuse Breaks Poly1305

If an attacker sees two messages authenticated with the same Poly1305 key, they can do algebra to recover that key. Once they have the key, they can forge authentication tags for any message.

This is why the nonce in ChaCha20-Poly1305 is so critical. It ensures each message gets a fresh Poly1305 key.

SHA Family: Hash Functions for Everything Else

Sometimes you need hash functions directly for key derivation, password storage, digital signatures, or building other MACs.

SHA-1: Don't Use It

SHA-1 produces 160-bit hashes and was standard for decades. It's now broken. Researchers can create collisions (two different inputs with the same hash) at practical cost. Don't use SHA-1 for anything security-critical.

SHA-2: The Current Standard

SHA-2 is actually a family. SHA-256 provides 256-bit output and works best on 32-bit systems. SHA-384 provides 384-bit output and is a truncated SHA-512. SHA-512 provides 512-bit output and works best on 64-bit systems. SHA-512/256 uses SHA-512 internals with 256-bit output and resists "length extension attacks."

For most purposes, SHA-256 is the right choice. Use SHA-512 if you're on 64-bit systems and want maximum security margin.

SHA-3: The Backup Plan

SHA-3 uses completely different internals than SHA-2. It's slightly slower in software but provides algorithmic diversity. If someone finds a weakness in SHA-2's design, SHA-3 probably isn't affected. Consider it insurance.

HMAC: Turning Hashes into MACs

HMAC (Hash-based Message Authentication Code) wraps a hash function to create an authentication tag. HMAC-SHA256 is common for API authentication, JWT tokens, and similar applications.

The formula looks like: HMAC(key, message) = Hash((key XOR pad1) + Hash((key XOR pad2) + message)). The double-hashing makes it secure even if the underlying hash has certain weaknesses.

HKDF: Deriving Keys from Keys

HKDF (HMAC-based Key Derivation Function) takes some initial key material and derives multiple keys from it. It's used extensively in TLS 1.3 and the Signal Protocol to create separate keys for encryption, authentication, and other purposes from a single shared secret.


Part 5: Making the Choice

Now that you understand the pieces, here's how to choose.

The Simple Decision Tree

Do you have AES hardware acceleration (AES-NI)? If yes, AES-256-GCM is probably fastest. If no, ChaCha20-Poly1305 is faster AND more secure because it has no timing leaks. If you don't know, use ChaCha20-Poly1305 as a safe default.

Are you encrypting disk or storage? If yes, use AES-256-XTS because it has no data expansion and works well with sector-level encryption.

Is nonce management tricky in your system? If yes, consider XChaCha20-Poly1305 or AES-256-GCM-SIV.

Are you working with constrained devices like IoT or embedded systems? If yes, use ChaCha20-Poly1305 because it provides consistent performance everywhere.

Quick Reference Table

| Situation | Recommendation | Why | |-----------|----------------|-----| | Web server with modern CPU | AES-256-GCM | Maximum throughput with AES-NI | | Mobile app | ChaCha20-Poly1305 | Consistent speed, no side channels | | Cross-platform library | ChaCha20-Poly1305 | Safe regardless of hardware | | Full-disk encryption | AES-256-XTS | No expansion, sector-compatible | | Multi-device sync with random nonces | XChaCha20-Poly1305 | 192-bit nonce enables safe random generation | | Key wrapping | AES-256-GCM-SIV | Nonce-misuse resistant |


Part 6: The Pitfalls

Understanding failure modes is as important as understanding the algorithms. These are the mistakes that turn "encrypted" into "compromised."

Nonce Reuse: The Silent Killer

For stream ciphers (ChaCha20, AES-CTR, AES-GCM):

If you encrypt two messages M1 and M2 with the same key and nonce, an attacker who captures both ciphertexts C1 and C2 can compute:

C1 XOR C2 = M1 XOR M2

Now they have the XOR of your plaintexts. With some analysis (especially if one message has known structure), they can often recover both messages completely.

For AES-GCM specifically:

It gets worse. GCM's authentication uses a secret value H derived from the key. If you reuse a nonce, the attacker can solve for H. Once they have H, they can forge authentication tags for any message. Total system compromise.

Real-world example: In 2014, Android's SecureRandom had a bug that caused ECDSA signature nonces to repeat. Attackers extracted private keys from Bitcoin transactions on the public blockchain.

Prevention: Use counters when you can guarantee ordering, or use XChaCha20 or GCM-SIV when you can't.

Padding Oracles: Why CBC Is Dangerous

CBC mode requires padding the message to a multiple of the block size. If the decryption code reveals whether padding is valid (even through timing differences), attackers can decrypt messages byte-by-byte.

The Lucky13 attack exploited tiny timing differences in TLS implementations, where differences of a few microseconds were enough. POODLE exploited similar issues in SSL 3.0.

Prevention: Use AEAD modes like GCM or ChaCha20-Poly1305. They don't have padding.

Timing Attacks: When Code Leaks Secrets

Software AES implementations often use lookup tables for the S-box. Which table entries you access depends on the key. By measuring cache timing, attackers can figure out which entries were accessed and recover the key.

This works even remotely. Bernstein demonstrated it over a network in 2005.

Prevention: Use AES-NI (constant-time in hardware) or ChaCha20 (no lookup tables at all).

Tag Comparison: The One-Byte Leak

When verifying authentication tags, this code is vulnerable:

// BAD: timing varies with matching prefix length
if (memcmp(received_tag, computed_tag, 16) == 0) { ... }

Standard memcmp returns immediately when it finds a difference. By measuring response time, attackers can determine how many bytes match and eventually forge valid tags.

Prevention: Use constant-time comparison functions like crypto_verify_16() or CRYPTO_memcmp() from your crypto library.


Part 7: Real-World Protocols

Let's see how production systems make these choices.

TLS 1.3: The Web's Encryption

TLS 1.3 mandates AEAD, which means no more CBC. The mandatory cipher suite is TLS_AES_128_GCM_SHA256. ChaCha20-Poly1305 is strongly recommended as an alternative, specifically for clients without AES hardware.

Modern browsers negotiate ChaCha20-Poly1305 with mobile devices and AES-GCM with desktops, automatically picking the faster option.

WireGuard: Opinionated VPN

WireGuard made a radical choice: ChaCha20-Poly1305 only. No negotiation, no options, no configuration.

Why? Simplicity and auditability. The entire codebase is around 4,000 lines. By picking one well-understood algorithm and optimizing for it, WireGuard avoids the complexity that creates vulnerabilities in protocols like OpenVPN and IPsec.

Signal Protocol: Messaging Security

Signal uses AES-256-CBC with HMAC-SHA256 in an Encrypt-then-MAC construction. The Double Ratchet algorithm combines Diffie-Hellman ratcheting (which provides new key agreement for each message exchange) with symmetric ratcheting (which derives new keys from old ones using HKDF).

This provides both forward secrecy (past messages stay secure if keys leak) and post-compromise security (security recovers after a breach).


Part 8: Future-Proofing for Quantum

Quantum computers threaten asymmetric cryptography (RSA, elliptic curves) severely. For symmetric cryptography, the impact is milder but real.

Grover's Algorithm

A quantum computer running Grover's algorithm can search a key space in √N time instead of N time. This effectively halves your key strength:

AES-128 drops from 2^128 to approximately 2^64 security. AES-256 drops from 2^256 to approximately 2^128 security.

2^64 is uncomfortably close to practical attacks. 2^128 remains safe.

Practical Recommendations

For new systems designed today, use 256-bit keys like AES-256 or ChaCha20 (which already uses 256-bit keys). Use SHA-384 or SHA-512 where hash security matters. Design for algorithm agility so you can swap algorithms later. Inventory your 128-bit deployments for future migration.

The practical quantum computing timeline extends to 2030-2035 and beyond, but "encrypt now, decrypt later" attacks mean sensitive long-term data needs protection today.


Conclusion

Symmetric cryptography has converged on a clear set of best practices.

For encryption: ChaCha20-Poly1305 is the universal safe choice. AES-256-GCM is faster when you have hardware support.

For hashing: SHA-256 for most purposes. SHA-512 for maximum security. Avoid SHA-1.

For key derivation: HKDF with SHA-256 or SHA-512.

For everything: Never reuse nonces. Always authenticate. Use constant-time comparisons.

Like Bellini's Sacred Allegory, a painting whose meaning remains encrypted after five centuries and accessible only to those with the right interpretive keys, these ciphers hide plaintext behind mathematical transformations that reveal their secrets only to those possessing the cryptographic key. The difference: modern cryptography's security rests on mathematical certainty rather than cultural knowledge.

Get the implementation right, and your secrets remain hidden not just from casual observers, but from any adversary, present or future.

Replies (0)

No replies yet. Be the first to leave a comment!