AES-256 vs ChaCha20: Which Encryption Algorithm Should You Choose?
Open a TLS handshake, a VPN config, or a disk encryption tool and you'll see the same two names. AES-256 is the long-vetted standard that banks, governments, and chip makers have leaned on for two decades. ChaCha20 is the software-first design Daniel J. Bernstein published in 2008, built to be fast on hardware that has no AES instructions at all. Both are considered strong. The real question is which one fits the machine your code actually runs on.
The 30-Second Verdict
For most projects the answer is boring in a good way: use the authenticated encryption your platform's crypto library already gives you — AES-256-GCM or ChaCha20-Poly1305 — and don't build your own. On servers and laptops made in the last decade, AES-GCM usually wins, because the CPU has dedicated AES instructions. On cheap ARM chips, older phones, or anything without AES acceleration, ChaCha20 is typically faster and far more predictable.
Two Different Designs
AES-256
AES is a block cipher. It scrambles fixed 128-bit blocks using a 256-bit key and fourteen rounds of substitution, shifting, and mixing. A block cipher doesn't encrypt a message by itself, so you wrap it in a mode: GCM for authenticated encryption, XTS for disk sectors, and CBC lingering in older protocols where you'd rather not be.
ChaCha20
ChaCha20 is a stream cipher. It takes a 256-bit key, a 96-bit nonce, and a block counter, runs twenty rounds of addition, rotation, and XOR, and produces a keystream that gets XORed with your plaintext. Pair it with Poly1305 for authentication and you have ChaCha20-Poly1305 — an authenticated cipher with no lookup tables to leak through.
Speed Depends on the Silicon
This is where the two split in practice.
AES on a modern x86-64 or ARMv8 core isn't really software at all. A handful of instructions handle a whole round, and memory bandwidth becomes the bottleneck long before the cipher does. AES-256 is a little slower than AES-128 because of the extra rounds, but on accelerated hardware the gap is small enough that you can choose 256 for the larger security margin and stop worrying about it.
ChaCha20 runs entirely in general-purpose registers. On a CPU with no AES unit, it can be several times faster than a table-based AES implementation, and it behaves the same everywhere: no branch on hardware capability, no cache-timing-sensitive lookups, no surprises when you move from a test box to a phone. It also parallelizes well, with vectorized implementations in most libraries, and its lower energy cost per byte can matter on battery-powered devices.
Hardware Support, Plainly
- x86 and x86-64: AES-NI has been standard since roughly 2010, and newer chips add wider vector AES. ChaCha20 gets AVX2 and AVX-512 optimizations in modern libraries.
- ARM: the ARMv8 crypto extensions cover AES, and most phones and ARM server chips from the mid-2010s onward include them. Very cheap or very old cores often don't.
- RISC-V, MIPS, microcontrollers: AES instructions are the exception, so ChaCha20 usually has the advantage.
- HSMs and validated modules: AES is nearly universal; ChaCha20 support is less common there.
Security: What Actually Goes Wrong
Neither cipher has a practical break. AES-256 carries a theoretical related-key weakness that requires an attacker who can already choose your keys, and twenty-round ChaCha20 has a comfortable margin. The failure modes worth losing sleep over are operational:
- Nonce reuse. With the same key, repeating a nonce in GCM or ChaCha20-Poly1305 leaks the XOR of plaintexts and can enable forgery. Use a counter where you can, and cap how much data one key protects.
- Weak randomness. Keys and nonces come from your operating system's CSPRNG, not from a timestamp or a counter you invented.
- Software AES on shared hardware. Table-based implementations can leak key material through cache timing. Use constant-time code or hardware instructions.
- Rolling your own mode. Unauthenticated encryption is a bug waiting to be found.
- Ignoring the tag. Verify authenticity before you act on decrypted data.
Both ciphers are strong. Almost every real-world break happens in the protocol around them, not in the algorithm itself.
Where Each One Shows Up
AES-256 appears in TLS 1.3, IPsec, WPA2 and WPA3, LUKS and BitLocker disk encryption via AES-XTS, and nearly every hardware security module. If you need FIPS 140 validation for a regulated deployment, AES is the well-trodden path; ChaCha20 support in validated modules has historically been thinner.
ChaCha20-Poly1305 is the default in WireGuard, a common choice for SSH, the browser fallback when AES acceleration isn't available or fast enough, and the cipher behind file encryption tools like age. Linux's Adiantum uses a ChaCha variant to bring fast disk encryption to devices too weak for AES-XTS.
How to Choose in Practice
- Always use an AEAD. AES-256-GCM or ChaCha20-Poly1305. Skip raw CTR or CBC unless you're maintaining legacy interoperability.
- Benchmark on your real target. Not on your dev laptop — on the phone, the embedded board, or the VM shape you actually deploy.
- Support both if you can't predict the client. TLS 1.3 negotiates this for you: AES-GCM when hardware helps, ChaCha20-Poly1305 when it doesn't.
- Let constraints decide. A FIPS requirement, an existing HSM, or a protocol that mandates a suite will settle the argument faster than any benchmark.
- Use a vetted library. libsodium, OpenSSL, BoringSSL, or your language's standard crypto package — with the defaults left intact.
- Protect the key lifecycle. Rotate keys, enforce nonce discipline, and plan how you'll retire old ciphertext.
Both algorithms are excellent, and neither will be the reason your system fails. Pick the one your platform accelerates and your library implements cleanly, then spend the time you saved on key management, nonce handling, and update paths — the parts that break real systems.
Photo: Damir K / Pexels




