2016-02-06 42 views
7

Sono obbligato a creare un generatore casuale di numeri casuali (CS) determinabilmente equo (deterministico &) crittografato in PHP. Stiamo eseguendo PHP 5 e PHP 7 non è davvero un'opzione in questo momento. Tuttavia, ho trovato un polyfill per le nuove funzioni CS di PHP 7, quindi ho implementato quella soluzione (https://github.com/paragonie/random_compat).PHP PRNG seminato, deterministico, crittograficamente sicuro (generatore di numeri pseudo-casuali). È possibile?

Ho pensato che srand() potesse essere utilizzato per seed random_int(), ma ora non sono sicuro se questo è il caso. È possibile seminare un CSPRNG? Se può essere seminato, l'output sarà deterministico (stesso risultato casuale, dato lo stesso seme)?

Ecco il mio codice:

require_once($_SERVER['DOCUMENT_ROOT']."/lib/assets/random_compat/lib/random.php"); 

$seed_a = 8138707157292429635; 
$seed_b = 'JuxJ1XLnBKk7gPASR80hJfq5Ey8QWEIc8Bt'; 

class CSPRNG{ 
    private static $RNGseed = 0; 

    public function generate_seed_a(){ 
     return random_int(0, PHP_INT_MAX); 
    } 

    public function generate_seed_b($length = 35){ 
     $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
     $randomString = ''; 
     for($i = 0; $i < $length; $i++){ 
      $randomString .= $characters[random_int(0, strlen($characters) - 1)]; 
     } 
     return $randomString; 
    } 

    public function seed($s = 0) { 
     if($s == 0){ 
      $this->RNGseed = $this->generate_seed_a(); 
     }else{ 
      $this->RNGseed = $s; 
     } 
     srand($this->RNGseed); 
    } 

    public function generate_random_integer($min=0, $max=PHP_INT_MAX, $pad_zeros = true){ 
     if($this->RNGseed == 0){ 
      $this->seed(); 
     } 
     $rnd_num = random_int($min, $max); 
     if($pad_zeros == true){ 
      $num_digits = strlen((string)$max); 
      $format_str = "%0".$num_digits."d"; 
      return sprintf($format_str, $rnd_num); 
     }else{ 
      return $rnd_num; 
     } 
    } 

    public function drawing_numbers($seed_a, $num_of_balls = 6){ 
     $this->seed($seed_a); 
     $draw_numbers = array(); 
     for($i = 0; $i < $num_of_balls; $i++) { 
      $number = ($this->generate_random_integer(1, 49)); 
      if(in_array($number, $draw_numbers)){ 
       $i = $i-1; 
      }else{ 
       array_push($draw_numbers, $number); 
      } 
     } 
     sort($draw_numbers); 
     return $draw_numbers; 
    } 
} 

$CSPRNG= new CSPRNG(); 

echo '<p>Seed A: '.$seed_a.'</p>'; 
echo '<p>Seed B: '.$seed_b.'</p>'; 
$hash = hash('sha1', $seed_a.$seed_b); 
echo '<p>Hash: '.$hash.'</p>'; 

$drawNumbers = $CSPRNG->drawing_numbers($seed_a); 
$draw_str = implode("-", $drawNumbers); 
echo "<br>Drawing: $draw_str<br>"; 

Quando viene eseguito questo codice, il disegno ($ draw_str) dovrebbe essere la stessa su ogni esecuzione, ma non lo è.

Per dimostrare che il disegno è giusto, un seme (seme A) viene scelto prima che il numero di vincita venga selezionato e mostrato. Viene generato anche un altro numero casuale (seme B). Il seme B è usato come un sale e combinato con seme A e il risultato è un hash. Questo hash viene mostrato all'utente prima del disegno. Verrà inoltre fornito il codice sorgente in modo che, quando viene selezionato il numero vincente, vengano rivelati entrambi i semi. Possono verificare che le corrispondenze hash e tutto è stato fatto in modo equo.

+1

Vuoi dire https://github.com/paragonie/seedspring? –

+0

Sì, ma richiede PHP 7. Non un'opzione. – compcentral

+0

Posso facilmente regolare il compositore.json per richiedere^5.6 |^7.0 e random_compat: \ –

risposta

4

Duskwuff chiede:

Come pensa di dimostrare che il seme è stato scelto in modo equo? Un utente sospetto può facilmente affermare di aver selezionato un seme che avrebbe prodotto risultati favorevoli per utenti specifici o che ha rivelato il seme a utenti specifici in anticipo.

Prima di esaminare le soluzioni, qual è esattamente il problema che si sta tentando di risolvere? Qual è il tuo modello di minaccia?


E suona come desideri SeedSpring (versione 0.3.0 supporta PHP 5.6).

$prng = new \ParagonIE\SeedSpring\SeedSpring('JuxJ1XLnBKk7gPAS'); 
$byte = $prng->getBytes(16); 
\var_dump(bin2hex($byte)); 

Questo dovrebbe sempre tornare:

string(32) "76482c186f7c5d1cb3f895e044e3c649" 

I numeri devono essere imparziale, ma dal momento che è basato fuori un seme pre-condivisa, non è, per definizione rigorosa, crittograficamente sicuro.

