Cryptographie · C3 Intégrité et authentification · Chapitre 2 · 5 h
MAC et chiffrement authentifié
HMAC, CMAC, Encrypt-then-MAC et ses alternatives, AEAD, dérivation de clés avec HKDF et Argon2.
Le chapitre 7 a fermé le hachage sur un avertissement : un haché n'est pas un MAC. Ce chapitre construit l'authentification qui manquait, puis la fond avec le chiffrement en un seul objet — le chiffrement authentifié, seul niveau auquel un praticien devrait avoir affaire. Il referme au passage l'oracle de padding du chapitre 4 et le nonce rejoué du chapitre 5.
Ce qu'un MAC garantit
Un code d'authentification de message est une fonction qui produit une étiquette courte. Bob, qui partage la clé , recalcule l'étiquette et n'accepte le message que si elle correspond. La garantie est l'inforgeabilité : sans la clé, un adversaire ne peut produire une étiquette valide pour un message qu'il n'a pas déjà vue authentifiée, même après en avoir observé beaucoup d'autres. C'est la propriété EUF-CMA que le chapitre 12 formalisera.
Deux limites, déjà posées au chapitre 1, méritent d'être rappelées. Un MAC ne chiffre pas : il protège l'intégrité, pas la confidentialité. Et il n'apporte pas la non-répudiation : la clé étant partagée, Bob peut fabriquer lui-même n'importe quelle étiquette, donc ne peut rien prouver à un tiers. Pour cela il faut une signature, au chapitre 9.
HMAC : le bon usage d'un haché
On a vu qu'assembler naïvement une clé et un haché échoue. HMAC, normalisé et omniprésent (TLS, IPsec, JWT), fait les choses correctement en imbriquant deux appels :
La passe intérieure, scellée par la clé, produit un état que la passe extérieure hache à nouveau. Le résultat visible est la sortie de la passe extérieure, qui n'est l'état interne d'aucun calcul qu'un attaquant puisse prolonger : l'extension de longueur du chapitre 7 est neutralisée. HMAC a une preuve de sécurité — il est inforgeable dès que la fonction de compression sous-jacente est une bonne primitive — ce qui explique qu'il ait survécu à l'affaiblissement de MD5 et de SHA-1 mieux que ces fonctions elles-mêmes.
Vous allez d'abord forger un MAC préfixe, puis constater que HMAC ne se laisse pas faire.
Exercice de code
Forgez un MAC préfixe H(clé ‖ message) sur un message rallongé, sans connaître la clé, par extension de longueur. Puis constatez pourquoi HMAC résiste.
Point de départ
// Une fonction de hachage Merkle-Damgård jouet. Ce qui compte : son
// empreinte EST son état interne final, comme MD5, SHA-1, SHA-2.
function compression(etat, bloc) {
let h = etat >>> 0;
for (let i = 0; i < bloc.length; i++) {
h = (h ^ bloc.charCodeAt(i)) >>> 0;
h = (h * 16777619) >>> 0; // mélange (FNV)
h = ((h << 13) | (h >>> 19)) >>> 0;
}
return h >>> 0;
}
const IV = 0x811c9dc5;
function hache(message, etatInitial = IV) {
let h = etatInitial;
for (let i = 0; i < message.length; i += 4) {
h = compression(h, message.slice(i, i + 4));
}
return h >>> 0;
}
// ── Le serveur ────────────────────────────────────────────────────────────
// Clé secrète, inconnue de l'attaquant. Le MAC « préfixe » est H(clé ‖ msg).
const CLE = "s3cr3t--"; // 8 octets, longueur connue de l'attaquant
const macPrefixe = (msg) => hache(CLE + msg);
const verifier = (msg, mac) => macPrefixe(msg) === mac;
// L'attaquant observe un message légitime et son MAC.
const msg = "montant=100&dest=alice";
const mac = macPrefixe(msg);
console.log("message légitime :", JSON.stringify(msg), "MAC =", mac);
// ── À VOUS ────────────────────────────────────────────────────────────────
// But : faire accepter un message RALLONGÉ, sans connaître CLE.
//
// Le MAC observé EST l'état interne après avoir haché (CLE + msg). Pour
// étendre, il suffit de REPRENDRE le hachage à partir de cet état, en y
// injectant votre suffixe. La clé n'intervient jamais.
//
// Ce haché jouet n'ajoute AUCUN rembourrage : il replie les octets un à un.
// L'extension est donc exacte sans rien reconstituer. Sur un vrai
// Merkle-Damgård, l'attaque exige en plus de deviner le « glue padding » que
// la fonction a inséré après (clé ‖ message) — une étape mécanique de plus,
// pas un obstacle, dès qu'on connaît la longueur de la clé.
const suffixe = "&dest=eve";
function forger(macObserve, suffixe) {
// à compléter : repartir de l'état macObserve et hacher le suffixe
return 0;
}
const macForge = forger(mac, suffixe);
const messageForge = msg + suffixe;
console.log("message forgé :", JSON.stringify(messageForge));
console.log("accepté par le serveur ?", verifier(messageForge, macForge));
// ── HMAC résiste ──────────────────────────────────────────────────────────
function hmac(cle, msg) {
const ipad = [...cle].map((c) => String.fromCharCode(c.charCodeAt(0) ^ 0x36)).join("");
const opad = [...cle].map((c) => String.fromCharCode(c.charCodeAt(0) ^ 0x5c)).join("");
return hache(opad + String(hache(ipad + msg)));
}
console.log("HMAC(clé, forgé) recalculé sans la clé ? Impossible : deux passes.");
Solution
function forger(macObserve, suffixe) {
// macObserve est exactement l'état interne après H(CLE + msg).
// On poursuit le hachage sur le suffixe, à partir de cet état.
return hache(suffixe, macObserve);
}
// Le serveur accepte : true. Le message a été rallongé avec « &dest=eve »,
// et son MAC forgé sans jamais connaître CLE.
//
// C'est l'attaque par extension de longueur du chapitre 7, exploitée pour
// forger. Elle marche parce que l'empreinte Merkle-Damgård EST l'état interne,
// et qu'un MAC préfixe expose donc un point de reprise.
//
// HMAC ferme la porte : HMAC(k,m) = H(k⊕opad ‖ H(k⊕ipad ‖ m)). Le résultat
// visible est la sortie de la passe EXTÉRIEURE ; il n'est l'état interne
// d'aucun calcul qu'on puisse prolonger utilement, car étendre exigerait de
// repartir de la passe intérieure, elle-même scellée par la clé. Un haché
// n'est pas un MAC ; HMAC en est un.
CMAC est l'alternative bâtie sur un chiffrement par blocs plutôt que sur un haché : utile quand un composant matériel embarque déjà AES et non SHA-2. Même service, brique différente.
Quiz · 1 question
En quoi la structure imbriquée de HMAC neutralise-t-elle l'attaque par extension de longueur ?
- Elle chiffre l'empreinte avec la clé — chiffrement
- La valeur publiée est la sortie de la passe extérieure, qui n'est l'état interne d'aucun calcul prolongeable — double passe
- Elle tronque l'empreinte pour cacher l'état interne — troncature
Réponse : L'extension de longueur exploite le fait que l'empreinte Merkle-Damgård EST l'état interne final. HMAC hache une seconde fois : ce qu'on publie est la sortie de la passe extérieure, prolonger cela n'aide en rien puisqu'il faudrait repartir de la passe intérieure, elle-même scellée par la clé. C'est structurel, pas une troncature ni un chiffrement ajouté.
Assembler chiffrement et intégrité : l'ordre compte
Chiffrement et MAC protègent des choses différentes, et on les veut tous les deux. Reste à savoir dans quel ordre les combiner — trois choix, et un seul est sûr en général.
Encrypt-then-MAC. On chiffre, puis on authentifie le chiffré : . À la réception, on vérifie le MAC avant de déchiffrer. Un chiffré modifié est rejeté immédiatement, sans qu'on regarde jamais son contenu ni son padding — l'oracle du chapitre 4 n'existe plus. C'est l'ordre correct, et c'est celui que TLS a fini par adopter.
MAC-then-Encrypt. On authentifie le clair, puis on chiffre l'ensemble. Le récepteur doit déchiffrer avant de pouvoir vérifier — donc il traite un chiffré potentiellement hostile, et son comportement pendant ce déchiffrement peut fuir. C'est l'ordre historique de TLS, et la porte par laquelle sont entrées les attaques par oracle de padding.
Encrypt-and-MAC. On chiffre le clair et on authentifie le clair séparément. Le MAC du clair peut trahir de l'information sur lui, et rien ne protège l'intégrité du chiffré. À éviter.
La règle tient en une phrase : authentifier le chiffré, et le vérifier avant tout déchiffrement. Elle referme à elle seule la faille du chapitre 4.
Deux clés distinctes, une pour chiffrer, une pour le MAC, sont requises — réutiliser la même clé pour deux usages est une source classique d'interactions imprévues. On les dérive d'un secret maître, comme la section suivante l'explique.
Quiz · 1 question
Quel ordre de composition referme l'attaque par oracle de padding du chapitre 4 ?
- MAC-then-Encrypt : authentifier le clair puis chiffrer — clair puis chiffrer
- Encrypt-then-MAC : authentifier le chiffré et vérifier le MAC avant de déchiffrer — chiffré puis vérifier avant
- Encrypt-and-MAC : chiffrer et authentifier le clair séparément — les deux sur le clair
Réponse : Avec Encrypt-then-MAC, le récepteur vérifie le MAC du chiffré AVANT de déchiffrer. Un chiffré modifié échoue sur le MAC et n'est jamais déchiffré : le serveur ne révèle donc plus rien sur le padding, et l'oracle du chapitre 4 disparaît. MAC-then-Encrypt, l'ordre historique de TLS, obligeait au contraire à déchiffrer d'abord — c'est par là que l'attaque passait.
AEAD : ne plus jamais composer soi-même
L'expérience a montré qu'assembler chiffrement et MAC à la main est une source d'erreurs sans fin : mauvais ordre, clés partagées, vérification en temps non constant. La réponse moderne est de fournir un objet unique et déjà correct : le chiffrement authentifié avec données associées (AEAD).
Une interface AEAD prend une clé, un nonce, un message et des données associées — des métadonnées authentifiées mais non chiffrées, comme un en-tête de paquet. Elle rend un chiffré et une étiquette en un seul appel, et refuse de déchiffrer si quoi que ce soit a été altéré. Les deux standards sont AES-GCM (AES-CTR plus une authentification dans le corps de Galois) et ChaCha20-Poly1305. Ce sont les seules primitives de chiffrement que TLS 1.3 autorise encore.
L'avertissement du chapitre 5 revient ici avec une gravité accrue. GCM exige un nonce unique par clé, et sa réutilisation ne se contente pas de révéler les clairs comme un flot ordinaire : elle expose la clé d'authentification elle-même, ce qui permet de forger des messages valides, pas seulement de lire. La faute passe de la confidentialité à l'intégrité. C'est la raison des constructions « résistantes au nonce » comme AES-GCM-SIV, qui dégradent gracieusement au lieu de s'effondrer.
Dériver les clés : KDF, HKDF, Argon2
Il reste une question qu'on a repoussée : d'où viennent les clés ? Rarement de l'aléa pur. Souvent d'un secret déjà négocié — le secret partagé d'un Diffie-Hellman, au chapitre 10 — ou d'un mot de passe. Deux situations, deux outils.
À partir d'un secret cryptographique. Le secret d'un échange de clés est aléatoire mais pas directement utilisable : il faut en tirer plusieurs clés indépendantes, de longueur voulue. HKDF procède en deux temps — extract concentre l'entropie en une clé maîtresse uniforme, expand en dérive autant de sous-clés que nécessaire, étiquetées par un contexte. C'est le dériveur de TLS 1.3.
À partir d'un mot de passe. Ici le secret a peu d'entropie et un attaquant peut le deviner par énumération. Une fonction rapide serait un cadeau : elle testerait des milliards de candidats par seconde. On veut au contraire une fonction délibérément lente et coûteuse. Argon2, lauréat de la compétition de 2015, est à coût mémoire : chaque essai exige une grande quantité de RAM, ce qui neutralise l'avantage des GPU et des circuits dédiés, imbattables sur le calcul pur mais limités en mémoire.
Deux réflexes indissociables du stockage de mots de passe. Le sel — une valeur aléatoire unique par utilisateur, stockée en clair — garantit que deux mots de passe identiques ont des empreintes différentes, et surtout rend inutilisable toute table précalculée : c'est la parade au compromis temps-mémoire du chapitre 6. Le facteur de coût doit être réévalué avec le temps, à mesure que le matériel progresse. Ne jamais stocker un mot de passe avec un simple SHA-256 : c'est trop rapide, et c'est exactement l'erreur qu'Argon2 corrige.
Ce que la suite en fait
Le bloc symétrique est complet : on sait chiffrer, authentifier, et dériver des clés — à condition d'en partager déjà une. C'est la question laissée entière : comment deux personnes qui ne se sont jamais rencontrées établissent-elles un secret commun sur un canal public ? La cryptographie asymétrique y répond, et le chapitre 9 l'ouvre avec RSA. HKDF y retrouvera sa place, en aval de l'échange de clés du chapitre 10 et du protocole TLS du chapitre 13.
À retenir
Flashcards · 3 cartes
- Pourquoi Encrypt-then-MAC est-il le bon ordre de composition ?
- On authentifie le CHIFFRÉ et l'on vérifie le MAC AVANT de déchiffrer. Un chiffré altéré est rejeté sans être déchiffré, donc le serveur ne fuit rien sur son contenu ni son padding : l'oracle du chapitre 4 disparaît. MAC-then-Encrypt oblige à déchiffrer d'abord et a laissé passer les attaques par oracle de padding.
- Qu'est-ce qu'un AEAD, et pourquoi la réutilisation de nonce y est-elle pire qu'en flot simple ?
- Un chiffrement authentifié avec données associées : clé, nonce, message, métadonnées authentifiées non chiffrées, en un appel qui refuse tout déchiffrement altéré (AES-GCM, ChaCha20-Poly1305). Réutiliser un nonce en GCM n'expose pas que les clairs : la clé d'AUTHENTIFICATION tombe, permettant de forger. La faute passe de la confidentialité à l'intégrité.
- Dériver une clé depuis un secret DH ou depuis un mot de passe : pourquoi deux outils différents ?
- Un secret DH est de haute entropie mais brut : HKDF l'extrait puis l'étend en sous-clés indépendantes (rapide, c'est voulu). Un mot de passe est de basse entropie et devinable : il faut au contraire une fonction LENTE et à coût mémoire — Argon2 — plus un sel unique par utilisateur qui casse les tables précalculées. Jamais un simple SHA-256 pour un mot de passe.
QCM de synthèse — Bloc II — Intégrité et authentification
Le QCM ci-dessous porte sur l'ensemble du bloc : plusieurs questions relient les leçons entre elles. En cas d'erreur, le bilan indique le chapitre à revoir.
QCM de bloc · 5 questions
Bloc II — Intégrité et authentification
1. Une empreinte de n bits offre n bits contre la préimage mais seulement n/2 contre les collisions. D'où vient cette asymétrie ?
- La préimage est un problème plus récent, moins étudié
- Pour une collision, l'attaquant choisit les DEUX messages : le paradoxe des anniversaires les fait apparaître vers 2^(n/2) ; pour la préimage, un seul message est libre, donc 2^n
- Les collisions ne concernent que Merkle-Damgård
Réponse : La liberté de choisir les deux messages change l'échelle : une collision parmi 2^n valeurs apparaît vers √(2^n) = 2^(n/2) (anniversaires), alors que trouver un antécédent d'une empreinte imposée reste à 2^n. D'où SHA-256 : 256 bits de sortie pour 128 bits de sécurité collision — et la chute de MD5 (128 bits) et SHA-1 (160 bits, collision à 2^63).
2. Pourquoi H(clé ‖ message) est-il un mauvais MAC avec une fonction Merkle-Damgård, et qu'est-ce qui le corrige ?
- La concaténation révèle la clé ; il faut la chiffrer
- L'empreinte EST l'état interne final : l'extension de longueur forge H(clé ‖ message ‖ suffixe) sans la clé ; HMAC, par sa double passe, referme la porte
- La fonction est trop lente ; CMAC la remplace pour la vitesse
Réponse : L'empreinte Merkle-Damgård est l'état interne final : en la connaissant, on reprend le calcul et l'on forge H(clé ‖ message ‖ padding ‖ suffixe) sans jamais voir la clé. HMAC hache deux fois — la valeur publiée est la sortie de la passe extérieure, qu'on ne peut prolonger utilement. Un haché n'est pas un MAC ; SHA-3, par construction éponge, est immunisé nativement.
3. Encrypt-then-MAC est le bon ordre de composition. Quelle attaque d'un bloc PRÉCÉDENT referme-t-il, et comment ?
- La rencontre au milieu, en doublant la clé du MAC
- L'oracle de padding (bloc I) : on vérifie le MAC du chiffré AVANT de déchiffrer, donc un chiffré altéré est rejeté sans qu'on regarde son padding
- Le nonce rejoué, en imposant un nonce aléatoire
Réponse : En authentifiant le chiffré et en vérifiant ce MAC avant tout déchiffrement, un chiffré modifié échoue sur le MAC et n'est jamais déchiffré : le serveur ne fuit plus rien sur le padding, et l'oracle du bloc I disparaît. MAC-then-Encrypt, l'ordre historique de TLS, obligeait à déchiffrer d'abord — c'est par là que l'attaque passait.
4. Réutiliser un nonce en mode GCM est plus grave qu'en chiffrement par flot simple. Pourquoi ?
- GCM chiffre plus lentement, l'erreur coûte plus cher en temps
- En flot, la réutilisation révèle les clairs ; en GCM elle expose aussi la CLÉ d'authentification, ce qui permet de FORGER des messages valides
- GCM n'utilise pas de nonce, donc la question ne se pose pas
Réponse : Un nonce rejoué en flot redonne c₁ ⊕ c₂ = m₁ ⊕ m₂ : perte de confidentialité. En GCM, la même faute expose en plus la clé d'authentification du corps de Galois, permettant de forger des messages valides — la faute passe de la confidentialité à l'intégrité. D'où les modes résistants au nonce comme AES-GCM-SIV, qui dégradent au lieu de s'effondrer.
5. Pour stocker des mots de passe, pourquoi Argon2 avec un sel plutôt qu'un simple SHA-256 ?
- SHA-256 n'est pas résistant aux collisions
- Un mot de passe a peu d'entropie : il faut une fonction LENTE et à coût mémoire (Argon2), et un sel unique par utilisateur qui rend inutilisable toute table précalculée
- SHA-256 ne peut pas hacher un mot de passe court
Réponse : SHA-256 est trop rapide : un attaquant teste des milliards de candidats par seconde. Un mot de passe étant devinable, on veut au contraire une fonction délibérément lente et à coût mémoire (Argon2, qui neutralise GPU et circuits dédiés), plus un sel unique par utilisateur — la parade au compromis temps-mémoire du bloc I, qui casse les tables arc-en-ciel.