Pregunta:
Estoy planeando usar esta implementación de Javascript BCrypt , pero como puede ver en el código, usa un conjunto de datos precalculado de 4 KB para los parámetros P
y S
necesarios para el hash de pez globo. También rastreé un conjunto de datos similar hasta el código C BCrypt original .
¿No debería utilizar el hash de pez globo un conjunto de datos calculado dinámicamente? ¿Cómo puede ser esto seguro? Voy a hacer esto en el lado del cliente, así que si un atacante lee el conjunto de datos almacenado estáticamente, ¿será más fácil encontrar una colisión para el valor hash de BCrypt?
Respuesta:
El algoritmo hash de contraseña de BCrypt se basa en una versión modificada del algoritmo de cifrado Blowfish. Los valores $ P $ son las subclaves redondas (utilizadas para XORing con los datos), y los valores $ S $ son componentes para cuadros de sustitución, ambos utilizados en una red Feistel.
Los valores iniciales para $ P $ y $ S $ son los mismos que en el algoritmo Blowfish original y provienen de los dígitos hexadecimales de la constante circular $ \ pi $.
Durante la programación de claves, estos valores $ P $ y $ S $ se utilizan para crear nuevos valores (dependientes de la clave) y luego se reemplazan por esos. Un punto crítico de Blowfish siempre fue que su programa clave es relativamente lento (casi tanto como cifrar 4 KB de datos, ya que en realidad ciframos la cadena cero con los valores actuales de $ P $ / $ S $ para crear los nuevos, y el tamaño de $ S $ y $ P $ juntos es de solo 4 KB).
Bcrypt lleva esta lentitud a un nuevo nivel, utilizando una programación clave costosa en lugar de la programación clave predeterminada de blowfish, que toma un tiempo (configurable) mucho más largo.
Esto esencialmente (después de una ronda inicial más complicada que incluye tanto salt como contraseña) ejecuta el programa de claves normal $ 2 ^ {\ operatorname {cost}} $ veces con cada uno de salt y contraseña como entrada, donde $ \ operatorname {cost} $ es el parámetro de costo (el documento bcrypt original propuesto $ \ operatorname {cost} = 6 $ para cuentas de usuario normales, $ \ operatorname {cost} = 8 $ para la cuenta de administrador). Por lo tanto, esto es (tiempo-) equivalente a encriptar $ 8 · 2 ^ {\ operatorname {cost}} + 4 $ KB de datos con un pez globo normal.
Luego, una cadena constante ( OrpheanBeholderScryDoubt
) se encripta (64 veces) con el cifrado de OrpheanBeholderScryDoubt
normal utilizando las cajas $ P $ y $ S $ así creadas como estado, produciendo una cadena de 192 bits, que luego se considera la salida bcrypt (que es normalmente almacenado junto con la sal y el parámetro de costo).
Como podemos ver, los valores realmente usados de $ P $ y $ S $ son muy no constantes , dependiendo en gran medida de la entrada de sal y contraseña. La fijación de los valores iniciales no es una vulnerabilidad, pero es necesario para obtener una salida idéntica para una entrada idéntica en cada implementación.
Viniendo de los dígitos de $ \ pi $, también es muy probable que no contengan una puerta trasera creada por los diseñadores del algoritmo (un número de nada en la manga ).
Además, para un algoritmo de hash de contraseñas, un ataque de colisión no es el ataque más importante: un atacante que encuentra dos contraseñas que dan (para una sal específica o un par de sales) el mismo hash, no tiene ninguna ventaja para encontrar las contraseñas de los hash existentes ( que también es muy probable que estén usando diferentes sales).