Un algoritmo perfetto implementato in modo scorretto è, semplicemente, insicuro.
Kyber è sicuro. La matematica è pubblicata, revisionata da pari, analizzata per anni. I parametri sono scelti con margini conservativi. La struttura Module-LWE è elegante e solida.
Ma Kyber non vive sulla carta. Vive nel codice. E il codice ha un problema che le equazioni non conoscono: il tempo non è uniforme.
Il tempo come oracolo
Gli attacchi di temporizzazione — timing attacks — sfruttano una discrepanza fondamentale tra il modello matematico e la macchina reale. Nel modello, un'operazione come x + y richiede "un'unità di tempo". Nella macchina reale, x + y può richiedere microsecondi diversi a seconda dei valori di x e y, dello stato della cache, della predizione dei salti del processore.
Per la crittografia, questa è una catastrofe: se il tempo di decifratura dipende dal segreto, l'attaccante può misurare il tempo e recuperare il segreto.
// Esempio illustrativo: un confronto NON costante in tempo
bool confronta(List<int> a, List<int> b) {
if (a.length != b.length) return false;
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) return false; // early exit: il tempo dipende da dove fallisce
}
return true;
}
Questa funzione restituisce false non appena trova una discrepanza. Se il primo byte fallisce, impiega poco. Se fallisce l'ultimo, impiega di più. Un attaccante che possa misurare il tempo di questo confronto può dedurre quanti byte ha indovinato, e con questo rompere la sicurezza byte per byte.
Flutter e la promessa infranta
Dart è un linguaggio che non garantisce tempo costante a livello di VM. Non esiste @ConstantTime né un attributo del compilatore che assicuri che un'operazione impiegherà lo stesso tempo indipendentemente dai suoi operandi.
Problema: Dart VM → nessuna garanzia di tempo costante
Contesto: Flutter per applicazioni mobili multipiattaforma
Rischi concreti nell'implementare Kyber in Dart puro:
1. Confronto di ciphertext → timing del FO transform (il passo più critico)
2. Decodifica di polinomi → timing di Compress/Decompress
3. NTT (Number Theoretic Transform) → accesso a tabelle secondo indice segreto
4. Campionamento CBD → ramificazione secondo bit del segreto
5. Deserializzazione di chiavi → lunghezza implicita o padding variabile
Ognuno di questi punti è un vettore d'attacco. Non perché Kyber sia debole — le equazioni sono corrette — ma perché l'implementazione in Dart filtra informazioni attraverso il tempo.
Il problema non è esclusivo di Dart. Qualsiasi linguaggio senza controllo fine sul tempo di esecuzione — JavaScript, Python, Java senza cure estreme — soffre dello stesso problema. La differenza è che Flutter/Dart viene usato sempre più per applicazioni che necessitano di crittografia (Signal, applicazioni finanziarie, messaggistica cifrata), e la tentazione di "scrivere Kyber in Dart puro, tanto, è post-quantistico" è grande.
Borges immaginò un impero dove i cartografi crearono una mappa così precisa che coincideva punto per punto con il territorio, fino a diventare inutile. In crittografia accade l'inverso: il paper è la mappa perfetta — ogni operazione definita, ogni parametro giustificato, ogni passo verificato — e l'implementazione è il territorio, che non coincide mai del tutto con la mappa. Tra la carta e la macchina c'è uno spazio che nessun teorema può chiudere.
L'ho constatato personalmente. Durante la mia fase di apprendimento ho implementato Kyber in Dart/Flutter — xkyber_crypto — seguendo FIPS 203 alla lettera: NTT, CBD, FO transform, Compress/Decompress. I test di black box passavano. I vettori di test della specifica coincidevano byte per byte. L'implementazione era matematicamente corretta. E tuttavia, era crittograficamente insostenibile.
Il paper assume un modello ideale dove ogni istruzione costa lo stesso. Il territorio — la VM di Dart, il compilatore JIT, la gerarchia di cache del processore — non fa questa supposizione. Il paper non menziona che l'accesso a una tabella NTT secondo un indice derivato dal segreto può far trapelare il segreto. Il paper non sa cosa sia una cache.
Sartre disse che l'inferno sono gli altri. Per il paper di Kyber, gli altri sono il mondo fisico in cui deve eseguirsi: la RAM, il garbage collector, il predittore di salti, la VM che riordina le istruzioni senza chiedere permesso. Il repository è archiviato da novembre 2025 come testimonianza che la distanza tra la mappa e il territorio non può sempre essere percorsa.
La differenza tra la mappa e il territorio
La ricerca sull'implementazione di Kyber documenta con precisione le ottimizzazioni necessarie affinché il codice sia costante in tempo: l'uso di Montgomery reduction invece della divisione, l'assenza di if nelle funzioni di campionamento, l'accesso sequenziale alle tabelle NTT senza dipendenza da dati segreti.
Livello Cosa promette Cosa può fallire
───── ─────────── ────────────────
Matematica Sicurezza LWE Nessun errore (corretta)
Algoritmo FO transform CCA-secure Nessun errore (corretto)
Linguaggio Espressività Senza garanzie di tempo costante
CPU Esecuzione Cache, predittori, pipeline
Ogni livello introduce nuovi rischi. Un algoritmo matematicamente perfetto, implementato in un linguaggio che non garantisce tempo costante, eseguito su una CPU che ottimizza dinamicamente: la sicurezza promessa dalle equazioni si infiltra attraverso ogni fessura dell'hardware.
Sartre scrisse che "l'inferno sono gli altri". In crittografia, l'inferno sono gli altri livelli: il compilatore che riordina le operazioni, la cache che filtra gli accessi, il garbage collector che introduce pause misurabili.
// Implementazione costante in tempo (C) — ogni ramo impiega lo stesso tempo
uint8_t ct_compare(const uint8_t *a, const uint8_t *b, size_t len) {
uint8_t result = 0;
for (size_t i = 0; i < len; i++) {
result |= a[i] ^ b[i]; // XOR: nessun early exit
}
return result; // 0 se uguali, != 0 se diversi
}
In C, questa funzione è costante in tempo (supponendo che il compilatore non la ottimizzi). In Dart, non c'è modo di garantire che la VM compili questo come un ciclo senza ramificazione dipendente dai dati. La stessa logica produce risultati di sicurezza diversi a seconda del linguaggio.
Cosa non c'è nel paper
Il paper di Kyber (Bos et al., 2018) descrive l'algoritmo. Non descrive come implementarlo in modo sicuro su ogni piattaforma. Non avverte che un confronto ingenuo di ciphertext in Dart può far trapelare la chiave. Non menziona che la VM di Dart può introdurre variazioni di tempo che annullano la sicurezza del FO transform.
Questa distanza — tra il paper e il prodotto — è dove si verificano la maggior parte delle vulnerabilità crittografiche reali. Non nelle equazioni. Nelle decisioni di implementazione.
| Livello | Dov'è documentato il rischio? |
|---|---|
| Algoritmo | Paper, FIPS 203 |
| Parametri | Paper, FIPS 203 |
| Implementazione sicura in C | Riferimento di pq-crystals |
| Implementazione sicura in Rust | Bindings, involucri |
| Implementazione sicura in Dart | Non esiste documentazione ufficiale |
| Implementazione in Flutter | Non esiste |
Non è che Kyber non possa essere implementato in modo sicuro in Dart. È che farlo richiede un livello di cura — e di conoscenza della VM di Dart — che non è documentato da nessuna parte. Ogni implementazione in Dart puro è, oggi, una scommessa.
In III: Il limite dell'errore, esploreremo la frontiera fisica della crittografia: l'energia necessaria per rompere uno schema, il limite di Landauer, e perché persino Kyber — come tutto il resto — è destinato a cadere, anche se forse non per le ragioni che ci aspettiamo.
Riferimenti incrociati con la ricerca:
- Implementazione — Dettagli di NTT, Compress/Decompress, FO transform
- Attacchi — Attacchi side-channel contro Kyber
- Parametri di sicurezza — Probabilità di fallimento e margini
- Struttura del KEM — Il FO transform e perché il confronto è critico
