cursus.

Cours 5 · MigrationLeçon 1 sur 2

Hybridation et déploiement

4 h de lecture7 sections Version PDF

À la fin de cette leçon, vous saurez

Combinateurs hybrides X25519 + ML-KEM, intégration TLS 1.3 et SSH, taille des handshakes et fragmentation, certificats post-quantiques.

Nous avons douze chapitres de théorie et de schémas. Reste à les mettre dans des protocoles qui existent déjà, tournent sur des équipements qu'on ne contrôle pas, et ne doivent pas cesser de fonctionner pendant l'opération. C'est un problème d'ingénierie, et il a ses propres pièges.

Le combinateur hybride

L'hybridation consiste à exécuter simultanément un mécanisme classique et un mécanisme post-quantique, puis à combiner les deux secrets obtenus. La propriété visée est explicite : la construction reste sûre tant qu'au moins l'un des deux tient.

La justification n'est pas théorique, elle est empirique — c'est le chapitre 10. Un déploiement hybride X25519 + SIKE n'a rien perdu le 30 juillet 2022. Un déploiement SIKE seul était nu du jour au lendemain.

Le combinateur retenu par TLS 1.3 est la concaténation des deux secrets, injectée dans la dérivation de clés :

secret=KDF(ssPQssclassiquetranscript)\mathit{secret} = \mathrm{KDF}\big( ss_{\mathrm{PQ}} \,\|\, ss_{\mathrm{classique}} \,\|\, \mathit{transcript} \big)

Pourquoi concaténer plutôt que faire un OU exclusif ? Le XOR est sûr si l'un des deux secrets est uniformément aléatoire, mais il ne l'est plus si un attaquant peut corréler les deux — ce qui n'est pas exclu quand les deux mécanismes partagent le même aléa ou le même générateur. La concaténation suivie d'un haché ne fait aucune hypothèse d'indépendance.

Un détail qui compte, et qui a été appris à la dure sur d'autres protocoles : le transcript doit entrer dans la dérivation. Sans lui, un attaquant peut parfois substituer un chiffré à un autre sans que la clé change. Les combinateurs modernes lient explicitement les chiffrés et les clés publiques à la clé dérivée.

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

Pourquoi concaténer les deux secrets plutôt que les combiner par OU exclusif ?

X25519MLKEM768 dans TLS 1.3

Le déploiement s'est fait sans changer TLS. Le groupe hybride est enregistré comme un groupe supplémentaire dans la négociation existante : le client propose une part de clé qui est la concaténation de la clé d'encapsulation ML-KEM-768 et de la clé publique X25519. Le serveur répond avec le chiffré ML-KEM et sa part X25519.

part clientpart serveur
X25519 seul32 o32 o
ML-KEM-768 seul1184 o1088 o
X25519MLKEM7681216 o1120 o

L'élégance de la solution est qu'elle ne demande aucune modification du protocole : un serveur qui ne connaît pas le groupe hybride l'ignore et retombe sur X25519. La migration peut donc se faire côté client sans coordination.

Le déploiement est réel et massif : les principaux navigateurs l'activent par défaut, et une part importante du trafic web est aujourd'hui protégée par un échange de clés hybride. C'est le point le plus encourageant du cours — la partie difficile a déjà commencé, et elle marche.

La fragmentation, seul incident notable

Un ClientHello classique fait quelques centaines d'octets et tient dans un paquet. Avec une part de clé de 1216 octets, il dépasse la MTU et se répartit sur deux segments TCP.

En théorie, c'est sans conséquence. En pratique, certains équipements intermédiaires — pare-feux, inspecteurs de trafic, terminaisons TLS anciennes — supposaient qu'un ClientHello tenait dans un seul segment. Ces implémentations ont échoué, et ces échecs ont constitué l'essentiel des incidents de la migration.

C'est une leçon d'ingénierie qui dépasse la cryptographie : une hypothèse implicite jamais énoncée finit toujours par être violée. Personne n'avait écrit que le ClientHello tenait dans un paquet ; tout le monde en dépendait.

Les certificats, le vrai problème

L'échange de clés est le cas facile. Les certificats sont le mur.

Une chaîne TLS transporte, pour chaque certificat, une clé publique et la signature de son émetteur, plus un CertificateVerify final. Le passage à ML-DSA-44 fait passer chaque certificat d'environ 500 octets à près de 4 kilooctets.

L'hybridation de l'échange de clés double le handshake — c'est absorbable. Passer les certificats en ML-DSA le multiplie par huit et déborde la fenêtre de congestion initiale, ce qui coûte un aller-retour supplémentaire à chaque connexion.

La fenêtre de congestion initiale vaut typiquement dix segments, soit environ 14 kilooctets. Un handshake classique en consomme un huitième ; un handshake entièrement post-quantique la sature ou la dépasse, ce qui ajoute un aller-retour — donc de la latence perceptible à chaque connexion.

