2 puntos por GN⁺ 2024-05-08 | 1 comentarios | Compartir por WhatsApp
  • Go 1.22 cambia la fuente predeterminada de números aleatorios en math/rand y math/rand/v2 por un generador criptográficamente fuerte, reduciendo mucho el impacto de errores cuando se usa math/rand donde debía usarse crypto/rand
  • El generador anterior de Go 1 era un registro de desplazamiento con retroalimentación lineal que usaba un estado de 607 uint64, por lo que observando solo 607 salidas se podían reconstruir valores pasados y futuros
  • El PCG-DXSM de math/rand/v2 mejoró la calidad estadística y el tamaño del estado, pero no garantiza la imprevisibilidad necesaria para valores secretos
  • El nuevo ChaCha8Rand usa una semilla de 32 bytes, vuelve a derivar la clave cada 16 bloques y mantiene 300 bytes de estado por núcleo; se aplica en math/rand/v2, parte de math/rand y en las semillas hash de los mapas
  • El costo en rendimiento es limitado: ChaCha8Rand es más lento que el generador de Go 1, pero no supera el doble, y en servidores comunes la diferencia no pasa de 3 ns, así que en la mayoría de los programas la ganancia de seguridad pesa más

El valor predeterminado de aleatoriedad que cambió en Go 1.22

  • Go 1.22 cambió el valor predeterminado para que math/rand y math/rand/v2 usen un generador seudoaleatorio criptográficamente fuerte
  • El objetivo es reducir el daño cuando un desarrollador usa por error math/rand en lugares donde se necesita crypto/rand
  • Las API de aleatoriedad de Go tradicionalmente se dividen en dos categorías
    • math/rand: aleatoriedad estadística para simulación, muestreo, análisis numérico, algoritmos aleatorios no criptográficos, fuzzing, shuffle y backoff exponencial
    • crypto/rand: aleatoriedad criptográfica para claves, tokens y otros casos donde se necesita imprevisibilidad

Por qué la aleatoriedad estadística no basta

  • Un generador estadístico puede ser suficiente para muchos usos no criptográficos si pasa pruebas estadísticas básicas
  • Sin embargo, si un observador conoce el algoritmo y ve suficientes salidas, a menudo puede predecir la secuencia posterior
  • srand y rand de Unix V3 fueron una forma temprana que luego influyó en las API de aleatoriedad de C y de varios otros lenguajes
    • Configuraban el estado con una sola semilla entera
    • Calculaban el siguiente valor con un generador congruencial lineal (LCG)
    • Como el estado interno era simple, con una sola salida era fácil calcular valores futuros
  • En un LCG se pueden elegir constantes para recorrer cada valor posible una vez antes de repetir, pero tienen la debilidad de que los bits bajos se repiten con ciclos cortos

Estructura y debilidades del generador de Go 1

  • El generador math/rand de Go 1 pertenece a la familia de registros de desplazamiento con retroalimentación lineal
  • Su estado interno es un slice vec con 607 uint64
    • vec[606] es el “tap”
    • vec[334] es el “feed”
    • Para producir el siguiente valor, suma tap y feed para crear x, guarda x en la posición feed y luego lo devuelve
  • La implementación real no mueve todo el slice, sino que retrocede solo las posiciones tap y feed para reducir el costo
    • Generar el siguiente valor requiere dos restas, dos sumas condicionales, dos cargas, una suma y un almacenamiento
  • Como el valor devuelto es un elemento del vector de estado interno, al leer 607 salidas se expone todo el estado
    • Si se rellena el mismo vec y se ejecuta el algoritmo, se pueden predecir valores futuros
    • Si se ejecuta el algoritmo al revés, también se pueden reconstruir valores pasados
  • El generador de Go 1 no fue diseñado para seguridad, y la calidad de los números generados también depende de cómo se inicializa vec

