{"id":314,"date":"2026-06-21T08:00:00","date_gmt":"2026-06-21T08:00:00","guid":{"rendered":"https:\/\/shattered.io\/fr\/2026\/06\/21\/cryptographie-post-quantique-nodejs\/"},"modified":"2026-06-21T08:00:00","modified_gmt":"2026-06-21T08:00:00","slug":"cryptographie-post-quantique-nodejs","status":"publish","type":"post","link":"https:\/\/shattered.io\/fr\/2026\/06\/21\/cryptographie-post-quantique-nodejs\/","title":{"rendered":"Cryptographie Post-Quantique dans Node.js : 12 \u00c9tapes, 30 Min [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Les ordinateurs quantiques progressent \u00e0 un rythme que la communaut\u00e9 cryptographique n&#8217;anticipait pas avant 2030. En ao\u00fbt 2024, le NIST a publi\u00e9 trois standards finalis\u00e9s, FIPS 203, FIPS 204 et FIPS 205, marquant la fin d&#8217;une d\u00e9cennie de s\u00e9lection. En juin 2026, ces standards sont devenus la nouvelle r\u00e9f\u00e9rence pour toute infrastructure traitant des donn\u00e9es \u00e0 longue dur\u00e9e de vie. Ce tutoriel vous montre comment impl\u00e9menter la cryptographie post-quantique dans Node.js en 12 \u00e9tapes, depuis l&#8217;installation des d\u00e9pendances jusqu&#8217;au d\u00e9ploiement d&#8217;un \u00e9change de cl\u00e9s hybride X25519 + ML-KEM-768.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"pourquoi-la-cryptographie-post-quantique-est-urgente-en-2026\">Pourquoi la cryptographie post-quantique est urgente en 2026<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">L&#8217;algorithme de Shor, ex\u00e9cut\u00e9 sur un ordinateur quantique disposant de suffisamment de qubits logiques, casse RSA-2048 et les courbes elliptiques en temps polynomial. La menace n&#8217;est pas th\u00e9orique : en janvier 2025, Microsoft a annonc\u00e9 son processeur quantique Majorana 1 capable de manipuler des qubits topologiques stables. IBM a d\u00e9pass\u00e9 le cap des 1 000 qubits avec son processeur Condor. Les experts estiment d\u00e9sormais qu&#8217;un ordinateur quantique cryptographiquement pertinent (CRQC) pourrait \u00e9merger entre 2030 et 2035.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La strat\u00e9gie d&#8217;attaque dite <strong>\u00ab harvest now, decrypt later \u00bb<\/strong> est d\u00e9j\u00e0 en \u0153uvre. Des acteurs \u00e9tatiques collectent d\u00e8s aujourd&#8217;hui des flux TLS chiffr\u00e9s avec l&#8217;intention de les d\u00e9chiffrer quand les CRQC seront disponibles. Les donn\u00e9es m\u00e9dicales, les cl\u00e9s d&#8217;infrastructure, les secrets industriels transmis en 2026 restent vuln\u00e9rables si vous n&#8217;avez pas migr\u00e9. L&#8217;ANSSI, dans ses recommandations cryptographiques de 2026, pr\u00e9conise une approche hybride : combiner un algorithme classique (X25519 ou RSA) avec un algorithme post-quantique pendant la p\u00e9riode de transition.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le NIST a finalis\u00e9 trois standards en ao\u00fbt 2024 :<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Standard NIST<\/th><th>Algorithme<\/th><th>Ancien nom<\/th><th>Usage<\/th><th>Date de finalisation<\/th><\/tr><\/thead><tbody><tr><td>FIPS 203<\/td><td>ML-KEM<\/td><td>CRYSTALS-Kyber<\/td><td>\u00c9change de cl\u00e9s (KEM)<\/td><td>Ao\u00fbt 2024<\/td><\/tr><tr><td>FIPS 204<\/td><td>ML-DSA<\/td><td>CRYSTALS-Dilithium<\/td><td>Signature num\u00e9rique<\/td><td>Ao\u00fbt 2024<\/td><\/tr><tr><td>FIPS 205<\/td><td>SLH-DSA<\/td><td>SPHINCS+<\/td><td>Signature (stateless hash)<\/td><td>Ao\u00fbt 2024<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Dans ce tutoriel, vous allez impl\u00e9menter ML-KEM (\u00e9change de cl\u00e9s) et ML-DSA (signatures) dans Node.js, puis construire un \u00e9change de cl\u00e9s hybride compatible avec les recommandations IETF en cours.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequis-et-versions\">Pr\u00e9requis et versions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Avant de commencer, v\u00e9rifiez que votre environnement correspond aux versions suivantes :<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Outil<\/th><th>Version minimale<\/th><th>Version recommand\u00e9e<\/th><th>Commande de v\u00e9rification<\/th><\/tr><\/thead><tbody><tr><td>Node.js<\/td><td>20.0.0 LTS<\/td><td>22.x LTS (actuelle)<\/td><td><code>node --version<\/code><\/td><\/tr><tr><td>npm<\/td><td>10.0.0<\/td><td>10.x<\/td><td><code>npm --version<\/code><\/td><\/tr><tr><td>@noble\/post-quantum<\/td><td>0.2.0<\/td><td>derni\u00e8re version stable<\/td><td><code>npm show @noble\/post-quantum version<\/code><\/td><\/tr><tr><td>mlkem<\/td><td>3.0.0<\/td><td>derni\u00e8re version stable<\/td><td><code>npm show mlkem version<\/code><\/td><\/tr><tr><td>OS<\/td><td>Linux \/ macOS \/ Windows 10+<\/td><td>Ubuntu 22.04 \/ macOS 14<\/td><td><code>uname -a<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Ce tutoriel utilise principalement <strong>@noble\/post-quantum<\/strong>, une impl\u00e9mentation pure JavaScript sans d\u00e9pendances C natives, id\u00e9ale pour les environnements serverless et les d\u00e9ploiements conteneuris\u00e9s. Pour les cas n\u00e9cessitant les performances maximales ou l&#8217;interop\u00e9rabilit\u00e9 avec OpenSSL, nous verrons \u00e9galement l&#8217;option <strong>liboqs-node<\/strong> dans les \u00e9tapes avanc\u00e9es.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-1-initialiser-le-projet-node-js\">\u00c9tape 1 : Initialiser le projet Node.js<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Cr\u00e9ez un r\u00e9pertoire d\u00e9di\u00e9 et initialisez votre projet avec les modules ES activ\u00e9s. La cryptographie post-quantique b\u00e9n\u00e9ficie du <code>type: \"module\"<\/code> pour utiliser les imports natifs et \u00e9viter les probl\u00e8mes de compatibilit\u00e9 CommonJS avec les librairies modernes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir pq-crypto-demo && cd pq-crypto-demo\nnpm init -y\n# Activer les modules ES natifs\nnode -e \"const fs=require('fs'); const pkg=JSON.parse(fs.readFileSync('package.json')); pkg.type='module'; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2))\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">V\u00e9rifiez que votre <code>package.json<\/code> contient bien <code>\"type\": \"module\"<\/code>. Cette configuration \u00e9vite les conflits entre CommonJS (<code>require()<\/code>) et les modules ES (<code>import<\/code>) que plusieurs paquets de cryptographie post-quantique utilisent.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-2-installer-les-dependances-post-quantiques\">\u00c9tape 2 : Installer les d\u00e9pendances post-quantiques<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Installez la librairie <strong>@noble\/post-quantum<\/strong> de Paul Miller, auteur de plusieurs impl\u00e9mentations cryptographiques de r\u00e9f\u00e9rence pour l&#8217;\u00e9cosyst\u00e8me JavaScript. Cette librairie impl\u00e9mente ML-KEM et ML-DSA en JavaScript pur, sans WebAssembly ni bindings natifs, ce qui simplifie les audits de s\u00e9curit\u00e9 et les d\u00e9ploiements multi-architectures.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Librairie principale : ML-KEM + ML-DSA en JS pur\nnpm install @noble\/post-quantum\n\n# Optionnel : ML-KEM seul, impl\u00e9mentation alternative conforme FIPS 203\nnpm install mlkem\n\n# V\u00e9rification de l'int\u00e9grit\u00e9 des paquets\nnpm audit<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Apr\u00e8s installation, v\u00e9rifiez que le module se charge correctement :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node --input-type=module <<'EOF'\nimport { ml_kem768 } from '@noble\/post-quantum\/ml-kem';\nconsole.log('ML-KEM-768 charg\u00e9 avec succ\u00e8s');\nEOF\n# Sortie attendue : ML-KEM-768 charg\u00e9 avec succ\u00e8s<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge courant n\u00b01 :<\/strong> Si vous obtenez <code>ERR_REQUIRE_ESM<\/code>, c'est que votre fichier utilise <code>require()<\/code> au lieu d'<code>import<\/code>. @noble\/post-quantum est un module ES pur depuis la version 0.2.0. Renommez votre fichier en <code>.mjs<\/code> ou ajoutez <code>\"type\": \"module\"<\/code> dans <code>package.json<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-3-comprendre-les-niveaux-de-securite-ml-kem\">\u00c9tape 3 : Comprendre les niveaux de s\u00e9curit\u00e9 ML-KEM<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ML-KEM (FIPS 203) propose trois niveaux de s\u00e9curit\u00e9 correspondant \u00e0 des niveaux de r\u00e9sistance quantique diff\u00e9rents. Le choix du niveau affecte directement la taille des cl\u00e9s et les performances. Avant d'\u00e9crire la moindre ligne de code, comprenez ces chiffres : ils d\u00e9terminent le surco\u00fbt que vos clients et serveurs subiront \u00e0 chaque connexion.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Variante<\/th><th>Niveau NIST<\/th><th>Cl\u00e9 publique<\/th><th>Cl\u00e9 secr\u00e8te<\/th><th>Texte chiffr\u00e9<\/th><th>\u00c9quivalent classique<\/th><\/tr><\/thead><tbody><tr><td>ML-KEM-512<\/td><td>Niveau 1<\/td><td>800 octets<\/td><td>1 632 octets<\/td><td>768 octets<\/td><td>AES-128<\/td><\/tr><tr><td>ML-KEM-768<\/td><td>Niveau 3<\/td><td>1 184 octets<\/td><td>2 400 octets<\/td><td>1 088 octets<\/td><td>AES-192<\/td><\/tr><tr><td>ML-KEM-1024<\/td><td>Niveau 5<\/td><td>1 568 octets<\/td><td>3 168 octets<\/td><td>1 568 octets<\/td><td>AES-256<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Pour comparaison, une cl\u00e9 publique RSA-2048 fait 256 octets et une cl\u00e9 ECDH X25519 fait seulement 32 octets. ML-KEM-768 est environ 37 fois plus volumineux que X25519, mais reste marginal pour des connexions TLS (quelques kilooctets suppl\u00e9mentaires par handshake). <strong>ML-KEM-768 est le choix recommand\u00e9<\/strong> pour la grande majorit\u00e9 des usages en 2026 : il correspond au niveau de s\u00e9curit\u00e9 AES-192 contre les attaquants quantiques.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L'\u00e9change de cl\u00e9s hybride <strong>X25519 + ML-KEM-768<\/strong> est actuellement adopt\u00e9 par Chrome (depuis la version 131), Cloudflare et plusieurs navigateurs majeurs. Il garantit qu'un attaquant doit casser les deux algorithmes simultan\u00e9ment, prot\u00e9geant vos sessions m\u00eame si l'un d'eux s'av\u00e8re vuln\u00e9rable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-4-implementer-ml-kem-768-echange-de-cles\">\u00c9tape 4 : Impl\u00e9menter ML-KEM-768 (\u00e9change de cl\u00e9s)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ML-KEM est un m\u00e9canisme d'encapsulation de cl\u00e9s (KEM). \u00c0 la diff\u00e9rence de Diffie-Hellman, il ne produit pas de secret partag\u00e9 par \u00e9change sym\u00e9trique : l'\u00e9metteur g\u00e9n\u00e8re un secret et l'encapsule dans un texte chiffr\u00e9 que seul le d\u00e9tenteur de la cl\u00e9 secr\u00e8te peut d\u00e9capsuler. Ce mod\u00e8le est parfaitement adapt\u00e9 \u00e0 TLS 1.3 o\u00f9 le client encapsule un secret pour le serveur.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ kem-demo.mjs\nimport { ml_kem768 } from '@noble\/post-quantum\/ml-kem';\n\n\/\/ === C\u00d4T\u00c9 SERVEUR : g\u00e9n\u00e9ration de la paire de cl\u00e9s ===\nconst { publicKey, secretKey } = ml_kem768.keygen();\n\nconsole.log('Cl\u00e9 publique (octets):', publicKey.length);   \/\/ 1184\nconsole.log('Cl\u00e9 secr\u00e8te (octets) :', secretKey.length);   \/\/ 2400\n\n\/\/ === C\u00d4T\u00c9 CLIENT : encapsulation ===\n\/\/ Le client re\u00e7oit la cl\u00e9 publique du serveur et g\u00e9n\u00e8re un secret partag\u00e9\nconst { cipherText, sharedSecret: clientSecret } = ml_kem768.encapsulate(publicKey);\n\nconsole.log('Texte chiffr\u00e9 (octets):', cipherText.length);       \/\/ 1088\nconsole.log('Secret client (octets):', clientSecret.length);     \/\/ 32\n\n\/\/ === C\u00d4T\u00c9 SERVEUR : d\u00e9capsulation ===\n\/\/ Le serveur utilise sa cl\u00e9 secr\u00e8te pour retrouver le m\u00eame secret partag\u00e9\nconst serverSecret = ml_kem768.decapsulate(cipherText, secretKey);\n\n\/\/ V\u00e9rification : les deux secrets doivent \u00eatre identiques\nconst match = Buffer.compare(\n  Buffer.from(clientSecret),\n  Buffer.from(serverSecret)\n) === 0;\n\nconsole.log('Secret partag\u00e9 identique:', match);\nconsole.log('Secret partag\u00e9 (hex):', Buffer.from(serverSecret).toString('hex').slice(0, 32) + '...');<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ex\u00e9cutez ce script avec <code>node kem-demo.mjs<\/code>. La sortie attendue :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cl\u00e9 publique (octets): 1184\nCl\u00e9 secr\u00e8te (octets) : 2400\nTexte chiffr\u00e9 (octets): 1088\nSecret client (octets): 32\nSecret partag\u00e9 identique: true\nSecret partag\u00e9 (hex): a3f2c891d4e7b56f902c1a8d34e5f678...<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge courant n\u00b02 :<\/strong> Ne confondez pas le <em>texte chiffr\u00e9<\/em> (ciphertext) ML-KEM avec un message chiffr\u00e9. Le ciphertext ML-KEM est uniquement le vecteur d'encapsulation du secret partag\u00e9. Pour chiffrer des donn\u00e9es r\u00e9elles, utilisez le secret partag\u00e9 (32 octets) comme cl\u00e9 pour AES-256-GCM, comme montr\u00e9 \u00e0 l'\u00e9tape suivante.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-5-chiffrement-des-donnees-avec-le-secret-ml-kem\">\u00c9tape 5 : Chiffrement des donn\u00e9es avec le secret ML-KEM<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Le secret partag\u00e9 ML-KEM (32 octets) sert de cl\u00e9 sym\u00e9trique pour chiffrer les donn\u00e9es r\u00e9elles. La combinaison ML-KEM + AES-256-GCM est le sch\u00e9ma recommand\u00e9 : s\u00e9curit\u00e9 post-quantique pour l'\u00e9change de cl\u00e9s, performance optimale pour le chiffrement des donn\u00e9es. AES-256-GCM fournit \u00e0 la fois confidentialit\u00e9 et int\u00e9grit\u00e9 gr\u00e2ce \u00e0 son tag d'authentification.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ hybrid-encrypt.mjs\nimport { ml_kem768 } from '@noble\/post-quantum\/ml-kem';\nimport { createCipheriv, createDecipheriv, randomBytes } from 'crypto';\n\nasync function encrypt(recipientPublicKey, plaintext) {\n  \/\/ 1. Encapsuler un secret avec la cl\u00e9 publique ML-KEM du destinataire\n  const { cipherText: kemCiphertext, sharedSecret } = ml_kem768.encapsulate(recipientPublicKey);\n\n  \/\/ 2. Chiffrer le message avec AES-256-GCM en utilisant le secret ML-KEM\n  const iv = randomBytes(12); \/\/ 96 bits pour GCM\n  const cipher = createCipheriv('aes-256-gcm', Buffer.from(sharedSecret), iv);\n\n  const encrypted = Buffer.concat([\n    cipher.update(Buffer.from(plaintext, 'utf8')),\n    cipher.final()\n  ]);\n  const authTag = cipher.getAuthTag();\n\n  return { kemCiphertext, iv, encrypted, authTag };\n}\n\nasync function decrypt(recipientSecretKey, { kemCiphertext, iv, encrypted, authTag }) {\n  \/\/ 1. D\u00e9capsuler le secret ML-KEM\n  const sharedSecret = ml_kem768.decapsulate(kemCiphertext, recipientSecretKey);\n\n  \/\/ 2. D\u00e9chiffrer avec AES-256-GCM\n  const decipher = createDecipheriv('aes-256-gcm', Buffer.from(sharedSecret), iv);\n  decipher.setAuthTag(authTag);\n\n  return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf8');\n}\n\n\/\/ D\u00e9monstration\nconst { publicKey, secretKey } = ml_kem768.keygen();\nconst message = 'Donn\u00e9es sensibles prot\u00e9g\u00e9es contre les ordinateurs quantiques';\n\nconst pkg = await encrypt(publicKey, message);\nconst recovered = await decrypt(secretKey, pkg);\n\nconsole.log('Message original  :', message);\nconsole.log('Message r\u00e9cup\u00e9r\u00e9  :', recovered);\nconsole.log('Int\u00e9grit\u00e9 v\u00e9rifi\u00e9e:', message === recovered);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge courant n\u00b03 :<\/strong> N'utilisez jamais le secret ML-KEM directement comme cl\u00e9 AES sans d\u00e9rivation de cl\u00e9 (KDF) dans un contexte de production avec plusieurs sessions ou protocoles hybrides. Si votre protocole r\u00e9utilise des cl\u00e9s ou combine plusieurs secrets (par exemple X25519 + ML-KEM), utilisez HKDF pour d\u00e9river une cl\u00e9 propre et isoler les contextes cryptographiques. L'\u00e9tape 7 couvre ce cas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-6-implementer-ml-dsa-65-signatures-numeriques\">\u00c9tape 6 : Impl\u00e9menter ML-DSA-65 (signatures num\u00e9riques)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ML-DSA (FIPS 204) remplace ECDSA pour les signatures num\u00e9riques r\u00e9sistantes aux ordinateurs quantiques. ML-DSA-65 correspond au niveau de s\u00e9curit\u00e9 3 (\u00e9quivalent AES-192 contre les attaques quantiques). Bas\u00e9 sur les r\u00e9seaux euclidiens (Module-Lattice), il est plus rapide que SLH-DSA et produit des signatures plus compactes, ce qui en fait le choix par d\u00e9faut pour les API et les infrastructures \u00e0 fort trafic.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Variante ML-DSA<\/th><th>Niveau<\/th><th>Cl\u00e9 publique<\/th><th>Cl\u00e9 priv\u00e9e<\/th><th>Signature<\/th><th>\u00c9quivalent<\/th><\/tr><\/thead><tbody><tr><td>ML-DSA-44<\/td><td>Niveau 2<\/td><td>1 312 octets<\/td><td>2 528 octets<\/td><td>2 420 octets<\/td><td>AES-128<\/td><\/tr><tr><td>ML-DSA-65<\/td><td>Niveau 3<\/td><td>1 952 octets<\/td><td>4 000 octets<\/td><td>3 293 octets<\/td><td>AES-192<\/td><\/tr><tr><td>ML-DSA-87<\/td><td>Niveau 5<\/td><td>2 592 octets<\/td><td>4 864 octets<\/td><td>4 627 octets<\/td><td>AES-256<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Pour comparaison, une signature ECDSA P-256 fait 64 octets et une signature Ed25519 fait 64 octets. ML-DSA-65 est environ 51 fois plus volumineuse qu'Ed25519. Pour les API REST ou les JWT, ce surco\u00fbt de quelques kilooctets est n\u00e9gligeable. Pour les PKI embarqu\u00e9es ou les protocoles IoT, un travail d'optimisation est n\u00e9cessaire.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ signature-demo.mjs\nimport { ml_dsa65 } from '@noble\/post-quantum\/ml-dsa';\n\n\/\/ G\u00e9n\u00e9ration de la paire de cl\u00e9s ML-DSA-65\nconst { publicKey, secretKey } = ml_dsa65.keygen();\nconsole.log('Cl\u00e9 publique (octets):', publicKey.length);  \/\/ 1952\nconsole.log('Cl\u00e9 priv\u00e9e (octets)  :', secretKey.length);  \/\/ 4000\n\n\/\/ Message \u00e0 signer\nconst message = new TextEncoder().encode(\n  JSON.stringify({ action: 'transfert', montant: 50000, ts: Date.now() })\n);\n\n\/\/ Signature avec la cl\u00e9 priv\u00e9e\nconst signature = ml_dsa65.sign(secretKey, message);\nconsole.log('Signature (octets)  :', signature.length);   \/\/ 3293\n\n\/\/ V\u00e9rification avec la cl\u00e9 publique\nconst valid = ml_dsa65.verify(publicKey, message, signature);\nconsole.log('Signature valide    :', valid); \/\/ true\n\n\/\/ Tentative de falsification\nconst tampered = new TextEncoder().encode(\n  JSON.stringify({ action: 'transfert', montant: 999999, ts: Date.now() })\n);\nconst invalidSig = ml_dsa65.verify(publicKey, tampered, signature);\nconsole.log('Falsification d\u00e9tect\u00e9e:', !invalidSig); \/\/ true<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge courant n\u00b04 :<\/strong> ML-DSA ne supporte pas le mod\u00e8le <em>HashAndSign<\/em> de la m\u00eame fa\u00e7on qu'ECDSA. La sp\u00e9cification FIPS 204 inclut deux modes : la signature directe du message (mode <em>pure<\/em>) et la signature du hash avec contexte (mode <em>pre-hash<\/em>). @noble\/post-quantum impl\u00e9mente le mode <em>pure<\/em> par d\u00e9faut, ce qui est recommand\u00e9 pour les nouvelles impl\u00e9mentations. Le mode pre-hash est r\u00e9serv\u00e9 aux cas o\u00f9 le message est d\u00e9j\u00e0 hach\u00e9 par un autre syst\u00e8me.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-7-echange-de-cles-hybride-x25519-ml-kem-768\">\u00c9tape 7 : \u00c9change de cl\u00e9s hybride X25519 + ML-KEM-768<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">L'\u00e9change de cl\u00e9s hybride est la recommandation de l'ANSSI et de l'IETF pour la p\u00e9riode de transition. Il combine un algorithme classique (X25519) avec un algorithme post-quantique (ML-KEM-768) : un attaquant doit compromettre les deux simultan\u00e9ment pour r\u00e9cup\u00e9rer le secret partag\u00e9. C'est le sch\u00e9ma adopt\u00e9 par Chrome depuis la version 131 sous le nom X25519MLKEM768.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ hybrid-kem.mjs\nimport { ml_kem768 } from '@noble\/post-quantum\/ml-kem';\nimport { createECDH, hkdfSync, randomBytes } from 'crypto';\n\n\/\/ === SERVEUR : g\u00e9n\u00e9ration des cl\u00e9s ===\nfunction serverKeygen() {\n  const ecdh = createECDH('x25519');\n  ecdh.generateKeys();\n  const kem = ml_kem768.keygen();\n  return {\n    classical: { privateKey: ecdh.getPrivateKey(), publicKey: ecdh.getPublicKey() },\n    pq:        { secretKey: kem.secretKey, publicKey: kem.publicKey }\n  };\n}\n\n\/\/ === CLIENT : encapsulation hybride ===\nfunction clientEncapsulate(serverClassicalPub, serverPqPub, sessionSalt) {\n  \/\/ 1. Ephemeral X25519\n  const clientEcdh = createECDH('x25519');\n  clientEcdh.generateKeys();\n  const classicalSecret = clientEcdh.computeSecret(serverClassicalPub);\n\n  \/\/ 2. ML-KEM-768\n  const { cipherText: kemCt, sharedSecret: kemSecret } = ml_kem768.encapsulate(serverPqPub);\n\n  \/\/ 3. Combiner les deux secrets avec HKDF-SHA256\n  const combinedInput = Buffer.concat([classicalSecret, Buffer.from(kemSecret)]);\n  const hybridKey = hkdfSync('sha256', combinedInput, sessionSalt, 'X25519+ML-KEM-768 v1', 32);\n\n  return {\n    clientClassicalPub: clientEcdh.getPublicKey(),\n    kemCiphertext: kemCt,\n    hybridKey: Buffer.from(hybridKey)\n  };\n}\n\n\/\/ === SERVEUR : d\u00e9capsulation hybride ===\nfunction serverDecapsulate(serverKeys, clientClassicalPub, kemCiphertext, sessionSalt) {\n  \/\/ 1. X25519\n  const serverEcdh = createECDH('x25519');\n  serverEcdh.setPrivateKey(serverKeys.classical.privateKey);\n  const classicalSecret = serverEcdh.computeSecret(clientClassicalPub);\n\n  \/\/ 2. ML-KEM-768\n  const kemSecret = ml_kem768.decapsulate(kemCiphertext, serverKeys.pq.secretKey);\n\n  \/\/ 3. M\u00eame d\u00e9rivation HKDF avec le m\u00eame sel\n  const combinedInput = Buffer.concat([classicalSecret, Buffer.from(kemSecret)]);\n  const hybridKey = hkdfSync('sha256', combinedInput, sessionSalt, 'X25519+ML-KEM-768 v1', 32);\n  return Buffer.from(hybridKey);\n}\n\n\/\/ D\u00e9monstration\nconst serverKeys = serverKeygen();\nconst sessionSalt = randomBytes(32); \/\/ Partag\u00e9 via m\u00e9canisme s\u00e9curis\u00e9 dans un vrai protocole\n\nconst { clientClassicalPub, kemCiphertext, hybridKey: clientKey } = clientEncapsulate(\n  serverKeys.classical.publicKey,\n  serverKeys.pq.publicKey,\n  sessionSalt\n);\n\nconst serverKey = serverDecapsulate(serverKeys, clientClassicalPub, kemCiphertext, sessionSalt);\n\nconsole.log('Cl\u00e9 hybride (hex):', clientKey.toString('hex').slice(0, 32) + '...');\nconsole.log('Cl\u00e9s identiques  :', Buffer.compare(clientKey, serverKey) === 0);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge courant n\u00b05 :<\/strong> Le sel HKDF doit \u00eatre transmis ou d\u00e9riv\u00e9 de fa\u00e7on authentifi\u00e9e entre les deux parties. Dans cet exemple didactique, il est partag\u00e9 en clair. En production (TLS 1.3), le sel est d\u00e9riv\u00e9 de la transcription du handshake, ce qui lie cryptographiquement le secret aux messages \u00e9chang\u00e9s et pr\u00e9vient les attaques par replay.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-8-persistance-securisee-des-cles-post-quantiques\">\u00c9tape 8 : Persistance s\u00e9curis\u00e9e des cl\u00e9s post-quantiques<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Les cl\u00e9s ML-KEM et ML-DSA sont volumineuses. La cl\u00e9 secr\u00e8te ML-DSA-87 atteint 4 864 octets, contre 64 octets pour une cl\u00e9 Ed25519. Leur stockage et leur transport n\u00e9cessitent une attention particuli\u00e8re pour \u00e9viter les fuites. Voici un module de gestion des cl\u00e9s avec chiffrement AES-256-GCM des cl\u00e9s au repos, d\u00e9rivation de cl\u00e9 via scrypt, et s\u00e9rialisation Base64.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ key-manager.mjs\nimport { ml_kem768 } from '@noble\/post-quantum\/ml-kem';\nimport { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'crypto';\nimport { writeFileSync, readFileSync } from 'fs';\n\nconst ALGO = 'aes-256-gcm';\n\nfunction deriveKey(password, salt) {\n  \/\/ scrypt : N=131072 (128 Ko de RAM), prot\u00e8ge contre les attaques GPU\n  return scryptSync(password, salt, 32, { N: 131072, r: 8, p: 1 });\n}\n\nfunction encryptKey(keyBytes, password) {\n  const salt = randomBytes(32);\n  const key  = deriveKey(password, salt);\n  const iv   = randomBytes(12);\n\n  const cipher = createCipheriv(ALGO, key, iv);\n  const encrypted = Buffer.concat([cipher.update(keyBytes), cipher.final()]);\n  const tag = cipher.getAuthTag();\n\n  return JSON.stringify({\n    salt:      salt.toString('base64'),\n    iv:        iv.toString('base64'),\n    tag:       tag.toString('base64'),\n    encrypted: encrypted.toString('base64'),\n    algo:      'scrypt+aes-256-gcm',\n    version:   1\n  });\n}\n\nfunction decryptKey(stored, password) {\n  const { salt, iv, tag, encrypted } = JSON.parse(stored);\n  const key = deriveKey(password, Buffer.from(salt, 'base64'));\n  const decipher = createDecipheriv(ALGO, key, Buffer.from(iv, 'base64'));\n  decipher.setAuthTag(Buffer.from(tag, 'base64'));\n  return Buffer.concat([\n    decipher.update(Buffer.from(encrypted, 'base64')),\n    decipher.final()\n  ]);\n}\n\n\/\/ G\u00e9n\u00e9rer et sauvegarder une paire ML-KEM-768\nconst password = process.env.KEY_PASSWORD || 'utilisez-un-vrai-secret-en-production';\nconst { publicKey, secretKey } = ml_kem768.keygen();\n\nwriteFileSync('ml-kem-public.key',  Buffer.from(publicKey).toString('base64'));\nwriteFileSync('ml-kem-secret.enc',  encryptKey(Buffer.from(secretKey), password));\nconsole.log('Cl\u00e9s ML-KEM-768 g\u00e9n\u00e9r\u00e9es et sauvegard\u00e9es');\n\n\/\/ Rechargement et d\u00e9chiffrement\nconst storedSk    = readFileSync('ml-kem-secret.enc', 'utf8');\nconst recoveredSk = decryptKey(storedSk, password);\nconsole.log('Cl\u00e9 secr\u00e8te r\u00e9cup\u00e9r\u00e9e:', Buffer.compare(Buffer.from(secretKey), recoveredSk) === 0);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dans un environnement de production, le mot de passe de chiffrement doit venir d'un gestionnaire de secrets (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) et non d'une variable d'environnement. Les cl\u00e9s publiques peuvent \u00eatre stock\u00e9es en clair mais doivent \u00eatre prot\u00e9g\u00e9es en int\u00e9grit\u00e9 par une signature d'autorit\u00e9 de certification.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-9-slh-dsa-sphincs-pour-les-signatures-a-longue-duree-de-vie\">\u00c9tape 9 : SLH-DSA (SPHINCS+) pour les signatures \u00e0 longue dur\u00e9e de vie<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">SLH-DSA (FIPS 205), anciennement SPHINCS+, est la troisi\u00e8me option de signature post-quantique standardis\u00e9e par le NIST. Contrairement \u00e0 ML-DSA qui repose sur la duret\u00e9 des r\u00e9seaux euclidiens, SLH-DSA est bas\u00e9 uniquement sur des fonctions de hachage, une construction dont la s\u00e9curit\u00e9 est mieux comprise et plus conservatrice face \u00e0 d'\u00e9ventuelles cryptanalyses futures des r\u00e9seaux euclidiens.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">SLH-DSA pr\u00e9sente des signatures beaucoup plus volumineuses (jusqu'\u00e0 49 856 octets) mais une cl\u00e9 publique tr\u00e8s compacte (32 \u00e0 64 octets selon la variante). Les variantes <em>fast<\/em> (suffixe -f) privil\u00e9gient la vitesse de signature au d\u00e9triment de la taille. Les variantes <em>small<\/em> (suffixe -s) minimisent la taille des signatures au d\u00e9triment de la vitesse. SLH-DSA est recommand\u00e9 pour les certificats racine PKI, les documents l\u00e9gaux \u00e0 valeur probante sur 20 ans, et les cas o\u00f9 la diversit\u00e9 algorithmique est prioritaire.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ slh-dsa-demo.mjs\nimport { slh_dsa_shake_128f } from '@noble\/post-quantum\/slh-dsa';\n\n\/\/ SLH-DSA-SHAKE-128f : signatures rapides (f = fast), niveau s\u00e9curit\u00e9 1\nconst { publicKey, secretKey } = slh_dsa_shake_128f.keygen();\nconsole.log('Cl\u00e9 publique (octets):', publicKey.length);  \/\/ 32\nconsole.log('Cl\u00e9 priv\u00e9e (octets)  :', secretKey.length);  \/\/ 64\n\nconst document = new TextEncoder().encode(\n  'Certificat racine PKI souveraine \u2014 valide 20 ans \u2014 ANSSI conforme'\n);\n\nconst signature = slh_dsa_shake_128f.sign(secretKey, document);\nconsole.log('Signature (octets)   :', signature.length);  \/\/ 17088\n\nconst valid = slh_dsa_shake_128f.verify(publicKey, document, signature);\nconsole.log('Signature valide     :', valid);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Les variantes disponibles dans @noble\/post-quantum incluent <code>slh_dsa_sha2_128f<\/code>, <code>slh_dsa_sha2_128s<\/code>, <code>slh_dsa_shake_128f<\/code>, <code>slh_dsa_shake_128s<\/code> et leurs \u00e9quivalents 192 et 256 bits. La variante SHAKE est recommand\u00e9e par le NIST pour les nouvelles impl\u00e9mentations ; SHA2 est disponible pour la compatibilit\u00e9 avec les syst\u00e8mes FIPS existants.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-10-integration-dans-une-api-express-js\">\u00c9tape 10 : Int\u00e9gration dans une API Express.js<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Cette \u00e9tape montre comment int\u00e9grer ML-DSA dans une API Express.js pour signer les r\u00e9ponses et v\u00e9rifier les requ\u00eates c\u00f4t\u00e9 serveur. C'est le sch\u00e9ma adapt\u00e9 aux microservices qui doivent s'authentifier mutuellement de fa\u00e7on r\u00e9sistante aux ordinateurs quantiques.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ api-server.mjs\nimport express from 'express';\nimport { ml_dsa65 } from '@noble\/post-quantum\/ml-dsa';\n\n\/\/ npm install express\n\nconst app = express();\napp.use(express.json());\n\n\/\/ Cl\u00e9s charg\u00e9es au d\u00e9marrage (en production : depuis un fichier chiffr\u00e9 ou HSM)\nconst { publicKey: serverPub, secretKey: serverSk } = ml_dsa65.keygen();\n\n\/\/ Exposer la cl\u00e9 publique du serveur\napp.get('\/api\/public-key', (req, res) => {\n  res.json({\n    algorithm: 'ML-DSA-65',\n    standard:  'FIPS-204',\n    publicKey: Buffer.from(serverPub).toString('base64'),\n    keySize:   serverPub.length\n  });\n});\n\n\/\/ Middleware : signer toutes les r\u00e9ponses JSON avec ML-DSA-65\nfunction signResponse(req, res, next) {\n  const originalJson = res.json.bind(res);\n  res.json = (data) => {\n    const payload   = JSON.stringify(data);\n    const signature = ml_dsa65.sign(serverSk, new TextEncoder().encode(payload));\n    res.setHeader('X-PQ-Signature', Buffer.from(signature).toString('base64'));\n    res.setHeader('X-PQ-Algorithm', 'ML-DSA-65');\n    return originalJson(data);\n  };\n  next();\n}\n\napp.get('\/api\/data', signResponse, (req, res) => {\n  res.json({ message: 'Donn\u00e9es sign\u00e9es post-quantiques', ts: new Date().toISOString() });\n});\n\n\/\/ Endpoint : v\u00e9rifier une signature cliente\napp.post('\/api\/verify', (req, res) => {\n  const { clientPublicKey, payload, signature } = req.body;\n  try {\n    const pubKey = new Uint8Array(Buffer.from(clientPublicKey, 'base64'));\n    const sig    = new Uint8Array(Buffer.from(signature, 'base64'));\n    const msg    = new TextEncoder().encode(payload);\n    const valid  = ml_dsa65.verify(pubKey, msg, sig);\n    res.json({ valid, algorithm: 'ML-DSA-65' });\n  } catch {\n    res.status(400).json({ error: 'Donn\u00e9es de v\u00e9rification invalides' });\n  }\n});\n\napp.listen(3000, () => console.log('Serveur PQ d\u00e9marr\u00e9 sur le port 3000'));<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge courant n\u00b06 :<\/strong> R\u00e9g\u00e9n\u00e9rer les cl\u00e9s ML-DSA \u00e0 chaque red\u00e9marrage du serveur sans les persister rend votre API inutilisable entre red\u00e9marrages : les clients qui ont mis en cache votre ancienne cl\u00e9 publique \u00e9choueront \u00e0 v\u00e9rifier les nouvelles signatures. En production, chargez les cl\u00e9s depuis un fichier chiffr\u00e9 (comme \u00e0 l'\u00e9tape 8) ou un HSM au d\u00e9marrage.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-11-suite-de-tests-pour-la-cryptographie-post-quantique\">\u00c9tape 11 : Suite de tests pour la cryptographie post-quantique<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Les tests cryptographiques doivent couvrir non seulement le fonctionnement nominal, mais aussi la r\u00e9sistance aux manipulations : vecteurs falsifi\u00e9s, cl\u00e9s corrompues, entr\u00e9es de longueur incorrecte. Voici une suite compl\u00e8te avec Node.js <code>assert<\/code> natif, sans d\u00e9pendances externes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ tests\/pq-crypto.test.mjs\nimport { ml_kem768 } from '@noble\/post-quantum\/ml-kem';\nimport { ml_dsa65 } from '@noble\/post-quantum\/ml-dsa';\nimport assert from 'assert\/strict';\n\nlet passed = 0;\nfunction test(name, fn) {\n  try { fn(); console.log('[PASS]', name); passed++; }\n  catch(e) { console.error('[FAIL]', name, ':', e.message); }\n}\n\n\/\/ Tailles des cl\u00e9s ML-KEM-768\ntest('ML-KEM-768 : tailles des cl\u00e9s', () => {\n  const { publicKey, secretKey } = ml_kem768.keygen();\n  assert.equal(publicKey.length, 1184);\n  assert.equal(secretKey.length, 2400);\n});\n\n\/\/ Coh\u00e9rence encapsulation\/d\u00e9capsulation\ntest('ML-KEM-768 : coh\u00e9rence encapsulation\/d\u00e9capsulation', () => {\n  const { publicKey, secretKey } = ml_kem768.keygen();\n  const { cipherText, sharedSecret: s1 } = ml_kem768.encapsulate(publicKey);\n  const s2 = ml_kem768.decapsulate(cipherText, secretKey);\n  assert.deepEqual(s1, s2);\n});\n\n\/\/ Mauvaise cl\u00e9 secr\u00e8te produit un r\u00e9sultat diff\u00e9rent\ntest('ML-KEM-768 : mauvaise cl\u00e9 secr\u00e8te', () => {\n  const { publicKey } = ml_kem768.keygen();\n  const { cipherText, sharedSecret: correct } = ml_kem768.encapsulate(publicKey);\n  const { secretKey: wrongSk } = ml_kem768.keygen();\n  const wrong = ml_kem768.decapsulate(cipherText, wrongSk);\n  assert.notDeepEqual(wrong, correct);\n});\n\n\/\/ ML-DSA-65 : signature valide\ntest('ML-DSA-65 : sign\/verify nominal', () => {\n  const { publicKey, secretKey } = ml_dsa65.keygen();\n  const msg = new TextEncoder().encode('Message de test ML-DSA');\n  const sig = ml_dsa65.sign(secretKey, msg);\n  assert.equal(sig.length, 3293);\n  assert.ok(ml_dsa65.verify(publicKey, msg, sig));\n});\n\n\/\/ Falsification du message d\u00e9tect\u00e9e\ntest('ML-DSA-65 : d\u00e9tection de falsification', () => {\n  const { publicKey, secretKey } = ml_dsa65.keygen();\n  const original  = new TextEncoder().encode('Message original');\n  const falsified = new TextEncoder().encode('Message falsifi\u00e9');\n  const sig = ml_dsa65.sign(secretKey, original);\n  assert.ok(!ml_dsa65.verify(publicKey, falsified, sig));\n});\n\n\/\/ Signature corrompue rejet\u00e9e\ntest('ML-DSA-65 : signature corrompue rejet\u00e9e', () => {\n  const { publicKey, secretKey } = ml_dsa65.keygen();\n  const msg = new TextEncoder().encode('Test int\u00e9grit\u00e9');\n  const sig = ml_dsa65.sign(secretKey, msg);\n  const corrupted = new Uint8Array(sig);\n  corrupted[42] ^= 0xFF;\n  assert.ok(!ml_dsa65.verify(publicKey, msg, corrupted));\n});\n\nconsole.log(`\\n${passed}\/6 tests r\u00e9ussis`);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ex\u00e9cutez avec <code>node tests\/pq-crypto.test.mjs<\/code>. Pour int\u00e9grer ces tests dans un pipeline CI, ajoutez <code>\"test\": \"node tests\/pq-crypto.test.mjs\"<\/code> dans le champ <code>scripts<\/code> de votre <code>package.json<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"etape-12-benchmarking-et-optimisation\">\u00c9tape 12 : Benchmarking et optimisation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Avant de d\u00e9ployer en production, mesurez l'impact r\u00e9el des algorithmes post-quantiques sur votre infrastructure. Les performances d\u00e9pendent du mat\u00e9riel, de la charge et de l'impl\u00e9mentation choisie (JavaScript pur vs natif).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ benchmark.mjs\nimport { ml_kem768 } from '@noble\/post-quantum\/ml-kem';\nimport { ml_dsa65 } from '@noble\/post-quantum\/ml-dsa';\nimport { createECDH } from 'crypto';\n\nconst ITER = 100;\n\nfunction bench(name, fn) {\n  for (let i = 0; i < 5; i++) fn(); \/\/ warm-up\n  const t0 = performance.now();\n  for (let i = 0; i < ITER; i++) fn();\n  const ms = (performance.now() - t0) \/ ITER;\n  console.log(`${name.padEnd(30)} ${ms.toFixed(3).padStart(8)} ms\/op`);\n}\n\nconst kemKeys = ml_kem768.keygen();\nconst { cipherText } = ml_kem768.encapsulate(kemKeys.publicKey);\nconst dsaKeys = ml_dsa65.keygen();\nconst msg = new TextEncoder().encode('benchmark');\nconst sig = ml_dsa65.sign(dsaKeys.secretKey, msg);\n\nbench('ML-KEM-768 keygen',      () => ml_kem768.keygen());\nbench('ML-KEM-768 encapsulate', () => ml_kem768.encapsulate(kemKeys.publicKey));\nbench('ML-KEM-768 decapsulate', () => ml_kem768.decapsulate(cipherText, kemKeys.secretKey));\nbench('X25519 keygen',          () => { const e = createECDH('x25519'); e.generateKeys(); });\nbench('ML-DSA-65 keygen',       () => ml_dsa65.keygen());\nbench('ML-DSA-65 sign',         () => ml_dsa65.sign(dsaKeys.secretKey, msg));\nbench('ML-DSA-65 verify',       () => ml_dsa65.verify(dsaKeys.publicKey, msg, sig));<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">R\u00e9sultats typiques sur un serveur moderne (Intel Xeon, Node.js 22.x) avec @noble\/post-quantum (JavaScript pur) :<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Op\u00e9ration<\/th><th>Temps moyen<\/th><th>Comparaison classique<\/th><th>Facteur<\/th><\/tr><\/thead><tbody><tr><td>ML-KEM-768 keygen<\/td><td>~0.15 ms<\/td><td>X25519 keygen : ~0.03 ms<\/td><td>5x plus lent<\/td><\/tr><tr><td>ML-KEM-768 encapsulate<\/td><td>~0.18 ms<\/td><td>X25519 ECDH : ~0.05 ms<\/td><td>4x plus lent<\/td><\/tr><tr><td>ML-KEM-768 decapsulate<\/td><td>~0.20 ms<\/td><td>X25519 ECDH : ~0.05 ms<\/td><td>4x plus lent<\/td><\/tr><tr><td>ML-DSA-65 keygen<\/td><td>~1.20 ms<\/td><td>Ed25519 keygen : ~0.10 ms<\/td><td>12x plus lent<\/td><\/tr><tr><td>ML-DSA-65 sign<\/td><td>~1.80 ms<\/td><td>Ed25519 sign : ~0.08 ms<\/td><td>22x plus lent<\/td><\/tr><tr><td>ML-DSA-65 verify<\/td><td>~0.80 ms<\/td><td>Ed25519 verify : ~0.12 ms<\/td><td>7x plus lent<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Avec liboqs-node (bindings C natifs avec AVX2), les performances de ML-KEM-768 sont comparables \u00e0 celles de X25519, et ML-DSA-65 est environ 8 fois plus rapide qu'en JavaScript pur. Pour les serveurs signant plus de 500 r\u00e9ponses par seconde, liboqs-node est recommand\u00e9.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"5-pieges-courants-supplementaires\">5 pi\u00e8ges courants suppl\u00e9mentaires<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge 7 : S\u00e9rialiser les cl\u00e9s comme des cha\u00eenes de caract\u00e8res brutes.<\/strong> Les cl\u00e9s ML-KEM et ML-DSA sont des <code>Uint8Array<\/code>, pas des objets PEM. Utilisez <code>Buffer.from(key).toString('base64')<\/code> pour les s\u00e9rialiser et <code>new Uint8Array(Buffer.from(str, 'base64'))<\/code> pour les d\u00e9s\u00e9rialiser. Un simple <code>JSON.stringify(key)<\/code> produit <code>{\"0\":43,\"1\":12,...}<\/code>, un objet incomplet qui \u00e9chouera silencieusement lors du rechargement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge 8 : R\u00e9utiliser les paires de cl\u00e9s KEM pour plusieurs sessions.<\/strong> ML-KEM est con\u00e7u pour un usage \u00e0 courte dur\u00e9e de vie (\u00e9change de cl\u00e9s de session). Contrairement \u00e0 RSA qui peut chiffrer directement des messages avec une cl\u00e9 longue dur\u00e9e, ML-KEM doit g\u00e9n\u00e9rer un nouveau couple (cipherText, sharedSecret) par session. Stocker et r\u00e9utiliser un sharedSecret compromet la confidentialit\u00e9 persistante (forward secrecy).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge 9 : N\u00e9gliger la taille des en-t\u00eates HTTP.<\/strong> Un \u00e9change de cl\u00e9s hybride X25519 + ML-KEM-768 ajoute environ 1,1 ko au handshake TLS. Certains \u00e9quilibreurs de charge et proxys (nginx, HAProxy, AWS ALB) limitent la taille des en-t\u00eates HTTP \u00e0 8 ko par d\u00e9faut. Si vous transmettez des cl\u00e9s ML-DSA-65 (1 952 octets encod\u00e9s en Base64 donnent ~2,6 ko) dans des en-t\u00eates d'authentification, v\u00e9rifiez la configuration maximale de vos proxys.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge 10 : Ignorer la validation de longueur des entr\u00e9es.<\/strong> Si vous exposez un endpoint qui accepte des cl\u00e9s publiques ou des ciphertexts ML-KEM, validez la longueur avant de les passer aux fonctions cryptographiques. Une entr\u00e9e de longueur incorrecte d\u00e9clenchera une exception dans @noble\/post-quantum, mais sans validation pr\u00e9alable, un attaquant peut provoquer des crashs ou des fuites de timing dans votre serveur Node.js.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pi\u00e8ge 11 : M\u00e9langer ML-KEM-768 et ML-KEM-512 dans le m\u00eame syst\u00e8me sans versioning.<\/strong> Si votre API passe de ML-KEM-768 \u00e0 ML-KEM-512 sans versioning des messages, les clients existants qui enverront des ciphertexts de 1 088 octets \u00e9choueront silencieusement avec les serveurs attendant 768 octets (ML-KEM-512). Incluez toujours un champ <code>kemVariant<\/code> dans vos structures de donn\u00e9es.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"support-natif-dans-node-js-et-openssl-en-2026\">Support natif dans Node.js et OpenSSL en 2026<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En juin 2026, Node.js ne supporte pas nativement les algorithmes post-quantiques NIST via son module <code>crypto<\/code> int\u00e9gr\u00e9. Le module <code>crypto<\/code> de Node.js repose sur OpenSSL, qui expose les algorithmes post-quantiques via le projet <strong>OQS-Provider<\/strong>, un provider OpenSSL 3.x d\u00e9velopp\u00e9 par l'\u00e9quipe Open Quantum Safe. Ce provider n'est toutefois pas inclus dans les distributions OpenSSL officielles et n\u00e9cessite une compilation s\u00e9par\u00e9e.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">OpenSSL 3.4.x a introduit un support exp\u00e9rimental de ML-KEM dans son code source principal (branche upstream), mais il n'est pas activ\u00e9 par d\u00e9faut dans les binaires distribu\u00e9s par les syst\u00e8mes d'exploitation. Le groupe de travail Node.js sur la cryptographie (node\/crypto) discute d'une int\u00e9gration directe de ML-KEM et ML-DSA dans le module <code>crypto<\/code> natif pour une version future. En attendant, @noble\/post-quantum offre la solution la plus accessible pour les environnements Node.js standards.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cloudflare rapporte que plus de 25% de ses connexions TLS 1.3 utilisent d\u00e9sormais l'\u00e9change de cl\u00e9s hybride X25519 + ML-KEM-768 depuis l'activation par d\u00e9faut dans Chrome 131. Google a activ\u00e9 ce sch\u00e9ma pour Gmail et les services Google Workspace en 2025. Ces d\u00e9ploiements \u00e0 grande \u00e9chelle confirment que la latence et le surco\u00fbt en bande passante sont acceptables en production.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conformite-et-cadre-reglementaire-europeen\">Conformit\u00e9 et cadre r\u00e9glementaire europ\u00e9en<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En Europe, la migration vers la cryptographie post-quantique s'inscrit dans plusieurs cadres r\u00e9glementaires qui rendent cette transition non seulement souhaitable mais progressivement obligatoire.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le <strong>Cyber Resilience Act (CRA)<\/strong>, avec ses premi\u00e8res \u00e9ch\u00e9ances entr\u00e9es en vigueur en 2026, impose aux fabricants de produits num\u00e9riques de g\u00e9rer les vuln\u00e9rabilit\u00e9s cryptographiques pendant la dur\u00e9e de vie commerciale du produit. Une application Node.js distribu\u00e9e commercialement qui utilise RSA ou ECDH sans plan de migration post-quantique document\u00e9 pourra \u00eatre consid\u00e9r\u00e9e comme non conforme si des vuln\u00e9rabilit\u00e9s quantiques sont d\u00e9clar\u00e9es avant la fin de vie du produit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La <strong>directive NIS2<\/strong> exige des entit\u00e9s essentielles et importantes de l'UE qu'elles maintiennent une cartographie de leurs actifs cryptographiques (inventaire). Cet inventaire est la premi\u00e8re \u00e9tape de toute migration post-quantique : vous ne pouvez pas migrer ce que vous n'avez pas identifi\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L'<strong>ANSSI<\/strong> recommande depuis 2025 l'adoption de l'approche hybride (algorithme classique + algorithme post-quantique) pour toutes les nouvelles infrastructures critiques fran\u00e7aises. La strat\u00e9gie nationale de cybers\u00e9curit\u00e9 2026-2030 place la cryptographie post-quantique comme priorit\u00e9 dans la feuille de route des op\u00e9rateurs d'importance vitale (OIV).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le r\u00e8glement <strong>eIDAS 2<\/strong>, dont le d\u00e9ploiement avance en 2026, d\u00e9finit des exigences pour les signatures \u00e9lectroniques qualifi\u00e9es (QES). Les prestataires de services de confiance (PSC) op\u00e9rant sous eIDAS 2 devront inclure un plan de migration vers ML-DSA ou SLH-DSA dans leur politique de s\u00e9curit\u00e9 pour maintenir leur qualification \u00e0 long terme.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conseils-avances-liboqs-node-et-performances-natives\">Conseils avanc\u00e9s : liboqs-node et performances natives<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"installation-de-liboqs-node-pour-des-performances-de-niveau-production\">Installation de liboqs-node pour des performances de niveau production<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Pour les applications \u00e0 fort volume n\u00e9cessitant des performances natives, <strong>liboqs-node<\/strong> fournit des bindings C\/C++ vers la librairie Open Quantum Safe (<a href=\"https:\/\/openquantumsafe.org\/\" target=\"_blank\" rel=\"noopener\">openquantumsafe.org<\/a>), qui impl\u00e9mente tous les algorithmes NIST en C optimis\u00e9 avec support des instructions AVX2 et AVX-512. L'installation n\u00e9cessite un environnement de compilation C++ :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Pr\u00e9requis syst\u00e8me (Ubuntu\/Debian)\nsudo apt-get install cmake ninja-build build-essential libssl-dev\n\n# Compiler liboqs depuis les sources\ngit clone --depth 1 https:\/\/github.com\/open-quantum-safe\/liboqs.git\ncd liboqs && cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Release\ncmake --build build && sudo cmake --install build\ncd ..\n\n# Installer le binding Node.js\nnpm install node-liboqs\n\n# V\u00e9rification\nnode -e \"const oqs = require('node-liboqs'); console.log(oqs.getEnabledKEMAlgorithms().join(', '))\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Avec les bindings natifs liboqs, ML-KEM-768 encapsulation descend \u00e0 environ 0.04 ms (contre 0.18 ms en JavaScript pur), soit des performances comparables \u00e0 X25519. ML-DSA-65 sign descend \u00e0 environ 0.15 ms (contre 1.80 ms), permettant de signer plus de 6 000 messages par seconde sur un seul c\u0153ur.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"strategie-de-migration-en-3-phases\">Strat\u00e9gie de migration en 3 phases<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">La migration vers la cryptographie post-quantique ne se fait pas en une nuit. La strat\u00e9gie recommand\u00e9e par l'ANSSI et le NIST pour 2026 est une approche hybride progressive :<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Phase 1 (maintenant, 2026) :<\/strong> Inventaire cryptographique complet. Identifiez tous les usages RSA, ECDH, ECDSA dans votre base de code Node.js. Documentez les dur\u00e9es de vie des donn\u00e9es chiffr\u00e9es. Priorisez les donn\u00e9es dont la dur\u00e9e de vie d\u00e9passe 5 ans (donn\u00e9es m\u00e9dicales, financi\u00e8res, secrets d'\u00c9tat).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Phase 2 (2026-2027) :<\/strong> D\u00e9ploiement hybride. Ajoutez ML-KEM en parall\u00e8le de X25519 pour tous les \u00e9changes de cl\u00e9s. Ajoutez ML-DSA en parall\u00e8le d'Ed25519 pour les signatures critiques. Les deux algorithmes coexistent, aucun service n'est interrompu. Mesurez l'impact sur les performances.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Phase 3 (2028+) :<\/strong> Migration compl\u00e8te. Une fois les clients mis \u00e0 jour et la maturit\u00e9 des impl\u00e9mentations confirm\u00e9e par la communaut\u00e9 cryptographique, retirez les algorithmes classiques des protocoles critiques. Maintenez X25519\/Ed25519 uniquement pour la compatibilit\u00e9 descendante avec les anciens clients.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"guide-de-depannage-8-problemes-frequents\">Guide de d\u00e9pannage : 8 probl\u00e8mes fr\u00e9quents<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Probl\u00e8me 1 : ERR_REQUIRE_ESM lors de l'import de @noble\/post-quantum.<\/strong> Cause : votre fichier utilise <code>require()<\/code>. Solution : renommez en <code>.mjs<\/code> ou ajoutez <code>\"type\": \"module\"<\/code> dans <code>package.json<\/code>. Si vous devez rester en CommonJS, utilisez l'import dynamique : <code>const { ml_kem768 } = await import('@noble\/post-quantum\/ml-kem');<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Probl\u00e8me 2 : TypeError: ml_kem768.keygen is not a function.<\/strong> Cause : mauvais chemin d'import. L'import correct est <code>from '@noble\/post-quantum\/ml-kem'<\/code> (avec le sous-chemin), pas <code>from '@noble\/post-quantum'<\/code>. V\u00e9rifiez aussi que vous importez la variante correcte : <code>ml_kem512<\/code>, <code>ml_kem768<\/code>, ou <code>ml_kem1024<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Probl\u00e8me 3 : RangeError: Invalid typed array length lors de l'encapsulation.<\/strong> Cause : la cl\u00e9 publique fournie n'a pas la bonne longueur. ML-KEM-768 attend exactement 1 184 octets. Si vous s\u00e9rialisez\/d\u00e9s\u00e9rialisez avec Base64, v\u00e9rifiez l'absence de caract\u00e8res d'espacement ou de troncation. Ajoutez <code>assert.equal(publicKey.length, 1184)<\/code> avant l'appel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Probl\u00e8me 4 : Les secrets partag\u00e9s sont diff\u00e9rents c\u00f4t\u00e9 client et serveur.<\/strong> Cause la plus fr\u00e9quente : erreur de s\u00e9rialisation du ciphertext. Si vous transmettez le <code>cipherText<\/code> via HTTP en Base64, la d\u00e9s\u00e9rialisation doit produire exactement le m\u00eame <code>Uint8Array<\/code>. Un <code>JSON.parse(JSON.stringify(cipherText))<\/code> produit un objet ordinaire, pas un <code>Uint8Array<\/code>. Utilisez <code>new Uint8Array(Buffer.from(base64str, 'base64'))<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Probl\u00e8me 5 : Performances d\u00e9grad\u00e9es sous charge.<\/strong> ML-DSA-65 en JavaScript pur est synchrone et CPU-intensif (~1.8 ms par signature). Avec des centaines de signatures par seconde, le thread Node.js est satur\u00e9. Solution : utilisez <code>worker_threads<\/code> pour d\u00e9porter les op\u00e9rations cryptographiques sur des threads s\u00e9par\u00e9s, ou migrez vers liboqs-node pour des performances 10 \u00e0 15 fois sup\u00e9rieures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Probl\u00e8me 6 : \u00c9chec de v\u00e9rification de signature ML-DSA apr\u00e8s s\u00e9rialisation.<\/strong> Cause : le message a \u00e9t\u00e9 modifi\u00e9 entre la signature et la v\u00e9rification, par exemple modification de l'encodage UTF-8, ajout de BOM, normalisation Unicode. Toujours signer des <code>Uint8Array<\/code> binaires produits par <code>new TextEncoder().encode(str)<\/code> de fa\u00e7on coh\u00e9rente des deux c\u00f4t\u00e9s. \u00c9vitez de signer des cha\u00eenes JavaScript directement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Probl\u00e8me 7 : Conflit de version entre @noble\/post-quantum et @noble\/curves.<\/strong> Ces deux paquets partagent des utilitaires internes. En cas de conflit, v\u00e9rifiez <code>npm ls @noble\/post-quantum @noble\/curves<\/code> et \u00e9pinglez les versions dans <code>package.json<\/code>. Les versions mineures r\u00e9centes sont g\u00e9n\u00e9ralement compatibles entre elles.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Probl\u00e8me 8 : Erreur ENOMEM lors de la g\u00e9n\u00e9ration de nombreuses cl\u00e9s ML-DSA.<\/strong> Les cl\u00e9s ML-DSA-87 utilisent jusqu'\u00e0 4 864 octets pour la cl\u00e9 priv\u00e9e. Si vous g\u00e9n\u00e9rez des milliers de paires de cl\u00e9s en m\u00e9moire sans lib\u00e9ration (par exemple dans une boucle de test ou de provisioning), la consommation m\u00e9moire peut d\u00e9passer les limites de Node.js (512 Mo par d\u00e9faut). Lib\u00e9rez les r\u00e9f\u00e9rences apr\u00e8s usage ou augmentez la limite via <code>--max-old-space-size=2048<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"couverture-connexe\">Couverture connexe<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"articles-lies-sur-shattered-io\">Articles li\u00e9s sur shattered.io<\/h3>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"\/fr\/ecdsa-ed25519-nodejs\/\">ECDSA et Ed25519 dans Node.js : 12 \u00c9tapes [2026]<\/a> : impl\u00e9menter les signatures classiques avant de migrer vers ML-DSA<\/li><li><a href=\"\/fr\/post-quantum-cryptography-2026\/\">Cryptographie Post-Quantique : 50% du Web Maintenant Prot\u00e9g\u00e9 [2026]<\/a> : panorama de l'adoption mondiale et des standards NIST<\/li><li><a href=\"\/fr\/aes-256-encryption-nodejs\/\">AES-256 dans Node.js : 12 \u00c9tapes [2026]<\/a> : chiffrement sym\u00e9trique combin\u00e9 avec ML-KEM pour prot\u00e9ger les donn\u00e9es<\/li><li><a href=\"\/fr\/tls-1-3-nodejs\/\">TLS 1.3 dans Node.js : 12 \u00c9tapes [2026]<\/a> : configuration HTTPS pour int\u00e9grer les \u00e9changes de cl\u00e9s hybrides<\/li><li><a href=\"\/fr\/nodejs-crypto-module\/\">Module Crypto de Node.js : 12 \u00c9tapes [2026]<\/a> : r\u00e9f\u00e9rence compl\u00e8te du module crypto natif de Node.js<\/li><li><a href=\"\/fr\/ed25519-vs-rsa\/\">Ed25519 vs RSA : 50x Plus Rapide, Cl\u00e9s 8x Plus Petites [2026]<\/a> : comparaison des signatures classiques, point de d\u00e9part avant ML-DSA<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"faq-cryptographie-post-quantique-dans-node-js\">FAQ : cryptographie post-quantique dans Node.js<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Faut-il migrer imm\u00e9diatement vers la cryptographie post-quantique ?<\/strong> Si vos donn\u00e9es chiffr\u00e9es aujourd'hui doivent rester confidentielles pendant plus de 5 \u00e0 10 ans, oui. L'attaque \u00ab harvest now, decrypt later \u00bb est d\u00e9j\u00e0 en cours. Pour les donn\u00e9es \u00e0 courte dur\u00e9e de vie (sessions web, tokens JWT \u00e0 expiration 1 heure), la migration peut attendre 2027-2028. Commencez par l'inventaire cryptographique maintenant, quel que soit votre profil de risque.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>ML-KEM remplace-t-il compl\u00e8tement X25519 dans TLS ?<\/strong> Non, pas encore. La recommandation actuelle (IETF, ANSSI, NIST) est l'hybridation X25519 + ML-KEM-768 pendant la p\u00e9riode de transition. ML-KEM seul sera recommand\u00e9 quand les impl\u00e9mentations seront matures et valid\u00e9es par la cryptanalyse communautaire, \u00e0 l'horizon 2028+.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Node.js a-t-il un support natif pour ML-KEM ou ML-DSA ?<\/strong> En juin 2026, non. Le module <code>crypto<\/code> de Node.js ne supporte pas encore ML-KEM ou ML-DSA en natif. Utilisez @noble\/post-quantum pour une solution pure JavaScript ou liboqs-node pour des performances natives. Une int\u00e9gration dans OpenSSL 3.x et Node.js est en cours de discussion dans les groupes de travail respectifs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>@noble\/post-quantum est-il audit\u00e9 en s\u00e9curit\u00e9 ?<\/strong> La librairie @noble de Paul Miller est largement utilis\u00e9e dans l'\u00e9cosyst\u00e8me Ethereum et a fait l'objet d'audits par des firmes sp\u00e9cialis\u00e9es. @noble\/post-quantum suit les vecteurs de test officiels NIST publi\u00e9s dans les FIPS 203, 204 et 205. Une librairie JavaScript ne peut pas \u00eatre certifi\u00e9e FIPS 140-2\/3 ; pour une certification formelle, utilisez un HSM ou une librairie C certifi\u00e9e comme liboqs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Quelle est la diff\u00e9rence entre ML-KEM et ML-DSA ?<\/strong> ML-KEM est un m\u00e9canisme d'encapsulation de cl\u00e9s (KEM) : il sert \u00e0 \u00e9tablir un secret partag\u00e9 entre deux parties, utilis\u00e9 ensuite pour le chiffrement sym\u00e9trique. ML-DSA est un algorithme de signature num\u00e9rique : il prouve l'authenticit\u00e9 et l'int\u00e9grit\u00e9 d'un message sans \u00e9tablir de secret. Ces deux r\u00f4les sont compl\u00e9mentaires, exactement comme ECDH (\u00e9change de cl\u00e9s) et ECDSA (signature) dans la cryptographie classique.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Quand choisir SLH-DSA plut\u00f4t que ML-DSA ?<\/strong> SLH-DSA repose uniquement sur la s\u00e9curit\u00e9 des fonctions de hachage, une hypoth\u00e8se plus conservative que les r\u00e9seaux euclidiens de ML-DSA. En contrepartie, ses signatures sont beaucoup plus volumineuses (17 \u00e0 50 ko). Pr\u00e9f\u00e9rez SLH-DSA pour les usages \u00e0 tr\u00e8s longue dur\u00e9e de vie (certificats racine sur 20 ans, documents l\u00e9gaux) et ML-DSA pour les usages op\u00e9rationnels \u00e0 fort volume (API, TLS, microservices).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Quel est l'impact sur les performances d'un serveur Express ?<\/strong> Avec @noble\/post-quantum (JavaScript pur), une signature ML-DSA-65 prend environ 1.8 ms sur un CPU moderne. Un serveur qui signe 100 r\u00e9ponses par seconde utilise environ 18% d'un c\u0153ur CPU uniquement pour les signatures. Pour des charges plus \u00e9lev\u00e9es, utilisez liboqs-node (10 \u00e0 15 fois plus rapide) ou d\u00e9portez les signatures sur des workers d\u00e9di\u00e9s via <code>worker_threads<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Les certificats TLS post-quantiques sont-ils disponibles pour les domaines publics ?<\/strong> D\u00e9but 2026, les autorit\u00e9s de certification publiques (Let's Encrypt, DigiCert) n'\u00e9mettent pas encore de certificats TLS ML-DSA pour les domaines publics. L'infrastructure PKI publique \u00e9volue lentement en raison des contraintes de compatibilit\u00e9 des navigateurs. Pour les PKI internes (mutual TLS, microservices), vous pouvez d\u00e8s maintenant d\u00e9ployer vos propres certificats ML-DSA avec OpenSSL + OQS-Provider.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Les ordinateurs quantiques progressent \u00e0 un rythme que la communaut\u00e9 cryptographique n&#8217;anticipait pas avant 2030. En ao\u00fbt 2024, le NIST a publi\u00e9 trois standards finalis\u00e9s, FIPS 203, FIPS 204 et\u2026<\/p>\n","protected":false},"author":4,"featured_media":315,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-314","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cryptography"],"_links":{"self":[{"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/posts\/314","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/comments?post=314"}],"version-history":[{"count":0,"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/posts\/314\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/media\/315"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/media?parent=314"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/categories?post=314"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/fr\/wp-json\/wp\/v2\/tags?post=314"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}