2011-09-15 3 views
5

Per i test di unità di un'utilità crittografica, vorrei poter forzare il generatore di numeri casuali crittografico di OpenSSL (sia RAND_bytes e RAND_pseudo_bytes) per restituire sequenze di byte prevedibili e ripetibili, in modo che vari i cifrari sono a loro volta prevedibili e possono essere cotti in vettori di test. (Tutti gli altri materiali chiave è sotto il mio controllo.)Forzare gli RNG di openssl per restituire una sequenza di byte ripetibile

So che questo sconfigge totalmente la sicurezza. Questo sarà usato solo per i test unitari.

non posso semplicemente chiamare RAND_seed con un seme fisso prima di ogni prova, a causa (sembra) il RNG automaticamente si semi di /dev/urandom se io lo voglia o no, e comunque RAND_seed non ripristinate la RNG, aggiunge solo il seme al pool di entropia.

C'è un modo per farlo? (In extremis, sembra che potrei scrivere il mio motore PRNG, ma mi piacerebbe pensare che c'è un'opzione più semplice.)

+0

Non è necessario un PRNG. Hai solo bisogno di un flusso prevedibile di valori; anche un contatore farà. –

risposta

5

È possibile forzare FIPS ANSI X9.31 RNG in una modalità di test in fase di esecuzione, ma non in SSLeay RNG (impostazione predefinita). Se si ricompila OpenSSL con -DPREDICT, l'RNG predefinito genererà una sequenza prevedibile di numeri, ma non è molto conveniente.

La funzione RAND_pseudo_bytes genera una serie di numeri prevedibili, il che significa che non aggiunge automaticamente entropia come RAND_bytes. Ma come hai notato è possibile solo aggiungere entropia al seme, non fornire esplicitamente il seme, quindi tra le esecuzioni del programma otterrai numeri diversi. Inoltre non utile.

Ma scrivere il proprio motore RNG prevedibile non è difficile. In realtà, io ti porto attraverso di essa facendo un motore di rand con stdlib di rand() al suo interno:

#include <cstdio> 
#include <cstdlib> 
#include <cassert> 
#include <openssl/rand.h> 

// These don't need to do anything if you don't have anything for them to do. 
static void stdlib_rand_cleanup() {} 
static void stdlib_rand_add(const void *buf, int num, double add_entropy) {} 
static int stdlib_rand_status() { return 1; } 

// Seed the RNG. srand() takes an unsigned int, so we just use the first 
// sizeof(unsigned int) bytes in the buffer to seed the RNG. 
static void stdlib_rand_seed(const void *buf, int num) 
{ 
     assert(num >= sizeof(unsigned int)); 
     srand(*((unsigned int *) buf)); 
} 

// Fill the buffer with random bytes. For each byte in the buffer, we generate 
// a random number and clamp it to the range of a byte, 0-255. 
static int stdlib_rand_bytes(unsigned char *buf, int num) 
{ 
     for(int index = 0; index < num; ++index) 
     { 
       buf[index] = rand() % 256; 
     } 
     return 1; 
} 

// Create the table that will link OpenSSL's rand API to our functions. 
RAND_METHOD stdlib_rand_meth = { 
     stdlib_rand_seed, 
     stdlib_rand_bytes, 
     stdlib_rand_cleanup, 
     stdlib_rand_add, 
     stdlib_rand_bytes, 
     stdlib_rand_status 
}; 

// This is a public-scope accessor method for our table. 
RAND_METHOD *RAND_stdlib() { return &stdlib_rand_meth; } 

int main() 
{ 
     // If we're in test mode, tell OpenSSL to use our special RNG. If we 
     // don't call this function, OpenSSL uses the SSLeay RNG. 
     int test_mode = 1; 
     if(test_mode) 
     { 
       RAND_set_rand_method(RAND_stdlib()); 
     } 

     unsigned int seed = 0x00beef00; 
     unsigned int rnum[5]; 

     RAND_seed(&seed, sizeof(seed)); 
     RAND_bytes((unsigned char *)&rnum[0], sizeof(rnum)); 
     printf("%u %u %u %u %u\n", rnum[0], rnum[1], rnum[2], rnum[3], rnum[4]); 

     return 0; 
} 

Ogni volta che si esegue questo programma, semi srand() con lo stesso numero e quindi si dà la stessa sequenza di numeri casuali ogni volta.

corruptor:scratch indiv$ g++ rand.cpp -o r -lcrypto -g 
corruptor:scratch indiv$ ./r 
1547399009 981369121 2368920148 925292993 788088604 
corruptor:scratch indiv$ ./r 
1547399009 981369121 2368920148 925292993 788088604 
corruptor:scratch indiv$ 
+0

Grazie! La documentazione di OpenSSL mi dava l'impressione che sovrascrivere il RNG interno fosse molto più difficile di quello, motivo per cui ero così riluttante a farlo. – zwol

+0

La documentazione implica anche che si dovrebbe fare questo con ENGINEs piuttosto che con RAND_METHODs, ma non riesco a capire come implementare il mio MOTORE. Potresti commentarlo? – zwol

+0

@Zack: Un ENGINE è un contenitore e espone un'interfaccia per caricare e rilasciare le implementazioni algoritmiche in modo dinamico. Questo è tutto. Quindi, se dovessi implementare un motore RNG come oggetto ENGINE, dovresti prima implementare ciò che ti ho mostrato sopra, e poi implementeresti un wrapper ENGINE attorno ad esso per ottenere tutti i vantaggi dell'interfaccia ENGINE. L'interfaccia dell'algoritmo rimane invariata (le funzioni 'RAND_ *' e la tabella 'RAND_METHOD'). Se si implementa un MOTORE, è comunque necessario supportare gli utenti OpenSSL che hanno compilato '-DOPENSSL_NO_ENGINE' continuando a fornire l'accesso alla tabella. – indiv

1

Scrivi un wrapper attorno alla libreria. Poi sostituiscilo al momento del test per la tua simulazione che restituisce i tuoi valori magici.

Ricordare, in un test di unità non si sta tentando di testare OpenSSL. Stai provando a testare il tuo codice.

+0

Questo sarebbe difficile come scrivere il mio motore PRNG per OpenSSL. Ottenere il PRN di OpenSSL per fare ciò che voglio dovrebbe essere molto più facile. – zwol

+0

OK, quindi ingannare il linker del progetto di test. Implementa il tuo RAND_bytes() e RAND_pseudo_bytes() in un modulo sorgente e verrà collegato prima che le librerie siano incluse. –

+0

"Implementa il tuo [PRNG crittografico]" è la cosa difficile da scrivere come il mio MOTORE. Il punto della domanda è che non voglio doverlo fare se può essere evitato. – zwol