Um gerador de números aleatórios verdadeiros minúsculo e independente de plataforma para qualquer FPGA (e até mesmo ASICs).
O neoTRNG pretende ser um gerador de números aleatórios VERDADEIRO (TRNG) pequeno e independente de plataforma que pode ser sintetizado para qualquer tecnologia alvo (FPGAs e até mesmo ASICs). É baseado em osciladores de anel simples de funcionamento livre, que são aprimorados por uma técnica especial para permitir a síntese para qualquer plataforma. O ruído de fase que ocorre durante a amostragem de osciladores de anel de funcionamento livre é usado como fonte de entropia física.
Este projeto é um "spin-off" do processador NEORV32 RISC-V onde o neoTRNG é implementado como módulo SoC padrão.
Principais recursos
Cuidado
É possível que haja pelo menos algumas correlações cruzadas entre sinais/eventos internos/externos e os números aleatórios gerados. Portanto, não há garantia alguma de que o neoTRNG forneça números aleatórios perfeitos ou mesmo criptograficamente seguros ! Veja os resultados da avaliação fornecidos ou (melhor ainda) teste você mesmo. Além disso, ainda não existe nenhum mecanismo de detecção de adulteração ou monitoramento de saúde on-line para verificar a integridade/qualidade dos dados aleatórios gerados.
Aviso
Manter o neoTRNG permanentemente ativado aumentará o consumo dinâmico de energia e também poderá causar aquecimento local do chip (ao usar configurações muito grandes). Além disso, interferência eletromagnética (EMI) adicional pode ser emitida pelo projeto.
Todo o design é implementado como um único arquivo VHDL rtl/neoTRNG.vhd
que não possui nenhuma dependência (como bibliotecas especiais, pacotes ou submódulos).
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 ;
O neoTRNG usa um único domínio de clock acionado pelo sinal clk_i
. O sinal de reinicialização do módulo rstn_i
é opcional (vinculado a '1'
se não for usado). Dados aleatórios são obtidos usando uma interface data/valid simples: sempre que um novo byte aleatório válido estiver disponível, a saída valid_o
será alta por exatamente um ciclo para que a saída data_o
possa ser amostrada pela lógica do usuário.
O sinal enable_i
é usado para inicializar e iniciar o TRNG. Antes que o TRNG possa ser usado, este sinal deve ser mantido baixo por pelo menos 100 ciclos de clock (dependendo da configuração) para garantir que todos os bits dos registradores de deslocamento internos sejam limpos novamente. Quando enable_i
é definido e valid_o
é definido pela primeira vez, o TRNG está operacional. Desabilitar o TRNG também exige enable_i
esteja baixo para a mesma quantidade de ciclos de clock. Quando enable_i
fica baixo, todos os osciladores de anel serão interrompidos, reduzindo a atividade de comutação dinâmica e o consumo de energia.
Três genéricos são fornecidos para configurar o neoTRNG. NUM_CELLS
define o número total de células de entropia. NUM_INV_START
define o número de inversores (= o comprimento do oscilador em anel) na primeira célula. Esses dois genéricos são descritos com mais detalhes na seção Arquitetura abaixo. O último SIM_MODE
genérico pode ser configurado para permitir a simulação do TRNG dentro de uma simulação RTL simples.
O neoTRNG é baseado em um número configurável ( NUM_CELLS
) de células de entropia. Cada célula fornece um oscilador de anel simples ("RO") que é construído usando um número ímpar de inversores. A frequência de oscilação do RO é definida pelo atraso de propagação dos elementos dentro do anel. Esta frequência não é estática, pois está sujeita a flutuações mínimas causadas por ruído térmico e ruído de disparo eletrônico. O estado do último inversor do RO é amostrado em um flip-flop usando um relógio estático ( clk_i
). Como a frequência do RO varia caoticamente ao longo do tempo, o ruído de fase inerente dos dados amostrados é usado como fonte real de entropia.
Cada célula de entropia gera um fluxo de dados aleatórios de 1 bit. As saídas de todas as células são misturadas usando uma porta XOR ampla antes que o fluxo seja desviado por um extrator de aleatoriedade simples. Vários bits desviados são amostrados/desserializados pela unidade de amostragem para fornecer um número aleatório em todo o byte. A unidade amostral também aplica um pós-processamento simples para melhorar a distribuição espectral dos números aleatórios.
Cada célula de entropia consiste em um oscilador de anel construído a partir de um número ímpar de travas inversoras . O comprimento do anel na primeira célula de entropia é definido pelo genérico NUM_INV_START
. Cada célula de entropia adicional adiciona outros 2 inversores a este comprimento inicial da cadeia. Conseqüentemente, cada célula de entropia adicional oscila em uma frequência mais baixa que a anterior.
Elementos assíncronos, como osciladores de anel, são difíceis de implementar de maneira independente de plataforma, pois geralmente exigem o uso de primitivos, atributos ou configurações de síntese específicos da plataforma/tecnologia. Para fornecer uma arquitetura agnóstica de alvo real, que pode ser sintetizada para qualquer tecnologia alvo, uma técnica especial é aplicada: cada inversor dentro da RO é seguido por um latch que fornece um reset global e também um latch-enable individual para chavear. a trava para o modo transparente.
As habilitações de latch individuais são controladas por um registrador de deslocamento longo que apresenta um FF distinto para cada latch na cadeia RO. Quando o TRNG está habilitado, este registrador de deslocamento começa a ser preenchido com uns. Assim, as travas são habilitadas individualmente, uma por uma, impossibilitando que a ferramenta de síntese corte qualquer lógica/elementos da cadeia RO, pois os estados de inicialização de cada trava podem (teoricamente) ser monitorados por lógica externa. O registro de deslocamento habilitado de todas as células de entropia é conectado em série para continuar este procedimento de inicialização em toda a matriz de entropia.
A imagem a seguir mostra o esquema simplificado da primeira célula de entropia que consiste em 5 elementos inversor-latch para o oscilador de anéis, 5 flip-flops para o registrador de deslocamento de habilitação e outros 2 flip-flops para o sincronizador.
Uma imagem mostrando ao FPGA o resultado do mapeamento (gerado pelo Intel Quartus Prime) da primeira célula de entropia pode ser vista aqui. Ele mostra que todos os elementos latch + inversor da cadeia do oscilador de anel foram mapeados com sucesso para LUT4s individuais.
Assim que o último bit do registrador de deslocamento habilitado em cadeia da célula de entropia é definido, a unidade de despolarização é iniciada. Esta unidade implementa um simples "Extrator de Aleatoriedade John von Neumann" para desviar o fluxo de dados aleatórios obtido. O extrator implementa um registrador de deslocamento de 2 bits que amostra o bit aleatório com XOR da matriz de células de entropia. A cada segundo ciclo, o extrator avalia os dois bits amostrados para verificar se há arestas em um par de bits não sobrepostos.
Sempre que uma borda é detectada, um sinal “válido” é enviado para a unidade de amostragem seguinte. Uma borda ascendente ( 01
) emite um bit de dados 1
e uma borda descendente ( 10
) emite um bit de dados 0
. Conseqüentemente, a unidade de eliminação de polarização requer pelo menos dois ciclos de clock para gerar um único bit aleatório. Se nenhuma borda for detectada ( 00
ou 11
), o sinal válido permanece baixo e a unidade de amostragem para.
A unidade de amostragem implementa um registrador de deslocamento de 8 bits para converter o fluxo de bits serial desviado em números aleatórios de bytes. Além disso, a unidade amostral fornece um pós-processamento simples para melhorar a distribuição espectral das amostras aleatórias obtidas.
Para gerar um byte de dados aleatórios, a unidade de amostragem redefine seu registrador de deslocamento interno para zero e começa a consumir 64 bits do fluxo aleatório desviado. O registrador de deslocamento é implementado como um registrador de deslocamento de feedback linear (LFSR) que faz um XOR do fluxo de entrada com o último bit do registrador para embaralhar e misturar ainda mais o fluxo de bits aleatório.
O neoTRNG é avaliado como parte do processador NEORV32, onde o neoTRNG está disponível como módulo SoC padrão. O processador foi sintetizado para um FPGA Intel Cyclone IV EP4CE22F17C6N
rodando a 100MHz. Para a avaliação foi utilizada a configuração padrão muito pequena: são implementadas três células de entropia onde a primeira implementa 5 inversores, a segunda implementa 9 inversores e a terceira implementa 11 inversores. Configurações mais complexas com células de entropia maiores/maiores podem fornecer qualidade aleatória "melhor".
NUM_CELLS = 3
NUM_INV_START = 5
SIM_MODE = false
Observação
Uma quantidade total de 4 MB de dados aleatórios foi obtida para as avaliações. Este conjunto de dados está disponível como arquivo binário entropy.bin
nos ativos de lançamento.
Para a análise simples do histograma, 4 MB de bytes aleatórios foram amostrados do neoTRNG. Os bytes obtidos foram acumulados de acordo com sua ocorrência e classificados em bins onde cada bin representa um padrão de bytes específico (1 byte = 8 bits = 256 padrões diferentes). O resultado foi então analisado quanto às suas propriedades estatí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
O obstinado conjunto de testes de números aleatórios (wikipedia, página inicial) de Robert G. Brown é um ótimo conjunto de ferramentas para testar a resistência e caracterizar geradores de números aleatórios.
Importante
? trabalho em andamento?
o dieharder precisa de um grande conjunto de amostras aleatórias (algo em torno de 4 GB). Caso contrário, os dados aleatórios serão retrocedidos, obviamente reduzindo a entropia geral. No momento estou usando uma conexão UART simples para transferir dados de um FPGA para o PC. Mas mesmo com taxas Baud mais altas, um conjunto de dados de 4 GB levaria séculos para ser enviado. Até que eu tenha um canal de transferência melhor (ou apenas muito tempo), esta avaliação é um "trabalho em andamento" .
Resultados de mapeamento para o neoTRNG implementado no processador NEORV32 RISC-V usando a configuração padrão. Resultados gerados para um FPGA Intel Cyclone EP4CE22F17C6N
rodando a 100 MHz 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)
Observação
As ferramentas de síntese podem emitir um aviso de que travas e loops combinatórios foram detectados. No entanto, isso não é uma falha de design, pois é exatamente isso que queremos.
A taxa máxima de geração do neoTRNG é definida por dois fatores:
Conseqüentemente, o neoTRNG requer pelo menos A * B = 2 * 64 = 128
ciclos de clock para emitir um byte aleatório. A avaliação do FPGA mostrou que o tempo real de amostragem é de cerca de 300 ciclos de clock. Assim, uma implementação rodando a 100 MHz pode gerar aproximadamente 330kB de dados aleatórios por segundo. Taxas de geração mais altas podem ser alcançadas executando várias instâncias do neoTRNG em paralelo.
Como os osciladores de anel assíncronos não podem ser simulados em rtl (devido aos loops combinatórios), o neoTRNG fornece um modo de simulação dedicado que é habilitado pelo genérico SIM_MODE
. Quando habilitado, um "atraso de propagação" implementado como um simples flip-flop é adicionado aos inversores do oscilador em anel.
Importante
O modo de simulação destina-se apenas a simulação/depuração! Projetos com SIM_MODE
habilitado podem ser sintetizados, mas não fornecerão nenhum número aleatório verdadeiro/físico !
A pasta sim
fornece um testbench simples para o neoTRNG usando a configuração padrão. O testbench enviará os bytes de dados aleatórios obtidos como valores decimais para o console do simulador. O testbench pode ser simulado com GHDL usando o script fornecido:
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
Os dados da forma de onda GHDL são armazenados em sim/neoTRNG_tb.ghw
e podem ser visualizados usando gtkwave
:
neoTRNG/sim$ gtkwave neoTRNG_tb.ghw
Uma simulação simples é executada pelo fluxo de trabalho de ação neoTRNG-sim
GitHub do projeto.