Chapitre 1 · 4 h
Hybridation et déploiement
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 :
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 · 1 question
Pourquoi concaténer les deux secrets plutôt que les combiner par OU exclusif ?
- Parce que le XOR est plus lent
- Parce que le XOR n'est sûr que si les deux secrets sont indépendants, hypothèse qu'on ne veut pas faire
- Parce que les deux secrets n'ont pas la même longueur
Réponse : Le XOR de deux valeurs est uniforme dès que l'une l'est ET qu'elles sont indépendantes. Or les deux mécanismes peuvent partager un générateur d'aléa ou être corrélés d'une manière qu'on n'anticipe pas. La concaténation suivie d'un haché ne fait aucune hypothèse d'indépendance : elle reste sûre tant que l'un des deux secrets est imprévisible, quelle que soit sa relation avec l'autre.
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 client | part serveur | |
|---|---|---|
| X25519 seul | 32 o | 32 o |
| ML-KEM-768 seul | 1184 o | 1088 o |
| X25519MLKEM768 | 1216 o | 1120 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.
Graphique
Taille totale d'un handshake TLS 1.3, en octets
- classique : 16341634
- hybride, certs EC : 39063906
- Falcon-512 : 63986398
- ML-DSA-44 : 1347013470
- SLH-DSA-128s : 2854228542
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 · 1 question
Faut-il migrer les certificats TLS en même temps que l'échange de clés ?
- Oui, sinon la chaîne est le maillon faible
- Non : l'échange de clés est urgent et peu coûteux, les certificats sont moins urgents et bien plus chers
- Non, les certificats ne sont pas concernés par la menace quantique
Réponse : Les certificats sont bien concernés — un attaquant quantique pourrait forger une signature ECDSA et usurper un serveur. Mais cette attaque exige la machine AU MOMENT de la connexion, alors que la récolte anticipée menace la confidentialité dès aujourd'hui. La chaîne n'est donc pas le maillon faible dans le temps, et comme elle coûte huit fois plus cher en octets, la dissocier est le bon arbitrage.
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 de code
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.
Point de départ
// 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`);
}
Solution
const MTU = 1500;
const SEGMENT = 1400;
const INITCWND = 10;
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;
function handshake(echange, signature, nbCerts) {
const e = ECHANGE[echange], s = SIGNATURE[signature];
const clientHello = 300 + e.client;
const serverHello = 150 + e.serveur;
// Chaque certificat de la chaîne porte SA clé publique et la signature de
// son émetteur : les deux coûts se cumulent, et se cumulent par certificat.
const chaine = nbCerts * (s.pk + s.sig + ENTETE_CERT);
// Le CertificateVerify prouve la possession de la clé du certificat
// terminal : une signature de plus, sur le transcript.
const certificateVerify = s.sig;
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`);
}
// Quatre lectures, et la troisième est celle qui surprend.
//
// 1. L'HYBRIDE SEUL EST ABORDABLE. Passer de 1600 à environ 3900 octets
// tient largement dans la fenêtre initiale. C'est pourquoi les
// navigateurs ont pu l'activer par défaut sans rien casser — sauf pour
// quelques équipements intermédiaires que le ClientHello fragmenté a
// perturbés, ce que la ligne d'avertissement signale.
//
// 2. CE SONT LES CERTIFICATS QUI COÛTENT. Le passage de certificats ECDSA à
// ML-DSA triple à lui seul le handshake. La chaîne pèse plus que
// l'échange de clés, et de loin.
//
// 3. SLH-DSA EST HORS JEU EN TLS. Avec des signatures de 7856 octets et deux
// certificats plus le CertificateVerify, on dépasse les 25 kio : trois
// aller-retours supplémentaires. C'est un excellent schéma pour signer un
// firmware une fois par trimestre, un très mauvais pour un handshake.
//
// 4. FALCON EST LE PLUS ÉCONOMIQUE des trois post-quantiques, ce qui explique
// l'intérêt persistant pour lui malgré ses difficultés d'implémentation.
//
// Conclusion opérationnelle : migrez d'abord l'échange de clés — urgent à
// cause de la récolte anticipée, et peu coûteux. Les certificats sont moins
// urgents (la signature n'a pas à résister rétroactivement) et bien plus
// chers : le temps gagné sert à laisser mûrir les solutions de compression
// de chaînes.
À retenir
Flashcards · 3 cartes
- Que garantit un combinateur hybride, et pourquoi concaténer plutôt que XOR ?
- Il garantit que l'ensemble reste sûr tant qu'AU MOINS UN des deux mécanismes tient — la leçon de SIKE. On concatène les deux secrets et le transcript avant de hacher, parce que le XOR exige l'indépendance des deux secrets, hypothèse qu'on ne veut pas avoir à faire.
- Pourquoi l'échange de clés a-t-il migré avant les certificats ?
- Deux raisons qui vont dans le même sens. L'urgence : la récolte anticipée menace la confidentialité dès aujourd'hui, alors qu'une signature exige la machine au moment de la connexion. Le coût : X25519MLKEM768 double le handshake, des certificats ML-DSA le multiplient par huit et débordent la fenêtre de congestion initiale, coûtant un aller-retour.
- Quel a été l'incident de déploiement principal de l'hybridation TLS ?
- La fragmentation du ClientHello. Avec 1216 octets de part de clé, il dépasse la MTU et s'étale sur deux segments TCP — ce qui est légal mais que certains équipements intermédiaires ne supportaient pas, parce qu'ils supposaient sans jamais l'écrire qu'un ClientHello tenait dans un paquet.