Digamos que estamos construyendo una función de comparación genérica segura para la sincronización para uso general. Hacerlo de modo que sea seguro cuando ambas cadenas tienen la misma longitud es bastante conocido. Sin embargo, no estoy seguro de cómo podemos hacerlo seguro si las cadenas tienen diferentes longitudes (arbitrarias).
Una cadena se considerará como "conocida" y la otra como "desconocida". Asumiremos que un atacante solo tiene el control del valor "desconocido". Idealmente, esta función no debería filtrar información sobre la longitud de la cadena conocida.
Una implementación trivial, como:
// 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;
}
El problema aquí es que puede haber una fuga de información en la memoria caché.
Por ejemplo, un tamaño de palabra en x64 es de 64 bits. Así que podemos caber 8 caracteres en un solo registro. Si el valor conocido es una cadena que tiene 7 caracteres o menos (ya que agregamos 1 al conocido_len), la comparación nunca requiere otra operación de carga para la cadena conocida, aunque la cadena desconocida lo hará.
En otras palabras, si el tamaño de la cadena desconocida difiere de la cadena conocida en uno o más límites de palabras, la cantidad total de "trabajo" que se está realizando puede cambiar.
Mi primer instinto sería comparar solo cadenas de igual tamaño, pero luego se filtraría la información de longitud (ya que la ejecución seguiría a varias ramas diferentes).
Entonces, esto deja dos preguntas:
-
¿Es esta pequeña diferencia lo suficiente como para preocuparse?
-
¿Se puede evitar este tipo de diferencia sin filtrar información sobre el tamaño conocido?