Why we chose XChaCha20-Poly1305 over AES-GCM for vault encryption
The cipher selection problem
When we designed TwinMail's vault encryption layer, we evaluated two primary candidates: AES-256-GCM and XChaCha20-Poly1305. Both are AEAD (Authenticated Encryption with Associated Data) ciphers with strong security properties. Both are widely deployed and audited.
The choice between them is not about raw security — both are considered secure under standard assumptions. The choice is about operational safety, nonce management, and the specific constraints of a local-first email client.
Nonce safety is the deciding factor
AES-GCM uses a 96-bit (12-byte) nonce. With random nonce generation, the birthday bound gives a collision probability of approximately 2^-32 after encrypting 2^32 messages under the same key. A nonce collision in AES-GCM is catastrophic: it leaks the authentication key and allows forgery of arbitrary ciphertexts.
XChaCha20-Poly1305 uses a 192-bit (24-byte) nonce. The birthday bound extends to approximately 2^96 messages before collision becomes probable. This is a qualitative difference, not merely a quantitative one.
Why nonce size matters for TwinMail
TwinMail's vault encrypts individual messages, overlay state operations, and sync blobs. A single vault may accumulate hundreds of thousands of encrypted records over its lifetime. With multiple devices generating encrypted records concurrently, the total record count grows faster than a single-device estimate would suggest.
Under AES-GCM with random nonces, a vault with 100,000 records has a nonce collision probability of roughly 1 in 10^19 — acceptable in isolation. But across millions of users, each with their own vault, the aggregate probability becomes concerning. This is the multi-key birthday problem: even if each individual vault is safe, the population-level risk grows linearly with user count.
XChaCha20-Poly1305 eliminates this concern entirely. The 192-bit nonce space is large enough that random generation is safe for any realistic workload.
Performance comparison
AES-GCM benefits from hardware acceleration via AES-NI instructions on x86 processors and ARMv8 cryptographic extensions. On hardware with these instructions, AES-GCM is typically 2-4x faster than ChaCha20-Poly1305 in raw throughput.
However, TwinMail's workload is not throughput-bound. Individual messages are typically 1-100 KB. Encrypting a 100 KB message takes approximately:
| Cipher | Time (no HW accel) | Time (with AES-NI/ARMv8) |
|---|---|---|
| AES-256-GCM | 12 us | 3 us |
| XChaCha20-Poly1305 | 15 us | 15 us |
Both are sub-millisecond for any realistic message size. The throughput advantage of AES-GCM is irrelevant when the total encryption time per user action is measured in microseconds.
On devices without hardware AES acceleration — which includes some ARM-based devices and older hardware — ChaCha20 is actually faster than AES-GCM. This makes XChaCha20-Poly1305 a more portable choice.
Key derivation
TwinMail derives vault encryption keys from the user's passphrase using Argon2id with the following parameters:
- Memory: 256 MB
- Iterations: 4
- Parallelism: 2
- Output: 256-bit key
These parameters are tuned for a 1-2 second derivation time on a 2024 mid-range laptop. The derived key is used directly with XChaCha20-Poly1305 — no additional key schedule is required, unlike AES which requires an expanded key schedule computed from the raw key.
The libsodium advantage
XChaCha20-Poly1305 is the default AEAD cipher in libsodium, the most widely audited and deployed cryptographic library for application developers. Using the default, well-tested path reduces the surface area for implementation errors.
TwinMail uses libsodium's crypto_aead_xchacha20poly1305_ietf_encrypt and crypto_aead_xchacha20poly1305_ietf_decrypt functions directly, via Rust bindings in the Tauri backend. This means:
- No custom cryptographic code
- No cipher negotiation
- No algorithm agility (which is itself an attack surface)
Decision summary
We chose XChaCha20-Poly1305 because:
- Nonce safety — 192-bit nonces eliminate multi-key birthday concerns at population scale
- Portability — consistent performance across hardware with and without AES acceleration
- Simplicity — libsodium default path, no key schedule, no cipher negotiation
- Auditability — single cipher, single library, no algorithm selection logic
The tradeoff is forgoing AES-NI acceleration on hardware that supports it. For TwinMail's workload, this is not a meaningful cost.