
Recupero completo della chiave ML-KEM-1024 da un confronto Fujisaki-Okamoto parziale in wolfSSL (CVE-2026-6330 NEON, CVE-2026-10097 AVX2)
CVE-2026-6330 (NEON) e CVE-2026-10097 (AVX2). Corretto in wolfSSL 5.9.2.
Il resoconto tecnico completo è pubblicato come
Cryptology ePrint Archive, Paper 2026/1682
(copia locale in paper/). Vedi Citazione più sotto.
ML-KEM (Kyber, FIPS 203) resiste agli attacchi a testo cifrato scelto grazie a un singolo controllo all'interno della decapsulazione: il ricevente ri-cifra il messaggio che ha appena decifrato e restituisce il segreto condiviso reale solo se il risultato corrisponde esattamente al testo cifrato in ingresso. Qualsiasi discrepanza attiva il rifiuto implicito e viene restituito un valore pseudocasuale. Questo controllo è la trasformata di Fujisaki-Okamoto, l'unica barriera che separa lo schema IND-CCA2 dallo schema malleabile sottostante.
wolfSSL ha implementato quel confronto in assembly SIMD scritto a mano, e su due backend confrontava solo una parte del testo cifrato. Su ARM64 NEON confrontava circa la metà, cosa che Nicholas Carlini (Wikipedia) ha scoperto e segnalato (CVE-2026-6330). Su x86-64 AVX2 confrontava 1536 dei 1568 byte in ML-KEM-1024 (CVE-2026-10097).
Su entrambi i backend un testo cifrato manomesso viene accettato come autentico, cosa che è stata segnalata come un
indebolimento della sicurezza IND-CCA2. Questo resoconto mostra che il difetto va oltre, su entrambi
i backend. I byte che il controllo salta rivelano il rumore di decifratura della decapsulazione, e quel
rumore è una funzione lineare della chiave segreta, quindi la chiave può essere recuperata tramite
regressione ai minimi quadrati. Non è un
problema reticolare e non è il classico attacco di disallineamento della chiave: sul backend AVX2 il
difetto valida tutto u, quindi i testi cifrati con u scelto che quegli attacchi richiedono vengono rifiutati.
La regressione del rumore di decifratura recupera invece la chiave dai coefficienti v non controllati.
L'intera chiave privata ML-KEM-1024 viene recuperata end-to-end contro il binario vulnerabile reale
su entrambi i backend AVX2 e NEON.
L'attacco richiede una chiave ML-KEM riutilizzata: destinatari HPKE, KEMTLS o chiavi fissate e incorporate. Una condivisione di chiave ibrida effimera TLS 1.3 usa una chiave nuova per ogni handshake e non è recuperabile in questo modo; in quel caso il difetto è solo una violazione di indistinguibilità. Entrambi i difetti sono corretti e pubblici, e questo è un resoconto post-divulgazione.
| NEON (CVE-2026-6330) | AVX2 (CVE-2026-10097) | |
|---|---|---|
| Backend | ARM64 NEON | x86-64 AVX2 |
| Difetto | confronta ~metà del testo cifrato | confronta 1536 di 1568 byte |
| Bug segnalato da | Nicholas Carlini (Anthropic) | 007bsd |
| Gravità CVE | Media (CVSS 4.0 6.3, CWE-327) | Alta (CVSS 4.0 8.3, CWE-697) |
| Recupero chiave, modello | completo (~500 ct) | completo (~1300 ct) |
| Recupero chiave, binario reale | 98,5% @ 600 ct (emulato QEMU) | 98,1% @ 350 ct (nativo) |
| Correzione | PR #10192 | PR #10430 |
"reale" è il risultato dimostrato sul binario effettivo: NEON richiede più testi cifrati (600 vs 350) perché la sua misurazione per coefficiente è molto più rumorosa (circa 4x). "modello" è una verifica senza rumore che la regressione recupera l'intera chiave (tutti i 2048 coefficienti, 100%); il suo conteggio di testi cifrati è determinato da quante equazioni produce ogni testo cifrato, non dal rumore, quindi non è paragonabile alla cifra reale e non riflette la difficoltà dell'attacco.
Entrambi i difetti sono stati trovati indipendentemente nella stessa finestra di rilascio di wolfSSL e corretti insieme
in 5.9.2 (vedi la
pagina delle vulnerabilità di sicurezza di wolfSSL per
entrambe le voci CVE). Il bug NEON di Carlini è stato documentato come un indebolimento IND-CCA2, e il bug AVX2 è
registrato come CVE per recupero della chiave. L'attacco di regressione del rumore di decifratura in questo repository recupera
la chiave su entrambi. I dettagli specifici per backend sono in
neon-cve-2026-6330/ e
avx2-cve-2026-10097/.
Un confronto incompleto nel controllo di rifiuto implicito di ML-KEM in wolfSSL, presente in due backend assembly SIMD. Confrontava meno di tutti i byte del testo cifrato, quindi la decapsulazione accetta testi cifrati che un'implementazione corretta rifiuta. I byte non controllati trasformano la decapsulazione in un oracolo per il rumore di decifratura, e questo è sufficiente per recuperare la chiave privata quando la chiave viene riutilizzata.
Sei colpito se hai usato wolfSSL con ML-KEM su una build con l'assembly SIMD vulnerabile: AVX2 (x86-64) in 5.7.0-5.9.1, o ARM64 NEON in 5.7.4-5.9.1. L'attacco di recupero della chiave richiede inoltre che la chiave privata ML-KEM sia riutilizzata tra le decapsulazioni, come con destinatari HPKE, KEMTLS, o una chiave fissata o incorporata. Una condivisione di chiave ibrida effimera TLS 1.3 usa una chiave nuova per ogni handshake e non è recuperabile; in quel caso il difetto è solo una violazione di indistinguibilità. La correzione è in wolfSSL 5.9.2.
Recuperare l'intera chiave segreta ML-KEM-1024 su entrambi i backend, inviando query di decapsulazione costruite ad arte e osservando, per ciascuna, se torna il segreto reale o un valore di rifiuto. Contro i binari reali questo ha recuperato la chiave con circa 10^5 a 10^6 query di decapsulazione.
Prendi un coefficiente del testo cifrato nella regione non controllata. Il suo bit di plaintext è deciso
dall'arrotondamento, m'_j = Compress_1(v_j - (s^T u)_j). Poiché quei byte non vengono confrontati,
variare il valore compresso di v_j e osservare l'unica uscita di decapsulazione che
cambia individua un confine di arrotondamento, e la posizione del confine misura il rumore
di decifratura δ_j con una precisione di circa ±q/64. In termini del segreto (s, e) e della
casualità di cifratura scelta dall'attaccante:
δ_j = ( e^T y - s^T(e1 + c_u) + e2 + c_v )_j (verificato esatto; piccolo; non avvolge mai mod q)
Questa è un'equazione lineare nel segreto a 2048 coefficienti (s, e), con coefficienti che l'attaccante
conosce. Ogni testo cifrato fornisce un'equazione di questo tipo per ogni coefficiente non controllato. Accumulando
abbastanza testi cifrati e risolvendo le equazioni normali si recupera l'intera chiave, e gli ultimi
pochi coefficienti sono fissati dalla relazione con la chiave pubblica e = t - A·s ∈ CBD(η). È
ordinaria regressione ai minimi quadrati, non un
problema reticolare.
I due backend differiscono in quanto rivelano. NEON lascia circa il 50% del testo cifrato non controllato (125 coefficienti in tre bande), contro circa il 2% per AVX2 (51 coefficienti in una banda). NEON richiede comunque più testi cifrati, perché i suoi byte non controllati sono non contigui e la sua misurazione per coefficiente è più rumorosa, quindi rivelare di più non rende il recupero più facile qui.
Gli attacchi di controllo del plaintext e di disallineamento della chiave (Ravi et al. 2020, Qin et al. 2021, Băetu et
al. 2019) recuperano la chiave in poche migliaia di query sottoponendo testi cifrati con
u sparso scelto dall'attaccante per isolare un coefficiente segreto per query. Il difetto AVX2
li rifiuta tutti: salta solo 32 byte di v e valida l'intero u, quindi sul
binario reale un testo cifrato con u sparso viene rifiutato e capovolgere un singolo bit di u viene
rifiutato 30 volte su 30. Il difetto NEON è più ampio, lasciando non controllata anche circa metà di u,
quindi quell'argomento è specifico di AVX2; ma l'attacco non si basa su questo. La
regressione del rumore di decifratura recupera la chiave dai coefficienti v non controllati su entrambi
i backend, sopravvivendo alla parte del controllo che ogni bug lascia intatta.
Il precedente rilevante, quindi, non sono quegli attacchi di disallineamento della chiave ma gli attacchi di fallimento di decifratura (D'Anvers et al. 2019, rif. 6). Sfruttano lo stesso termine di rumore di decifratura, una funzione lineare del segreto, ma solo attraverso il raro evento che quel termine attraversi il confine di decodifica e causi un fallimento di decifratura, quindi il recupero lì è statistico e richiede molti più testi cifrati. Il confronto incompleto rende invece quel termine direttamente misurabile, a circa ±q/64, quindi non servono fallimenti e l'attacco si riduce alla regressione ai minimi quadrati ordinaria sopra descritta.
In un modello di riferimento fedele (kyber-py) per entrambi i backend, l'identità sopra è verificata esatta e la regressione recupera tutti i 2048 coefficienti segreti. Contro i binari wolfSSL reali pre-correzione, attaccando una chiave esportata riutilizzata, ogni harness esegue un auto-controllo contro la chiave di verità di base esportata dall'oracolo prima di riportare qualsiasi numero:
(s, e) a 400 testi cifrati. Il solo segreto s è 1005/1024 = 98,1% a
350 ct, la cifra registrata nel CVE.(s, e) a 600 testi cifrati, con l'errore del risolutore che converge
monotonicamente verso l'esattezza.La chiave recuperata è la coppia di chiavi generata da wolfSSL che l'oracolo esporta, e l'attacco viene eseguito
contro l'assembly SIMD nella forma distribuita, non contro un suo modello. Trascrizioni complete:
avx2-cve-2026-10097/live_recover_avx2.out,
neon-cve-2026-6330/live_recover_neon.out.
Alta, anche se non catastrofica. Richiede una chiave riutilizzata, un oracolo accetta/rifiuta e un numero elevato ma pratico di query. Non è un canale laterale: non c'è misurazione di temporizzazione o consumo, solo un bug logico in un confronto. Non colpisce le implementazioni corrette né lo standard ML-KEM stesso. Per confronto, una perdita di temporizzazione indotta dal compilatore nella decapsulazione ML-KEM di liboqs (CVE-2024-36405), un problema correlato di recupero completo della chiave segreta dell'ondata 2024 di perdite di temporizzazione ML-KEM insieme a KyberSlash, è stato valutato CVSS 7.5 da NIST.
Il codice d'attacco per backend, l'analisi e i passaggi di riproduzione sono in
avx2-cve-2026-10097/ e
neon-cve-2026-6330/.
Questo repository documenta due scoperte sorelle della stessa finestra di rilascio di wolfSSL e un attacco che le rompe entrambe.
Entrambi i problemi sono stati segnalati privatamente, corretti e assegnati a CVE prima di questo resoconto. Corretti in wolfSSL 5.9.2.
Il resoconto completo è pubblicato come
Cryptology ePrint Archive, Paper 2026/1682
(copia 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}
}