Ricorda che SeedSpring è stato creato come implementazione di un giocattolo/bozza di concetto piuttosto che come soluzione di sicurezza open source ufficiale di Paragon Initiative Enterprises, quindi sentiti libero di sostituirlo e modificarlo in base alle tue esigenze. (Dubito che il nostro ramo raggiungerà mai una "versione 1.0.0 stabile").

(Inoltre, se accettate/assegnate la taglia a una qualsiasi di queste risposte, la risposta di Aaron Toponce è più corretta.Crittografia del nonce con la modalità ECB è più performante della crittografia di un lungo flusso di byte NUL con AES -CTR, per lo stesso vantaggio di sicurezza Questa è una delle rarissime occasioni in cui la modalità ECB è ok

+0

Guarda la mia modifica per vedere come dimostriamo che il disegno è stato eseguito correttamente. – compcentral

5

In primo luogo, non si dovrebbe implementare il proprio spazio utente CSPRNG. Il sistema operativo su cui è installato PHP 5 già invia un CSPRNG, e dovresti utilizzarlo per tutta la tua casualità, a meno che tu non sappia che puoi usarlo, o che le prestazioni sono un problema. Dovresti utilizzare random_int(), random_bytes() o openssl_random_pseudo_bytes().

Tuttavia, se è necessario implementare un CSPRNG di userspace, è possibile farlo semplicemente utilizzando una libreria AES (E.G .: libsodium) e crittografando un contatore. Psuedocodarlo sarebbe:

Uint-128 n = 0; 
while true: 
    output = AES-ECB(key, n); 
    n++; 

Essi chiave AES, in questo caso, ha bisogno di entropia sufficiente a resistere ad un attacco sofisticato, o la sicurezza del vostro CSPRNG userspace cade a parte, naturalmente. La chiave potrebbe essere bcrypt() di una password fornita dall'utente.

A condizione che il contatore rappresentato come un numero intero senza segno a 128 bit sia sempre univoco, si otterrà sempre un output univoco ogni volta che il generatore viene "seminato" con un nuovo contatore. Se è seminato con un contatore utilizzato in precedenza, ma con una chiave diversa, anche l'output sarà diverso. Lo scenario migliore, sarebbe una chiave che cambia e un contatore che cambia ogni volta che viene chiamato il generatore.

Potresti essere tentato di utilizzare un indicatore di data e ora di precisione, ad esempio l'accuratezza del microsecondo, nel contatore. Questo va bene, tranne che corri il rischio che qualcuno o qualcosa manipolino l'orologio di sistema. Pertanto, se l'orologio può essere manipolato, il generatore CSPRNG può essere compromesso. È meglio fornire una nuova chiave ogni volta che si chiama il generatore e iniziare a crittografare con uno zero a 128 bit.

Inoltre, si noti che stiamo utilizzando la modalità ECB con AES. Non spaventare. La BCE ha problemi con il mantenimento della struttura nel testo cifrato fornito dal testo in chiaro. In termini generali, non si dovrebbe usare la modalità ECB. Tuttavia, con 128 bit di dati, verrà crittografato solo un singolo blocco ECB, quindi non ci saranno perdite di dati strutturati. La BCE è preferibile rispetto a CTR per uno spazio utente CSPRNG, poiché non è necessario tenere traccia di una chiave, di un oggetto contatore e dei dati da crittografare. Sono necessari solo una chiave e i dati. Assicurati solo di non crittografare più di 128 bit di dati e non avrai mai bisogno di più di 1 blocco.

È possibile seminare un CSPRNG?

Sì, e deve essere sempre seminato. Se osservi il tuo sistema operativo GNU/Linux, probabilmente noterai un file in /var/lib/urandom/random-seed. Quando il sistema operativo si arresta, crea quel file dal CSPRNG. Al prossimo avvio, questo file viene utilizzato per seminare il CSPRNG di kernelspace per impedire il riutilizzo dello stato precedente del generatore. Ad ogni spegnimento, quel file dovrebbe cambiare.

Se può essere inizializzato, l'output sarà deterministico (stesso risultato casuale, dato stesso seme)?

Sì. Fornito lo stesso seme, chiave, ecc., L'output è deterministico, quindi l'output sarà lo stesso. Se una delle variabili cambia, l'output sarà diverso. Questo è il motivo per cui ogni chiamata del generatore dovrebbe essere reinserita.

+0

Hai completamente perso il punto. Questo codice è per disegnare un numero della lotteria. L'unica cosa veramente importante è che l'output è lo stesso dato un seme scelto (per dimostrare che il disegno è giusto). Devo sapere come dovrebbe essere fatto se è possibile. Sto già usando un polyfill random_int() (non è disponibile in modo nativo in PHP5).Sono abbastanza sicuro che stia usando openssl_random_pseudo_bytes() dietro le quinte ma non ho controllato. Non è un problema. La sicurezza non è molto importante finché il PRNG è CS. – compcentral

+1

@compcentral Come intendi dimostrare che il seme è stato scelto in modo equo? Un utente sospetto può facilmente affermare di aver selezionato un seme che avrebbe prodotto risultati favorevoli per utenti specifici o che ha rivelato il seme a utenti specifici in anticipo. – duskwuff

+0

Esistono protocolli crittografici per lotterie giuste che sono equi se almeno un partecipante è onesto. –