Bei AES-GCM vs. ChaCha20-Poly1305 geht es um die Wahl zwischen den beiden Algorithmen für authentifizierte Verschlüsselung, die fast den gesamten verschlüsselten Datenverkehr tragen: TLS 1.3, QUIC, SSH, WireGuard, age und den Großteil der Verschlüsselung auf Anwendungsebene. Richtig eingesetzt sind beide sicher, beide versagen gravierend, sobald sich eine Nonce wiederholt, und ihre Geschwindigkeit hängt stark von der Hardware ab.
Auf einen Blick
| AES-GCM | ChaCha20-Poly1305 | |
|---|---|---|
| Spezifikation | NIST SP 800-38D (2007) | RFC 8439 (2018, ersetzt RFC 7539) |
| Schlüssellängen | 128, 192, 256 Bit | 256 Bit |
| Nonce | 96 Bit empfohlen | 96 Bit (192 Bit bei XChaCha20) |
| Tag | 128 Bit (kürzer erlaubt, nicht empfohlen) | 128 Bit |
| Maximale Datenmenge pro Nachricht | etwa 64 GiB (2³² − 2 Blöcke) | 256 GiB (2³² 64-Byte-Blöcke) |
| Budget an Zufalls-Nonces pro Schlüssel | 2³² Nachrichten (SP 800-38D) | Gleiche Größenordnung bei 96-Bit-Nonces; mit XChaCha20 praktisch unbegrenzt |
| Nonce-Wiederverwendung | Gibt das XOR der Klartexte und den GHASH-Schlüssel preis und ermöglicht Fälschungen | Gibt das XOR der Klartexte preis und ermöglicht Fälschungen |
| Durchsatz, 16 KiB, OpenSSL 3.6.5 | 10,4 GB/s (AES-128), 8,8 GB/s (AES-256) | 2,1 GB/s |
| Durchsatz, 16-Byte-Nachrichten | 0,14 GB/s (AES-256) | 0,52 GB/s |
| Zeitkonstant ohne Hardware-Unterstützung | Schwierig (tabellenbasiertes AES gibt über den Cache Informationen preis) | Von Haus aus (Addieren, Rotieren, XOR) |
| Zugelassen nach FIPS 140-3 | Ja | Nein |
| TLS 1.3 | TLS_AES_128_GCM_SHA256 (Pflicht), TLS_AES_256_GCM_SHA384 (empfohlen) | TLS_CHACHA20_POLY1305_SHA256 (empfohlen) |
| Weitere Einsatzgebiete | JWE A128GCM und A256GCM, IPsec, SSH aes256-gcm@openssh.com, Festplatten- und Cloud-Verschlüsselung | WireGuard, SSH chacha20-poly1305@openssh.com, age, libsodium |
Durchsatz gemessen mit openssl speed -evp auf einem Kern eines Apple M4 Max, der Befehle für AES und Polynommultiplikation besitzt.
Die Leistung hängt von der CPU ab
AES-GCM ist für Hardware gebaut: AES-Runden laufen als einzelne Befehle (AES-NI auf x86, die Kryptografie-Erweiterungen von ARMv8), und GHASH nutzt die übertragslose Multiplikation. Stehen beide zur Verfügung, erreicht es hier etwa 10 GB/s pro Kern, rund das Fünffache von ChaCha20-Poly1305. Ohne sie muss AES entweder Lookup-Tabellen verwenden, die langsam sind und über das Cache-Timing Schlüsselbits verraten, oder Bitslicing-Code, der zeitkonstant, aber langsam ist. ChaCha20 braucht nur 32-Bit-Additionen, Rotationen und XORs und läuft daher auf jeder CPU schnell und zeitkonstant, auch auf einfachen Smartphones und Mikrocontrollern.
Bei kleinen Nachrichten schrumpft der Abstand oder kehrt sich um: Mit 16-Byte-Eingaben erreichte ChaCha20-Poly1305 0,52 GB/s gegenüber 0,14 GB/s für AES-256-GCM, weil die Initialisierungskosten von GCM überwiegen. Bei typischen TLS-Records (bis 16 KiB) auf Servern ist AES-GCM schneller; bei vielen winzigen Paketen ist der Unterschied so oder so gering.
Nonces und Grenzen
Beide sind Verschlüsselungsverfahren im Zählermodus mit Einmal-Authentifikatoren, daher darf sich das Paar (Schlüssel, Nonce) nie wiederholen. Eine Wiederholung legt das XOR der beiden Klartexte offen. Bei GCM kann ein Angreifer außerdem den Authentifizierungs-Teilschlüssel ermitteln und unter diesem Schlüssel Nachrichten fälschen; bei Poly1305 wird der Einmalschlüssel offengelegt, was Fälschungen für diese Nonce erlaubt. NIST SP 800-38D begrenzt zufällig erzeugte 96-Bit-IVs auf 2³² Aufrufe pro Schlüssel, damit die Kollisionswahrscheinlichkeit vernachlässigbar bleibt; TLS umgeht das Problem, indem es Nonces aus den Sequenznummern der Records ableitet und die Schlüssel erneuert.
Erzeugt dein System für viele Nachrichten unter einem Schlüssel zufällige Nonces, verwende eine Konstruktion mit größerem Nonce-Raum oder mit Resistenz gegen Nonce-Missbrauch:
- XChaCha20-Poly1305 (192-Bit-Nonces, beschrieben in einem Internet-Draft der IRTF und unter anderem in libsodium implementiert), bei dem zufällige Nonces für praktisch unbegrenzt viele Nachrichten sicher sind;
- AES-GCM-SIV (RFC 8452), bei dem eine Nonce-Wiederverwendung nur verrät, ob zwei Nachrichten identisch waren;
- oder Schlüsselableitung pro Nachricht oder pro Sitzung, wie es TLS, age und HPKE tun.
Keiner der beiden Algorithmen bindet das Chiffrat an einen bestimmten Schlüssel (kein Key Commitment): Ein Chiffrat lässt sich so konstruieren, dass es unter zwei verschiedenen Schlüsseln gültig entschlüsselt wird. Das spielt bei Verfahren mit mehreren Empfängern oder mit Passwörtern eine Rolle (das Problem der „Invisible Salamanders“). Ergänze ein Key Commitment, wenn Angreifer Schlüssel wählen können.
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
const plaintext = Buffer.from("card ending 4242, amount 19.99");
const aad = Buffer.from("order=7781");
for (const algorithm of ["aes-256-gcm", "chacha20-poly1305"]) {
const key = randomBytes(32);
const nonce = randomBytes(12); // 96-bit nonce for both; never reuse with the same key
const cipher = createCipheriv(algorithm, key, nonce, { authTagLength: 16 });
cipher.setAAD(aad);
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
const tag = cipher.getAuthTag();
const decipher = createDecipheriv(algorithm, key, nonce, { authTagLength: 16 });
decipher.setAAD(aad);
decipher.setAuthTag(tag);
const roundTrip = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
console.log(algorithm.padEnd(18), "ciphertext", ciphertext.length, "bytes + tag", tag.length, "bytes, decrypts:", roundTrip.equals(plaintext));
}
aes-256-gcm ciphertext 30 bytes + tag 16 bytes, decrypts: true
chacha20-poly1305 ciphertext 30 bytes + tag 16 bytes, decrypts: true
Wie TLS zwischen beiden wählt
TLS 1.3 verlangt von jeder Implementierung Unterstützung für TLS_AES_128_GCM_SHA256 und empfiehlt AES-256-GCM und ChaCha20-Poly1305. Clients ohne AES-Hardware führen ChaCha20 an erster Stelle auf; Server, die diese Präferenz respektieren, liefern jedem Client das für ihn schnellere Verschlüsselungsverfahren. OpenSSL setzt das mit der Option PrioritizeChaCha (SSL_OP_PRIORITIZE_CHACHA) um, die ChaCha20 nur dann bevorzugt, wenn der Client es zuerst aufgeführt hat; BoringSSL nutzt für denselben Zweck Gruppen gleicher Präferenz. Der TLS-Konfigurationsgenerator erzeugt Servereinstellungen, die beide Cipher Suites beibehalten.
Wann welches Verfahren passt
- TLS- und QUIC-Server: Aktiviere beide; bevorzuge AES-GCM, aber gib Clients, die ChaCha20 an die erste Stelle setzen, auch ChaCha20.
- Umgebungen mit FIPS 140-3: AES-GCM; ChaCha20-Poly1305 ist kein zugelassener Algorithmus.
- Mobilgeräte, IoT und Mikrocontroller ohne AES-Beschleunigung: ChaCha20-Poly1305.
- Verschlüsselung auf Anwendungsebene mit zufälligen Nonces und langlebigen Schlüsseln: XChaCha20-Poly1305 oder AES-GCM-SIV.
- JWE:
A256GCM(oderA128GCM); ChaCha20 steht nicht in der Kern-Registry von JWA. - Sehr große Einzelnachrichten (über 64 GiB): Teile sie in Chunks mit je eigener Nonce auf (wie age es mit ChaCha20-Poly1305-Chunks von 64 KiB tut); verlass dich nicht auf die Grenzen pro Nachricht.
Hinweise zur Migration
In einem gut entworfenen System ändert der Wechsel des AEAD-Verfahrens das Format des Chiffrats, aber sonst nichts.
- Versioniere dein Format: Speichere mit jedem Chiffrat einen Algorithmusbezeichner und eine Schlüssel-ID, damit alte Daten lesbar bleiben.
- Trenne die Schlüssel nach Algorithmus: Leite unterschiedliche Schlüssel ab (zum Beispiel mit HKDF und verschiedenen info-Strings), statt einen Schlüssel für beide zu verwenden.
- Prüfe die Nonce-Erzeugung erneut: Zähler müssen Neustarts überdauern; zufällige Nonces müssen innerhalb der oben genannten Grenzen bleiben, sonst steht der Wechsel zu XChaCha20 an.
- Verschlüssle nach und nach neu: Entschlüssle mit dem alten Algorithmus und schreibe mit dem neuen, sobald Datensätze ohnehin angefasst werden – es sei denn, eine Compliance-Frist erfordert eine Massenmigration.
- Miss auf der echten Hardware: Die Rangfolge bei der Geschwindigkeit kehrt sich zwischen Server-CPUs und kleinen Geräten um.