cursus.

Cours 3 · Intégrité et authentificationLeçon 2 sur 2

MAC et chiffrement authentifié

5 h de lecture8 sections Version PDF

À la fin de cette leçon, vous saurez

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 MAC(k,m)\text{MAC}(k, m) qui produit une étiquette courte. Bob, qui partage la clé kk, 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 :

HMAC(k,m)=H((kopad)H((kipad)m))\text{HMAC}(k, m) = H\big((k \oplus \text{opad}) \,\Vert\, H((k \oplus \text{ipad}) \,\Vert\, m)\big)

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 · JavaScript · à vous de jouer

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.

En attente
// 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.");

Console de sortie
Le résultat s'affiche dans la console

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 · vérifiez votre compréhension Sans réponse

En quoi la structure imbriquée de HMAC neutralise-t-elle l'attaque par extension de longueur ?

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é : MAC(km,c)\text{MAC}(k_m, c). À 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 · vérifiez votre compréhension Sans réponse

Quel ordre de composition referme l'attaque par oracle de padding du chapitre 4 ?

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 · 1 / 3Toucher pour retourner

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 · question 1 / 5 Sans réponse

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 ?

0 / 5 traitées
Fin de la leçon

Vous avez parcouru les 8 sections.

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