Qué mejoró PCG y qué límites siguen ahí

  • math/rand/v2 adoptó PCG de Melissa O’Neill como un generador estadístico más moderno
  • El PCG de Go se basa en un LCG de 128 bits y usa una función scramble para reducir el estado de 128 bits a una salida de 64 bits
  • Durante la discusión de la propuesta, Go adoptó una variante scramble basada en multiplicación a partir de la propuesta de O’Neill
    • Esa forma se conoce como PCG-DXSM
    • Numpy también usa esta forma de PCG
  • PCG tiene un estado mucho más pequeño que el generador de Go 1
    • Generador de Go 1: 607 uint64
    • PCG: dos uint64
  • PCG es menos sensible al estado inicial y pasa varias pruebas estadísticas, pero no garantiza la imprevisibilidad
    • PCG-XSL-RR puede invertirse
    • Tampoco sorprendería que PCG-DXSM pudiera invertirse
  • Para generar valores secretos, hace falta otro tipo de generador y no PCG

Aleatoriedad criptográfica y el papel del sistema operativo

  • La aleatoriedad criptográfica debe seguir siendo prácticamente impredecible incluso para un observador que conoce el método de generación y ha visto muchas salidas previas
  • Los protocolos criptográficos, las claves secretas, el comercio moderno y la privacidad en línea dependen de ella
  • En la práctica, el suministro de aleatoriedad lo gestiona el sistema operativo
    • Recolecta aleatoriedad de dispositivos físicos como mouse, teclado, disco y tiempos de red
    • Más recientemente, también aprovecha ruido eléctrico medido directamente por la CPU
  • Cuando el sistema operativo reúne suficiente aleatoriedad, por ejemplo al menos 256 bits, genera una secuencia larga usando un hash o un algoritmo criptográfico
  • Antes era común usar archivos de dispositivo como /dev/random, pero hoy los sistemas operativos ofrecen llamadas al sistema directas
  • crypto/rand de Go oculta las diferencias entre sistemas operativos y ofrece la misma interfaz, rand.Read

Diseño de ChaCha8Rand

  • El nuevo ChaCha8Rand de Go 1.22 es una forma ligeramente modificada del cifrado de flujo ChaCha de Daniel J. Bernstein
  • ChaCha es ampliamente conocido por su variante ChaCha20, usada también en TLS y SSH
  • Too Much Crypto de Jean-Philippe Aumasson considera seguro también a ChaCha8, la variante de 8 rondas, y ChaCha8 es alrededor de 2.5 veces más rápido
  • ChaCha8Rand usa ChaCha8 como rand.Source, pero en vez de hacer XOR del bloque generado con la entrada, usa directamente el flujo aleatorio
    • Eso equivale a cifrar o descifrar datos compuestos solo por ceros

Cambios específicos en ChaCha8Rand

  • ChaCha8Rand usa una semilla de 32 bytes como clave de ChaCha8
  • ChaCha8 genera bloques de 64 bytes y hace sus cálculos tratando el bloque como 16 uint32
  • Una implementación común puede calcular 4 bloques a la vez con instrucciones SIMD, pero para usarlos como entrada XOR hay que desentrelazar los bloques interleaved
    • ChaCha8Rand define ese mismo bloque interleaved como flujo aleatorio para eliminar el costo de unshuffle
  • En la etapa final de cada bloque de ChaCha8 se suman ciertos valores a cada uint32
    • La mitad son material de clave y la otra mitad son constantes conocidas
    • ChaCha8Rand no vuelve a sumar esas constantes conocidas, eliminando así la mitad de las sumas finales
  • En cada bloque generado número 16, usa los últimos 32 bytes como clave para los siguientes 16 bloques
    • Ese rekeying aporta una forma de seguridad hacia adelante
    • Incluso si se expone todo el estado del generador en memoria, solo se pueden recuperar valores desde el último rekeying, no los anteriores
  • Go publicó la especificación C2SP de ChaCha8Rand y casos de prueba para que otras implementaciones con la misma semilla compartan la misma reproducibilidad que la implementación de Go