Plusieurs pistes sont explorées pour comprimer les chaînes : supprimer les certificats intermédiaires que le client possède déjà, négocier les ancres de confiance, ou remplacer la chaîne par une preuve d'appartenance à un arbre de Merkle — l'idée du chapitre 9, appliquée à la PKI.

En attendant, la recommandation opérationnelle est claire, et elle découle du chapitre 1 :

  • l'échange de clés est urgent — récolte anticipée — et peu coûteux : migrez-le maintenant, en hybride ;
  • les certificats sont moins urgents — une signature vérifiée aujourd'hui ne craint pas une machine future — et bien plus chers : attendez que les solutions de compression mûrissent.

Cette dissociation est la décision d'architecture la plus importante du bloc.

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

Faut-il migrer les certificats TLS en même temps que l'échange de clés ?

SSH et le reste

SSH a migré avant TLS, et sans hybridation débattue : OpenSSH a adopté un échange hybride par défaut dès 2022, d'abord avec un mécanisme à réseaux non normalisé, puis avec ML-KEM une fois la FIPS 203 publiée. Le contexte s'y prêtait — un client et un serveur, peu d'équipements intermédiaires, des mises à jour fréquentes.

Les autres chantiers avancent plus lentement, et pour des raisons instructives. IKEv2 et IPsec souffrent de la fragmentation UDP. Les protocoles de messagerie doivent gérer des clés pré-publiées à long terme. Les objets connectés n'ont ni la mémoire ni la bande passante, et leur cycle de remplacement se compte en décennies — c'est là que l'inégalité de Mosca est la plus défavorable.

À vous

Exercice · JavaScript · à vous de jouer

Complétez le coût de la chaîne de certificats, puis identifiez ce qui coûte vraiment — ce n'est pas l'échange de clés.

En attente
// Combien d'octets coûte un handshake TLS 1.3 selon ce qu'on y met ?
// Modèle simplifié mais aux bons ordres de grandeur.

const MTU = 1500;          // trame Ethernet
const SEGMENT = 1400;      // charge utile TCP typique
const INITCWND = 10;       // fenêtre de congestion initiale, en segments

const ECHANGE = {
  "X25519":            { client: 32,   serveur: 32 },
  "ML-KEM-768":        { client: 1184, serveur: 1088 },
  "X25519MLKEM768":    { client: 1216, serveur: 1120 },
};

const SIGNATURE = {
  "ECDSA P-256": { pk: 64,   sig: 64 },
  "Ed25519":     { pk: 32,   sig: 64 },
  "ML-DSA-44":   { pk: 1312, sig: 2420 },
  "Falcon-512":  { pk: 897,  sig: 666 },
  "SLH-DSA-128s":{ pk: 32,   sig: 7856 },
};

const ENTETE_CERT = 400;   // ASN.1, extensions, noms — hors clé et signature

function handshake(echange, signature, nbCerts) {
  const e = ECHANGE[echange], s = SIGNATURE[signature];
  const clientHello = 300 + e.client;
  const serverHello = 150 + e.serveur;

  // À COMPLÉTER — coût de la chaîne de certificats puis du CertificateVerify.
  // Chaque certificat porte une clé publique, la signature de son émetteur,
  // et l'en-tête. Le CertificateVerify est UNE signature de plus.
  const chaine = 0;
  const certificateVerify = 0;

  const total = clientHello + serverHello + chaine + certificateVerify + 100;
  return { clientHello, serverHello, chaine, certificateVerify, total };
}

const CAS = [
  ["classique",            "X25519",         "ECDSA P-256", 2],
  ["hybride, certs RSA/EC", "X25519MLKEM768", "ECDSA P-256", 2],
  ["tout post-quantique",   "X25519MLKEM768", "ML-DSA-44",   2],
  ["PQ avec Falcon",        "X25519MLKEM768", "Falcon-512",  2],
  ["PQ avec SLH-DSA",       "X25519MLKEM768", "SLH-DSA-128s", 2],
];

console.log("cas                    | CH   | SH   | chaîne | total  | segments | tient en initcwnd");
for (const [nom, e, s, n] of CAS) {
  const h = handshake(e, s, n);
  const seg = Math.ceil(h.total / SEGMENT);
  console.log(
    `${nom.padEnd(22)} | ${String(h.clientHello).padStart(4)} | ${String(h.serverHello).padStart(4)}` +
    ` | ${String(h.chaine).padStart(6)} | ${String(h.total).padStart(6)}` +
    ` | ${String(seg).padStart(8)} | ${seg <= INITCWND ? "oui" : "NON (+1 aller-retour)"}`
  );
  if (h.clientHello > MTU - 100) console.log(`   ↑ ClientHello dépasse la MTU : fragmenté sur 2 segments`);
}

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

À 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.