El código fuente completo de JDK (no solo las clases estándar, sino también las clases específicas de Sun y el código nativo de C ++) solía estar disponible bajo una "licencia de investigación", y tengo una copia en mi disco, para JDK 1.6 (Supongo que hoy en día irías a OpenJDK).
Vemos que crear una instancia java.security.SecureRandom
implica invocar el PRNG predeterminado, que es la clase específica de Sun sun.security.provider.SecureRandom
.
El PRNG se siembra a partir de sun.security.provider.SeedGenerator
, que a su vez intentará basarse en lo que proporciona el sistema operativo, es decir, /dev/urandom
o /dev/random
en sistemas similares a Unix (como Linux), CryptoAPI's CryptGenRandom()
en Windows. Si tales cosas no están disponibles (o están desactivadas desde la configuración de JVM), entonces la semilla se extraerá cronometrando la velocidad a la que el sistema operativo puede cambiar de contexto entre hilos; este es un mecanismo alternativo que rara vez, si alguna vez, se activa.
La semilla se amplía con lo que parece ser un PRNG personalizado y hecho en casa basado en SHA-1. Funciona así:
- Hay un estado de 160 bits s , que tiene 20 bytes, y también se interpreta como un módulo entero 2160 aplicando la convención little-endian.
- La salida es producida por trozos de 20 bytes.
- Para producir el siguiente fragmento c :
-
c = SHA-1 ( s )
-
s = s + c + 1 mod 2160
Hay un mecanismo que garantiza que s se cambie en algún momento; Si s parece no haber cambiado con la actualización, entonces su primer byte se incrementa con fuerza. Esto nunca se invoca; la probabilidad de tal ocurrencia es 2-160 y forzarla con una semilla especial requeriría romper la resistencia de preimagen de SHA-1, lo cual no sabemos cómo hacerlo.
Hablando criptográficamente, esto no es atroz; debería alcanzar un ciclo, en promedio, solo después de aproximadamente 280 trozos, y el ciclo debe tener la misma longitud promedio; eso es mucho (unos 24 millones de billones de gigabytes), así que eso es bueno. Si SHA-1 es un oracle aleatorio entonces el PRNG es" obviamente "seguro siempre que los atacantes no puedan enumerar un espacio de 160 bits (en realidad no pueden) y una semilla dada no se usa para producir más de 24 millones de billones de gigabytes ( y no lo hará, ni en un tiempo razonable o incluso irrazonable). Sabemos que SHA-1 es no un oráculo aleatorio, pero aún parece lo suficientemente aleatorio.
Personalmente, no consideraría que este PRNG sea un problema para el uso criptográfico; no tiene una "constante mágica" y, por lo tanto, es muy poco probable que sea de puerta trasera. La única parte mala de esto es que no es no estándar , por lo tanto no está estudiado.
Si (cuando) necesito tener, en Java, una aleatoriedad que sea lo suficientemente buena para la criptografía formal (es decir, buena, y también demostrablemente buena para fines reglamentarios), luego uso java.security.SecureRandom
para generar una inicialización inicial (al menos 16 bytes), que luego ejecuto a través de HMAC_DRBG (especificado en NIST SP800-90A ) (la misma publicación contiene el Dual_EC_DRBG de la fama siniestra, pero HMAC_DRBG todavía es considerado bueno y limpio por los criptógrafos).