Dónde se aplica en la biblioteca estándar

  • El runtime de Go mantiene un estado ChaCha8Rand por núcleo sembrado con aleatoriedad criptográfica proporcionada por el sistema operativo
    • El tamaño del estado es de 300 bytes por núcleo
    • En un sistema de 16 núcleos, queda en un nivel similar a los 4,872 bytes del estado compartido único del generador de Go 1
    • Gracias al estado por núcleo, se pueden generar números aleatorios rápido y sin contención por locks
  • Las funciones de paquete de math/rand/v2 siempre usan ChaCha8Rand
    • Por ejemplo: rand.N, rand.Float64
  • Las funciones de paquete de math/rand usan ChaCha8Rand si no se llamó a rand.Seed
    • Por ejemplo: rand.Intn, rand.Float64
    • Si se llama a rand.Seed, debe volver al generador de Go 1 por compatibilidad
  • El runtime también elige la semilla hash de nuevos mapas con ChaCha8Rand en lugar del generador previo basado en wyrand
    • Si un atacante conoce cierta función hash usada por la implementación de mapas, puede preparar entradas para llevar al mapa a comportamiento cuadrático
    • Usar una semilla por mapa, en lugar de una semilla global, también evita otros comportamientos degenerados
    • No está del todo claro si las semillas de mapa necesitan obligatoriamente aleatoriedad criptográfica, pero el cambio fue simple y prudente
  • Si se necesita una instancia separada de ChaCha8Rand, se puede crear directamente rand.ChaCha8

Reducir el impacto de errores de seguridad

  • Go busca reducir o eliminar errores comunes con impacto en seguridad para ayudar a escribir código seguro por defecto
  • Cuando math/rand.Read quedó deprecated en Go 1.20, se descubrió que algunos desarrolladores lo usaban en lugares donde hacía falta crypto/rand, como la generación de material de clave
  • En Go 1.20, ese error podía convertirse en un problema de seguridad grave
    • Había que investigar para qué se usó la clave
    • Cómo pudo haberse filtrado
    • Y si otras salidas aleatorias le dieron al atacante pistas para derivarla
  • En Go 1.22, el mismo error sigue siendo un error, pero es menos probable que termine en un desastre de seguridad
  • Aun así, para valores secretos sigue siendo mejor usar crypto/rand
    • El kernel del sistema operativo puede proteger mejor los valores aleatorios
    • El kernel sigue agregando entropía nueva al generador
    • Y su implementación ha recibido más revisión

Casos que no parecen criptográficos

  • Generar UUID aleatorios puede parecer un caso donde math/rand basta, porque un UUID no es un valor secreto
  • Pero si math/rand se siembra con la hora actual, distintas computadoras ejecutándose en el mismo instante pueden producir el mismo valor
    • Eso es todavía más probable en sistemas donde la hora actual solo tiene precisión de milisegundos
  • Incluso con el sembrado automático basado en entropía del sistema operativo introducido en Go 1.20, la semilla del generador de Go 1 sigue siendo solo un entero de 63 bits
    • Un programa que genera un UUID al arrancar tiene solo 2⁶³ posibles primeros UUID
    • Después de unos 2³¹ UUID aparece la posibilidad de colisión
  • ChaCha8Rand en Go 1.22 se siembra con 256 bits de entropía
    • Hay 2²⁵⁶ posibles primeros UUID
    • No hace falta preocuparse por colisiones
  • El balanceo de carga donde un servidor frontend asigna solicitudes aleatoriamente a servidores backend también puede necesitar aleatoriedad impredecible
    • Si un atacante observa la asignación y conoce un algoritmo predecible, puede concentrar solicitudes costosas en un backend específico
    • Con el generador de Go 1 era raro pero posible
    • En Go 1.22 ya no es un problema

