¿Cuál es el mejor método para obtener un número aleatorio verdadero (en lugar de pseudo) en Arduino, o al menos la mejor aproximación posible? Según tengo entendido, la función randomSeed (analogRead (x)) no es lo suficientemente aleatoria.
Si es posible, el método debería aprovechar la configuración básica de Arduino solo (sin sensores adicionales). Las soluciones con sensores externos son bienvenidas si mejoran significativamente la aleatoriedad sobre la configuración básica.
programming
Rexcirus
fuente
fuente
Respuestas:
La biblioteca Entropy usa:
Me gusta esta solución porque no usa ningún pin de su microcontrolador y no requiere ningún circuito externo. Esto también lo hace menos sujeto a fallas externas.
Además de una biblioteca, también proporcionan un bosquejo que demuestra el uso de la misma técnica utilizada para generar una semilla aleatoria para el PRNG del microcontrolador sin la biblioteca: https://sites.google.com/site/astudyofentropy/project-definition / timer-jitter-entropy-sources / entropy-library / arduino-random-seed
fuente
randomSeed(analogRead(x))
solo producirá 255 secuencias de números, lo que hace que sea trivial probar todos los combos y producir un oráculo que se puede acoplar a su flujo de salida, prediciendo toda la salida al 100%. Sin embargo, estás en el camino correcto, es solo un juego de números y necesitas MUCHOS más de ellos. Por ejemplo, tomar 100 lecturas analógicas de 4 ADC, resumirlas todas y alimentarlasrandomSeed
sería mucho mejor. Para una seguridad máxima, necesita una entrada impredecible y una mezcla no determinista.No soy un criptógrafo, pero he pasado miles de horas investigando y construyendo generadores aleatorios de hardware y software, así que permítanme compartir algo de lo que he aprendido:
Entrada impredecible:
Entrada potencialmente impredecible:
Entrada externa impredecible:
RANDOM_REG32
rápido e impredecible, una paradarecolectar Lo último que quieres hacer es escupir entropía tal como viene. Es más fácil adivinar un lanzamiento de moneda que un cubo de monedas. Sumar es bueno.
unsigned long bank;
entonces más tardebank+= thisSample;
es bueno; se volcará.bank[32]
es aún mejor, sigue leyendo. Desea recopilar al menos 8 muestras de entrada para cada porción de salida, idealmente mucho más.Protección contra el envenenamiento Si calentar el tablero causa una cierta fluctuación máxima de reloj, es un vector de ataque. Lo mismo con la voladura de RFI hacia las entradas analogRead (). Otro ataque común simplemente desconecta la unidad y por lo tanto descarga toda la entropía acumulada. No debe generar números hasta que sepa que es seguro hacerlo, incluso a costa de la velocidad.
Es por eso que desea mantener algo de entropía a largo plazo, utilizando EEPROM, SD, etc. Busque en el PRNG de Fortuna , que usa 32 bancos, cada uno actualizado la mitad de veces que el anterior. Eso hace que sea difícil atacar a los 32 bancos en un tiempo razonable.
Procesamiento Una vez que recolecta "entropía", debe limpiarla y separarla de la entrada de una manera difícil de revertir. SHA / 1/256 es bueno para esto. Puede usar SHA1 (o incluso MD5 realmente) para la velocidad ya que no tiene una vulnerabilidad de texto sin formato. Para cosechar, nunca use el banco de entopy completo, y SIEMPRE SIEMPRE agregue una "sal" a la salida que es diferente cada vez para evitar salidas idénticas sin cambios en el banco de entropía:
output = sha1( String(micros()) + String(bank[0]) + [...] );
la función sha oculta las entradas y blanquea la salida, protegiendo contra semillas débiles, bajo acumulado ent, y otros problemas comunes.Para usar entradas de temporizador, debe hacerlas indeterministas. Este es un simple como
delayMicroseconds(lastSample % 255)
; que pausa una cantidad de tiempo impredecible, haciendo que las lecturas de reloj "sucesivas" no sean uniformes en la diferencia. Haga eso de forma regular, comoif(analogRead(A1)>200){...}
, siempre que A1 sea ruidoso o esté conectado a una entrada dinámica. Hacer que cada bifurcación de su flujo sea bastante difícil de determinar evitará el criptoanálisis en la salida descompilada / extraída.La verdadera seguridad es cuando el atacante conoce todo su sistema y aún no puede vencerlo.
Por último, revisa tu trabajo. Ejecute su salida a través de ENT.EXE (también disponible para nix / mac) y vea si es bueno. Lo más importante es la distribución de chi cuadrado, que generalmente debe estar entre 33% y 66%. Si obtienes 1.43% o 99.999% o algo así de nervioso, más de una prueba en una fila, tu azar es una mierda. También desea que los informes ENT de entropía estén lo más cerca posible de 8 bits por byte,> 7.9 con seguridad.
TLDR: La forma más simple de prueba de tontos es con el HWRNG de ESP8266. Es rápido, uniforme e impredecible. Ejecute algo como esto en un ESP8266 que ejecute el núcleo Ardunio, y use serial para hablar con el AVR:
** editar
Aquí hay un boceto de HWRNG sin formato que escribí hace un tiempo, operando no solo como un colector, sino como un CSPRNG completo escupiendo desde el puerto serie. Está diseñado para un pro-mini, pero debe ser fácilmente adaptable a otras placas. Puede usar solo pines analógicos flotantes, pero es mejor agregarles cosas, preferiblemente cosas diferentes. Al igual que los micrófonos, los LDR, los termistores (recortados a la máxima dispersión alrededor de la temperatura ambiente) e incluso cables largos. Funciona bastante bien en ENT si tienes incluso un ruido moderado.
El boceto integra varias nociones que he mencionado en mi respuesta y comentarios de seguimiento: acumulación de entropía, estiramiento sobremuestreando una entropía menos que ideal (von Neumann dijo que es genial), y un hash a la uniformidad. Se renuncia a la estimación de la calidad de la entropía a favor de "dar algo posiblemente dinámico" y mezclar usando una primitiva criptográfica.
fuente
Desde mi experiencia,
analogRead()
en un pin flotante tiene muy baja entropía. Tal vez uno o dos bits de aleatoriedad por llamada. Definitivamente quieres algo mejor. La inquietud del temporizador de vigilancia, como se propone en la respuesta de per1234, es una buena alternativa. Sin embargo, genera entropía a una velocidad bastante lenta, lo que puede ser un problema si lo necesita justo cuando se inicia el programa. Dandavis tiene bastantes buenas sugerencias, pero generalmente requieren un ESP8266 o hardware externo.Hay una fuente de entropía interesante que aún no se ha mencionado: el contenido de la RAM no inicializada. Cuando la MCU se enciende, algunos de sus bits de RAM (los que tienen los transistores más simétricos) se inician en un estado aleatorio. Como se discutió en este artículo de hackaday , esto puede usarse como una fuente de entropía. Solo está disponible en un arranque en frío, por lo que puede usarlo para llenar un grupo de entropía inicial, que luego repondría periódicamente de otra fuente potencialmente lenta. De esta manera, su programa puede comenzar su trabajo sin tener que esperar a que el grupo se llene lentamente.
Aquí hay un ejemplo de cómo esto podría cosecharse en un Arduino basado en AVR. El fragmento de código debajo de XOR carga toda la RAM para construir una semilla que luego alimenta
srandom()
. La parte difícil es que la recolección debe realizarse antes de que el tiempo de ejecución C inicialice las secciones de memoria .data y .bss, y luego la semilla debe guardarse en un lugar donde el tiempo de ejecución C no se sobrescriba. Esto se hace mediante el uso de secciones de memoria específicas .Tenga en cuenta que, en un reinicio en caliente , la SRAM se conserva, por lo que todavía tiene todo el contenido de su grupo de entropía. Este mismo código se puede usar para preservar la entropía recopilada a través de un reinicio.
Editar : solucionó un problema en mi versión inicial de
seed_from_ram()
que funcionaba en el global enrandom_seed
lugar de usar un localseed
. Esto podría llevar a que la semilla sea XOR consigo misma, destruyendo toda la entropía cosechada hasta ahora.fuente
analogRead()
ser utilizable si sabe lo que está haciendo. Solo debe tener cuidado de no sobreestimar su aleatoriedad al actualizar una estimación de la entropía de su grupo. Mi punto sobre elanalogRead()
que se entiende principalmente como una crítica a un pobre sin embargo, a menudo repetida “receta” :randomSeed(analogRead(0))
sólo una vez ensetup()
y asumir que es suficiente.analogRead(0)
tiene 1 bit de entropía por llamada, llamarlo repetidamente producirá 10000/8 = 1.25 KBytes / seg de entropía, 150 veces más que la biblioteca de Entropía.Si realmente no necesita entropía y simplemente desea obtener una secuencia diferente de números pseudoaleatorios en cada inicio, puede usar EEPROM para iterar a través de semillas consecutivas. Técnicamente, el proceso será completamente determinista, pero en términos prácticos es mucho mejor que
randomSeed(analogRead(0))
en un pin no conectado, lo que a menudo lo hará comenzar con la misma semilla de 0 o 1023. Guardar la próxima semilla en EEPROM garantizará que comience con un diferente semilla cada vez.Si necesita una entropía real, puede recolectarla a partir de la deriva del reloj o amplificando el ruido externo. Y si necesita mucha entropía, el ruido externo es la única opción viable. El diodo Zener es una opción popular, especialmente si tiene una fuente de voltaje por encima de 5-6V (también funcionará con 5V con un diodo Zener apropiado, pero producirá menos entropía):
( fuente )
La salida del amplificador debe conectarse a un pin analógico, que producirá varios bits de entropía con cada uno
analogRead()
hasta decenas de MHz (más rápido de lo que Arduino puede muestrear).fuente