cursus.

Cours 2 · Cryptographie symétriqueLeçon 3 sur 4

Chiffrement par flot

2 h de lecture7 sections Version PDF

À la fin de cette leçon, vous saurez

Registres à décalage, ChaCha20, et ce que coûte exactement la réutilisation d'un nonce.

Un chiffrement par flot réalise le rêve du chapitre 3 sous une forme atteignable : il engendre, à partir d'une clé courte, une suite pseudo-aléatoire aussi longue que le message, et l'applique par XOR comme le faisait Vernam. On échange la perfection prouvée contre une clé maniable — et l'on hérite, mot pour mot, de la fragilité du masque réutilisé.

Le principe, et sa condition unique

Le schéma est celui de Vernam, avec un générateur à la place du hasard :

flot=G(k,nonce)c=mflot\text{flot} = G(k, \text{nonce}) \qquad c = m \oplus \text{flot}

Toute la sécurité se reporte sur GG : sa sortie doit être indistinguable d'une suite aléatoire pour tout adversaire en temps polynomial. Si elle l'est, le chiffré ne révèle rien de plus qu'un masque jetable — mais la garantie est désormais calculatoire, elle vaut ce que vaut GG.

Le nonce mérite qu'on s'y arrête, car il est la raison d'être du chapitre. Une même clé sert à chiffrer des milliers de messages ; pour que la suite masquante diffère à chaque fois, on adjoint à la clé un nonce — un numéro utilisé une seule fois. La règle est absolue : à clé fixée, jamais deux fois le même nonce. On verra qu'elle est aussi facile à énoncer que difficile à tenir.

Les LFSR, et pourquoi la linéarité tue

Les premiers générateurs matériels furent des registres à décalage à rétroaction linéaire (LFSR). Un registre de bits se décale à chaque top d'horloge, et le bit entrant est un XOR de certaines positions. C'est rapide, compact, et le cauchemar du cryptographe : précisément parce que tout y est linéaire.

L'algorithme de Berlekamp-Massey reconstitue un LFSR de LL bits à partir de seulement 2L2L bits de sa sortie. Un LFSR seul n'offre donc aucune sécurité. Les chiffres réels qui en dérivent — A5/1 du GSM, E0 du Bluetooth, RC4 par un autre chemin — combinent plusieurs registres par une fonction non linéaire pour briser cette structure, et tous ont fini par tomber : la combinaison retardait l'attaque sans la supprimer. La leçon est générale, et elle revient au chapitre 6 : la linéarité est l'ennemi, et la non-linéarité de l'AES n'est pas un ornement.

ChaCha20, le flot moderne

La conception a changé de camp. ChaCha20, dessiné par Daniel Bernstein en 2008 et normalisé dans le RFC 8439, n'est pas un registre : c'est un chiffrement CTR bâti sur une fonction de brouillage. Une clé de 256 bits et un nonce de 96 bits amorcent un état de 512 bits ; vingt tours d'additions, de rotations et de XOR — la ronde « ARX », sans aucune table — produisent chaque bloc de flot, indexé par un compteur.

L'absence de table est un avantage de sécurité, pas seulement de vitesse : sans accès mémoire dépendant du secret, ChaCha20 est naturellement à temps constant, là où une boîte S tabulée expose l'implémentation aux attaques par cache. C'est pourquoi TLS 1.3 le retient comme alternative à AES-GCM, notamment sur les processeurs sans accélération AES matérielle.

Mais ChaCha20 reste un chiffrement par flot, et la règle du nonce s'y applique sans indulgence.

Le nonce rejoué, encore

Reprenons deux messages chiffrés sous la même clé et le même nonce. Le flot est identique, donc :

c1c2=(m1flot)(m2flot)=m1m2c_1 \oplus c_2 = (m_1 \oplus \text{flot}) \oplus (m_2 \oplus \text{flot}) = m_1 \oplus m_2

C'est l'équation du chapitre 3, au signe près de rien. La suite masquante s'annule, l'adversaire obtient le XOR des clairs, et un seul message connu — un en-tête, une formule convenue — dévoile l'autre par simple XOR. Aucune attaque sur ChaCha20 lui-même : la faute est dans l'usage.

Ce n'est pas une hypothèse d'école. Le WEP du Wi-Fi tirait un IV de 24 bits seulement, dont la répétition était garantie par le paradoxe des anniversaires après quelques heures de trafic. La faute s'est répétée sur des consoles, des messageries, des VPN. Elle est tellement récurrente qu'une famille entière de constructions — le chiffrement « résistant à la réutilisation de nonce », comme AES-GCM-SIV — existe pour en limiter les dégâts.

