2 puntos por GN⁺ 2024-05-08 | 1 comentarios | Compartir por WhatsApp
  • 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

 
GN⁺ 2024-05-08
Opinión de Hacker News

En resumen, esto es lo que dice:

  • En Go 1.20, la función Read del paquete math/rand quedó obsoleta (deprecated), y se detectaron casos en los que se usó incorrectamente en lugar de crypto/rand. Esto lleva al error de usar un generador determinista de números aleatorios vulnerable desde el punto de vista de seguridad.
  • Cambiar el generador aleatorio predeterminado de Go por un CSPRNG (generador seudoaleatorio criptográficamente seguro) es un mejor enfoque para la seguridad. Lo ideal es que el PRNG se elija solo cuando se necesite explícitamente.
  • Herramientas de análisis estático como gosec o golangci-lint emiten advertencias sobre el uso de math/rand.
  • El paquete math/rand/v2 usa 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 usar crypto/rand.
  • math/rand de Go 1 puede considerarse, más precisamente, un additive lagged Fibonacci generator.
  • El nuevo 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.
  • Se valora como un enfoque amigable para desarrolladores que evita errores como los de java.util.Random de Java.
  • También se plantea la duda de por qué se usa ChaCha8 y no un cifrado de bloque con aceleración por hardware, como AES-GCM.