EN
Rapport d'expérimentation

RNG Lab

Quatre familles de générateurs, huit tests statistiques, une attaque. Ce rapport cherche à savoir si « passer les tests statistiques » veut dire « être sûr ». La réponse est non, et la démonstration tient en deux lignes du tableau ci-dessous.

La thèse. Un test statistique constate l'absence de structure visible. Il ne dit rien de la prédictibilité. Ce sont deux propriétés distinctes, et seule la seconde protège un secret.

Deux résultats de ce banc d'essai suffisent à trancher : un générateur qui passe les huit tests et dont on prédit 100 % des sorties futures, et une source d'entropie rigoureusement nulle qui passe elle aussi les huit tests.

Le verdictMatrice des résultats

Échantillon de 1 000 000 bits par source, seuil de rejet à 1 %. Survolez une case pour lire la p-valeur.

MonobitRunsSérie max de 1Khi-deuxShannonAutocorr. 1-32SpectralIncompress.score
LCG (octet de poids faible)PRNG4/8
Mersenne Twister (MT19937)PRNG8/8
secrets (CSPRNG système)CSPRNG8/8
Jitter CPU — extraction naïveTRNG logiciel0/8
Jitter CPU — brutTRNG logiciel0/8
Jitter CPU — débiaisé (von Neumann)TRNG logiciel2/8
Jitter CPU — conditionné SHA-256TRNG logiciel8/8
Source NULLE + SHA-256Contre-exemple8/8

Lecture : « 8/8 » ne signifie pas « sûr ». Trois lignes affichent 8/8 pour trois raisons complètement différentes — dont une source qui ne contient aucune entropie.

Acte ICe que les tests attrapent — et ce qu'ils manquent

Le générateur congruentiel linéaire est le mauvais élève de service. On publie son octet de poids faible, l'erreur classique : les bits de poids faible d'un LCG ont des périodes minuscules.

Il est pourtant loin d'échouer partout. Son entropie de Shannon vaut 8.00000 bits par octet — le maximum théorique exact — et son khi-deux sur les 256 valeurs d'octet donne p = 1, une uniformité parfaite. Sa distribution est irréprochable.

Et pourtant zlib le comprime à 0.0065 de sa taille, soit un facteur 153. Une suite réellement aléatoire est incompressible ; celle-ci est presque entièrement structurée.

03066120255 — valeur de l'octet
LCG (octet de poids faible)secrets

Histogramme des valeurs d'octet. Le LCG (rouge) est une ligne parfaitement plate — plus plate que le CSPRNG, dont les fluctuations sont justement le signe qu'il est aléatoire. Une distribution trop parfaite est un symptôme, pas une qualité.

La leçon : l'entropie de Shannon et le khi-deux mesurent la distribution marginale, c'est-à-dire la fréquence de chaque valeur prise isolément. Ils sont aveugles à l'ordre des valeurs. Il faut des tests structurels — compression, spectral, autocorrélation — pour voir la séquence.

069277seuil de rejet à 1 %décalage 132 — écart-type de l'autocorrélation
LCGJitter débiaisésecrets

Autocorrélation aux décalages 1 à 32, en écarts-types. Au-delà de la ligne pointillée, la dépendance est statistiquement établie.

Acte IILa source physique, mise à l'épreuve

C'est ici que l'idée du générateur matériel maison rencontre la réalité. Notre source physique est le jitter d'horloge : on exécute une charge de calcul fixe et on mesure sa durée, qui varie sous l'effet du cache, de l'ordonnanceur, de la fréquence et des interruptions. Du vrai bruit physique, sans composant à souder.

Premier piège : la source silencieusement morte

L'extraction évidente consiste à prendre le bit de poids faible de l'écart entre deux lectures d'horloge. Sous Windows, perf_counter_ns a une granularité de 100 ns : tous les écarts sont des multiples de 100, et leur bit de poids faible vaut invariablement 0.

Mesure : P(bit = 1) = 0.0000, min-entropie 0,000 bit par bit. La source produit un flux de zéros à 8 Mbit/s. Elle « fonctionne », elle ne lève aucune erreur, et elle ne contient rien.

Deuxième piège : le débiaisage ne fait pas tout

Avec une extraction correcte, la source vit : P(bit = 1) = 0.3970. Biaisée, donc, mais réelle. Le correctif classique est le débiaisage de von Neumann, qui garantit une sortie parfaitement équilibrée — à condition que les bits soient indépendants.

Après von Neumann, on mesure P(bit = 1) = 0.5024. Sur un million de bits, cet écart à 0,5 est massivement significatif. Ce n'est pas du bruit de mesure : c'est la preuve que les bits bruts ne sont pas indépendants. L'hypothèse de von Neumann est violée, donc sa garantie ne s'applique pas. Le flux débiaisé échoue encore 6 tests sur 8.

Jitter — extraction naïve0.000 bit/bitJitter — brut0.730 bit/bitJitter — après von Neumann0.993 bit/bitIdéal (1 bit par bit)1.000 bit/bit