Exercice · JavaScript · à vous de jouer

Deux messages ont été chiffrés en flot avec le même nonce. À partir des deux chiffrés et du clair connu de l'un, retrouvez le secret de l'autre.

En attente
// ChaCha20 est un chiffrement par flot : flot = ChaCha(clé, nonce, compteur),
// puis chiffré = clair ⊕ flot. Ici un générateur jouet tient lieu de ChaCha —
// peu importe sa qualité, c'est la RÉUTILISATION qui casse tout.

function flot(cle, nonce, longueur) {
  // Générateur déterministe (xorshift ensemencé par clé+nonce). Non
  // cryptographique, mais reproductible : mêmes (clé, nonce) ⇒ même flot.
  let x = (cle ^ (nonce * 2654435761)) >>> 0 || 1;
  const out = [];
  for (let i = 0; i < longueur; i++) {
    x ^= x << 13; x >>>= 0;
    x ^= x >> 17;
    x ^= x << 5;  x >>>= 0;
    out.push(x & 0xff);
  }
  return out;
}

const enOctets = (s) => [...s].map((c) => c.charCodeAt(0));
const enTexte = (o) => o.map((c) => String.fromCharCode(c)).join("");
const xor = (a, b) => a.map((x, i) => x ^ b[i]);

const CLE = 0xC0FFEE;
const NONCE = 42; // LE MÊME pour les deux messages : c'est l'erreur.

// Message public, connu de l'attaquant (un en-tête, une formule de politesse).
const CONNU = "bonjour, voici le rapport : ";
// Message secret, que l'attaquant veut lire.
const SECRET = "le budget reel est de 4 millions";

const n = Math.min(CONNU.length, SECRET.length);
const cConnu = xor(enOctets(CONNU.slice(0, n)), flot(CLE, NONCE, n));
const cSecret = xor(enOctets(SECRET.slice(0, n)), flot(CLE, NONCE, n));

// ── À VOUS ────────────────────────────────────────────────────────────────
// Vous disposez de : cConnu, cSecret, et du clair CONNU. Pas de la clé, pas du
// flot. Retrouvez le début de SECRET.
//
// Indice : cConnu ⊕ cSecret élimine le flot. Il ne reste qu'à réintroduire ce
// que vous savez.

function retrouverSecret(cConnu, cSecret, connuClair) {
  // à compléter
  return "";
}

console.log(JSON.stringify(retrouverSecret(cConnu, cSecret, CONNU.slice(0, n))));

Console de sortie
Le résultat s'affiche dans la console
Quiz · vérifiez votre compréhension Sans réponse

ChaCha20 est réputé sûr. Pourtant l'exercice retrouve un message secret. Où est la faute ?

Flot ou blocs ?

Le partage n'est pas celui qu'on croit. AES en mode CTR est un chiffrement par flot ; ChaCha20 est structuré comme CTR. La vraie ligne de fracture n'est pas flot contre blocs, mais avec ou sans authentification. Ni ChaCha20 brut ni AES-CTR ne protègent l'intégrité : un attaquant qui inverse un bit du chiffré inverse le bit correspondant du clair, sans être détecté — la malléabilité du chapitre 1.

C'est pourquoi on ne déploie jamais ces primitives nues. On les enveloppe : ChaCha20 devient ChaCha20-Poly1305, AES-CTR devient AES-GCM. Ces objets authentifiés sont le sujet du chapitre 8, et ils sont le seul niveau auquel un praticien devrait avoir affaire.

Quiz · vérifiez votre compréhension Sans réponse

Un attaquant intercepte un message chiffré en ChaCha20 brut et inverse le troisième bit du chiffré. Que se passe-t-il ?

Ce que la suite en fait

Le chapitre 6 quitte les modes pour attaquer les primitives : il montre par quelles méthodes — différentielle, linéaire, rencontre au milieu — on évalue la solidité d'un AES ou d'un DES, et pourquoi la linéarité rencontrée ici chez les LFSR est le fil conducteur de toute la cryptanalyse symétrique. Le chapitre 8, lui, referme la malléabilité en ajoutant l'authentification qui manque à tout chiffrement de ce chapitre.

À retenir

Flashcards · 1 / 3Toucher pour retourner
Fin de la leçon

Vous avez parcouru les 7 sections.

Marquez-la terminée pour faire avancer votre parcours, ou revenez sur un point avant de passer à la suite.