Características de rendimiento

  • Las ventajas de seguridad de ChaCha8Rand tienen un costo pequeño, pero su rendimiento sigue estando en la misma categoría que el generador de Go 1 y PCG
  • Se comparan dos operaciones
    • Uint64: devuelve el siguiente uint64 del flujo aleatorio
    • N(1000): devuelve un número aleatorio en el rango [0, 1000)
  • En chips x86 de 64 bits, si se compila con GOARCH=386 y se ejecuta en modo de 32 bits, PCG puede ser más lento que ChaCha8Rand debido a su multiplicación de 128 bits
    • ChaCha8Rand usa aritmética SIMD de 32 bits
  • En algunos sistemas, Go 1: Uint64 es más rápido que PCG: Uint64, pero Go 1: N(1000) es más lento que PCG: N(1000)
    • N(1000) en Go 1 usa dos divisiones enteras de 64 bits para reducir el rango
    • N(1000) en PCG y ChaCha8 usa el algoritmo más rápido de math/rand/v2, que evita divisiones la mayor parte del tiempo
  • En general, ChaCha8Rand es más lento que el generador de Go 1, pero no llega a ser más de 2 veces más lento
  • En servidores típicos, la diferencia no supera los 3 ns, y son muy pocos los programas donde eso llegue a ser un cuello de botella

Conclusión

  • Go 1.22 mejora la seguridad de los programas sin requerir cambios de código
  • La idea central es reforzar math/rand para reducir el problema común de usarlo por error en lugar de crypto/rand
  • También existen casos como el paquete npm keypair, que intenta generar pares de claves RSA con Math.random de JavaScript cuando no está disponible la Web Crypto API
  • La seguridad de un sistema no puede depender de asumir que los desarrolladores nunca se equivocan
  • ChaCha8Rand de Go 1.22 muestra que usar un generador seudoaleatorio criptográficamente fuerte incluso para aleatoriedad “matemática” puede ofrecer un rendimiento competitivo frente a otros generadores

