# Ciphers
All ciphers are AEAD, from the RustCrypto project. Nonce length is 12 bytes
and tag length is 16 bytes for all three.
| `aes-128-gcm` | 16 bytes | `aes-gcm` |
| `aes-256-gcm` | 32 bytes | `aes-gcm` |
| `chacha20-poly1305` | 32 bytes | `chacha20poly1305` |
The alias `chacha20-ietf-poly1305` is accepted and treated as
`chacha20-poly1305`. The default cipher (when none is specified) is
`chacha20-poly1305`.
Selected with the `cipher` config field / `-m, --cipher` flag; **both ends
must use the same cipher** (there is no negotiation — a mismatch just means
packets fail to decrypt and are dropped).
## Hardware acceleration (especially on ARM)
`chacha20-poly1305` uses runtime SIMD feature detection, so it is fast out of
the box on every target. AES-GCM uses AES-NI automatically on x86-64, **but on
aarch64 (Raspberry Pi, Apple Silicon, Windows-on-ARM) the ARMv8 AES backend is
gated behind compile-time target features** — a plain `cargo build` for ARM
runs AES-GCM in slow constant-time software. So:
- On ARM hardware that lacks (or isn't built for) AES acceleration, prefer
`chacha20-poly1305` — it is both faster and simpler there.
- To use AES-GCM at full speed on ARM, build with the crypto features
enabled:
```sh
RUSTFLAGS="-C target-feature=+aes,+neon" cargo build --release
RUSTFLAGS="-C target-cpu=native" cargo build --release
```
## Does the cipher matter for throughput?
At broadband rates (≤ 100 Mbit/s) the link, not the AEAD, is the bottleneck —
cipher choice barely moves the numbers. On CPU-bound links (or ARM without AES
acceleration) it matters more. See the [benchmarks](./benchmarks).