- En Go 1.22 se cambió el comportamiento para que tanto el paquete existente
math/rand como el nuevo paquete math/rand/v2 usen un generador de números aleatorios criptográficamente seguro. Con esto se ofrece una mejor aleatoriedad y se reduce mucho el daño que puede ocurrir cuando un desarrollador usa math/rand por error en lugar de crypto/rand.
Diferencia entre aleatoriedad estadística y aleatoriedad criptográfica
- La aleatoriedad estadística es adecuada para simulaciones, muestreo, análisis numérico, algoritmos aleatorios no criptográficos, pruebas aleatorias, mezcla de entradas, retroceso exponencial aleatorio, etc.
- Incluso fórmulas matemáticas muy básicas y fáciles de calcular funcionan suficientemente bien para esos usos. Sin embargo, un observador que conozca el algoritmo utilizado puede predecir la secuencia futura después de ver cierta cantidad de valores.
- La aleatoriedad criptográfica debe ser, en la práctica, completamente impredecible aunque se hayan observado valores generados previamente.
- Los protocolos criptográficos seguros, las claves secretas, el comercio moderno y la privacidad en línea dependen en gran medida de la aleatoriedad criptográfica.
El generador math/rand de Go 1
- Usa el método Linear-feedback shift register (LFSR).
- Tiene el problema de que su estado interno queda completamente expuesto como un vector de 607 valores
uint64.
- Si se leen 607 valores del generador, todo el estado queda expuesto y es posible predecir los valores posteriores.
El generador PCG de math/rand/v2
- Usa el algoritmo PCG de Melissa O'Neill. Es un LCG de 128 bits con posprocesamiento.
- Todo el estado es un único número de 128 bits, y la actualización se hace con multiplicación y suma de 128 bits.
- En Go, siguiendo la propuesta de O'Neill, se usa una función de mezcla basada en multiplicación en lugar de una basada en XOR, para mezclar los bits de forma más agresiva.
- Requiere más cómputo que el generador de Go 1, pero necesita mucha menos memoria para guardar el estado, es menos sensible al valor del estado inicial y además supera pruebas estadísticas que otros generadores no pasan.
- Aun así, PCG sigue sin ser impredecible.
Aleatoriedad criptográfica
- En última instancia, el sistema operativo debe recolectar aleatoriedad real a partir del ruido de dispositivos físicos.
- Una vez que se recolecta suficiente aleatoriedad (256 bits o más), se puede expandir con un hash criptográfico o un algoritmo de cifrado para producir una secuencia aleatoria de longitud arbitraria.
- El paquete
crypto/rand de Go abstrae estas diferencias entre interfaces del sistema operativo y ofrece la misma interfaz rand.Read.
El generador ChaCha8Rand
- Es un generador nuevo creado a partir de una variante del cifrador de flujo ChaCha de DJB.
- Usa ChaCha8, la versión de 8 rondas. Es 2.5 veces más rápido que ChaCha20 y sigue siendo seguro.
- Usa una semilla de 32 bytes como clave de ChaCha8. Cada 16 bloques, usa los últimos 32 bytes del bloque generado como clave para los siguientes 16 bloques, ofreciendo secreto hacia adelante.
rand.Float64, rand.N y otras funciones de math/rand/v2 usan siempre este generador.
math/rand también usa este generador. Sin embargo, si se llama a rand.Seed, se usa el generador de Go 1.
- El runtime también usa ChaCha8Rand para elegir la semilla hash de los mapas nuevos.
Corrección de errores de seguridad
- Go 1.22 fortalece
math/rand, haciendo los programas más seguros sin necesidad de cambios en el código.
- Por ejemplo, si alguien usó por error
Read de math/rand para generar claves, en Go 1.20 eso era un problema de seguridad grave, pero en Go 1.22 pasa a ser solo un error.
- Incluso en usos que no parecen “criptográficos”, como generar UUID o distribuir carga entre servidores frontend, usar ChaCha8Rand lo hace mucho más robusto que el generador de Go 1.
Rendimiento
- ChaCha8Rand ofrece un rendimiento similar al generador de Go 1 y a PCG.
- En código de 32 bits, ChaCha8Rand es más rápido que PCG, que necesita multiplicación de 128 bits.
- Gracias a los algoritmos de
math/rand/v2 que evitan la división de 64 bits, en operaciones N(1000) ChaCha8Rand o PCG a veces son más rápidos que el generador de Go 1.
- En general, ChaCha8Rand es más lento que el generador de Go 1, pero nunca llega a ser más de dos veces más lento, y en servidores comunes la diferencia no supera los 3 ns.
Opinión de GN⁺
- La adopción de ChaCha8Rand en Go 1.22 puede considerarse un caso ejemplar de mejora a nivel de lenguaje que eleva mucho la seguridad con una pérdida mínima de rendimiento. Resulta especialmente llamativo que bloquee desde el propio lenguaje errores que los desarrolladores cometen con frecuencia.
- Como también se menciona en el texto, este tipo de error no se limita a Go, sino que aparece con frecuencia en otros lenguajes. La seguridad del sistema no debería depender de errores del desarrollador, así que otros lenguajes también deberían avanzar, como Go, hacia el uso de generadores seudoaleatorios criptográficamente fuertes incluso para números aleatorios “matemáticos”.
- Sin embargo, ChaCha8Rand no es apropiado para primitivas criptográficas como
crypto_box o xchacha20poly1305. Para esos usos, todavía se debe usar crypto/rand directamente.
- Fue algo inesperado que el runtime de Go también pasara a usar ChaCha8Rand para elegir la semilla hash de los mapas. No está del todo claro si una semilla hash necesita obligatoriamente números aleatorios criptográficos, pero se nota la conciencia de seguridad del equipo de desarrollo al intentar bloquear de raíz posibles ataques molestos.
- Ahora que la calidad de
math/rand, uno de los paquetes básicos del lenguaje, ha mejorado tanto, parece probable que en adelante también se use más directamente en aplicaciones. Los proyectos que hasta ahora recurrían a bibliotecas separadas de generación aleatoria por la previsibilidad de math/rand podrían beneficiarse de este cambio.
1 comentarios
Opinión de Hacker News
En resumen, esto es lo que dice:
Readdel paquetemath/randquedó obsoleta (deprecated), y se detectaron casos en los que se usó incorrectamente en lugar decrypto/rand. Esto lleva al error de usar un generador determinista de números aleatorios vulnerable desde el punto de vista de seguridad.gosecogolangci-lintemiten advertencias sobre el uso demath/rand.math/rand/v2usa el cifrado ChaCha8 y se inicializa con entropía del sistema, lo que da una impresión de ser "seguro", pero sigue siendo inadecuado para tareas sensibles a la seguridad. Para eso se debe usarcrypto/rand.math/randde Go 1 puede considerarse, más precisamente, un additive lagged Fibonacci generator.math/rand, incluso en el peor de los casos, tiene aproximadamente la mitad de la velocidad del generador aleatorio no seguro anterior, y en la mayoría de los benchmarks casi no hubo diferencia. Go está logrando un equilibrio adecuado entre seguridad y rendimiento en la biblioteca estándar.java.util.Randomde Java.