
Récupération complète de la clé ML-KEM-1024 à partir d'une comparaison partielle de Fujisaki-Okamoto dans wolfSSL (CVE-2026-6330 NEON, CVE-2026-10097 AVX2)
CVE-2026-6330 (NEON) et CVE-2026-10097 (AVX2). Corrigé dans wolfSSL 5.9.2.
Le rapport technique complet est publié dans
Cryptology ePrint Archive, Paper 2026/1682
(copie locale dans paper/). Voir Citation ci-dessous.
ML-KEM (Kyber, FIPS 203) résiste aux attaques par texte chiffré choisi grâce à une vérification lors de la décapsulation : le récepteur ré-encrypte le message qu'il vient de déchiffrer et ne renvoie le véritable secret partagé que si le résultat correspond exactement au texte chiffré d'entrée. Toute discordance déclenche un rejet implicite, et une valeur pseudo-aléatoire est renvoyée à la place. Cette vérification est la transformée de Fujisaki-Okamoto, la barrière unique séparant le schéma IND-CCA2 du schéma malléable sous-jacent.
wolfSSL a implémenté cette comparaison en assembleur SIMD écrit à la main, et sur deux backends, elle ne comparait qu'une partie du texte chiffré. Sur ARM64 NEON, elle comparait environ la moitié, ce que Nicholas Carlini (Wikipedia) a découvert et signalé (CVE-2026-6330). Sur x86-64 AVX2, elle comparait 1536 des 1568 octets du ML-KEM-1024 (CVE-2026-10097).
Sur l'un ou l'autre backend, un texte chiffré falsifié est accepté comme authentique, ce qui a été signalé comme un
affaiblissement de la sécurité IND-CCA2. Ce rapport montre que la faille va plus loin, sur les deux
backends. Les octets que la vérification ignore divulguent le bruit de déchiffrement de la décapsulation, et ce
bruit est une fonction linéaire de la clé secrète, de sorte que la clé peut être récupérée par
régression des moindres carrés. Ce n'est pas un
problème de réseau euclidien, et ce n'est pas l'attaque classique par discordance de clé : sur le backend AVX2, la
faille valide tout u, donc les textes chiffrés à u choisi que ces attaques exigent sont rejetés.
La régression du bruit de déchiffrement récupère plutôt la clé à partir des coefficients v non vérifiés.
La clé privée ML-KEM-1024 complète est récupérée de bout en bout contre le binaire vulnérable en conditions réelles
sur les backends AVX2 et NEON.
L'attaque nécessite une clé ML-KEM réutilisée : destinataires HPKE, KEMTLS, ou clés épinglées et intégrées. Un échange de clés hybride éphémère TLS 1.3 utilise une nouvelle clé par négociation et n'est pas récupérable de cette manière ; là, la faille n'est qu'une rupture de distinction. Les deux failles sont corrigées et publiques, et il s'agit d'un rapport post-divulgation.
| NEON (CVE-2026-6330) | AVX2 (CVE-2026-10097) | |
|---|---|---|
| Backend | ARM64 NEON | x86-64 AVX2 |
| Faille | compare ~la moitié du texte chiffré | compare 1536 des 1568 octets |
| Bogue signalé par | Nicholas Carlini (Anthropic) | 007bsd |
| Sévérité CVE | Moyenne (CVSS 4.0 6.3, CWE-327) | Élevée (CVSS 4.0 8.3, CWE-697) |
| Récupération de clé, modèle | complète (~500 ct) | complète (~1300 ct) |
| Récupération de clé, binaire en conditions réelles | 98,5 % @ 600 ct (émulation QEMU) | 98,1 % @ 350 ct (natif) |
| Correctif | PR #10192 | PR #10430 |
« en conditions réelles » désigne le résultat démontré sur le binaire réel : NEON nécessite plus de textes chiffrés (600 contre 350) car sa mesure par coefficient est beaucoup plus bruitée (environ 4x). « modèle » est une vérification sans bruit que la régression récupère la clé complète (les 2048 coefficients, 100 %) ; son nombre de textes chiffrés est fixé par le nombre d'équations que chaque texte chiffré produit, et non par le bruit, donc il n'est pas comparable au chiffre en conditions réelles et ne reflète pas la difficulté de l'attaque.
Les deux failles ont été découvertes indépendamment dans la même fenêtre de version de wolfSSL et corrigées ensemble
dans 5.9.2 (voir la
page des vulnérabilités de sécurité de wolfSSL pour
les deux entrées CVE). Le bogue NEON de Carlini a été documenté comme un affaiblissement IND-CCA2, et le bogue AVX2 est
enregistré en CVE comme récupération de clé. L'attaque par régression du bruit de déchiffrement dans ce dépôt récupère
la clé sur les deux. Les détails spécifiques au backend se trouvent dans
neon-cve-2026-6330/ et
avx2-cve-2026-10097/.
Une comparaison incomplète dans la vérification de rejet implicite du ML-KEM de wolfSSL, présente dans deux backends d'assembleur SIMD. Elle comparait moins que tous les octets du texte chiffré, de sorte que la décapsulation accepte des textes chiffrés qu'une implémentation correcte rejette. Les octets non vérifiés transforment la décapsulation en oracle pour le bruit de déchiffrement, et cela suffit à récupérer la clé privée lorsque la clé est réutilisée.
Vous êtes concerné si vous avez utilisé wolfSSL avec ML-KEM sur une compilation avec l'assembleur SIMD vulnérable : AVX2 (x86-64) dans 5.7.0-5.9.1, ou ARM64 NEON dans 5.7.4-5.9.1. L'attaque de récupération de clé nécessite en outre que la clé privée ML-KEM soit réutilisée entre les décapsulations, comme avec les destinataires HPKE, KEMTLS, ou une clé épinglée ou intégrée. Un échange de clés hybride éphémère TLS 1.3 utilise une nouvelle clé par négociation et n'est pas récupérable ; là, la faille n'est qu'une rupture de distinction. Le correctif se trouve dans wolfSSL 5.9.2.
Récupérer l'intégralité de la clé secrète ML-KEM-1024 sur l'un ou l'autre backend, en envoyant des requêtes de décapsulation forgées et en observant, pour chacune, si le secret réel ou une valeur de rejet revient. Contre les binaires en conditions réelles, cela a récupéré la clé avec environ 10^5 à 10^6 requêtes de décapsulation.
Prenons un coefficient de texte chiffré dans la région non vérifiée. Son bit de texte clair est décidé par
arrondi, m'_j = Compress_1(v_j - (s^T u)_j). Parce que ces octets ne sont pas comparés,
balayer la valeur compressée de v_j et observer la seule sortie de décapsulation qui
change localise une frontière d'arrondi, et la position de la frontière mesure le bruit de déchiffrement
δ_j avec une précision d'environ ±q/64. En termes du secret (s, e) et du
caractère aléatoire de chiffrement choisi par l'attaquant :
δ_j = ( e^T y - s^T(e1 + c_u) + e2 + c_v )_j (vérifié exact ; petit ; ne dépasse jamais mod q)
C'est une équation linéaire dans le secret (s, e) à 2048 coefficients, avec des coefficients que l'attaquant
connaît. Chaque texte chiffré donne une telle équation par coefficient non vérifié. En empilant
suffisamment de textes chiffrés et en résolvant les équations normales, on récupère toute la clé, et les derniers
coefficients sont épinglés par la relation de clé publique e = t - A·s ∈ CBD(η). C'est
une régression des moindres carrés ordinaire, pas un
problème de réseau euclidien.
Les deux backends diffèrent dans la quantité de fuite. NEON laisse environ 50 % du texte chiffré non vérifié (125 coefficients dans trois bandes), contre environ 2 % pour AVX2 (51 coefficients dans une bande). NEON nécessite néanmoins plus de textes chiffrés, car ses octets non vérifiés sont non contigus et sa mesure par coefficient est plus bruitée, donc fuir davantage ne rend pas la récupération plus facile ici.
Les attaques par vérification de texte clair et par discordance de clé (Ravi et al. 2020, Qin et al. 2021, Băetu et
al. 2019) récupèrent la clé en quelques milliers de requêtes en soumettant des textes chiffrés avec un
u creux choisi par l'attaquant pour isoler un coefficient secret par requête. La faille AVX2
les rejette toutes : elle ne saute que 32 octets de v et valide tout u, donc sur
le binaire en conditions réelles, un texte chiffré à u creux est rejeté et inverser un seul bit de u est
rejeté 30 fois sur 30. La faille NEON est plus large, laissant environ la moitié de u non vérifié
également, donc cet argument est spécifique à AVX2 ; mais l'attaque ne s'y appuie pas. La
régression du bruit de déchiffrement récupère la clé à partir des coefficients v non vérifiés sur les deux
backends, survivant à la partie de la vérification que chaque bogue laisse intacte.
Le précédent pertinent, alors, n'est pas ces attaques par discordance de clé mais les attaques par échec de déchiffrement (D'Anvers et al. 2019, réf 6). Elles exploitent le même terme de bruit de déchiffrement, une fonction linéaire du secret, mais seulement à travers l'événement rare de ce terme franchissant la frontière de décodage et provoquant un échec de déchiffrement, donc la récupération y est statistique et nécessite beaucoup plus de textes chiffrés. La comparaison incomplète rend au contraire ce même terme directement mesurable, à environ ±q/64, donc aucun échec n'est nécessaire et l'attaque se réduit à la régression des moindres carrés ordinaire ci-dessus.
Dans un modèle de référence fidèle (kyber-py) pour les deux backends, l'identité ci-dessus est vérifiée exacte et la régression récupère les 2048 coefficients secrets. Contre les binaires wolfSSL en conditions réelles avant correctif, attaquant une clé exportée réutilisée, chaque harnais exécute une auto-vérification contre la clé de vérité terrain exportée de l'oracle avant de rapporter un quelconque nombre :
(s, e) à 400 textes chiffrés. Le secret s seul est 1005/1024 = 98,1 % à
350 ct, le chiffre enregistré dans la CVE.(s, e) à 600 textes chiffrés, avec l'erreur du solveur convergeant
de manière monotone vers l'exact.La clé récupérée est la paire de clés générée par wolfSSL que l'oracle exporte, et l'attaque s'exécute
contre l'assembleur SIMD sous sa forme livrée plutôt que contre un modèle de celui-ci. Transcriptions complètes :
avx2-cve-2026-10097/live_recover_avx2.out,
neon-cve-2026-6330/live_recover_neon.out.
Élevée, bien que non catastrophique. Elle nécessite une clé réutilisée, un oracle accepter/rejeter, et un nombre important mais pratique de requêtes. Ce n'est pas un canal auxiliaire : il n'y a pas de mesure de temps ou de consommation, seulement un bogue logique dans une comparaison. Cela n'affecte pas les implémentations correctes ni la norme ML-KEM elle-même. À titre de comparaison, une fuite temporelle induite par le compilateur dans la décapsulation ML-KEM de liboqs (CVE-2024-36405), un problème connexe de récupération complète de clé secrète de la vague 2024 de fuites temporelles ML-KEM aux côtés de KyberSlash, a été évaluée CVSS 7.5 par le NIST.
Le code d'attaque par backend, l'analyse et les étapes de reproduction se trouvent dans
avx2-cve-2026-10097/ et
neon-cve-2026-6330/.
Ce dépôt documente deux failles sœurs issues de la même fenêtre de version de wolfSSL, et une attaque qui les brise toutes deux.
Les deux problèmes ont été signalés en privé, corrigés et assignés à des CVE avant ce rapport. Corrigés dans wolfSSL 5.9.2.
Le rapport complet est publié comme
Cryptology ePrint Archive, Paper 2026/1682
(copie locale : paper/).
@misc{cryptoeprint:2026/1682,
author = {Bhabani Sankar Das},
title = {Incomplete Ciphertext Comparison in {ML}-{KEM}: From an {IND}-{CCA2} Break to Key Recovery},
howpublished = {Cryptology {ePrint} Archive, Paper 2026/1682},
year = {2026},
url = {https://eprint.iacr.org/2026/1682}
}