Un generador de números aleatorios verdadero, pequeño e independiente de la plataforma para cualquier FPGA (e incluso ASIC).
El neoTRNG pretende ser un generador de números aleatorios VERDADEROS (TRNG) pequeño e independiente de la plataforma que se puede sintetizar para cualquier tecnología de destino (FPGA e incluso ASIC). Se basa en osciladores en anillo simples y libres, que se mejoran mediante una técnica especial para permitir la síntesis para cualquier plataforma. El ruido de fase que se produce al muestrear osciladores en anillo de funcionamiento libre se utiliza como fuente de entropía física.
Este proyecto es un "derivado" del procesador NEORV32 RISC-V donde el neoTRNG se implementa como módulo SoC predeterminado.
Características clave
Precaución
Es posible que haya al menos algunas correlaciones cruzadas entre señales/eventos internos/externos y los números aleatorios generados. Por lo tanto, no hay ninguna garantía de que neoTRNG proporcione números aleatorios perfectos o incluso criptográficamente seguros . Vea los resultados de la evaluación proporcionados o (aún mejor) pruébelo usted mismo. Además, todavía no existe ningún mecanismo de detección de manipulación ni monitoreo de salud en línea disponible para verificar la integridad/calidad de los datos aleatorios generados.
Advertencia
Mantener el neoTRNG permanentemente habilitado aumentará el consumo de energía dinámica y también podría provocar un calentamiento local del chip (cuando se utilizan configuraciones muy grandes). Además, el diseño podría emitir interferencias electromagnéticas (EMI) adicionales.
Todo el diseño se implementa como un único archivo VHDL rtl/neoTRNG.vhd
que no tiene ninguna dependencia (como bibliotecas, paquetes o submódulos especiales).
entity neoTRNG is
generic (
NUM_CELLS : natural range 1 to 99 := 3 ; -- number of ring-oscillator cells
NUM_INV_START : natural range 3 to 99 := 5 ; -- number of inverters in first cell, has to be odd
SIM_MODE : boolean := false -- enable simulation mode (no physical random if enabled!)
);
port (
clk_i : in std_ulogic ; -- module clock
rstn_i : in std_ulogic ; -- module reset, low-active, async, optional
enable_i : in std_ulogic ; -- module enable (high-active)
valid_o : out std_ulogic ; -- data_o is valid when set (high for one cycle)
data_o : out std_ulogic_vector ( 7 downto 0 ) -- random data byte output
);
end neoTRNG ;
El neoTRNG utiliza un dominio de reloj único impulsado por la señal clk_i
. La señal de reinicio del módulo rstn_i
es opcional (vinculada a '1'
si no se usa). Los datos aleatorios se obtienen utilizando una interfaz simple de datos/válidos: siempre que esté disponible un nuevo byte aleatorio válido, la salida valid_o
será alta durante exactamente un ciclo para que la lógica del usuario pueda muestrear la salida data_o
.
La señal enable_i
se utiliza para inicializar e iniciar TRNG. Antes de poder utilizar el TRNG, esta señal debe mantenerse baja durante al menos varios 100 ciclos de reloj (dependiendo de la configuración) para garantizar que todos los bits de los registros de desplazamiento internos se borren nuevamente. Cuando se establece enable_i
y valid_o
se establece por primera vez, el TRNG está operativo. Deshabilitar TRNG también requiere que enable_i
esté bajo durante la misma cantidad de ciclos de reloj. Cuando enable_i
baje, todos los osciladores en anillo se detendrán, lo que reducirá la actividad de conmutación dinámica y el consumo de energía.
Se proporcionan tres genéricos para configurar el neoTRNG. NUM_CELLS
define el número total de celdas de entropía. NUM_INV_START
define el número de inversores (= la longitud del oscilador en anillo) en la primera celda. Estos dos genéricos se describen con más detalle en la sección Arquitectura a continuación. El último SIM_MODE
genérico se puede configurar para permitir la simulación del TRNG dentro de una simulación RTL simple.
El neoTRNG se basa en un número configurable ( NUM_CELLS
) de celdas de entropía. Cada celda proporciona un oscilador en anillo ("RO") simple que se construye utilizando un número impar de inversores. La frecuencia de oscilación del RO está definida por el retardo de propagación de los elementos dentro del anillo. Esta frecuencia no es estática ya que está sujeta a mínimas fluctuaciones causadas por el ruido térmico y el ruido de disparo electrónico. El estado del último inversor del RO se muestrea en un flip-flop utilizando un reloj estático ( clk_i
). Como la frecuencia del RO varía caóticamente con el tiempo, el ruido de fase inherente de los datos muestreados se utiliza como fuente de entropía real.
Cada celda de entropía genera un flujo de datos aleatorios de 1 bit. Las salidas de todas las celdas se mezclan utilizando una puerta XOR ancha antes de dessesgar la secuencia mediante un extractor de aleatoriedad simple. La unidad de muestreo muestrea/deserializa varios bits dessesgados para proporcionar un número aleatorio en todo el byte. La unidad de muestreo también aplica un posprocesamiento simple para mejorar la distribución espectral de los números aleatorios.
Cada celda de entropía consta de un oscilador en anillo que se construye a partir de un número impar de pestillos inversores . La longitud del anillo en la primera celda de entropía está definida por el genérico NUM_INV_START
. Cada celda de entropía adicional agrega otros 2 inversores a esta longitud de cadena inicial. Por lo tanto, cada celda de entropía adicional oscila a una frecuencia más baja que la anterior.
Los elementos asincrónicos como los osciladores en anillo son difíciles de implementar de forma independiente de la plataforma, ya que generalmente requieren el uso de primitivas, atributos o configuraciones de síntesis específicos de la plataforma/tecnología. Para proporcionar una arquitectura real independiente del objetivo, que pueda sintetizarse para cualquier tecnología objetivo, se aplica una técnica especial: a cada inversor dentro del RO le sigue un pestillo que proporciona un reinicio global y también un pestillo individual que permite cambiar. el pestillo al modo transparente.
Las habilitaciones de pestillos individuales se controlan mediante un registro de desplazamiento largo que presenta un FF distinto para cada pestillo de la cadena RO. Cuando el TRNG está habilitado, este registro de desplazamiento comienza a llenarse con unos. Por lo tanto, los pestillos se habilitan individualmente uno por uno, lo que hace imposible que la herramienta de síntesis recorte cualquier lógica/elemento de la cadena RO, ya que los estados de inicio de cada pestillo pueden (teóricamente) monitorearse mediante lógica externa. El registro de desplazamiento de habilitación de todas las celdas de entropía está conectado en cadena para continuar este procedimiento de inicio en toda la matriz de entropía.
La siguiente imagen muestra el esquema simplificado de la primera celda de entropía que consta de 5 elementos de pestillo inversor para el oscilador de anillos, 5 flip-flops para el registro de desplazamiento de habilitación y otros 2 flip-flops para el sincronizador.
Aquí se puede ver una imagen que muestra el resultado del mapeo de la FPGA (generado por Intel Quartus Prime) de la primera celda de entropía. Muestra que todos los elementos de pestillo + inversor de la cadena del oscilador en anillo se asignaron con éxito a LUT4 individuales.
Tan pronto como se establece el último bit del registro de desplazamiento de habilitación en cadena de la celda de entropía, se inicia la unidad de despolarización. Esta unidad implementa un simple "Extractor de aleatoriedad John von Neumann" para eliminar el sesgo del flujo de datos aleatorios obtenido. El extractor implementa un registro de desplazamiento de 2 bits que muestrea el bit aleatorio XOR de la matriz de celdas de entropía. En cada segundo ciclo, el extractor evalúa los dos bits muestreados para comprobar si hay bordes en un par de bits que no se superpongan.
Cada vez que se detecta un flanco, se envía una señal "válida" a la siguiente unidad de muestreo. Un flanco ascendente ( 01
) emite un bit de datos 1
y un flanco descendente ( 10
) emite un bit de datos 0
. Por tanto, la unidad de despolarización requiere al menos dos ciclos de reloj para generar un único bit aleatorio. Si no se detecta ningún borde ( 00
u 11
), la señal válida permanece baja y la unidad de muestreo se detiene.
La unidad de muestreo implementa un registro de desplazamiento de 8 bits para convertir el flujo de bits sin sesgo en serie en números aleatorios de bytes. Además, la unidad de muestra proporciona un posprocesamiento simple para mejorar la distribución espectral de las muestras aleatorias obtenidas.
Para generar un byte de datos aleatorios, la unidad de muestreo restablece su registro de desplazamiento interno a cero y comienza a consumir 64 bits del flujo aleatorio sin sesgo. El registro de desplazamiento se implementa como registro de desplazamiento de retroalimentación lineal (LFSR) que aplica XOR al flujo de entrada con el último bit del registro para codificar y mezclar aún más el flujo de bits aleatorio.
El neoTRNG se evalúa como parte del procesador NEORV32, donde el neoTRNG está disponible como módulo SoC estándar. El procesador fue sintetizado para un FPGA Intel Cyclone IV EP4CE22F17C6N
funcionando a 100MHz. Para la evaluación se ha utilizado la configuración por defecto muy pequeña: se implementan tres celdas de entropía donde la primera implementa 5 inversores, la segunda implementa 9 inversores y la tercera implementa 11 inversores. Configuraciones más complejas con más celdas de entropía más grandes podrían proporcionar una "mejor" calidad aleatoria.
NUM_CELLS = 3
NUM_INV_START = 5
SIM_MODE = false
Nota
Para las evaluaciones se ha obtenido una cantidad total de 4MB de datos aleatorios. Este conjunto de datos está disponible como archivo binario entropy.bin
en los activos de la versión.
Para el análisis de histograma simple, se tomaron muestras de 4 MB de bytes aleatorios del neoTRNG. Los bytes obtenidos se acumularon según su aparición y se clasificaron en contenedores donde cada contenedor representa un patrón de bytes específico (1 byte = 8 bits = 256 patrones diferentes). Luego se analizó el resultado con respecto a sus propiedades estadísticas:
[NOTE] integer numbers only
Number of samples: 4194304
Arithmetic mean: 127 (optimum would be 127)
Histogram occurrence
Average: 16384 (optimum would be 4194304/256 = 16384)
Min: 16051 = average - 333 (deviation) at bin 183 (optimum deviation would be 0)
Max: 16706 = average + 322 (deviation) at bin 144 (optimum deviation would be 0)
Average dev.: +/- 96 (optimum would be 0)
$ ent entropy.bin
Entropy = 7.994306 bits per byte.
Optimum compression would reduce the size
of this 4194304 byte file by 0 percent.
Chi square distribution for 4194304 samples is 16726.32, and randomly
would exceed this value less than 0.01 percent of the times.
Arithmetic mean value of data bytes is 127.9417 (127.5 = random).
Monte Carlo value for Pi is 3.132416851 (error 0.29 percent).
Serial correlation coefficient is 0.000496 (totally uncorrelated = 0.0).
$ rngtest < entropy.bin
rngtest 5
Copyright (c) 2004 by Henrique de Moraes Holschuh
This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
rngtest: starting FIPS tests...
rngtest: entropy source drained
rngtest: bits received from input: 33554432
rngtest: FIPS 140-2 successes: 1676
rngtest: FIPS 140-2 failures: 1
rngtest: FIPS 140-2(2001-10-10) Monobit: 0
rngtest: FIPS 140-2(2001-10-10) Poker: 0
rngtest: FIPS 140-2(2001-10-10) Runs: 1
rngtest: FIPS 140-2(2001-10-10) Long run: 0
rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
rngtest: input channel speed: (min=138.214; avg=1557.190; max=2119.276)Mibits/s
rngtest: FIPS tests speed: (min=32.660; avg=106.337; max=111.541)Mibits/s
rngtest: Program run time: 330110 microseconds
El incondicional conjunto de pruebas de números aleatorios (wikipedia, página de inicio) de Robert G. Brown es un excelente conjunto de herramientas para realizar pruebas de estrés y caracterizar generadores de números aleatorios.
Importante
? trabajo en progreso?
El intransigente necesita un gran conjunto de muestras aleatorias (alrededor de 4 GB). De lo contrario, los datos aleatorios se rebobinan, lo que obviamente reduce la entropía general. En este momento estoy usando una conexión UART simple para transferir datos desde una FPGA a la PC. Pero incluso a velocidades de baudios más altas, un conjunto de datos de 4 GB tardaría años en enviarse. Hasta que tenga un mejor canal de transferencia (o simplemente mucho tiempo), esta evaluación es un "trabajo en progreso" .
Resultados del mapeo para neoTRNG implementado dentro del procesador NEORV32 RISC-V usando la configuración predeterminada. Resultados generados para un FPGA Intel Cyclone EP4CE22F17C6N
funcionando a 100MHz usando Intel Quartus Prime.
Module Hierarchy Logic Cells Logic Registers
------------------------------------------------------------------------------------
neoTRNG:neoTRNG_inst 56 (27) 46 (19)
neoTRNG_cell:entropy_source:0:neoTRNG_cell_inst 8 (8) 7 (7)
neoTRNG_cell:entropy_source:1:neoTRNG_cell_inst 10 (10) 9 (9)
neoTRNG_cell:entropy_source:2:neoTRNG_cell_inst 14 (14) 11 (11)
Nota
Las herramientas de síntesis pueden emitir una advertencia de que se han detectado bloqueos y bucles combinatorios. Sin embargo, esto no es un defecto de diseño, ya que es exactamente lo que queremos.
La tasa máxima de generación del neoTRNG está definida por dos factores:
Por lo tanto, neoTRNG requiere al menos A * B = 2 * 64 = 128
ciclos de reloj para emitir un byte aleatorio. La evaluación de FPGA ha demostrado que el tiempo de muestreo real es de alrededor de 300 ciclos de reloj. Por tanto, una implementación que funcione a 100 MHz puede generar aproximadamente 330 kB de datos aleatorios por segundo. Se pueden lograr tasas de generación más altas ejecutando varias instancias de neoTRNG en paralelo.
Dado que los osciladores de anillo asíncronos no se pueden simular con rtl (debido a los bucles combinatorios), neoTRNG proporciona un modo de simulación dedicado que está habilitado por el genérico SIM_MODE
. Cuando está habilitado, se agrega un "retardo de propagación" implementado como un simple flip-flop a los inversores del oscilador en anillo.
Importante
¡El modo de simulación está destinado únicamente a simulación/depuración! Los diseños con SIM_MODE
habilitado se pueden sintetizar, ¡pero no proporcionarán ningún número aleatorio verdadero/físico !
La carpeta sim
proporciona un banco de pruebas simple para neoTRNG usando la configuración predeterminada. El banco de pruebas enviará los bytes de datos aleatorios obtenidos como valores decimales a la consola del simulador. El banco de pruebas se puede simular con GHDL utilizando el script proporcionado:
neoTRNG/sim$ sh ghdl.sh
../rtl/neoTRNG.vhd:105:3:@0ms:(assertion note): [neoTRNG] The neoTRNG (v3.2) - A Tiny and Platform-Independent True Random Number Generator, https://github.com/stnolting/neoTRNG
../rtl/neoTRNG.vhd:112:3:@0ms:(assertion warning): [neoTRNG] Simulation-mode enabled (NO TRUE/PHYSICAL RANDOM)!
18
210
147
5
79
94
70
100
185
246
203
220
ghdl:info: simulation stopped by --stop-time @100us
Los datos de forma de onda GHDL se almacenan en sim/neoTRNG_tb.ghw
y se pueden ver usando gtkwave
:
neoTRNG/sim$ gtkwave neoTRNG_tb.ghw
El flujo de trabajo de acción neoTRNG-sim
GitHub del proyecto ejecuta una simulación simple.