Min-entropie par bit — -log2(max(p0, p1)). C'est la mesure qui compte pour une source d'entropie (NIST SP 800-90B) car elle se règle sur l'issue la plus probable, donc sur le meilleur pari d'un attaquant. L'entropie de Shannon, qui moyenne, est bien plus flatteuse.

Rendement de von Neumann : 21.9 % des bits survivent, pour un débit final de 76.2 kbit/s. Le CSPRNG du système délivre 15.4 Gbit/s — un rapport de 604 522 × en faveur du logiciel.

LCG (octet de poids faible)63.12 Mbit/sMersenne Twister (MT19937)4 468.27 Mbit/ssecrets (CSPRNG système)15 384.61 Mbit/sJitter CPU — extraction naïve8.77 Mbit/sJitter CPU — brut0.44 Mbit/sJitter CPU — débiaisé (von Neumann)0.09 Mbit/sJitter CPU — conditionné SHA-2560.03 Mbit/sSource NULLE + SHA-256329.45 Mbit/s

Débit de production, échelle logarithmique.

Acte IIISHA-256 lave plus blanc

Dernière étape du montage classique : conditionner la sortie par une fonction de hachage. Appliqué à notre jitter débiaisé, SHA-256 fait passer la source de 2/8 à 8/8. Victoire ?

Alors appliquons exactement le même conditionnement à la source nulle — le flux de zéros de l'acte II, entropie mesurée à 0,000 bit. Résultat : 8/8 également, avec des p-valeurs tout aussi confortables.

Un générateur sans aucune entropie est statistiquement indiscernable d'un bon générateur. Les tests ne mesurent pas la source : ils mesurent le conditionneur. SHA-256 produit une sortie d'apparence parfaite à partir de n'importe quelle entrée, y compris un compteur.

C'est exactement pour cela qu'un générateur matériel maison est risqué. Si la diode vieillit, se sature ou se débranche, la sortie reste impeccable aux tests — et devient entièrement prédictible pour qui devine le compteur. Les vrais TRNG certifiés (AIS-31, NIST SP 800-90B) imposent pour cette raison des tests de santé sur la source brute, en continu, avant tout conditionnement.

Acte IVL'attaque : Mersenne Twister, 8/8 et prédictible

MT19937 est le générateur de random en Python, de PHP, de Ruby, d'Excel. Il passe les huit tests sans la moindre faiblesse. Sa période est de 219937−1.

Il conserve 624 mots de 32 bits d'état interne. Chaque sortie est produite en appliquant à un mot d'état une fonction de « tempering » faite de décalages et de XOR. Cette fonction est bijective : elle brouille, elle ne cache pas. On l'inverse.

y ^= y >> 18                    → inversible en une passe
y ^= (y << 15) & 0xEFC60000     → inversible par itération
y ^= (y << 7)  & 0x9D2C5680     → inversible par itération
y ^= y >> 11                    → inversible par itération

Observer 624 sorties consécutives, c'est donc lire l'état interne complet. On reconstruit un clone, et on prédit la suite.

5 000/5 000sorties de MT19937 prédites après observation de 624 valeurs
100 %taux de réussite de l'attaque
0/5 000sur le CSPRNG, avec exactement la même attaque

Aucun des huit tests ne voit cette faiblesse, et aucun ne le pourrait : la sortie de MT19937 est statistiquement excellente. La faille n'est pas dans la distribution, elle est dans la structure de l'algorithme.

Ce qu'il faut en retenir

ConstatConséquence
Le LCG a une entropie de Shannon parfaite et se comprime d'un facteur 153 Mesurer l'entropie d'une sortie ne dit rien de sa qualité
Le jitter naïf produit 0 bit d'entropie sans lever d'erreur Une source physique doit être mesurée, jamais supposée
Von Neumann laisse un déséquilibre de 0.5024 Corriger le biais ne corrige pas la corrélation
Une source nulle conditionnée par SHA-256 passe les 8 tests Les tests jugent le conditionneur, pas la source
MT19937 passe les 8 tests et se prédit à 100 % Réussir les tests n'est pas une preuve de sécurité
Le CSPRNG est 604 522 × plus rapide que le TRNG logiciel Fabriquer son propre aléa coûte cher et rapporte moins

D'où la conclusion pratique, qui est aussi le principe de conception de KeyMint : pour un secret, on utilise le CSPRNG du système. Il est déjà alimenté par l'entropie matérielle, déjà conditionné, déjà audité — et il ne meurt pas en silence.

Méthode

Les huit tests sont implémentés dans ce projet, sans bibliothèque de statistiques. Les p-valeurs reposent sur une fonction gamma incomplète régularisée écrite à la main, validée contre SciPy dans la suite de tests. Chaque test est par ailleurs éprouvé sur des suites délibérément défectueuses dont on connaît le défaut — un test qui ne rejette jamais rien ne vaut rien.

py run_lab.py       # banc d'essai → report/results.json
py make_report.py   # → report/index.html + index.en.html
py tests/test_rnglab.py