Giochiamo a poker con il computer, solo tu, io e un server di cui entrambi ci fidiamo. Il server utilizza un generatore di numeri pseudo-casuale che viene inizializzato con un seme a 32 bit proprio prima di giocare. Quindi ci sono circa quattro miliardi di mazzi possibili.
Ho cinque carte in mano - apparentemente non stiamo giocando a Texas Hold 'Em. Supponiamo che le carte vengano distribuite una a me, una a te, una a me, una a te e così via. Quindi ho la prima, la terza, la quinta, la settima e la nona carta nel mazzo.
In precedenza ho eseguito il generatore di numeri pseudo-casuale quattro miliardi di volte, una volta per ogni seme, e ho scritto la prima carta generata per ciascuna in un database. Supponiamo che la mia prima carta sia la regina di picche. Ciò mostra solo una come prima carta in una su ogni 52 possibili mazzi, quindi abbiamo ridotto i mazzi possibili da quattro miliardi a circa 80 milioni circa.
Supponiamo che la mia seconda carta sia il tre di cuori. Ora eseguo il mio RNG 80 milioni di volte in più usando gli 80 milioni di semi che producono la regina di picche come primo numero. Mi ci vogliono un paio di secondi. Scrivo tutti i mazzi che producono i tre cuori come terza carta, la seconda carta nella mia mano. Questo è di nuovo solo circa il 2% dei deck, quindi ora siamo scesi a 2 milioni di deck.
Supponiamo che la terza carta nella mia mano sia il 7 di fiori. Ho un database di 2 milioni di semi che distribuiscono le mie due carte; Corro il mio RNG altre 2 milioni di volte per trovare il 2% di quei mazzi che producono il 7 di fiori come terza carta, e siamo scesi a soli 40 mila mazzi.
Vedi come va. Corro il mio RNG 40000 più volte per trovare tutti i semi che producono la mia quarta carta, e questo ci porta fino a 800 mazzi, e poi eseguo altre 800 volte per ottenere i ~ 20 semi che producono la mia quinta carta, e ora ho appena genera quei venti mazzi di carte e so che hai una delle venti mani possibili. Inoltre, ho un'ottima idea di ciò che disegnerò dopo.
Ora capisci perché la vera casualità è importante? Nel modo in cui lo descrivi, pensi che la distribuzione sia importante, ma la distribuzione non è ciò che rende casuale un processo. L'imprevedibilità è ciò che rende casuale un processo.
AGGIORNARE
Sulla base dei commenti (ora eliminati a causa della loro natura non costruttiva), almeno lo 0,3% delle persone che hanno letto questo è confuso riguardo al mio punto. Quando le persone sostengono contro i punti che non ho fatto, o peggio, sostengono per i punti che ho fatto fanno sul presupposto che io non li faccio, allora so che ho bisogno di spiegare in modo più chiaro e con attenzione.
Sembra esserci una particolare confusione nella distribuzione delle parole, quindi desidero richiamare con attenzione gli usi.
Le domande a portata di mano sono:
- In cosa differiscono i numeri pseudocasuali e quelli veramente casuali?
- Perché la differenza è importante?
- Le differenze hanno qualcosa a che fare con la distribuzione dell'output del PRNG?
Cominciamo considerando il modo perfetto per generare un mazzo casuale di carte con cui giocare a poker. Quindi vedremo come le altre tecniche per la generazione dei deck sono diverse e se è possibile sfruttare questa differenza.
Partiamo dal presupposto che abbiamo una scatola magica etichettata TRNG
. Come suo input gli diamo un numero intero n maggiore o uguale a uno e come output ci dà un numero veramente casuale compreso tra uno e n, incluso. L'output della casella è del tutto imprevedibile (quando viene dato un numero diverso da uno) e qualsiasi numero tra uno e n è probabile come un altro; vale a dire che la distribuzione è uniforme . (Esistono altri controlli statistici più avanzati di casualità che potremmo eseguire; sto ignorando questo punto in quanto non è pertinente al mio argomento. TRNG è perfettamente statisticamente casuale per ipotesi.)
Iniziamo con un mazzo di carte non mischiato. Chiediamo alla casella un numero compreso tra 1 e 52 - ovvero TRNG(52)
. Qualunque numero restituisca, contiamo quelle carte dal nostro mazzo ordinato e rimuoviamo quella carta. Diventa la prima carta nel mazzo mescolato. Quindi chiediamo TRNG(51)
e facciamo lo stesso per selezionare la seconda carta e così via.
Un altro modo di vederlo è: ce ne sono 52! = 52 x 51 x 50 ... x 2 x 1 mazzi possibili, che è approssimativamente 2 226 . Ne abbiamo scelto uno a caso.
Ora distribuiamo le carte. Quando guardo le mie carte non ho idea di quali carte tu abbia. (A parte il fatto ovvio che non hai nessuna delle carte che ho.) Potrebbero essere qualsiasi carta, con uguale probabilità.
Quindi fammi assicurarmi di spiegarlo chiaramente. Abbiamo una distribuzione uniforme di ogni singola uscita di TRNG(n)
; ognuno prende un numero compreso tra 1 e n con probabilità 1 / n. Inoltre, il risultato di questo processo è che abbiamo scelto uno dei 52! possibili piattaforme con una probabilità di 1/52 !, quindi la distribuzione sopra l'insieme di possibili ponti è anche uniforme.
Tutto ok.
Supponiamo ora di avere una scatola meno magica, etichettata PRNG
. Prima di poterlo utilizzare, è necessario eseguire il seeding con un numero senza segno a 32 bit.
A parte: perché 32 ? Non è possibile eseguire il seeding con un numero di 64 o 256 o 10000 bit? Sicuro. Ma (1) in pratica la maggior parte dei PRNG standardizzati sono seminati con un numero a 32 bit e (2) se hai 10000 bit di casualità per creare il seme, allora perché stai usando un PRNG? Hai già una fonte di 10000 bit di casualità!
Comunque, tornando a come funziona il PRNG: dopo che è stato seminato, puoi usarlo nello stesso modo in cui lo usi TRNG
. Cioè, gli passi un numero, n, e ti restituisce un numero compreso tra 1 e n, incluso. Inoltre, la distribuzione di tale output è più o meno uniforme . Cioè, quando chiediamo PRNG
un numero compreso tra 1 e 6, otteniamo 1, 2, 3, 4, 5 o 6 ciascuno circa un sesto del tempo, indipendentemente dal seme.
Voglio sottolineare questo punto più volte perché sembra essere quello che confonde alcuni commentatori. La distribuzione del PRNG è uniforme in almeno due modi. In primo luogo, supponiamo di scegliere un seme particolare. Ci aspetteremmo che la sequenza PRNG(6), PRNG(6), PRNG(6)...
un milione di volte produrrebbe una distribuzione uniforme dei numeri tra 1 e 6. E in secondo luogo, se scegliamo un milione di semi diversi e chiamassimo PRNG(6)
una volta per ogni seme, ci aspetteremmo di nuovo una distribuzione uniforme dei numeri da 1 a 6. L'uniformità del PRNG attraverso una di queste operazioni non è rilevante per l'attacco che sto descrivendo .
Si dice che questo processo sia pseudo-casuale perché il comportamento della scatola è in realtà completamente deterministico; sceglie da uno dei 2 32 possibili comportamenti basati sul seme. Cioè, una volta che viene seminato, PRNG(6), PRNG(6), PRNG(6), ...
produce una sequenza di numeri con una distribuzione uniforme, ma quella sequenza è interamente determinata dal seme. Per una determinata sequenza di chiamate, ad esempio PRNG (52), PRNG (51) ... e così via, ci sono solo 2 32 sequenze possibili. Il seme essenzialmente sceglie quale otteniamo.
Per generare un mazzo il server ora genera un seme. (Come? Torneremo a quel punto.) Poi si chiamano PRNG(52)
, PRNG(51)
e così via per generare il ponte, simile a prima.
Questo sistema è suscettibile all'attacco che ho descritto. Per attaccare il server, prima di tutto, seminiamo la nostra copia della scatola con 0 e chiediamo PRNG(52)
e annotiamo. Quindi ri-seminiamo con 1, chiediamo PRNG(52)
e scriviamo, fino a 2 32 -1.
Ora, il server di poker che utilizza PRNG per generare mazzi deve in qualche modo generare un seme. Non importa come lo fanno. Potrebbero chiamare TRNG(2^32)
per ottenere un seme veramente casuale. Oppure potrebbero prendere il tempo corrente come seme, il che non è affatto casuale; So che ora è tanto quanto te. Il punto del mio attacco è che non importa, perché ho il mio database . Quando vedo la mia prima carta posso eliminare il 98% dei semi possibili. Quando vedo la mia seconda carta posso eliminare il 98% in più, e così via, fino a quando non riesco ad arrivare a una manciata di possibili semi e sapere con molta probabilità cosa c'è nella tua mano.
Ora, ancora una volta, voglio sottolineare che qui il presupposto è che se chiamassimo PRNG(6)
un milione di volte otterremmo ogni numero circa un sesto delle volte . Quella distribuzione è (più o meno) uniforme e se l'uniformità di quella distribuzione è tutto ciò che ti interessa , va bene. Il punto della domanda era : ci sono altre cose PRNG(6)
che ci interessano della distribuzione? e la risposta è sì . Ci preoccupiamo anche dell'imprevedibilità .
Un altro modo di esaminare il problema è che anche se la distribuzione di un milione di chiamate PRNG(6)
potrebbe andare bene, poiché il PRNG sta scegliendo tra solo 2 32 possibili comportamenti, non può generare tutti i possibili deck. Può generare solo 2 32 dei 2 226 mazzi possibili; una piccola frazione. Quindi la distribuzione sul set di tutti i deck è pessima. Ma ancora una volta, l'attacco fondamentale qui si basa sulla nostra capacità di prevedere con successo il comportamento passato e futuro di PRNG
un piccolo campione della sua produzione.
Consentitemi di dirlo una terza o quattro volte per assicurarmi che questo affondi. Ci sono tre distribuzioni qui. Innanzitutto, la distribuzione del processo che produce il seme casuale a 32 bit. Questo può essere perfettamente casuale, imprevedibile e uniforme e l'attacco continuerà a funzionare . In secondo luogo, la distribuzione di un milione di chiamate a PRNG(6)
. Questo può essere perfettamente uniforme e l'attacco continuerà a funzionare. Terzo, ho descritto la distribuzione dei mazzi scelti dal processo pseudo-casuale. Quella distribuzione è estremamente scarsa; solo una minima parte dei possibili mazzi IRL può essere scelta. L'attacco dipende dalla prevedibilità del comportamento del PRNG in base alla conoscenza parziale del suo output .
A parte: questo attacco richiede che l'attaccante sappia o sia in grado di indovinare quale sia l'algoritmo esatto utilizzato dal PRNG. Se questo è realistico o no è una domanda aperta. Tuttavia, quando si progetta un sistema di sicurezza, è necessario progettarlo per proteggerlo dagli attacchi anche se l'utente malintenzionato conosce tutti gli algoritmi nel programma . Detto in altro modo: la parte di un sistema di sicurezza che deve rimanere segreta affinché il sistema sia sicuro è chiamata "chiave". Se il tuo sistema dipende dalla sua sicurezza dagli algoritmi che usi come segreto, la tua chiave contiene quegli algoritmi . Questa è una posizione estremamente debole in cui trovarsi!
Andare avanti.
Supponiamo ora di avere una terza scatola magica etichettata CPRNG
. È una versione crittografica di PRNG
. Prende un seme a 256 bit anziché un seme a 32 bit. Condivide con PRNG
la proprietà che il seme sceglie tra uno di 2 256 comportamenti possibili. E come le altre nostre macchine, ha la proprietà che un gran numero di chiamate CPRNG(n)
produce una distribuzione uniforme dei risultati tra 1 e n: ognuno avviene 1 / n del tempo. Possiamo eseguire il nostro attacco contro di esso?
Il nostro attacco originale ci impone di memorizzare 2 32 mappature dai semi a PRNG(52)
. Ma 2 256 è un numero molto più grande; è del tutto impossibile eseguirlo CPRNG(52)
più volte e archiviare i risultati.
Ma supponiamo che ci sia un altro modo per prendere il valore di CPRNG(52)
e da ciò dedurre un fatto sul seme? Finora siamo stati piuttosto stupidi, costringendo solo brutalmente tutte le possibili combinazioni. Possiamo guardare dentro la scatola magica, capire come funziona e dedurre fatti sul seme in base all'output?
No. I dettagli sono troppo complicati da spiegare, ma i CPRNG sono progettati in modo intelligente in modo che sia impossibile dedurre qualsiasi fatto utile sul seme dal primo output di CPRNG(52)
o da qualsiasi sottoinsieme dell'output, non importa quanto sia grande .
OK, quindi ora supponiamo che il server stia usando CPRNG
per generare deck. Ha bisogno di un seme a 256 bit. Come sceglie quel seme? Se sceglie un valore che un attaccante può prevedere, improvvisamente l'attacco diventa di nuovo praticabile . Se riusciamo a determinare quello dei 2 256 possibili seed, è probabile che solo quattro miliardi di essi saranno scelti dal server, quindi saremo di nuovo in affari . Possiamo montare di nuovo questo attacco, prestando attenzione solo al piccolo numero di semi che possono essere generati.
Pertanto, il server dovrebbe lavorare per garantire che il numero di 256 bit sia distribuito uniformemente , ovvero ogni possibile seed viene scelto con probabilità di 1/2 256 . Fondamentalmente il server dovrebbe chiamare TRNG(2^256)-1
per generare il seme per CPRNG
.
E se potessi hackerare il server e scrutarlo per vedere quale seme è stato scelto? In tal caso, l'attaccante conosce l'intero passato e il futuro del CPRNG . L'autore del server deve proteggersi da questo attacco! (Certo, se riesco a montare con successo questo attacco, probabilmente posso anche trasferire semplicemente i soldi sul mio conto bancario direttamente, quindi forse non è poi così interessante. Il punto è: il seme deve essere un segreto difficile da indovinare, e un un numero a 256 bit veramente casuale è davvero difficile da indovinare.)
Tornando al mio precedente punto sulla difesa in profondità: il seme a 256 bit è la chiave di questo sistema di sicurezza. L'idea di un CPRNG è che il sistema è sicuro fintanto che la chiave è sicura ; anche se ogni altro fatto sull'algoritmo è noto, fintanto che puoi mantenere segreta la chiave, le carte dell'avversario sono imprevedibili.
OK, quindi il seme dovrebbe essere sia segreto che uniformemente distribuito perché se non lo è, possiamo montare un attacco. Partiamo dal presupposto che la distribuzione degli output di CPRNG(n)
sia uniforme. Che dire della distribuzione sul set di tutti i mazzi possibili?
Potresti dire: ci sono 2 256 sequenze possibili emesse dal CPRNG, ma ci sono solo 2 226 mazzi possibili. Quindi ci sono più sequenze possibili rispetto ai deck, quindi stiamo bene; ogni possibile mazzo IRL è ora (con alta probabilità) possibile in questo sistema. E questo è un buon argomento tranne ...
2 226 è solo un'approssimazione di 52 !. Dividilo. 2 256/52 ! non può essere un numero intero perché, per prima cosa, 52! è divisibile per 3 ma nessuna potenza di due lo è! Dal momento che questo non è un numero intero ora abbiamo la situazione in cui tutti i mazzi sono possibili , ma alcuni mazzi sono più probabili di altri .
Se ciò non è chiaro, considera la situazione con numeri più piccoli. Supponiamo di avere tre carte, A, B e C. Supponiamo di usare un PRNG con un seme a 8 bit, quindi ci sono 256 possibili semi. Esistono 256 possibili output in PRNG(3)
base al seme; non c'è modo di avere un terzo di essi come A, un terzo di essi B e un terzo di loro C perché 256 non è uniformemente divisibile per 3. Ci deve essere un piccolo orientamento verso uno di essi.
Allo stesso modo, 52 non si divide uniformemente in 2 256 , quindi ci deve essere una certa propensione verso alcune carte come la prima carta scelta e una distorsione da altre.
Nel nostro sistema originale con un seme a 32 bit c'era un grosso pregiudizio e la stragrande maggioranza dei mazzi possibili non fu mai prodotta. In questo sistema è possibile produrre tutti i deck, ma la distribuzione dei deck è ancora imperfetta . Alcuni mazzi sono leggermente più probabili di altri.
Ora la domanda è: abbiamo un attacco basato su questo difetto? e la risposta è in pratica, probabilmente no . CPRNGs sono progettati in modo che se il seme sia realmente casuale , allora è computazionalmente impossibile capire la differenza tra CPRNG
e TRNG
.
OK, quindi riassumiamo.
In cosa differiscono i numeri pseudocasuali e quelli veramente casuali?
Differiscono nel livello di prevedibilità che esibiscono.
- I numeri veramente casuali non sono prevedibili.
- Tutti i numeri pseudo-casuali sono prevedibili se il seme può essere determinato o indovinato.
Perché la differenza è importante?
Perché ci sono applicazioni in cui la sicurezza del sistema si basa sull'imprevedibilità .
- Se si utilizza un TRNG per scegliere ciascuna scheda, il sistema non è disponibile.
- Se si utilizza un CPRNG per scegliere ciascuna carta, il sistema è sicuro se il seme è sia imprevedibile che sconosciuto.
- Se viene utilizzato un PRNG normale con un piccolo spazio seme, il sistema non è sicuro indipendentemente dal fatto che il seme sia imprevedibile o sconosciuto; uno spazio di semi abbastanza piccolo è suscettibile agli attacchi di forza bruta del tipo che ho descritto.
La differenza ha a che fare con la distribuzione dell'output del PRNG?
L'uniformità di distribuzione o la sua assenza per le singole chiamate a RNG(n)
non è rilevante per gli attacchi che ho descritto.
Come abbiamo visto, sia a PRNG
che CPRNG
producono scarse distribuzioni della probabilità di scegliere un singolo mazzo di tutti i mazzi possibili. Il PRNG
è notevolmente peggio, ma entrambi hanno problemi.
Un'altra domanda:
Se TRNG è molto meglio di CPRNG, che a sua volta è molto meglio di PRNG, perché qualcuno usa CPRNG o PRNG?
Due ragioni.
Primo: spese. TRNG è costoso . Generare numeri veramente casuali è difficile. I CPRNG danno buoni risultati per molte chiamate arbitrariamente con una sola chiamata a TRNG per il seed. Il lato negativo è ovviamente che devi mantenere segreto quel seme .
Secondo: a volte vogliamo prevedibilità e tutto ciò che ci interessa è una buona distribuzione. Se stai generando dati "casuali" come input di programma per una suite di test, e mostra un bug, sarebbe bello che eseguire di nuovo la suite di test produca di nuovo il bug!
Spero che ora sia molto più chiaro.
Infine, se ti è piaciuto, allora potresti goderti qualche ulteriore lettura sul tema della casualità e delle permutazioni: