ChaCha20-Poly1305 est l’alternative moderne à AES-GCM pour le chiffrement authentifié symétrique. Conçu par Daniel J. Bernstein en 2008, standardisé en RFC 8439 en 2018, il s’est imposé en quelques années comme le second choix universel — utilisé par TLS 1.3 (en option à côté d’AES-GCM), WireGuard (qui ne propose que ChaCha20-Poly1305), Signal, OpenSSH, et la plupart des protocoles récents. Son intérêt principal : il tourne plus vite qu’AES-GCM en logiciel pur sur des processeurs sans accélération matérielle AES (smartphones d’entrée de gamme, microcontrôleurs, vieux serveurs ARM) — ce qui en fait l’option pertinente pour beaucoup de PC ouest-africains. Ce tutoriel propose une démarche en sept étapes pour chiffrer et déchiffrer en ChaCha20-Poly1305 avec OpenSSL et Python, comparer aux performances d’AES-GCM, et choisir entre les deux selon le contexte.
Pour la vue d’ensemble, voir le guide principal : Cryptographie pratique pour développeurs et sysadmins.
Prérequis
- OpenSSL 3.0+ (incluant le support ChaCha20-Poly1305)
- Python 3.10+ avec la bibliothèque cryptography
- Maîtrise du précédent tutoriel AES-GCM recommandée
- 30 minutes
Étape 1 — Comprendre l’algorithme
ChaCha20 est un chiffrement par flux à 256 bits de clé. Contrairement à AES qui chiffre des blocs de 128 bits, ChaCha20 produit un flux de pseudo-aléatoire de la taille des données et XORe avec elles. Poly1305 est un MAC (Message Authentication Code) utilisé pour authentifier le chiffré. La combinaison ChaCha20-Poly1305 — exactement comme AES-GCM — fournit du chiffrement authentifié : confidentialité plus détection des altérations.
Trois différences pratiques avec AES-GCM méritent d’être connues. Premièrement, ChaCha20 utilise un nonce de 96 bits identique à GCM en RFC 8439 — la migration code-à-code est mécanique. Deuxièmement, ChaCha20 n’a pas de bénéfice à être implémenté en matériel — le code C ou même Python pur tourne près des limites du processeur, alors qu’AES sans instruction AES-NI rame. Troisièmement, ChaCha20 est conçu pour être résistant aux attaques par canal auxiliaire (timing) en logiciel, ce qu’AES n’est qu’avec une instruction matérielle dédiée.
Règle pratique : sur un serveur Intel ou AMD x86_64 récent, AES-GCM accéléré par AES-NI est plus rapide. Sur ARM sans accélération crypto (typiquement Raspberry Pi ancien, microcontrôleurs IoT, certains téléphones d’entrée de gamme), ChaCha20-Poly1305 gagne. Pour le code multi-plateforme qui doit tourner partout, ChaCha20 est le choix prudent.
Étape 2 — Chiffrer et déchiffrer en Python
L’API de la bibliothèque cryptography est volontairement identique à AESGCM — même signature de méthodes, juste un nom différent :
import os
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
def chiffrer(donnees: bytes, cle: bytes, donnees_associees: bytes = b'') -> bytes:
nonce = os.urandom(12)
chacha = ChaCha20Poly1305(cle)
ciphertext = chacha.encrypt(nonce, donnees, donnees_associees)
return nonce + ciphertext
def dechiffrer(donnees_chiffrees: bytes, cle: bytes, donnees_associees: bytes = b'') -> bytes:
nonce = donnees_chiffrees[:12]
ciphertext = donnees_chiffrees[12:]
chacha = ChaCha20Poly1305(cle)
return chacha.decrypt(nonce, ciphertext, donnees_associees)
# Test
cle = ChaCha20Poly1305.generate_key() # 32 octets
chiffre = chiffrer(b"Donnees ultra-sensibles", cle, b"contexte:facturation:2026")
print(chiffre.hex())
print(dechiffrer(chiffre, cle, b"contexte:facturation:2026"))
Comme pour AESGCM, les règles cardinales restent : nonce différent à chaque chiffrement, clé de 256 bits, données associées authentifiées sans être chiffrées. Le tag d’authentification de 16 octets est concaténé en fin du ciphertext, transparent pour l’API.
Pour migrer un code AES-GCM existant vers ChaCha20-Poly1305, le travail consiste essentiellement à remplacer AESGCM par ChaCha20Poly1305 dans les imports et les instanciations. Les anciennes clés AES-256 (32 octets) sont directement utilisables comme clés ChaCha20 (32 octets) — sans rechiffrer les données existantes, mais en marquant clairement quelle suite a chiffré quel ciphertext (les deux ne sont pas inter-déchiffrables).
Étape 3 — Chiffrer un fichier avec OpenSSL
OpenSSL 3 supporte ChaCha20-Poly1305 via la commande enc. La syntaxe :
openssl enc -chacha20 -salt -pbkdf2 -iter 600000 -in rapport.pdf -out rapport.enc
L’option -chacha20 sélectionne ChaCha20 (sans Poly1305 dans la commande enc, qui ne propose pas l’AEAD direct). Pour le chiffrement authentifié complet, on passe par openssl enc côté chiffrement par flux et on calcule un HMAC séparé, ou — plus simple — on bascule sur Python.
Le déchiffrement reprend la même formulation avec -d :
openssl enc -d -chacha20 -pbkdf2 -iter 600000 -in rapport.enc -out rapport.dec
Pour la production, le script Python de l’étape précédente — qui utilise ChaCha20Poly1305 AEAD — reste préférable au mode brut d’OpenSSL CLI.
Étape 4 — Mesurer les performances réelles
Petit benchmark Python pour comparer AES-GCM et ChaCha20-Poly1305 sur la machine locale :
import os, time
from cryptography.hazmat.primitives.ciphers.aead import AESGCM, ChaCha20Poly1305
donnees = os.urandom(100 * 1024 * 1024) # 100 Mo
cle = os.urandom(32)
nonce = os.urandom(12)
t0 = time.perf_counter()
AESGCM(cle).encrypt(nonce, donnees, None)
t_aes = time.perf_counter() - t0
t0 = time.perf_counter()
ChaCha20Poly1305(cle).encrypt(nonce, donnees, None)
t_cha = time.perf_counter() - t0
print(f"AES-GCM: {100/t_aes:.1f} Mo/s")
print(f"ChaCha20-Poly1305: {100/t_cha:.1f} Mo/s")
Sur un PC Intel récent avec AES-NI, AES-GCM tourne typiquement entre 1 000 et 3 000 Mo/s, tandis que ChaCha20-Poly1305 plafonne autour de 500 à 1 000 Mo/s. Sur un Raspberry Pi 4 (Cortex-A72 sans extensions crypto matérielles), le rapport s’inverse : ChaCha20 est deux à trois fois plus rapide qu’AES.
Le test n’est utile qu’en relatif sur la cible de production. Mesurer plutôt que supposer.
Étape 5 — Cas d’usage : VPN WireGuard
WireGuard, le VPN moderne qui remplace progressivement OpenVPN dans les déploiements sysadmin, n’utilise que ChaCha20-Poly1305 — pas d’AES, pas de négociation d’algorithme. Ce choix opinion est délibéré : Bernstein et l’équipe WireGuard estiment qu’une seule suite cryptographique soigneusement choisie élimine toute une classe d’attaques par négociation (downgrade) sans sacrifier la sécurité.
Pour un sysadmin déployant WireGuard sur un VPS Hetzner, OVH ou Sonatel Cloud, ce tutoriel donne les fondamentaux. Lire ensuite la documentation WireGuard pour la configuration concrète : wg-quick, fichiers .conf, paire de clés par pair (générées avec wg genkey), routage. Le tunnel chiffre tout le trafic en ChaCha20-Poly1305, authentifie par Curve25519, et tourne souvent à des débits proches du gigabit même sur du matériel modeste — précisément grâce au choix logiciel-friendly de ChaCha20.
Étape 6 — Cas d’usage : TLS 1.3
TLS 1.3 propose trois suites de chiffrement officielles : TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, et TLS_CHACHA20_POLY1305_SHA256. Le serveur et le client négocient celle qu’ils préfèrent — sur les browsers modernes, le client annonce souvent ChaCha20 en tête de liste s’il n’a pas d’AES-NI.
Pour vérifier les suites supportées par un serveur (par exemple itskillscenter.io) :
openssl s_client -tls1_3 -connect itskillscenter.io:443 -ciphersuites TLS_CHACHA20_POLY1305_SHA256 < /dev/null 2>&1 | grep -E "Cipher|Protocol"
Si le serveur accepte la suite, la sortie indique Protocol: TLSv1.3 et Cipher: TLS_CHACHA20_POLY1305_SHA256. Si ChaCha20-Poly1305 n’est pas dans la liste configurée côté Nginx, ajouter dans la configuration TLS la directive spécifique TLS 1.3 (Nginx 1.19.4+) : ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256; puis recharger Nginx. À noter que la directive ssl_ciphers classique ne s’applique qu’aux versions TLS 1.2 et inférieures — pour TLS 1.3 sur Nginx, c’est bien ssl_conf_command Ciphersuites qu’il faut utiliser.
Les mathématiques en clair
ChaCha20 est l’exemple parfait d’une cryptographie moderne minimaliste : pas de tables, pas d’instructions matérielles spécifiques, juste trois opérations élémentaires répétées un grand nombre de fois. Cette simplicité n’est pas une faiblesse — c’est précisément ce qui rend ChaCha20 rapide en logiciel et résistant aux attaques par canal auxiliaire. Les mathématiques sous-jacentes tiennent en deux lignes une fois qu’on a vu l’idée.
L’idée centrale : ARX (Add, Rotate, XOR)
ChaCha20 manipule un état de 16 mots de 32 bits chacun, soit 512 bits ou 64 octets au total, arrangé comme une matrice 4×4. Les seules opérations utilisées sont : l’addition modulo 2³² (l’addition usuelle des entiers 32 bits avec retenue ignorée), la rotation circulaire à gauche (les bits qui sortent à gauche reviennent à droite), et le XOR bit à bit. Aucune table de substitution, aucune multiplication coûteuse, aucune dépendance au matériel : ces trois opérations sont disponibles à pleine vitesse sur tout processeur depuis les années 1970.
Le bloc de base est appelé quarter-round et opère sur quatre mots a, b, c, d :
a += b; d ^= a; d = ROTL(d, 16);
c += d; b ^= c; b = ROTL(b, 12);
a += b; d ^= a; d = ROTL(d, 8);
c += d; b ^= c; b = ROTL(b, 7);
C’est tout. Un quarter-round mélange complètement les quatre mots en huit opérations. Le mot-clé : il n’y a aucune branche conditionnelle, aucune lecture mémoire dépendante des données. Le temps d’exécution ne dépend pas de la valeur des données — propriété précieuse contre les attaques par timing qui exploitent les différences de durée d’exécution.
De ChaCha20 le block au flux pseudo-aléatoire
L’état initial de 16 mots est rempli ainsi : 4 mots de constantes (la chaîne « expand 32-byte k » convertie en 4 mots de 32 bits, choix arbitraire mais fixé), 8 mots pour la clé de 256 bits, 1 mot de compteur et 3 mots de nonce (le standard RFC 8439 utilise un nonce de 96 bits soit 3 mots, et un compteur de 32 bits soit 1 mot, totalisant 16 mots).
Sur cet état, ChaCha20 applique le quarter-round huit fois par paire de rounds : quatre fois sur les colonnes (mots verticalement), puis quatre fois sur les diagonales. Cette alternance colonne-diagonale est ce qui assure la diffusion totale de l’information sur l’ensemble des 512 bits. 20 rounds au total — d’où le « 20 » dans le nom — appliqués à l’état, puis on additionne le résultat à l’état initial. Le résultat de 64 octets est le bloc de flux qui sera XORé avec 64 octets de message clair.
Pour chiffrer plus de 64 octets, on incrémente le compteur (mot 13 de l’état) et on recommence. Chaque bloc produit 64 octets de flux. Avec un compteur 32 bits, un même couple (clé, nonce) peut chiffrer jusqu’à 2³² blocs, soit 256 Go de données — bien plus que ce qu’on chiffre en pratique avec une seule clé.
Poly1305, l’authentification par hash universel
ChaCha20 seul fournit la confidentialité. Poly1305 ajoute l’authentification — la garantie que le chiffré n’a pas été altéré. La construction repose sur un principe mathématique élégant : un hash universel polynomial évalué dans le corps fini Z/(2¹³⁰ − 5)Z, c’est-à-dire les entiers modulo le grand nombre premier 2¹³⁰ − 5. Ce nombre premier particulier — appelé premier de Mersenne légèrement modifié — a été choisi parce que les opérations modulo 2¹³⁰ − 5 sont rapides à implémenter en logiciel.
Concrètement, Poly1305 prend une clé r (générée par le premier bloc ChaCha20 avec compteur 0) et calcule sur le message M découpé en blocs de 16 octets m_1, m_2, …, m_n la valeur : h = ((m_1 × r^n + m_2 × r^(n-1) + … + m_n × r) mod (2¹³⁰ − 5)) + s, où s est une seconde clé de 128 bits. La RFC 8439 spécifie ce calcul sous forme de récurrence à la Horner — accumulateur initialisé à 0, puis pour chaque bloc n_i : a := (a + n_i) × r mod (2¹³⁰ − 5) — qui est mathématiquement équivalent à la formule développée ci-dessus. Le résultat tronqué à 128 bits est le tag d’authentification. Cette construction a une propriété formellement prouvée : la probabilité qu’un attaquant qui ne connaît pas r et s fabrique un faux message acceptable est de l’ordre de n × 2⁻¹⁰⁷, soit pratiquement nulle pour des messages de taille raisonnable.
Pourquoi le nonce ne doit jamais se répéter
Comme pour AES-GCM, la sécurité de ChaCha20-Poly1305 dépend critiquement du nonce unique par clé. La raison mathématique : si deux messages distincts M_1 et M_2 sont chiffrés avec la même clé et le même nonce, l’attaquant qui observe les deux ciphertexts C_1 et C_2 peut calculer C_1 ⊕ C_2 = M_1 ⊕ M_2. Le flux pseudo-aléatoire s’annule, et l’attaquant obtient directement le XOR des deux clairs — souvent suffisant pour reconstituer chacun grâce aux redondances du langage naturel ou des formats de fichier.
Pire pour Poly1305 : la réutilisation d’un nonce permet à l’attaquant de forger de fausses authentifications, en exploitant le fait que la clé r reste la même. C’est pourquoi os.urandom(12) à chaque chiffrement est l’invariant fondamental — un programmeur qui « optimise » en réutilisant un nonce statique ouvre une faille catastrophique sans message d’erreur.
Pourquoi ChaCha20 est plus rapide qu’AES en logiciel pur
AES en logiciel pur sans instruction AES-NI demande de manipuler la S-box (table de 256 entrées) et de faire des multiplications dans GF(2⁸) — chaque opération demande des accès mémoire dont le timing peut varier selon le cache du processeur. Sur un Cortex-A53 ou un ATmega, ces accès dominent le temps d’exécution. ChaCha20, lui, n’utilise que des registres entiers 32 bits et trois opérations de base — un Cortex-A53 chiffre environ 100 Mo/s en ChaCha20 contre 30 Mo/s en AES-CTR sans accélération. Le rapport s’inverse sur un Intel récent grâce à AES-NI qui exécute un round AES en un seul cycle d’horloge — là, AES-GCM peut atteindre plusieurs gigaoctets par seconde.
Étape 7 — Quand préférer ChaCha20 à AES
Quatre situations rendent ChaCha20-Poly1305 préférable. Premièrement, le matériel sans accélération AES : Raspberry Pi des trois premières générations, certains microcontrôleurs ESP32, smartphones d’entrée de gamme — ChaCha20 y tourne deux à dix fois plus vite. Deuxièmement, la portabilité du code : une bibliothèque pure Python ou pure JavaScript qui implémente ChaCha20 reste correcte en performances, alors qu’une implémentation pure Python d’AES est désespérément lente. Troisièmement, l’évitement des side-channels en environnement non-contrôlé (code embarqué dans un client lourd, inspection mémoire possible) : ChaCha20 est plus simple à implémenter en temps constant. Quatrièmement, la cohérence avec les écosystèmes modernes : un projet greenfield qui utilise WireGuard, Signal Protocol ou libsodium reste naturellement sur ChaCha20.
À l’inverse, AES-GCM reste préférable sur du serveur Intel/AMD avec AES-NI, ou quand la conformité réglementaire l’exige (FIPS 140 par exemple, qui valide AES-GCM mais pas systématiquement ChaCha20-Poly1305 selon les versions).
Vérification du livrable
- Le script Python chiffre et déchiffre correctement en ChaCha20-Poly1305
- Le benchmark a été lancé sur la cible réelle
- Le choix entre AES et ChaCha20 est justifié par le matériel
- Le nonce est aléatoire à chaque chiffrement
- Les altérations sont détectées par l’AEAD
Erreurs fréquentes
| Erreur | Conséquence | Solution |
|---|---|---|
| Confondre ChaCha20 brut et ChaCha20-Poly1305 | Pas d’authentification du chiffré | Toujours utiliser le combo AEAD ChaCha20-Poly1305 |
| Réutiliser un nonce | Confidentialité brisée | os.urandom(12) à chaque chiffrement |
| Comparer perf sans tester | Choix arbitraire | Benchmark sur la cible de production |
| ChaCha20 dans un environnement FIPS strict | Non-conformité | AES-GCM dans ce contexte |
Smartphones d’entrée de gamme et VPN d’équipe
Trois cas locaux où ChaCha20 fait gagner. Le parc de PC modestes en PME ou association — Atom, Celeron d’entrée de gamme, vieux portables i3 sans AES-NI — chiffre l’archive sauvegarde nettement plus vite avec ChaCha20 qu’avec AES-CTR, qui dépend lourdement de l’accélération matérielle absente sur ces processeurs. Les VPN d’équipe sur VPS abordable (Hetzner CCX11, OVH VLE-1) où WireGuard avec ChaCha20-Poly1305 tient le débit fibre Sonatel sans saturer le CPU, alors qu’OpenVPN AES s’essoufflerait sur le même VPS à 4 USD par mois. Et les microcontrôleurs IoT connectés (capteurs LoRa, balises de tracking, équipements agricoles instrumentés) qui doivent chiffrer leurs données : ChaCha20-Poly1305 est typiquement la seule option viable en termes de cycles CPU disponibles sur un ESP32 ou un Cortex-M0.
Tutoriels frères
- Chiffrer en AES-256-GCM
- Chiffrer et signer en RSA
- Du Lucifer au DES, du 3DES à AES — l’évolution des chiffrements par blocs
Pour aller plus loin
- 🔝 Retour au guide principal : Cryptographie pratique pour développeurs et sysadmins
Sources et références
- RFC 8439 — ChaCha20-Poly1305 standardisé
- WireGuard — documentation officielle
- RFC 8446 — TLS 1.3 et suites de chiffrement
- Page ChaCha de Daniel J. Bernstein