Jouons au poker informatique, juste vous, moi et un serveur auquel nous faisons confiance. Le serveur utilise un générateur de nombre pseudo-aléatoire qui est initialisé avec une graine 32 bits juste avant de jouer. Il y a donc environ quatre milliards de ponts possibles.
J'ai cinq cartes dans la main - apparemment, nous ne jouons pas au Texas Hold 'Em. Supposons que les cartes soient distribuées une pour moi, une pour vous, une pour moi, une pour vous, etc. J'ai donc les première, troisième, cinquième, septième et neuvième cartes dans le paquet.
Auparavant, j'avais exécuté le générateur de nombres pseudo-aléatoires quatre milliards de fois, une fois avec chaque graine, et noté la première carte générée pour chacune dans une base de données. Supposons que ma première carte est la reine de pique. Cela ne représente qu'une carte parmi les premières cartes sur 52, nous avons donc réduit le nombre de cartes possibles de quatre à environ 80 millions.
Supposons que ma deuxième carte est le trois de coeurs. Maintenant, je lance mon RNG 80 millions de fois en utilisant les 80 millions de graines qui produisent la reine de pique comme premier chiffre. Cela me prend quelques secondes. J'écris tous les paquets qui produisent les trois coeurs comme la troisième carte - la deuxième carte dans ma main. Encore une fois, cela ne représente que 2% environ des ponts. Nous en sommes maintenant à 2 millions.
Supposons que la troisième carte dans ma main est le 7 des clubs. J'ai une base de données de 2 millions de graines qui distribue mes deux cartes; Je lance mon RNG encore 2 millions de fois pour trouver la troisième carte des 2% de ces decks qui produisent les 7 clubs, et il ne reste plus que 40 000 decks.
Vous voyez comment cela se passe. Je lance plusieurs fois mon RNG 40000 pour trouver toutes les graines qui produisent ma quatrième carte, ce qui nous ramène à 800 decks, puis je le lance encore 800 fois pour obtenir les ~ 20 graines qui produisent ma cinquième carte. générer ces vingt jeux de cartes et je sais que vous avez l'une des vingt mains possibles. De plus, j'ai une très bonne idée de ce que je vais dessiner ensuite.
Maintenant, voyez-vous pourquoi le vrai hasard est important? Selon votre description, la distribution est importante, mais ce n’est pas ce qui rend un processus aléatoire. L’imprévisibilité est ce qui rend un processus aléatoire.
MISE À JOUR
D'après les commentaires (maintenant supprimés en raison de leur nature non constructive), au moins 0,3% des personnes qui ont lu ceci sont confus quant à mon point. Quand les gens vont à l' encontre des points que je ne l' ai pas fait, ou pire, faire valoir des points que je ne fais l'hypothèse que je ne les ai pas, je sais que je dois expliquer plus clairement et soigneusement.
Il semble y avoir une confusion particulière autour de la distribution des mots , je tiens donc à rappeler les usages avec soin.
Les questions à résoudre sont les suivantes:
- Comment les nombres pseudo-aléatoires et les nombres vraiment aléatoires diffèrent-ils?
- Pourquoi la différence est-elle importante?
- Les différences ont-elles quelque chose à voir avec la distribution de la production du PRNG?
Commençons par examiner le moyen idéal pour générer un jeu de cartes aléatoire permettant de jouer au poker. Ensuite, nous verrons en quoi les autres techniques de génération de decks sont différentes et s'il est possible de tirer parti de cette différence.
Commençons par supposer que nous avons une boîte magique étiquetée TRNG
. En entrée, nous lui donnons un entier n supérieur ou égal à un et en sortie, un nombre véritablement aléatoire compris entre un et n, inclus. La sortie de la boîte est totalement imprévisible (lorsqu'on lui attribue un nombre autre qu'un) et tout nombre compris entre un et n est aussi probable qu'un autre; c'est-à-dire que la distribution est uniforme . (Nous pourrions effectuer d'autres vérifications statistiques plus poussées de l'aléa statistique; j'ignore ce point car cela ne correspond pas à mon argument. TRNG est parfaitement statistiquement aléatoire par hypothèse.)
Nous commençons avec un jeu de cartes non mélangé. Nous demandons à la case un nombre compris entre un et 52 - c'est-à-dire TRNG(52)
,. Quel que soit le nombre de cartes rendues, nous comptons le nombre de cartes de notre deck trié et retirons cette carte. Cela devient la première carte du jeu mélangé. Ensuite, nous demandons TRNG(51)
et faisons de même pour sélectionner la deuxième carte, et ainsi de suite.
Une autre façon de voir les choses est la suivante: il y en a 52! = 52 x 51 x 50 ... x 2 x 1 ponts possibles, ce qui correspond à environ 2 226 . Nous avons choisi l'un d'entre eux au hasard.
Maintenant, nous distribuons les cartes. Quand je regarde mes cartes, je n'ai aucune idée de ce que vous avez. (Mis à part le fait évident que vous n'avez aucune des cartes que j'ai.) Ce pourrait être n'importe quelle carte, avec une probabilité égale.
Alors laissez-moi m'assurer que je l'explique clairement. Nous avons une distribution uniforme de chaque sortie individuelle de TRNG(n)
; chacun choisit un nombre compris entre 1 et n avec une probabilité de 1 / n. En outre, le résultat de ce processus est que nous avons choisi l'un des 52! ponts possibles avec une probabilité de 1/52 !, de sorte que la distribution sur l’ensemble des ponts possibles est également uniforme.
D'accord.
Supposons maintenant que nous ayons une boîte moins magique, étiquetée PRNG
. Avant de pouvoir l' utiliser, il doit être tête de série avec un nombre non signé de 32 bits.
AUSSI: Pourquoi 32 ? Ne pourrait-il pas être associé à un nombre de 64, 256 ou 10 000 bits? Sûr. Mais (1) dans la pratique, la plupart des PRNG disponibles dans le commerce sont associées à un nombre de 32 bits et (2) si vous avez 10 000 bits aléatoires pour créer la graine, pourquoi utilisez-vous un PRNG? Vous avez déjà une source de 10000 bits de hasard!
Quoi qu'il en soit, revenons à la façon dont fonctionne le PRNG: une fois qu’il a été semé, vous pouvez l’utiliser de la même façon TRNG
. C'est-à-dire que vous lui attribuez un nombre n et qu'il vous restitue un nombre compris entre 1 et n inclus. De plus, la distribution de cette production est plus ou moins uniforme . Autrement dit, lorsque nous demandons PRNG
un nombre compris entre 1 et 6, nous obtenons 1, 2, 3, 4, 5 ou 6 chacun environ un sixième du temps, quelle que soit la graine.
Je tiens à souligner ce point à plusieurs reprises, car il semble que ce soit la source de confusion pour certains commentateurs. La distribution du PRNG est uniforme à au moins deux égards. Premièrement, supposons que nous choisissions une graine en particulier. Nous nous attendions à ce que la séquence PRNG(6), PRNG(6), PRNG(6)...
un million de fois produise une distribution uniforme de nombres entre 1 et 6. Et deuxièmement, si nous choisissions un million de semences différentes et appelions PRNG(6)
une fois pour chaque graine, nous nous attendrions à une distribution uniforme de nombres de 1 à 6. L'uniformité du GNRP au cours de l'une ou l'autre de ces opérations n'est pas pertinente pour l'attaque que je décris .
Ce processus est dit pseudo-aléatoire car le comportement de la boîte est en réalité totalement déterministe; il choisit parmi l'un des 2 32 comportements possibles en fonction de la graine. C'est-à-dire qu'une fois qu'il est semé, il PRNG(6), PRNG(6), PRNG(6), ...
produit une séquence de nombres avec une distribution uniforme, mais cette séquence est entièrement déterminée par la graine. Par exemple, pour une séquence d'appels donnée, PRNG (52), PRNG (51), etc., il n'y a que 2 32 séquences possibles. La graine choisit essentiellement celle que nous obtenons.
Pour générer une plate-forme, le serveur génère maintenant une graine. (Comment? Nous reviendrons sur ce point.) Ensuite , ils appellent PRNG(52)
, PRNG(51)
et ainsi de suite pour générer la plate - forme, semblable auparavant.
Ce système est sujet à l'attaque que j'ai décrite. Pour attaquer le serveur, nous devons d'abord, à l'avance, ensemencer notre propre copie de la boîte avec 0, puis demander PRNG(52)
et écrire cette information. Ensuite, nous re-semons avec 1, demandons PRNG(52)
et notons-le jusqu’à 2 32 -1.
Maintenant, le serveur de poker qui utilise PRNG pour générer des decks doit générer une graine en quelque sorte. Peu importe comment ils le font. Ils pourraient appeler TRNG(2^32)
pour obtenir une graine vraiment aléatoire. Ils pourraient aussi prendre l’heure actuelle comme une graine, ce qui n’est guère aléatoire. Je sais quelle heure il est autant que toi. Le point de mon attaque est que cela n’a aucune importance, car j’ai ma base de données . Quand je vois ma première carte, je peux éliminer 98% des graines possibles. Quand je vois ma deuxième carte, je peux éliminer 98% de plus, et ainsi de suite, jusqu'à ce que je puisse finalement en arriver à une poignée de semences possibles et savoir avec une grande probabilité ce qui est dans votre main.
Maintenant, encore une fois, je tiens à souligner que l’hypothèse retenue ici est que si nous appelions PRNG(6)
un million de fois, nous aurions chaque numéro environ un sixième du temps . Cette distribution est (plus ou moins) uniforme , et si l'uniformité de cette distribution est tout ce qui vous intéresse , c'est bien. La question était la suivante: y a-t-il autre chose que la distribution de ce PRNG(6)
qui nous intéresse? et la réponse est oui . Nous nous soucions également de l' imprévisibilité .
Une autre façon d’envisager le problème est que, même si la distribution d’un million d’appels PRNG(6)
pourrait bien se dérouler, car le PRNG choisit parmi seulement 2 32 comportements possibles, il ne peut pas générer toutes les réponses possibles. Il ne peut générer que 2 32 des 2 226 ponts possibles; une infime fraction. La distribution sur l’ensemble des decks est donc très mauvaise. Mais là encore, l’attaque fondamentale consiste à pouvoir prédire avec succès le comportement passé et futur d’ PRNG
un petit échantillon de ses résultats.
Permettez-moi de dire ceci une troisième ou quatre fois pour m'assurer que cela est bien intégré. Premièrement, la distribution du processus qui produit la graine aléatoire 32 bits. Cela peut être parfaitement aléatoire, imprévisible et uniforme et l’attaque fonctionnera toujours . Deuxièmement, la distribution d'un million d'appels à PRNG(6)
. Cela peut être parfaitement uniforme et l’attaque fonctionnera toujours. Troisièmement, la distribution des paquets choisie par le processus pseudo-aléatoire que j'ai décrit. Cette distribution est extrêmement pauvre; seule une infime fraction des ponts IRL possibles peut éventuellement être choisie. L'attaque dépend de la prévisibilité du comportement du PRNG sur la base d'une connaissance partielle de ses résultats .
EN DEHORS: Cette attaque nécessite que l'attaquant sache ou soit capable de deviner quel est l'algorithme exact utilisé par le PRNG. Que ce soit réaliste ou non est une question ouverte. Cependant, lors de la conception d'un système de sécurité, vous devez le concevoir pour qu'il soit sécurisé contre les attaques, même si l'attaquant connaît tous les algorithmes du programme . En d'autres termes: la partie d'un système de sécurité qui doit rester secrète pour que le système soit sécurisé s'appelle la "clé". Si votre système dépend pour sa sécurité des algorithmes que vous utilisez étant un secret, votre clé contient ces algorithmes . C'est une position extrêmement faible pour être dans!
Passer à autre chose
Supposons maintenant que nous ayons une troisième boîte magique étiquetée CPRNG
. C'est une version de crypto-force de PRNG
. Il faut une graine 256 bits plutôt qu'une graine 32 bits. Il partage avec PRNG
la propriété que la graine choisit parmi l’un des 2 256 comportements possibles. Et, à l'instar de nos autres machines, il a la propriété de faire un grand nombre d'appels pour CPRNG(n)
produire une distribution uniforme des résultats entre 1 et n: chacune se produit 1 / n des fois. Pouvons-nous lancer notre attaque contre elle?
Notre attaque initiale nécessite que nous stockions 2 32 cartographies à partir de graines PRNG(52)
. Mais 2 256 est un nombre beaucoup plus grand; il est totalement impossible de l'exécuter CPRNG(52)
autant de fois et de stocker les résultats.
Mais supposons qu'il y ait un autre moyen de prendre la valeur CPRNG(52)
et d'en déduire un fait à propos de la graine? Nous avons été assez stupides jusqu'à présent, nous avons simplement forcé toutes les combinaisons possibles. Pouvons-nous regarder à l'intérieur de la boîte magique, comprendre comment cela fonctionne et déduire des faits sur la graine en fonction de la sortie?
Non . Les détails sont trop compliquées à expliquer, mais CPRNGs sont intelligemment conçus de sorte qu'il est impossible d' en déduire tout fait utile sur la graine de la première sortie CPRNG(52)
ou de tout sous - ensemble de la production, peu importe la taille .
OK, supposons maintenant que le serveur utilise CPRNG
pour générer des decks. Il faut une graine de 256 bits. Comment choisit-il cette graine? S'il choisit une valeur qu'un attaquant peut prédire , l'attaque redevient soudainement viable . Si nous pouvons déterminer que sur les 2 256 semences possibles, seuls quatre milliards d'entre elles sont susceptibles d'être choisies par le serveur, nous sommes de retour dans les affaires . Nous pouvons à nouveau monter cette attaque, en ne prêtant attention qu'au petit nombre de graines pouvant éventuellement être générées.
Le serveur doit donc faire en sorte que le nombre de 256 bits soit uniformément distribué - en d’autres termes, chaque graine possible est choisie avec une probabilité de 1/2 256 . Fondamentalement, le serveur devrait appeler TRNG(2^256)-1
pour générer le germe CPRNG
.
Que se passe-t-il si je peux pirater le serveur et y jeter un œil pour voir quelle graine a été choisie? Dans ce cas, l'attaquant connaît tout le passé et l'avenir du CPRNG . L'auteur du serveur doit se prémunir contre cette attaque! (Bien sûr, si je réussis à monter cette attaque, je pourrai probablement aussi transférer l'argent directement sur mon compte bancaire, alors ce n'est peut-être pas si intéressant. L'important, c'est que la graine doit être un secret difficile à deviner, et nombre vraiment aléatoire 256 bits est sacrément difficile à deviner.)
Revenons à mon point précédent sur la défense en profondeur: la valeur de départ de 256 bits est la clé de ce système de sécurité. L'idée d'un CPRNG est que le système est sécurisé tant que la clé est sécurisée . Même si tous les autres faits concernant l'algorithme sont connus, tant que vous pouvez garder la clé secrète, les cartes de l'adversaire sont imprévisibles.
OK, la graine doit donc être à la fois secrète et uniformément distribuée, sinon, nous pouvons organiser une attaque. Nous supposons que la distribution des produits CPRNG(n)
est uniforme. Qu'en est-il de la distribution sur l'ensemble des decks possibles?
Vous pourriez dire: le CPRNG produit 2 256 séquences possibles, mais il n’ya que 2 226 ponts possibles. Par conséquent, il y a plus de séquences possibles que de decks, donc tout va bien; toutes les cartes IRL possibles sont maintenant (avec une probabilité élevée) possibles dans ce système. Et c'est un bon argument, sauf que ...
2 226 n’est qu’une approximation de 52 !. Divisez-le. 2 256/52 ! ne peut pas être un nombre entier parce que, d'une part, 52! est divisible par 3 mais aucune puissance de deux n'est! Comme il ne s’agit pas d’un nombre entier, nous sommes dans une situation où tous les ponts sont possibles , mais certains ponts sont plus susceptibles que d’autres .
Si ce n'est pas clair, considérons la situation avec des nombres plus petits. Supposons que nous ayons trois cartes, A, B et C. Supposons que nous utilisions un PRNG avec une graine à 8 bits, il y a donc 256 graines possibles. Il y a 256 sorties possibles de PRNG(3)
selon la graine; il est impossible qu'un tiers d'entre eux soit A, un tiers d'entre eux B et un tiers d'entre eux C, car 256 n'est pas également divisible par 3. Il doit y avoir un léger parti pris pour l'un d'eux.
De même, 52 ne se divise pas également en 2 256 , il doit donc y avoir un biais envers certaines cartes en tant que première carte choisie et un biais par rapport aux autres.
Dans notre système original avec une graine 32 bits, il y avait un biais énorme et la grande majorité des ponts possibles n’ont jamais été produits. Dans ce système, tous les ponts peuvent être produits, mais la distribution des ponts est toujours défectueuse . Certains ponts sont très légèrement plus susceptibles que d'autres.
Maintenant, la question est: avons-nous une attaque basée sur cette faille? et la réponse est en pratique, probablement pas . CPRNGs sont conçus de telle sorte que si la graine est vraiment aléatoire alors il est infaisable de faire la différence entre CPRNG
et TRNG
.
OK, alors résumons.
Comment les nombres pseudo-aléatoires et les nombres vraiment aléatoires diffèrent-ils?
Ils diffèrent par le niveau de prévisibilité qu'ils présentent.
- Les nombres vraiment aléatoires ne sont pas prévisibles.
- Tous les nombres pseudo-aléatoires sont prévisibles si la graine peut être déterminée ou devinée.
Pourquoi la différence est-elle importante?
Parce qu'il existe des applications où la sécurité du système repose sur l' imprévisibilité .
- Si un TRNG est utilisé pour choisir chaque carte, le système est inattaquable.
- Si un CPRNG est utilisé pour choisir chaque carte, le système est sécurisé si la valeur de départ est imprévisible et inconnue.
- Si un PRNG ordinaire avec un petit espace d’origine est utilisé, le système n’est pas sécurisé, que l’initialisation soit imprévisible ou inconnue; un espace de germination assez petit est susceptible d'attaques par force brute du type que j'ai décrit.
La différence a-t-elle quelque chose à voir avec la distribution de la production du PRNG?
L'uniformité de la distribution ou de l' absence de celle - ci pour des appels individuels à RNG(n)
ne concerne pas les attaques que je viens de décrire.
Comme nous l’avons vu, a PRNG
et CPRNG
produit de mauvaises distributions de la probabilité de choisir n’importe quel deck de tous les decks possibles. Le PRNG
est considérablement pire, mais les deux ont des problèmes.
Encore une question:
Si TRNG est tellement meilleur que CPRNG, qui à son tour est tellement meilleur que PRNG, pourquoi quelqu'un utilise-t-il CPRNG ou PRNG?
Deux raisons.
Premièrement: les dépenses. TRNG est cher . Générer des nombres vraiment aléatoires est difficile. Les CPRNG donnent de bons résultats pour un nombre arbitraire d'appels avec un seul appel à TRNG pour la graine d'origine. L'inconvénient est bien sûr que vous devez garder cette graine secrète .
Deuxièmement: nous voulons parfois de la prévisibilité et la distribution nous importe simplement . Si vous générez des données "aléatoires" en tant qu'entrées de programme pour une suite de tests et que cela indique un bogue, il serait bien que l'exécution de la suite de tests produise à nouveau le bogue!
J'espère que c'est maintenant beaucoup plus clair.
Enfin, si vous appréciez cela, vous pourrez approfondir vos connaissances sur le hasard et les permutations: