Cybersécurité · C1 Fondations · Chapitre 2 · 6 h
Cryptographie appliquée
Utiliser les primitives sans les réimplémenter : modes de chiffrement, hachage et sel, HMAC et AEAD, signature, X.509 et TLS, bcrypt et Argon2, aléa, ouverture post-quantique.
En octobre 2013, Adobe perd 153 millions de comptes. Les mots de passe n'étaient pas hachés : ils étaient chiffrés, en 3DES, en mode ECB, avec une clé unique pour toute la base. Deux utilisateurs ayant le même mot de passe avaient donc le même bloc chiffré. Comme les indices de récupération, eux, étaient stockés en clair, il a suffi de lire quelques indices — « nom de mon chien », « 123 quatre fois » — pour étiqueter les blocs les plus fréquents et déchiffrer la base entière, sans jamais toucher à la clé.
Chaque brique employée était solide. L'assemblage était faux, et sur les quatre erreurs commises — chiffrer au lieu de hacher, un mode qui préserve les répétitions, une clé unique, des métadonnées en clair — aucune n'exigeait de casser quoi que ce soit.
C'est tout le sujet de ce chapitre. Vous n'implémenterez aucune primitive et vous n'en casserez aucune : vous apprendrez à les assembler correctement, ce qui est l'endroit exact où la cryptographie déployée échoue.
La règle du chapitre
N'implémentez jamais une primitive cryptographique. N'inventez jamais un protocole. Utilisez une bibliothèque éprouvée, à son niveau le plus élevé.
Ce n'est pas de la modestie. Une implémentation naïve d'AES est correcte fonctionnellement et vulnérable en pratique — parce qu'elle fuit par le temps d'exécution, par le cache, par la consommation. Le chapitre 12 de l'UE de cryptographie post-quantique du master est entièrement consacré à ces canaux auxiliaires ; retenez ici qu'un code qui « marche » n'est pas un code sûr.
Le corollaire est pratique : préférez toujours l'API la plus abstraite disponible. libsodium
et son crypto_secretbox, l'AEAD de la bibliothèque standard, un TLS géré par le système —
plutôt que « AES + mode + padding + IV » assemblés à la main. Chaque paramètre que l'API vous
laisse choisir est un paramètre que vous pouvez choisir mal.
Chiffrement symétrique : la primitive, puis le mode
Le chiffrement symétrique emploie la même clé pour chiffrer et déchiffrer. La primitive standard est AES, en clés de 128, 192 ou 256 bits ; elle chiffre des blocs de 128 bits, et rien de plus. C'est le mode opératoire qui dit comment enchaîner ces blocs — et c'est lui qui décide de la sécurité réelle.
| Mode | Ce qu'il fait | Verdict |
|---|---|---|
| ECB | chaque bloc chiffré indépendamment | à proscrire : blocs égaux, chiffrés égaux |
| CBC | chaque bloc est mélangé au précédent, IV aléatoire | acceptable, mais malléable et sensible à l'oracle de padding |
| CTR | transforme AES en chiffrement par flot, nonce + compteur | bon, à condition de ne jamais réutiliser le nonce |
| GCM | CTR + authentification intégrée | le défaut : confidentialité et intégrité |
L'image du « pingouin ECB » est devenue canonique : une image chiffrée en ECB reste parfaitement reconnaissable, parce que les zones uniformes produisent des blocs chiffrés identiques. C'est exactement l'erreur d'Adobe, un pixel remplacé par un mot de passe.
Deux règles opérationnelles se retiennent telles quelles.
Un IV ou un nonce n'est pas un secret, mais il ne se répète jamais. L'IV de CBC doit être imprévisible et aléatoire ; le nonce de CTR et de GCM doit seulement être unique pour une clé donnée. Réutiliser un nonce en GCM ne fait pas que révéler le xor de deux clairs : cela permet de récupérer la clé d'authentification et de forger des messages valides. C'est la défaillance la plus fréquente du chiffrement moderne, et elle est totale.
Chiffrer n'est pas protéger l'intégrité. En CTR comme en GCM sans vérification, inverser
un bit du chiffré inverse le bit correspondant du clair. Un attaquant qui sait où se trouve le
montant dans VIREMENT 0100 EUR le modifie sans lire le message ni connaître la clé. D'où la
section sur l'AEAD plus bas — et d'où le fait que GCM soit le mode par défaut plutôt que CTR.
Hachage : ce qu'une empreinte garantit
Une fonction de hachage produit une empreinte de taille fixe. On lui demande trois propriétés :
- résistance à la préimage : à partir de l'empreinte, on ne retrouve pas l'entrée ;
- résistance à la seconde préimage : à partir d'un message, on n'en trouve pas un autre de même empreinte ;
- résistance aux collisions : on ne trouve pas deux messages quelconques de même empreinte.
La troisième est la plus fragile, à cause du paradoxe des anniversaires : une empreinte de
n bits ne coûte que 2^(n/2) essais à faire collisionner. C'est pourquoi 128 bits d'empreinte
ne donnent que 64 bits de sécurité en collision, et pourquoi SHA-256 est le minimum courant.
| Fonction | Statut |
|---|---|
| MD5 | cassée. Collisions en secondes. À n'employer pour rien qui touche à la sécurité |
| SHA-1 | cassée. Collision publique en 2017 (SHAttered), puis attaque à préfixe choisi en 2020 |
| SHA-256, SHA-512 | recommandées |
| SHA-3, BLAKE2, BLAKE3 | recommandées ; SHA-3 est immune à l'extension de longueur |
Trois confusions à défaire.
Hacher n'est pas chiffrer. Un hachage ne se déchiffre pas : il n'y a pas de clé et l'opération n'est pas inversible. Dire « le mot de passe est crypté » est doublement faux — et le verbe français est « chiffrer ».
Une empreinte seule ne prouve pas l'origine. Publier un fichier et son SHA-256 sur le même serveur ne protège de rien : qui modifie le fichier modifie l'empreinte. Il faut un canal distinct, ou une signature.
MD5 et SHA-1 ne sont pas « faibles », elles sont cassées. La nuance importe : contre les collisions, aucune longueur de clé ni aucun sel ne les répare.
Stocker un mot de passe
C'est le cas d'usage où l'intuition trompe le plus, parce que la bonne réponse consiste à choisir délibérément une fonction lente.
Un serveur vérifie un mot de passe une fois par connexion : 250 ms de calcul y sont invisibles. Un attaquant qui a volé la base en teste des milliards — et il paie les mêmes 250 ms par essai. La lenteur ne coûte presque rien au défenseur et tout à l'attaquant. C'est l'unique cas de ce cours où l'inefficacité est la mesure de sécurité.
Le stockage correct combine trois éléments, dont aucun ne remplace les autres :
- Un sel aléatoire, unique par utilisateur, stocké en clair à côté de l'empreinte. Il ne rend pas un mot de passe faible plus difficile à deviner : il empêche de mutualiser l'attaque — plus de table arc-en-ciel, plus de table précalculée réutilisable, et deux utilisateurs de même mot de passe n'ont plus la même empreinte.
- Une fonction lente et paramétrable : Argon2id de préférence, sinon scrypt ou bcrypt. PBKDF2 reste acceptable quand une contrainte de conformité l'impose, mais il se parallélise trop bien sur GPU.
- Des paramètres calibrés sur votre matériel : pour Argon2id, viser de l'ordre de 64 Mio de mémoire et une durée de 200 à 500 ms sur le serveur cible. La consommation mémoire est le point clé — c'est elle qui neutralise l'avantage des GPU et des circuits dédiés.
On y ajoute parfois un poivre : un secret global, stocké hors de la base — variable d'environnement, module matériel. Il ne sert que dans un scénario, mais un scénario fréquent : la base fuit, le code ne fuit pas. Le poivre ne remplace jamais le sel.
Quiz · 1 question
Une équipe stocke ses mots de passe en SHA-256 avec un sel aléatoire de 32 octets par utilisateur. Est-ce correct ?
- Oui : sel unique et fonction moderne, c'est l'état de l'art — sel + fonction moderne
- Non : SHA-256 est trop rapide, il faut une fonction lente comme Argon2id ou bcrypt — vitesse de la fonction
- Non : le sel doit rester secret, sinon il ne sert à rien — secret du sel
Réponse : Le sel est fait pour être public — il est stocké en clair à côté de l'empreinte, et son rôle est d'empêcher la mutualisation de l'attaque, pas de cacher quoi que ce soit. Le vrai défaut est la vitesse : SHA-256 est conçue pour être rapide, et un GPU en calcule des milliards par seconde. Le sel oblige l'attaquant à traiter les comptes un par un, mais chaque compte reste bon marché. Il faut les deux : le sel contre la mutualisation, la lenteur contre le débit.
Intégrité et authenticité : HMAC, puis AEAD
Pour prouver qu'un message n'a pas été modifié et vient bien du détenteur de la clé, on
emploie un code d'authentification de message. Le standard est HMAC, construit sur une
fonction de hachage — HMAC-SHA256 — et sa construction n'est pas décorative : elle existe
précisément pour résister à l'attaque par extension de longueur, qui casse le naïf
hacher(clé + message).
Quand on veut à la fois chiffrer et authentifier, trois assemblages sont possibles et un seul est sûr en général : chiffrer puis authentifier le chiffré (encrypt-then-MAC). Les deux autres ordres ont produit des vulnérabilités réelles dans TLS et dans SSH.
En pratique, on ne fait plus l'assemblage soi-même : on emploie un chiffrement authentifié (AEAD), qui fait les deux en une opération et refuse de déchiffrer un message altéré.
| Construction | Quand |
|---|---|
| AES-GCM | matériel avec accélération AES — c'est-à-dire presque tout serveur ou processeur récent |
| ChaCha20-Poly1305 | logiciel pur, mobile, embarqué : plus rapide et plus résistant aux attaques par cache |
| XChaCha20-Poly1305 | même chose avec un nonce assez long pour être tiré au hasard sans risque de collision |
Les AEAD acceptent aussi des données associées : authentifiées mais non chiffrées — un en-tête, un identifiant de session, un numéro de version. C'est l'endroit propre où lier un chiffré à son contexte, et donc empêcher qu'il soit rejoué ailleurs.
Un détail d'implémentation, souvent négligé et pourtant décisif : toute comparaison de
secret se fait en temps constant. Un === sur une signature s'arrête au premier octet
différent et révèle, par le temps de réponse, combien d'octets étaient corrects — ce qui suffit
à forger la valeur octet par octet. Toute bibliothèque sérieuse fournit une comparaison
dédiée ; utilisez-la.
Asymétrique : échanger une clé, signer un message
La cryptographie asymétrique emploie deux clés liées mathématiquement : une publique, une privée. Elle résout deux problèmes que le symétrique ne sait pas résoudre — se mettre d'accord sur une clé sans s'être jamais rencontrés, et prouver une origine à un tiers.
| Usage | Qui emploie quoi | Standards |
|---|---|---|
| Échange de clés | on dérive un secret commun | ECDH sur X25519 ; RSA-KEM en héritage |
| Signature | privée pour signer, publique pour vérifier | Ed25519, ECDSA, RSA-PSS |
| Chiffrement | publique pour chiffrer, privée pour déchiffrer | RSA-OAEP, uniquement pour de petites données |
Trois points qui reviennent tout le temps.
On ne chiffre pas un gros message en asymétrique. C'est des milliers de fois plus lent, et RSA est borné par la taille du module. Le schéma réel est hybride : une clé symétrique aléatoire chiffre le message, et l'asymétrique ne protège que cette clé. TLS, PGP, S/MIME et les messageries chiffrées font tous exactement cela.
Signature et MAC ne donnent pas la même garantie. Un MAC repose sur une clé partagée : le vérifieur peut fabriquer lui-même n'importe quel message valide, donc rien ne prouve l'origine devant un tiers. Seule la signature apporte la non-répudiation.
La confidentialité persistante est une propriété du protocole, pas de la primitive. Si la clé de session dérive d'une clé statique, la compromission de celle-ci déchiffre tout le trafic passé enregistré. C'est pourquoi TLS 1.3 impose un échange éphémère : la clé de session n'est reconstituable à partir d'aucun secret durable.
Certificats, PKI, TLS
Une clé publique ne dit pas à qui elle appartient. Un certificat X.509 est cette clé publique accompagnée d'une identité — un nom de domaine — et d'une signature d'une autorité de certification. Le navigateur ne fait confiance au certificat que parce qu'il fait déjà confiance à l'autorité, dont la clé est dans son magasin racine : la confiance est déléguée le long d'une chaîne, jusqu'à une racine installée d'avance.
Ce que TLS garantit : la confidentialité et l'intégrité du transport, et l'authentification
du serveur — vous parlez bien à banque.example.
Ce que TLS ne garantit pas, et qu'on lui prête constamment : que le site d'en face soit honnête ; que l'application derrière soit sûre ; que la donnée soit protégée une fois arrivée. Un site de hameçonnage a un cadenas vert, obtenu gratuitement et automatiquement. Le cadenas authentifie le domaine, pas l'intention.
Trois erreurs d'implémentation à connaître, parce qu'elles sont fréquentes et silencieuses :
- Désactiver la vérification du certificat pour faire taire une erreur en développement, puis livrer ce code. La connexion reste chiffrée — contre n'importe qui sauf l'attaquant du milieu, c'est-à-dire contre personne d'utile.
- Vérifier la chaîne mais pas le nom. Un certificat valide pour un autre domaine reste un certificat valide ; sans contrôle du nom, l'attaquant présente le sien.
- Ignorer la révocation et l'expiration. OCSP agrafé, listes de révocation, durées de vie courtes : la révocation reste le point faible de la PKI, et la réponse moderne est la réduction de la durée de validité — quelques mois — plutôt que la révocation elle-même.
Deux mécanismes utiles complètent le tableau. La transparence des certificats publie dans des journaux publics auditables tout certificat émis, ce qui rend une émission frauduleuse détectable après coup. L'épinglage fige la clé attendue dans l'application : très efficace sur une application mobile que vous maîtrisez, très dangereux sur le web, où une erreur bloque le domaine pour la durée de l'épinglage.
Quiz · 1 question
Une application mobile envoie les identifiants de ses utilisateurs en HTTPS vers son API. Le développeur a ajouté une option qui accepte les certificats auto-signés, pour tester derrière le proxy de l'entreprise. Que se passe-t-il en production ?
- Rien : le trafic reste chiffré en AES, donc illisible pour un tiers — chiffrement du transport
- N'importe qui sur le chemin peut présenter son propre certificat et lire tout le trafic — authentification du pair
- Le risque est limité : seul l'opérateur du réseau pourrait intercepter — acteur privilégié
Réponse : Le chiffrement sans authentification du pair ne protège de rien : vous chiffrez soigneusement à destination de l'attaquant. Celui-ci présente un certificat auto-signé, l'application l'accepte, il déchiffre, lit, modifie, puis rechiffre vers le vrai serveur — l'homme du milieu du chapitre 5, monté en trois commandes sur un Wi-Fi public. Ce n'est pas un risque limité à l'opérateur : tout appareil du même réseau local peut se placer sur le chemin par usurpation ARP.
L'aléa, et pourquoi il casse tout
Toute la cryptographie repose sur des valeurs imprévisibles : clés, IV, nonces, sels, jetons de session, jetons de réinitialisation de mot de passe. Un aléa prévisible rend l'ensemble inutile, sans qu'aucune primitive n'ait été cassée.
La règle est binaire. Utilisez un générateur cryptographique — /dev/urandom,
getrandom(), crypto.randomBytes, crypto.getRandomValues, secrets en Python. Jamais
un générateur généraliste : Math.random(), rand(), random.random(), ni quoi que ce soit
initialisé par l'horloge. Ces derniers sont conçus pour la simulation, et leur état interne se
reconstitue à partir de quelques sorties observées.
L'histoire donne la mesure du risque. En 2008, une ligne retirée par erreur d'OpenSSL dans Debian a réduit l'entropie du générateur au seul identifiant de processus : environ 32 000 clés possibles, pendant près de deux ans. Toutes les clés SSH, TLS et OpenVPN générées dans cet intervalle étaient énumérables en quelques minutes. Aucun algorithme n'était en cause.
Ouverture : ce que le quantique change
Un ordinateur quantique de taille suffisante casserait, par l'algorithme de Shor, toute la cryptographie asymétrique déployée : RSA, Diffie-Hellman, courbes elliptiques. Le symétrique et le hachage tiennent mieux — l'algorithme de Grover ne donne qu'un gain quadratique, dont on se protège en doublant les tailles, d'où la préférence pour AES-256 et SHA-384.
L'échéance est incertaine, mais une menace est déjà actuelle : récolter maintenant, déchiffrer plus tard. Un adversaire enregistre aujourd'hui du trafic chiffré qu'il déchiffrera le jour venu. Toute donnée dont la valeur dépasse dix ans est donc concernée dès à présent.
Les standards existent depuis 2024 : ML-KEM (FIPS 203) pour l'échange de clés, ML-DSA (FIPS 204) et SLH-DSA (FIPS 205) pour la signature. Le déploiement en cours est hybride — X25519 combiné à ML-KEM — de façon que la sécurité tienne si l'une des deux familles tombe. Le reste appartient au master ; l'UE de cryptographie post-quantique y consacre quatorze chapitres.
À vous
L'exercice suit le fil de tous les TP de ce cours : attaquer d'abord, corriger ensuite.
Vous recevez une base de mots de passe hachés qui a fui. Vous la cassez avec un dictionnaire de douze mots, en comptant vos hachages. Puis vous ajoutez un sel par compte et vous rejouez exactement la même attaque. Ce n'est pas le nombre de comptes cassés qui change — c'est le coût, et c'est là qu'est la leçon.
Exercice de code
Une base de mots de passe hachés a fui. Cassez-la avec un dictionnaire de douze mots, en comptant vos hachages. Ajoutez ensuite un sel par compte et rejouez la même attaque : ce n'est pas le nombre de comptes cassés qui change, c'est le coût.
Point de départ
// Une base d'utilisateurs a fui. Les mots de passe sont « hachés », donc
// tout va bien — c'est du moins ce qu'affirme le communiqué de presse.
//
// La fonction de hachage ci-dessous est un jouet : le vrai SHA-256 ne
// changerait RIEN à ce que vous allez montrer. C'est le schéma qui est en
// cause, pas la primitive.
function hacher(texte) {
let h = 0x811c9dc5;
for (let i = 0; i < texte.length; i++) {
h ^= texte.charCodeAt(i);
h = Math.imul(h, 0x01000193) >>> 0;
}
return h.toString(16).padStart(8, "0");
}
// ── La fuite ──────────────────────────────────────────────────────────────
const FUITE = [
{ compte: "amina", hache: hacher("soleil") },
{ compte: "bakary", hache: hacher("123456") },
{ compte: "chantal", hache: hacher("motdepasse") },
{ compte: "diallo", hache: hacher("soleil") },
{ compte: "eve", hache: hacher("Tr0ub4dor&3-2026") },
{ compte: "fatou", hache: hacher("azerty") },
{ compte: "gerard", hache: hacher("123456") },
{ compte: "hawa", hache: hacher("soleil") },
];
const DICTIONNAIRE = [
"123456", "azerty", "motdepasse", "soleil", "qwerty", "admin",
"bonjour", "football", "password", "iloveyou", "000000", "abc123",
];
// ── ATTAQUE 1 — à vous ────────────────────────────────────────────────────
// Construisez une table haché → mot, puis retrouvez les comptes cassés.
// Combien de fois hachez-vous ? Retenez ce nombre.
let calculs1 = 0;
function attaquerSansSel() {
const casses = [];
// à compléter
return casses;
}
// ── LA CORRECTION — à vous aussi ──────────────────────────────────────────
// Ajoutez un sel ALÉATOIRE ET DISTINCT par compte, stocké en clair à côté du
// haché, et rejouez la même attaque.
function selAleatoire() {
return Math.random().toString(36).slice(2, 10);
}
const FUITE_SALEE = [
{ compte: "amina", sel: "", hache: "" }, // à construire
];
let calculs2 = 0;
function attaquerAvecSel() {
const casses = [];
// à compléter
return casses;
}
// ── Mesure ────────────────────────────────────────────────────────────────
const c1 = attaquerSansSel();
console.log("Sans sel :", c1.length, "comptes cassés sur", FUITE.length, "—", calculs1, "hachages");
const c2 = attaquerAvecSel();
console.log("Avec sel :", c2.length, "comptes cassés sur", FUITE_SALEE.length, "—", calculs2, "hachages");
Solution
// ── ATTAQUE 1 ─────────────────────────────────────────────────────────────
let calculs1 = 0;
function attaquerSansSel() {
// UNE table, calculée une seule fois, valable pour tous les comptes.
const table = new Map();
for (const mot of DICTIONNAIRE) {
table.set(hacher(mot), mot);
calculs1++;
}
const casses = [];
for (const u of FUITE) {
const mot = table.get(u.hache); // lecture, pas de calcul
if (mot) casses.push(u.compte + " = " + mot);
}
return casses;
}
// 7 comptes sur 8, pour 12 hachages. Et le coût ne dépend PAS du nombre de
// comptes : la même table casserait huit millions d'utilisateurs.
// Remarquez au passage que amina, diallo et hawa ont le même haché — donc le
// même mot de passe. La base non salée fuit cette information à elle seule.
// Seule eve résiste, et c'est son mot de passe qui la sauve, pas le schéma.
// ── LA CORRECTION ─────────────────────────────────────────────────────────
const FUITE_SALEE = FUITE.map((u, i) => {
const clair = ["soleil", "123456", "motdepasse", "soleil",
"Tr0ub4dor&3-2026", "azerty", "123456", "soleil"][i];
const sel = selAleatoire();
return { compte: u.compte, sel, hache: hacher(sel + clair) };
});
let calculs2 = 0;
function attaquerAvecSel() {
const casses = [];
for (const u of FUITE_SALEE) {
// La table ne se réutilise plus : il faut la REFAIRE pour chaque sel.
for (const mot of DICTIONNAIRE) {
calculs2++;
if (hacher(u.sel + mot) === u.hache) {
casses.push(u.compte + " = " + mot);
break;
}
}
}
return casses;
}
// Toujours 7 comptes cassés — mais pour ~70 hachages au lieu de 12, et
// surtout un coût désormais PROPORTIONNEL au nombre de comptes.
//
// C'est exactement ce que le sel fait, et rien de plus : il ne rend pas un
// mot de passe faible plus difficile à deviner, il détruit la MUTUALISATION
// de l'attaque. Plus de table arc-en-ciel, plus de table précalculée
// réutilisable, plus de hachés identiques révélant des mots de passe égaux.
//
// Sur cette base de 8 comptes le facteur est de 6 ; sur 10 millions de
// comptes il est de 10 millions. C'est la seule raison pour laquelle le sel
// existe.
//
// Il reste la deuxième moitié du travail, que ce jouet ne montre pas :
// hacher LENTEMENT. Notre fonction fait quelques nanosecondes, ce qui laisse
// un attaquant en tester des milliards par seconde sur GPU, sel ou pas.
// Argon2id ou bcrypt calibrés à ~250 ms le ramènent à quelques milliers par
// seconde. Sel ET lenteur : l'un contre la mutualisation, l'autre contre le
// débit. Ni l'un ni l'autre ne se remplace.
Ce que la suite en fait
La cryptographie est un outil, pas une solution ; elle protège une donnée en transit ou au repos, et ne dit rien de qui a le droit d'y accéder. C'est précisément l'objet du chapitre 3, qui reprend le stockage des mots de passe vu ici pour le placer dans une chaîne complète d'authentification, et y ajoute les sessions, les jetons et les modèles d'autorisation.
Le chapitre 6 réemploiera TLS pour les VPN et les tunnels, et le chapitre 7 pour la sécurité des API et des JWT — où l'on verra qu'un jeton signé mal vérifié annule tout ce qui précède.
À retenir
Flashcards · 4 cartes
- Pourquoi le sel ne suffit-il pas à protéger une base de mots de passe ?
- Parce qu'il ne traite qu'un des deux problèmes. Le sel détruit la MUTUALISATION de l'attaque : plus de table arc-en-ciel, plus d'empreintes identiques révélant des mots de passe égaux. Mais chaque compte reste bon marché à attaquer si la fonction est rapide. Il faut aussi la LENTEUR — Argon2id, scrypt ou bcrypt calibrés à 200-500 ms — pour effondrer le débit de l'attaquant.
- Qu'est-ce que TLS garantit, et qu'est-ce qu'on lui prête à tort ?
- Il garantit la confidentialité et l'intégrité du transport, et l'authentification du SERVEUR : vous parlez bien au domaine annoncé. Il ne dit rien de l'honnêteté du site, de la sécurité de l'application derrière, ni du sort de la donnée une fois arrivée. Un site de hameçonnage a un cadenas valide : le cadenas authentifie le domaine, pas l'intention.
- Que se passe-t-il si un nonce est réutilisé avec la même clé en AES-GCM ?
- L'effondrement complet. Comme en CTR, le xor des deux clairs se déduit des deux chiffrés — mais surtout, la réutilisation permet de récupérer la clé d'authentification et donc de FORGER des messages valides. C'est la défaillance la plus fréquente du chiffrement moderne : le nonce n'est pas un secret, mais il ne doit jamais se répéter pour une clé donnée.
- Pourquoi ne chiffre-t-on jamais un gros message directement en RSA ?
- Parce que c'est des milliers de fois plus lent que le symétrique et borné par la taille du module. Le schéma réel est HYBRIDE : une clé symétrique aléatoire chiffre le message, et l'asymétrique ne protège que cette clé. TLS, PGP, S/MIME et les messageries chiffrées fonctionnent tous ainsi.