Un petit générateur de nombres aléatoires véritables et indépendant de la plate-forme pour n'importe quel FPGA (et même ASIC).
Le neoTRNG vise à être un VRAI générateur de nombres aléatoires (TRNG) petit et indépendant de la plate-forme qui peut être synthétisé pour n'importe quelle technologie cible (FPGA et même ASIC). Il est basé sur de simples oscillateurs en anneau libres, améliorés par une technique spéciale afin de permettre la synthèse pour n'importe quelle plate-forme. Le bruit de phase qui se produit lors de l'échantillonnage d'oscillateurs en anneau libres est utilisé comme source d'entropie physique.
Ce projet est un « spin-off » du processeur NEORV32 RISC-V où le neoTRNG est implémenté comme module SoC par défaut.
Principales fonctionnalités
Prudence
Il est possible qu'il y ait au moins certaines corrélations croisées entre les signaux/événements internes/externes et les nombres aléatoires générés. Par conséquent, il n’y a aucune garantie que le neoTRNG fournisse des nombres aléatoires parfaits, voire cryptographiquement sécurisés ! Consultez les résultats d’évaluation fournis ou (mieux encore) testez-le par vous-même. De plus, il n’existe pas encore de mécanisme de détection de falsification ou de surveillance de la santé en ligne pour vérifier l’intégrité/la qualité des données aléatoires générées.
Avertissement
Garder le neoTRNG activé en permanence augmentera la consommation d'énergie dynamique et pourrait également provoquer un échauffement local de la puce (lors de l'utilisation de très grandes configurations). De plus, des interférences électromagnétiques (EMI) supplémentaires peuvent être émises par la conception.
L'ensemble de la conception est implémenté sous la forme d'un seul fichier VHDL rtl/neoTRNG.vhd
qui n'a aucune dépendance (comme des bibliothèques spéciales, des packages ou des sous-modules).
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 ;
Le neoTRNG utilise un seul domaine d'horloge piloté par le signal clk_i
. Le signal de réinitialisation du module rstn_i
est facultatif (lié à '1'
s'il n'est pas utilisé). Les données aléatoires sont obtenues à l'aide d'une simple interface données/valide : chaque fois qu'un nouvel octet aléatoire valide est disponible, la sortie valid_o
sera haute pendant exactement un cycle afin que la sortie data_o
puisse être échantillonnée par la logique utilisateur.
Le signal enable_i
est utilisé pour initialiser et démarrer le TRNG. Avant que le TRNG puisse être utilisé, ce signal doit être maintenu bas pendant au moins plusieurs 100 cycles d'horloge (selon la configuration) pour garantir que tous les bits des registres à décalage internes sont à nouveau effacés. Lorsque enable_i
est défini et valid_o
devient défini pour la première fois, le TRNG est opérationnel. La désactivation du TRNG nécessite également enable_i
soit faible pour le même nombre de cycles d'horloge. Lorsque enable_i
devient faible, tous les oscillateurs en anneau seront arrêtés, réduisant ainsi l'activité de commutation dynamique et la consommation d'énergie.
Trois génériques sont fournis pour configurer le neoTRNG. NUM_CELLS
définit le nombre total de cellules d'entropie. NUM_INV_START
définit le nombre d'inverseurs (= la longueur de l'oscillateur en anneau) dans la toute première cellule. Ces deux génériques sont décrits plus en détail dans la section Architecture ci-dessous. Le dernier SIM_MODE
générique peut être défini pour permettre la simulation du TRNG dans une simulation RTL simple.
Le neoTRNG est basé sur un nombre configurable ( NUM_CELLS
) de cellules d'entropie. Chaque cellule fournit un simple oscillateur en anneau (« RO ») construit à l'aide d'un nombre impair d'onduleurs. La fréquence d'oscillation du RO est définie par le délai de propagation des éléments à l'intérieur de l'anneau. Cette fréquence n'est pas statique car elle est soumise à des fluctuations minimes causées par le bruit thermique et le bruit de tir électronique. L'état du dernier inverseur du RO est échantillonné dans une bascule à l'aide d'une horloge statique ( clk_i
). Comme la fréquence du RO varie de manière chaotique au fil du temps, le bruit de phase inhérent aux données échantillonnées est utilisé comme source d'entropie réelle.
Chaque cellule d'entropie génère un flux de 1 bit de données aléatoires. Les sorties de toutes les cellules sont mélangées à l’aide d’une large porte XOR avant que le flux ne soit débiaisé par un simple extracteur aléatoire. Plusieurs bits débiaisés sont échantillonnés/désérialisés par l'unité d'échantillonnage pour fournir un nombre aléatoire à l'échelle de l'octet. L'unité d'échantillonnage applique également un post-traitement simple afin d'améliorer la distribution spectrale des nombres aléatoires.
Chaque cellule d'entropie est constituée d'un oscillateur en anneau construit à partir d'un nombre impair de verrous inverseurs . La longueur de l'anneau dans la toute première cellule d'entropie est définie par le générique NUM_INV_START
. Chaque cellule d'entropie supplémentaire ajoute 2 onduleurs supplémentaires à cette longueur de chaîne initiale. Par conséquent, chaque cellule d’entropie supplémentaire oscille à une fréquence inférieure à celle précédente.
Les éléments asynchrones tels que les oscillateurs en anneau sont difficiles à implémenter de manière indépendante de la plate-forme car ils nécessitent généralement l'utilisation de primitives, d'attributs ou de paramètres de synthèse spécifiques à la plate-forme/à la technologie. Afin de fournir une véritable architecture indépendante de la cible, qui peut être synthétisée pour n'importe quelle technologie cible, une technique spéciale est appliquée : chaque inverseur à l'intérieur du RO est suivi d'un verrou qui fournit une réinitialisation globale et également d'un verrou individuel permettant de commuter. le loquet en mode transparent.
Les activations de verrouillage individuelles sont contrôlées par un long registre à décalage qui présente un FF distinct pour chaque verrou de la chaîne RO. Lorsque le TRNG est activé, ce registre à décalage commence à se remplir de uns. Ainsi, les verrous sont activés individuellement un par un, ce qui rend impossible pour l'outil de synthèse de supprimer la logique/les éléments de la chaîne RO car les états de démarrage de chaque verrou peuvent (théoriquement) être surveillés par une logique externe. Les registres à décalage d'activation de toutes les cellules d'entropie sont connectés en série pour poursuivre cette procédure de démarrage sur l'ensemble du réseau d'entropie.
L'image suivante montre le schéma simplifié de la toute première cellule d'entropie composée de 5 éléments inverseurs-verrouillage pour l'oscillateur en anneaux, de 5 bascules pour le registre à décalage d'activation et de 2 autres bascules pour le synchroniseur.
Une image montrant au FPGA le résultat du mappage (généré par Intel Quartus Prime) de la toute première cellule d'entropie peut être vue ici. Cela montre que tous les éléments de verrouillage + inverseur de la chaîne d'oscillateurs en anneau ont été mappés avec succès sur des LUT4 individuels.
Dès que le dernier bit du registre à décalage d'activation en chaîne de la cellule d'entropie est activé, l'unité de dépolarisation démarre. Cette unité implémente un simple « extracteur aléatoire John von Neumann » pour débiaiser le flux de données aléatoires obtenu. L'extracteur implémente un registre à décalage de 2 bits qui échantillonne le bit aléatoire XOR du réseau de cellules d'entropie. Tous les deux cycles, l'extracteur évalue les deux bits échantillonnés pour vérifier les bords d'une paire de bits qui ne se chevauchent pas.
Chaque fois qu'un front a été détecté, un signal "valide" est envoyé à l'unité d'échantillonnage suivante. Un front montant ( 01
) émet un bit de données 1
et un front descendant ( 10
) émet un bit de données 0
. Par conséquent, l'unité de dépolarisation nécessite au moins deux cycles d'horloge pour générer un seul bit aléatoire. Si aucun front n'est détecté ( 00
ou 11
) le signal valide reste bas et l'unité d'échantillonnage s'arrête.
L'unité d'échantillonnage implémente un registre à décalage de 8 bits pour convertir le flux binaire débiaisé en série en nombres aléatoires à l'échelle de l'octet. De plus, l'unité d'échantillonnage fournit un post-traitement simple pour améliorer la distribution spectrale des échantillons aléatoires obtenus.
Afin de générer un octet de données aléatoires, l'unité d'échantillonnage réinitialise son registre à décalage interne à zéro et commence à consommer 64 bits du flux aléatoire débiaisé. Le registre à décalage est implémenté sous la forme d' un registre à décalage à rétroaction linéaire (LFSR) qui effectue un OU exclusif sur le flux d'entrée avec le dernier bit du registre pour brouiller et mélanger davantage le flux binaire aléatoire.
Le neoTRNG est évalué dans le cadre du processeur NEORV32, où le neoTRNG est disponible en tant que module SoC standard. Le processeur a été synthétisé pour un FPGA Intel Cyclone IV EP4CE22F17C6N
fonctionnant à 100 MHz. Pour l'évaluation, la très petite configuration par défaut a été utilisée : trois cellules d'entropie sont implémentées où la première implémente 5 onduleurs, la seconde implémente 9 onduleurs et la troisième implémente 11 onduleurs. Des configurations plus complexes avec des cellules d'entropie plus nombreuses ou plus grandes pourraient fournir une "meilleure" qualité aléatoire.
NUM_CELLS = 3
NUM_INV_START = 5
SIM_MODE = false
Note
Une quantité totale de 4 Mo de données aléatoires a été obtenue pour les évaluations. Cet ensemble de données est disponible sous forme de fichier binaire entropy.bin
dans les ressources de la version.
Pour l'analyse simple de l'histogramme, 4 Mo d'octets aléatoires ont été échantillonnés à partir du neoTRNG. Les octets obtenus ont été accumulés en fonction de leur occurrence et triés dans des groupes où chaque groupe représente un modèle d'octet spécifique (1 octet = 8 bits = 256 modèles différents). Le résultat a ensuite été analysé au regard de ses propriétés statistiques :
[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
La suite de tests de nombres aléatoires la plus intransigeante (wikipedia, page d'accueil) de Robert G. Brown est un excellent ensemble d'outils pour tester et caractériser les générateurs de nombres aléatoires.
Important
? travaux en cours ?
les plus purs et durs ont besoin d'un grand ensemble d'échantillons aléatoires (environ 4 Go). Sinon, les données aléatoires sont rembobinées, réduisant évidemment l'entropie globale. À l'heure actuelle, j'utilise une simple connexion UART pour transférer des données d'un FPGA vers le PC. Mais même avec des débits en bauds plus élevés, l'envoi d'un ensemble de données de 4 Go prendrait du temps . Jusqu'à ce que j'aie un meilleur canal de transfert (ou juste beaucoup de temps), cette évaluation est un "travail en cours" .
Résultats du mappage pour le neoTRNG implémenté dans le processeur NEORV32 RISC-V en utilisant la configuration par défaut. Résultats générés pour un FPGA Intel Cyclone EP4CE22F17C6N
fonctionnant à 100 MHz avec 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)
Note
Les outils de synthèse peuvent émettre un avertissement indiquant que des verrous et des boucles combinatoires ont été détectés. Cependant, il ne s’agit pas d’un défaut de conception puisque c’est exactement ce que nous recherchons.
Le taux de génération maximum du neoTRNG est défini par deux facteurs :
Par conséquent, le neoTRNG nécessite au moins A * B = 2 * 64 = 128
cycles d'horloge pour émettre un octet aléatoire. L'évaluation du FPGA a montré que le temps d'échantillonnage réel est d'environ 300 cycles d'horloge. Ainsi, une implémentation fonctionnant à 100 MHz peut générer environ 330 Ko de données aléatoires par seconde. Des taux de génération plus élevés peuvent être obtenus en exécutant plusieurs instances neoTRNG en parallèle.
Étant donné que les oscillateurs en anneau asynchrones ne peuvent pas être simulés en RTL (en raison des boucles combinatoires), le neoTRNG fournit un mode de simulation dédié qui est activé par le générique SIM_MODE
. Lorsqu'il est activé, un "retard de propagation" implémenté comme une simple bascule est ajouté aux inverseurs de l'oscillateur en anneau.
Important
Le mode simulation est destiné uniquement à la simulation/débogage ! Les conceptions avec SIM_MODE
activé peuvent être synthétisées mais ne fourniront aucun nombre aléatoire vrai/physique !
Le dossier sim
fournit un banc de test simple pour le neoTRNG utilisant la configuration par défaut. Le banc de test affichera les octets de données aléatoires obtenus sous forme de valeurs décimales sur la console du simulateur. Le banc de test peut être simulé avec GHDL en utilisant le script fourni :
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
Les données de forme d'onde GHDL sont stockées dans sim/neoTRNG_tb.ghw
et peuvent être visualisées à l'aide de gtkwave
:
neoTRNG/sim$ gtkwave neoTRNG_tb.ghw
Une simple simulation est exécutée par le workflow d'action neoTRNG-sim
GitHub du projet.