Chapitre 1 · 5 h
Réseaux : fondements
Réseau, base, déterminant ; SVP, CVP et SIVP ; l'arrondi de Babai selon la base ; réduction LLL et BKZ, et l'estimation de sécurité qui en découle.
Le NIST a publié le 13 août 2024 trois normes post-quantiques. Deux d'entre elles, FIPS 203 pour l'encapsulation de clé et FIPS 204 pour la signature, reposent sur le même objet mathématique — et cet objet n'est ni un entier à factoriser, ni un groupe où le logarithme discret serait difficile. C'est un réseau euclidien.
Ce chapitre installe l'objet, ses problèmes difficiles, et la seule chose qui décide de leur difficulté : la base dont on dispose pour le décrire. À la fin, vous aurez fait tourner deux fois le même algorithme d'attaque sur le même réseau, et obtenu deux réponses dont une seule est bonne.
Un réseau, c'est une grille engendrée
Soit des vecteurs linéairement indépendants de . Le réseau qu'ils engendrent est l'ensemble de leurs combinaisons à coefficients entiers :
Tout tient dans le . Avec des coefficients réels, la même famille engendrerait un sous-espace vectoriel — un plan continu, où « le point le plus proche » n'a aucun sens puisque tous les points y sont. Avec des coefficients entiers, on obtient une grille de points isolés, et les questions de proximité redeviennent des questions.
Prenons un exemple qui servira tout le chapitre, dans le plan :
Quels points obtient-on ? Un point vérifie et , d'où et . Les deux coefficients et sont entiers exactement quand est multiple de 5. Ce réseau est donc
C'est un sous-ensemble de qui en retient un point sur cinq. Retenez ce chiffre 5, il va revenir.
Le même réseau, une infinité de bases
Un réseau n'a pas une base, il en a une infinité. Si est une matrice à coefficients entiers de déterminant — on la dit unimodulaire — alors engendre exactement le même réseau que . La condition sur le déterminant est ce qui rend entière elle aussi : sans elle, on obtiendrait un sous-réseau plus grossier, pas le même réseau.
Appliquons-la à notre exemple, avec de déterminant :
| angle | déterminant | |||
|---|---|---|---|---|
| base courte | 2,24 | 2,24 | 90,0° | 5 |
| base longue | 5,00 | 8,06 | 7,1° | 5 |
Les deux dernières colonnes disent l'essentiel. L'angle s'effondre — les vecteurs de la seconde base sont presque alignés — mais le déterminant ne bouge pas. C'est une propriété du réseau, pas de la base : géométriquement, c'est l'aire du parallélogramme construit sur les vecteurs de base, et cette aire vaut 5 dans les deux cas. Un point de réseau pour cinq unités d'aire, quelle que soit la manière de le décrire.
On note ce déterminant , et il mesure la densité du réseau. Un petit déterminant, beaucoup de points ; un grand déterminant, des points clairsemés.
Quiz · 1 question
On remplace une base d'un réseau par une autre base du même réseau. Qu'est-ce qui change ?
- L'ensemble des points du réseau — l'objet lui-même
- La longueur et l'angle des vecteurs de base — la description
- Le déterminant du réseau — l'aire fondamentale
Réponse : Changer de base ne change ni les points — c'est le même réseau par définition — ni le déterminant, invariant parce que la matrice de passage a un déterminant de ±1. Seule la FORME de la description change : longueurs et angles. Toute la cryptographie à base de réseaux tient dans cet écart entre un objet invariant et des descriptions inégales.
Trois problèmes, une seule difficulté
Sur un réseau, trois problèmes servent de fondation.
SVP (Shortest Vector Problem) : trouver le vecteur non nul le plus court. Sa longueur est notée . Dans notre exemple, .
CVP (Closest Vector Problem) : étant donné un point du plan qui n'est pas dans le réseau, trouver le point du réseau le plus proche. C'est le problème central du chapitre.
SIVP (Shortest Independent Vectors Problem) : trouver vecteurs indépendants tous courts. C'est la version dont Regev tire, au chapitre suivant, la réduction pire cas vers cas moyen de LWE.
Aucun de ces problèmes n'est utilisé sous sa forme exacte. La cryptographie emploie leurs versions approchées : trouver un vecteur au plus fois plus long que l'optimum, avec polynomial en . Cette nuance mérite d'être dite franchement à des étudiants, car elle est souvent escamotée.
SVP exact est NP-difficile sous réductions randomisées, CVP exact est NP-difficile. Mais la sécurité des schémas à réseaux ne repose pas sur cette NP-difficulté. Pour , le problème approché tombe dans : il ne peut donc pas être NP-difficile sans effondrer la hiérarchie polynomiale. Ce sur quoi on s'appuie, c'est l'absence d'algorithme efficace connu — classique ou quantique — pour ces facteurs d'approximation, après quarante ans de tentatives. C'est une hypothèse solide, pas un théorème.
Une borne utile pour situer les ordres de grandeur : le théorème de Minkowski garantit . Ici, , et le vrai vaut 2,24 — la borne est respectée, et lâche. Pour les grandes dimensions on lui préfère l'heuristique gaussienne, , qui n'a de sens qu'asymptotiquement.
L'arrondi de Babai
Voici l'algorithme le plus simple qui attaque CVP. Il tient en trois lignes.
Fonction Babai(B, t) : point du réseau c ← coordonnées de t dans la base B // n nombres RÉELS Pour i de 1 à n Faire a[i] ← Arrondi(c[i]) // n entiers FinPour Retourner a[1]·b[1] + … + a[n]·b[n]FinFonctionOn décompose la cible dans la base, on arrondit chaque coordonnée, on recombine. Le résultat est nécessairement un point du réseau, puisque ses coordonnées sont entières.
La lecture géométrique est plus parlante que le code. Arrondir toutes les coordonnées à l'entier le plus proche revient à demander : dans quelle cellule fondamentale la cible tombe-t-elle ? Or cette cellule est un parallélogramme dessiné par la base — un carré si la base est orthogonale, un fuseau très allongé si elle ne l'est pas. Une cellule presque carrée entoure son point ; un fuseau peut contenir un point qui en est très éloigné.
Animation · 7 étapes
Un réseau, deux bases, un même point à approcher
- Les points du réseau — Toutes les combinaisons à coefficients ENTIERS de deux vecteurs, et rien d'autre. La grille est infinie ; la fenêtre n'en montre qu'un morceau.
- Une base courte — Deux vecteurs de longueur 2,24 et 2,24, à 90,0° l'un de l'autre. L'aire du parallélogramme qu'ils dessinent est le déterminant du réseau : 5.
- Une base longue du même réseau — Longueurs 5,00 et 8,06, écart de 7,1° seulement. Ces vecteurs engendrent pourtant exactement les mêmes points, et leur parallélogramme a la même aire : 5.
- Le point à approcher — La cible (5,5 ; 2,6) n'est pas un point du réseau. Le problème CVP demande le point du réseau qui en est le plus proche.
- Arrondi dans la base courte — Coordonnées de la cible dans cette base : (2,14 ; 1,68). Arrondies, elles donnent (2 ; 2), donc le point (6 ; 2), à 0,78 de la cible. La cellule est presque carrée : la cible tombe dans celle du bon point.
- Arrondi dans la base longue — Même cible, même algorithme : (-3,06 ; 2,60) s'arrondit en (-3 ; 3), donc le point (9 ; 3) — à 3,52, soit 4,5 fois plus loin. La cellule est un fuseau : elle contient la cible tout en s'étirant hors du cadre.
- Ce que la base décide — Le réseau n'a pas changé, l'algorithme non plus. Seule la forme de la base change la réponse. Une base courte est une information qu'on peut garder secrète tout en publiant une base longue du même réseau : c'est l'intuition de la trappe.
Déroulons les nombres pour la cible , qui n'est pas dans le réseau.
| Base employée | Coordonnées de | Arrondi | Point renvoyé | Distance |
|---|---|---|---|---|
| courte | (2,14 ; 1,68) | (2 ; 2) | (6 ; 2) | 0,78 |
| longue | (−3,06 ; 2,60) | (−3 ; 3) | (9 ; 3) | 3,52 |
Le même réseau, le même algorithme, la même cible, et un résultat 4,5 fois pire. Les deux points renvoyés appartiennent bien au réseau — et sont tous deux multiples de 5. Mais l'un est le plus proche et l'autre non.
Deux précisions d'honnêteté. D'abord, la base courte est ici exactement orthogonale — — ce qui rend l'arrondi non seulement bon mais exact : le point renvoyé est le plus proche, et l'exercice ci-dessous le confirme par force brute. Une base seulement réduite, sans être orthogonale, donne un bon résultat, pas toujours l'optimal. Ensuite, l'écart n'est pas systématique : pour beaucoup de cibles, la base longue tombe juste par chance. Ce qui compte est qu'elle ne le garantit pas.
On tient là l'intuition de la trappe : le réseau est public, une base longue peut être publiée sans rien révéler, et la base courte du même réseau constitue le secret qui rend CVP facile. Attention toutefois — et c'est un point que la suite du cours corrigera — les schémas normalisés ne procèdent pas ainsi. GGH et NTRUSign publiaient littéralement une mauvaise base, et tous deux ont été cassés : les signatures fuyaient de l'information sur la base secrète. ML-KEM place son secret ailleurs, dans le bruit de LWE. L'intuition « même objet, deux descriptions, une seule utilisable » reste juste ; sa mise en œuvre naïve, non.
Quiz · 1 question
Pourquoi l'arrondi de Babai se trompe-t-il avec la base longue ?
- Parce que la base longue engendre un réseau différent
- Parce que sa cellule fondamentale est un fuseau qui s'étire loin du point qu'elle entoure
- Parce que l'arrondi accumule des erreurs de calcul flottant
Réponse : Les deux bases engendrent le même réseau, et le calcul est exact — 2,14 et −3,06 s'arrondissent sans ambiguïté. L'algorithme renvoie le point dont la cellule contient la cible ; quand cette cellule est un fuseau très allongé, elle contient des points du plan situés très loin de son centre. La géométrie de la base, rien d'autre.
Réduire une base : Gauss, LLL, BKZ
Si une bonne base rend CVP facile, la question devient : peut-on fabriquer une bonne base à partir d'une mauvaise ? C'est le problème de la réduction, et c'est là que se joue réellement la sécurité.
En dimension 2, la réponse est oui, et l'algorithme est élémentaire. La réduction de Gauss (ou de Lagrange) répète deux opérations : garder le plus court des deux vecteurs en premier, puis raccourcir l'autre en lui retranchant le multiple entier du premier qui le rapproche le plus de l'origine, soit avec .
Sur notre base longue :
| Tour | arrondi | |||
|---|---|---|---|---|
| départ | (5, 0) | (8, 1) | 40/25 = 1,6 | 2 |
| 1 | (−2, 1) | (5, 0) | −10/5 = −2 | −2 |
| 2 | (−2, 1) | (1, 2) | — | arrêt |
Deux tours suffisent : on retombe sur et , c'est-à-dire la base courte au signe et à l'ordre près. En dimension 2, une mauvaise base ne protège rien. La sécurité des réseaux ne vient pas du principe, elle vient de la dimension — ML-KEM-768 travaille sur 768 inconnues, et le réseau attaqué en compte davantage encore.
En dimension quelconque, l'algorithme de référence est LLL (Lenstra, Lenstra, Lovász, 1982). Il s'exécute en temps polynomial et garantit
Une garantie exponentielle en produite par un algorithme polynomial : en dimension 768, le facteur vaut , ce qui ne garantit rien du tout. En pratique LLL fait beaucoup mieux que sa borne, et c'est cet écart entre la garantie prouvée et le comportement observé qui oblige toute l'estimation de sécurité à devenir expérimentale.
Pour mesurer ce comportement, on utilise le facteur de Hermite , défini par
Plus est proche de 1, meilleure est la réduction. LLL plafonne autour de quelle que soit la dimension, et cela ne suffit pas à casser les schémas normalisés. D'où BKZ, qui généralise LLL : au lieu de travailler sur des paires de vecteurs, il résout SVP exactement sur des blocs de taille et propage. LLL est le cas . Augmenter améliore — au prix d'un oracle SVP en dimension , dont le coût est exponentiel.
Estimer une sécurité qu'on ne sait pas prouver
Le modèle employé par les spécifications de ML-KEM et ML-DSA s'appelle core-SVP. Il compte le coût d'un seul appel à l'oracle SVP en dimension , en ignorant délibérément le nombre d'appels et les facteurs polynomiaux. Les meilleurs cribles connus donnent opérations en classique et en quantique.
| coût classique | coût quantique | ||
|---|---|---|---|
| 2 (LLL) | ≈ 1,0219 (mesuré) | polynomial | polynomial |
| 100 | 1,0093 | ||
| 200 | 1,0063 | ||
| 400 | 1,0040 | ||
| 600 | 1,0030 | ||
| 873 | 1,0023 |
Attaquer ML-KEM-768 demande, d'après l'analyse publiée, un de l'ordre de 620, soit environ opérations classiques ; ML-KEM-1024 se situe vers . Ces chiffres sont des estimations dans un modèle de coût, pas des théorèmes, et il faut le dire aux étudiants sans détour. Le modèle core-SVP est conservateur sur un point — il n'attribue qu'un seul appel d'oracle là où l'attaque réelle en fait beaucoup — et optimiste sur un autre, puisqu'il ignore le coût mémoire du criblage, qui est colossal. Les niveaux de sécurité annoncés bougent quand la cryptanalyse progresse, et ils ont déjà bougé.
Deux remarques pour finir. La colonne quantique n'apporte qu'un gain marginal, 0,265 contre 0,292 : contrairement à Shor sur RSA, aucun effondrement n'est attendu ici, et c'est précisément pour cela que les réseaux ont été retenus. Et la dernière colonne explique le choix des jeux de paramètres : les niveaux 1, 3 et 5 du NIST se lisent directement sur ces exposants.
À vous
Le fil le plus formateur sur les réseaux est d'implémenter puis de casser. Complétez l'arrondi de Babai, puis regardez les deux bases répondre différemment à la même question. La force brute qui suit vérifie laquelle a raison — un luxe permis par la dimension 2, et la raison même pour laquelle les schémas réels travaillent en dimension plusieurs centaines.
Exercice de code
Complétez l'arrondi de Babai, puis comparez ce que les deux bases du même réseau répondent à la même question.
Point de départ
// Deux bases du MÊME réseau : les points {(x, y) entiers | x + 2y ≡ 0 mod 5}.
const COURTE = { b1: [1, 2], b2: [2, -1] }; // orthogonale, vecteurs de norme √5
const LONGUE = { b1: [5, 0], b2: [8, 1] }; // presque alignés, 7,1° d'écart
const CIBLE = [5.5, 2.6]; // pas un point du réseau
const det = (B) => B.b1[0] * B.b2[1] - B.b1[1] * B.b2[0];
// Coordonnées RÉELLES de t dans la base : t = c1·b1 + c2·b2.
function coordonnees(B, t) {
const d = det(B);
return [
(t[0] * B.b2[1] - t[1] * B.b2[0]) / d,
(B.b1[0] * t[1] - B.b1[1] * t[0]) / d,
];
}
const combiner = (B, c1, c2) => [
c1 * B.b1[0] + c2 * B.b2[0],
c1 * B.b1[1] + c2 * B.b2[1],
];
function babai(B, t) {
const [c1, c2] = coordonnees(B, t);
// À COMPLÉTER — un point du réseau a des coordonnées ENTIÈRES dans la base.
// En l'état, la fonction renvoie la cible elle-même : distance nulle, mais
// le « point » trouvé n'appartient pas au réseau.
const a1 = c1;
const a2 = c2;
const point = combiner(B, a1, a2);
return { c1, c2, a1, a2, point, d: Math.hypot(point[0] - t[0], point[1] - t[1]) };
}
// Un point est dans le réseau si ses coordonnées dans une base sont entières.
function dansLeReseau(p) {
const [c1, c2] = coordonnees(COURTE, p);
return Number.isInteger(Math.round(c1 * 1e9) / 1e9) &&
Number.isInteger(Math.round(c2 * 1e9) / 1e9);
}
const n2 = (x) => x.toFixed(2);
for (const [nom, B] of [["courte", COURTE], ["longue", LONGUE]]) {
const r = babai(B, CIBLE);
console.log(
`base ${nom.padEnd(7)} coords (${n2(r.c1)}, ${n2(r.c2)})` +
` → (${r.a1}, ${r.a2}) → point (${r.point}) ` +
` d = ${n2(r.d)} dans le réseau : ${dansLeReseau(r.point)}`
);
}
// Référence : le VRAI plus proche, cherché par force brute. Possible ici
// parce que la dimension est 2 ; c'est exactement ce qui devient hors de
// portée en dimension 768.
let meilleur = null;
for (let a = -20; a <= 20; a++) {
for (let b = -20; b <= 20; b++) {
const p = combiner(COURTE, a, b);
const d = Math.hypot(p[0] - CIBLE[0], p[1] - CIBLE[1]);
if (!meilleur || d < meilleur.d) meilleur = { p, d };
}
}
console.log(`force brute → point (${meilleur.p}) d = ${n2(meilleur.d)}`);
// Fourni : la réduction de Gauss, qui transforme une base du plan en la plus
// courte possible. Lancez-la sur LONGUE et regardez ce qu'elle rend.
function gauss(B) {
let u = B.b1.slice();
let v = B.b2.slice();
const norme = (w) => Math.hypot(w[0], w[1]);
for (let k = 0; k < 50; k++) {
if (norme(u) > norme(v)) [u, v] = [v, u];
const mu = Math.round((u[0] * v[0] + u[1] * v[1]) / (u[0] * u[0] + u[1] * u[1]));
const w = [v[0] - mu * u[0], v[1] - mu * u[1]];
if (norme(w) >= norme(u)) return { b1: u, b2: w };
v = w;
}
return { b1: u, b2: v };
}
console.log("Gauss(LONGUE) =", JSON.stringify(gauss(LONGUE)));
Solution
const COURTE = { b1: [1, 2], b2: [2, -1] };
const LONGUE = { b1: [5, 0], b2: [8, 1] };
const CIBLE = [5.5, 2.6];
const det = (B) => B.b1[0] * B.b2[1] - B.b1[1] * B.b2[0];
function coordonnees(B, t) {
const d = det(B);
return [
(t[0] * B.b2[1] - t[1] * B.b2[0]) / d,
(B.b1[0] * t[1] - B.b1[1] * t[0]) / d,
];
}
const combiner = (B, c1, c2) => [
c1 * B.b1[0] + c2 * B.b2[0],
c1 * B.b1[1] + c2 * B.b2[1],
];
function babai(B, t) {
const [c1, c2] = coordonnees(B, t);
// L'arrondi, c'est TOUT l'algorithme. La difficulté du problème ne tient
// pas à cette ligne mais à la base dans laquelle elle est appliquée.
const a1 = Math.round(c1);
const a2 = Math.round(c2);
const point = combiner(B, a1, a2);
return { c1, c2, a1, a2, point, d: Math.hypot(point[0] - t[0], point[1] - t[1]) };
}
function dansLeReseau(p) {
const [c1, c2] = coordonnees(COURTE, p);
return Number.isInteger(Math.round(c1 * 1e9) / 1e9) &&
Number.isInteger(Math.round(c2 * 1e9) / 1e9);
}
const n2 = (x) => x.toFixed(2);
for (const [nom, B] of [["courte", COURTE], ["longue", LONGUE]]) {
const r = babai(B, CIBLE);
console.log(
`base ${nom.padEnd(7)} coords (${n2(r.c1)}, ${n2(r.c2)})` +
` → (${r.a1}, ${r.a2}) → point (${r.point}) ` +
` d = ${n2(r.d)} dans le réseau : ${dansLeReseau(r.point)}`
);
}
let meilleur = null;
for (let a = -20; a <= 20; a++) {
for (let b = -20; b <= 20; b++) {
const p = combiner(COURTE, a, b);
const d = Math.hypot(p[0] - CIBLE[0], p[1] - CIBLE[1]);
if (!meilleur || d < meilleur.d) meilleur = { p, d };
}
}
console.log(`force brute → point (${meilleur.p}) d = ${n2(meilleur.d)}`);
function gauss(B) {
let u = B.b1.slice();
let v = B.b2.slice();
const norme = (w) => Math.hypot(w[0], w[1]);
for (let k = 0; k < 50; k++) {
if (norme(u) > norme(v)) [u, v] = [v, u];
const mu = Math.round((u[0] * v[0] + u[1] * v[1]) / (u[0] * u[0] + u[1] * u[1]));
const w = [v[0] - mu * u[0], v[1] - mu * u[1]];
if (norme(w) >= norme(u)) return { b1: u, b2: w };
v = w;
}
return { b1: u, b2: v };
}
console.log("Gauss(LONGUE) =", JSON.stringify(gauss(LONGUE)));
// Trois choses à lire dans la sortie.
//
// 1. Les deux bases renvoient des points DIFFÉRENTS : (6, 2) à 0,78 de la
// cible, (9, 3) à 3,52. Même réseau, même algorithme, même cible.
// 2. La force brute confirme (6, 2) : avec une base orthogonale, l'arrondi
// ne se contente pas d'être bon, il est exact.
// 3. Gauss ramène LONGUE à { b1: [-2, 1], b2: [1, 2] }, soit COURTE au signe
// et à l'ordre près. En dimension 2, la mauvaise base ne protège rien —
// la sécurité vient de la dimension, pas du principe.
Ce que la suite en fait
Un réseau tel qu'il vient d'être décrit ne fait pas encore un cryptosystème : il manque le moyen d'engendrer, à la demande, une instance dont on connaît le secret sans que personne d'autre ne puisse le retrouver. C'est le rôle de LWE, au chapitre suivant, qui reformule tout ce chapitre en une seule phrase — résoudre un système linéaire bruité — et l'accompagne d'une réduction pire cas vers cas moyen que la géométrie seule ne fournissait pas.
Ce que vous emportez d'ici : le réseau est l'objet, la base est la description, et toute la construction repose sur l'écart entre les deux.
À retenir
Flashcards · 3 cartes
- Qu'est-ce qui est invariant quand on change la base d'un réseau ?
- L'ensemble des points et le déterminant — l'aire de la cellule fondamentale. La matrice de passage étant unimodulaire (déterminant ±1), elle préserve le volume. Seuls les longueurs et les angles des vecteurs de base changent, et c'est là-dessus que repose la trappe.
- Pourquoi la sécurité des réseaux ne repose-t-elle PAS sur la NP-difficulté de SVP ?
- Parce que la cryptographie emploie les versions approchées, avec un facteur γ polynomial en n. Pour γ ≥ √n, le problème est dans NP ∩ coNP, donc pas NP-difficile sauf effondrement de la hiérarchie polynomiale. La confiance vient de l'absence d'algorithme efficace connu, pas d'un théorème.
- Que mesure le facteur de Hermite δ₀, et pourquoi BKZ-β le fait-il baisser ?
- δ₀ mesure la qualité d'une réduction : ‖b₁‖ ≈ δ₀ⁿ · det(L)^(1/n), donc plus δ₀ est proche de 1, plus le vecteur obtenu est court. BKZ résout SVP exactement sur des blocs de taille β : augmenter β améliore δ₀ au prix d'un coût exponentiel, environ 2^(0,292β) en classique.