1 comentarios

 
GN⁺ 2024-05-08
Opiniones de Hacker News
  • Tal como se menciona en el artículo, en rclone cometimos exactamente este error.
    Al refactorizar código que usaba Read de crypto/rand, el import cambió automáticamente y, probablemente al mezclarse con código que usaba math/rand, parece que goimports lo cambió a math/rand.
    Como resultado, en lugar de un generador de números aleatorios seguro, terminamos usando un generador determinista que rclone sembraba con la hora, y no lo notamos en el diff :-(
    https://www.cvedetails.com/cve/CVE-2020-28924/
    Así que estoy muy a favor de este cambio.

    • Duele leerlo, lo siento. En 2016 se cambió goimports para que prefiriera crypto/rand, así que no está claro qué pasó durante la refactorización.
      Tal vez en el mismo archivo entró código que usaba APIs exclusivas de math/rand.
      https://go-review.googlesource.com/24847
      En cualquier caso, me alegra que se esté ordenando este aspecto.
    • A mí también me reportaron una vulnerabilidad porque creyeron que se estaba usando math/rand. En realidad no era así; solo habían confundido varios archivos, así que no fue grave, pero muestra lo confuso que es todo esto.
      Con text/template y html/template pasa algo parecido. Viéndolo en retrospectiva, este tipo de ocultamiento por nombres de paquetes fue una mala idea.
    • También vi que, si buscas "secure password generation golang", casi todos los ejemplos usan math/rand.
      Peor aún: todos reinicializan la semilla con la hora actual justo antes de generar la contraseña.
      Lo descubrí después de encontrar que alguien había usado math/rand en nuestro código y ponerme a investigar de dónde lo había copiado.
    • goimports trató de forma especial a math/rand.Read y crypto/rand.Read casi desde el principio.
      Sin embargo, en https://github.com/golang/tools/commit/0835c735343e0d8e375f0... de 2016 se menciona una época en la que "rand.Read" podía interpretarse como "math/rand".
      Quizá les tocó justo en ese período.
    • No parecería tan difícil ofrecer una llamada de API con un nombre como "PredictableRand".
  • La semana pasada spacey también lo publicó en https://news.ycombinator.com/item?id=40237491, pero parece que ese post quedó enterrado por error como duplicado de https://news.ycombinator.com/item?id=40224864.
    Los dos posts del blog de go.dev son dos entregas de la misma serie, pero son bastante distintos. Este artículo trata sobre algoritmos eficientes de generación de números aleatorios seguros, mientras que el anterior trata sobre diseño de APIs en Go.

  • Russell Cox publica constantemente excelentes blogs técnicos, propuestas y trabajos.
    Si quieres mejorar la claridad de tu escritura y de tu pensamiento, empezar por Russell Cox es un muy buen punto de partida.

    • Su serie sobre autómatas de estados finitos y expresiones regulares hizo que me metiera de lleno en ese campo.
      En ese momento ni siquiera sabía quién era Russ Cox, pero esa serie de artículos era realmente extraordinaria.
      Probablemente sea el material gratuito de mayor calidad sobre implementación de expresiones regulares; lo siguiente serían varios libros centrados en compiladores, pero no son gratis ni fáciles de encontrar buscando en la web.
    • También hace buenas demos en video: https://research.swtch.com/acme
  • Me pasó usar math/rand donde era absolutamente necesario crypto/rand.
    Como resultado, en una versión temprana de dnscrypt-proxy2 se usaron claves estáticas.
    La causa fue una extensión de VSCode que agregaba imports automáticamente. En todos los archivos fuente que necesitaban aleatoriedad segura importé crypto/rand directamente y con cuidado, pero se me pasó un archivo; todo compilaba y funcionaba bien, y no noté que la extensión había agregado silenciosamente el import de math/rand en ese archivo específico.
    Desde entonces importo crypto/rand con el alias cryptorand para evitar que se autoimporte el rand equivocado.
    Como referencia, Zig también usa un generador de números aleatorios basado en ChaCha8, y en operaciones criptográficas el usuario no puede proporcionar su propio generador: siempre se usa uno seguro. Para pruebas, algunas funciones reciben una semilla explícita.
    Para entornos restringidos, la biblioteca estándar también incluye un generador más pequeño basado en la permutación Ascon y la construcción Reverie.

    • No sé exactamente qué pasó en tu caso, pero es muy probable que haya sido distinto de lo que describes.
      En 2016 goimports se cambió para preferir crypto/rand sobre math/rand (https://go-review.googlesource.com/24847), y eso fue antes de que existiera el soporte de VSCode para Go.
    • Otra persona dijo lo mismo. Sinceramente, la práctica de agregar imports automáticamente me parece completamente extraña y socava el propósito mismo de separar los nombres en distintos espacios de nombres.
  • Incluso en la década de 2020, a menudo me preguntaba por qué las implementaciones predeterminadas de números aleatorios en varios lenguajes de programación usan generadores rápidos como LFSR o MT.
    Me parece mejor asumir de forma conservadora que la gente no sabe si necesita un generador de números pseudoaleatorios o un generador de números pseudoaleatorios criptográficamente seguro, cambiar el valor predeterminado a este último y hacer que solo quienes necesiten el primero lo elijan explícitamente.

    • La nueva API orientada a objetos de números aleatorios de PHP 8.2 hizo exactamente eso.
      Si el desarrollador no elige explícitamente el motor de números aleatorios que va a usar, recibe un generador criptográficamente seguro.
      Ahora la parte difícil es convencer a la gente de migrar a la nueva API. Más aún, ni siquiera es fácil pasar de mt_rand(), que usa una instancia global de Mt19937, a random_int() basado en CSPRNG, que ya está disponible desde PHP 7.0.
      [1] https://www.php.net/releases/8.2/en.php#random_extension
    • Hace poco empecé a usar una nueva biblioteca de Go que genera ID aleatorios para varios componentes de una estructura de datos compleja.
      En mi caso de uso había decenas de miles de componentes y, al perfilarlo, una parte considerable del tiempo de inicialización de la estructura de datos se iba en Read() de crypto/rand, que en mi MacBook estaba ejecutando llamadas al sistema.
      Al parchear la biblioteca para que usara Read() de math/rand, el rendimiento mejoró mucho.
      Además de que math/rand era más rápido, me preocupaba agotar sin una buena razón el pool de entropía del sistema. En este caso, la única razón por la que los ID debían ser aleatorios era poder agregar más componentes más adelante después de serializar/deserializar la estructura de datos, y yo no tenía intención de hacer eso.
      No sé exactamente cómo encaja el momento del cambio descrito en este blog con mi experiencia. Quizás yo usaba una versión antigua de la biblioteca y, si ahora crypto/rand en la práctica ya casi no se distingue de math/rand, pues me parece bien :-)
    • Uno de los mejores argumentos a favor de usar un CSPRNG, en este caso ChaCha8, es que en los benchmarks queda dentro de un factor de 2 frente a PCG.
      El tamaño del estado sigue siendo relativamente grande (64 bytes contra 16 bytes), pero es mucho mejor que mt19937 o que los PRNG antiguos de Go.
      Si un CSPRNG fuera mucho más lento, como suele pasar con los CSPRNG normales que no son variantes de ChaCha con rondas reducidas, sería menos atractivo como valor predeterminado.
    • ¿En qué otros casos se necesitaría el primero? Solo se me ocurre una semilla fija cuando se necesitan resultados reproducibles, por ejemplo en pruebas o verificación.
      Hay otro pequeño factor que empuja a la gente hacia los PRNG aunque no necesite una semilla. Las API de CSPRNG incluyen errores que siempre hay que manejar ante fallas de llamadas al sistema o falta de entropía.
      ¿Con qué frecuencia falla realmente una lectura de crypto/rand? ¿Cuánto habría que leer para agotar la entropía en sistemas modernos? Nunca he visto una falla ni con miles de millones de solicitudes, y dd también funciona bien.
      Me pregunto si para la mayoría de los casos de uso una API estilo Must/panic sería el valor predeterminado adecuado.
      Además, revisé el paquete secrets de Python (https://docs.python.org/3/library/secrets.html) y no menciona en absoluto que pueda lanzar excepciones. ¿En la práctica simplemente no ocurre?
    • Me gusta el enfoque de que “todos los números aleatorios del sistema deberían venir de un CSPRNG salvo que se opte explícitamente por salir”.
      A cambio de perder un poco de rendimiento, se obtiene una garantía mucho más fuerte de no provocar un desastre por usar el generador de números aleatorios equivocado.
      Es una lástima que en casi todos los lenguajes los desarrolladores todavía tengan que preocuparse por este borde filoso.
  • Para quienes no lo sepan, gosec y su extensión golangci-lint advierten sobre el uso de math/rand.
    https://github.com/securego/gosec/blob/d3b2359ae29fe344f4df5...

    • Una de las cosas que más me gustan de math/rand/v2 es que en la empresa puedo usarlo sin directivas nolint ni la discusión posterior en el PR.
  • Todavía estoy interpretando las recomendaciones sobre seguridad y las nuevas opciones de v2.
    El post del blog usa frases como “para valores secretos se necesita otra cosa” y luego entra en detalle sobre aleatoriedad criptográfica, ChaCha8 y cómo se inicializa con números aleatorios del sistema, así que da una impresión muy “segura”.
    Pero la documentación del paquete dice esto:
    ... but it should not be used for security-sensitive work ... This package's outputs might be easily predictable regardless of how it's seeded. For random numbers suitable for security-sensitive work, see the crypto/rand package.
    Entonces, ¿por qué el post del blog parece insinuar que math/rand/v2 se usa para “valores secretos”?
    En resumen, ¿significa que todo lo sensible todavía debe usar crypto/rand, y que las mejoras descritas aquí son una red de seguridad para cuando alguien use math/rand/v2 de forma inapropiada?

    • Sí. math/rand/v2 no es lo óptimo, pero usarlo por error donde debería usarse crypto/rand ya no se convierte de inmediato en una falla de seguridad crítica.
      El artículo también dice esto:
      Sigue siendo mejor usar crypto/rand. El kernel del sistema operativo puede hacer un mejor trabajo manteniendo en secreto los valores aleatorios frente a varios ataques de observación; el kernel sigue agregando nueva entropía al generador y, además, ha recibido más revisión. Pero usar math/rand por accidente ya no es un desastre de seguridad.
  • Incluso en el peor benchmark, la nueva estrategia fue apenas alrededor de la mitad más lenta que un generador de números aleatorios no seguro, y en la mayoría de los benchmarks la diferencia fue mucho menor
    Go logra un buen equilibrio entre seguridad y rendimiento para la biblioteca estándar y las apps construidas sobre ella. Ojalá otros ecosistemas también lo sigan
    Si una aplicación necesita números aleatorios rápidos y no seguros, debe implementar su propio generador interno
    Tener números aleatorios no seguros al alcance de la mano es una herramienta para dispararse en el pie que se puede guardar

    • Sinceramente, esto se ve peor
      Incentivar a la gente a asumir que una primitiva "random" es criptográficamente segura fomenta malas prácticas
      Hacer que math/rand/v2 sea criptográficamente seguro puede resolver un problema, pero ahora algo que no parece prometer seguridad queda en un estado de “está bien”
      En general, no existe la convención de que las funciones de math/rand sean criptográficamente seguras. Si cambiamos eso para que el código malo funcione correctamente por accidente, puede ocultar qué otros errores estamos cometiendo si estamos cayendo en equivocaciones tan evidentes como esta
  • El math/rand de Go 1 sería más preciso llamarlo generador aditivo de Fibonacci con retardo
    La primera presentación fue el paper de Green, Smith y Klem
    [1] https://doi.org/10.1145/320998.321006

    • En ese paper no parece mencionarse la parte de “retardo”. O quizá se me pasó
      También conozco https://www.leviathansecurity.com/blog/attacking-gos-lagged-..., y ahí también lo llaman generador de Fibonacci con retardo
      Hace unos meses, Rob Pike y yo intercambiamos correos con Don Mitchell, quien escribió la versión original en C del generador de Go 1, y le preguntamos cómo describiría el algoritmo; respondió: “según recuerdo, Jim y yo implementamos un generador similar al LFSR de Marsaglia”
      Creo que ambas descripciones, generador de Fibonacci con retardo y generador similar a LFSR, son correctas desde perspectivas distintas. Cualquiera de las dos está bien, pero en el artículo decidimos usar la descripción del autor original
  • Si tuviera que señalar un detalle menor, aquí parece que se mezclan aleatoriedad estadística y generador de números pseudoaleatorios
    La definición de aleatoriedad estadística en la wiki es: “se dice que una secuencia numérica es estadísticamente aleatoria cuando no contiene patrones ni regularidades reconocibles”
    ¿Esa definición aplica también a un generador de números verdaderamente aleatorios (TRNG)? Deberíamos esperar que sí. Al menos a largo plazo o en el límite debería ser así. Si no, no sería un TRNG
    Un TRNG debe generar, a largo plazo, una “secuencia numérica sin patrones ni regularidades reconocibles”
    Por lo tanto, se puede decir que la aleatoriedad estadística no significa PRNG, pero también puede aplicarse a un TRNG
    El problema parece venir de que existen muchas pruebas de aleatoriedad estadística para verificar si un PRNG tiene una forma limitada de aleatoriedad estadística
    Así que, para identificar un PRNG, creo que habría sido más adecuado usar “generador de números pseudoaleatorios” en lugar de “aleatoriedad estadística”. Aun así, es un detalle muy menor