Diciamo che stiamo costruendo una funzione di confronto sicura a tempo generico per uso generale. Fare in modo che sia sicuro quando entrambe le stringhe sono di uguale lunghezza è abbastanza noto. Tuttavia, quello di cui non sono sicuro è come possiamo renderlo sicuro se le stringhe hanno lunghezze diverse (arbitrarie).
Una stringa verrà considerata come "conosciuta" e l'altra come "sconosciuta". Assumiamo che un utente malintenzionato abbia solo il controllo del valore "sconosciuto". Idealmente, questa funzione non dovrebbe contenere alcuna informazione sulla lunghezza della stringa nota.
Un'implementazione banale, come ad esempio:
// returns 1 is same, 0 otherwise
int timing_safe_compare(char *known, size_t known_len, char *unknown, size_t unknown_len) {
// Safe since all strings **will** be null terminated
size_t mod_len = known_len + 1;
size_t i;
int result = 0;
result = known_len - unknown_len;
for (i = 0; i < unknown_len; i++) {
result |= known[i % mod_len] ^ unknown[i];
}
return result == 0 ? 1 : 0;
}
Il problema qui è che potrebbe esserci una perdita di informazioni nella cache.
Ad esempio, una dimensione di parola in x64 è di 64 bit. Quindi possiamo inserire 8 caratteri in un singolo registro. Se il valore noto è una stringa di 7 caratteri o meno (poiché aggiungiamo 1 a known_len), il confronto non richiede mai un'altra operazione di caricamento per la stringa nota, anche se la stringa sconosciuta lo farà.
In altre parole, se la dimensione della stringa sconosciuta differisce dalla stringa conosciuta da uno o più limiti di parole, la quantità totale di "lavoro" in corso potrebbe cambiare.
Il mio primo istinto sarebbe di confrontare solo stringhe di uguali dimensioni, ma poi le informazioni sulla lunghezza sarebbero trapelate (poiché l'esecuzione seguirà diversi rami diversi).
Quindi, questo lascia due domande:
-
Questa piccola differenza è abbastanza per essere preoccupata?
-
Questo tipo di differenza può essere evitato senza perdite di informazioni sulle dimensioni conosciute?