2011-09-15 11 views
5

Para las pruebas unitarias de una utilidad criptográfica, me gustaría poder forzar el generador de números aleatorios criptográficos de OpenSSL (RAND_bytes y RAND_pseudo_bytes) para devolver secuencias de bytes predecibles y repetibles, de modo que los textos cifrados son a su vez predecibles y pueden hornearse en vectores de prueba. (El resto del material clave está bajo mi control.)Fuerza los RNG de openssl para devolver una secuencia de bytes repetible

Sé que esto es totalmente contrario a la seguridad. Esto solo se usará para pruebas unitarias.

no puedo simplemente llamar RAND_seed con una semilla fija antes de cada prueba, ya que (al parecer) el RNG automáticamente semillas en sí de /dev/urandom lo quiera o no, y en cualquier caso RAND_seed no restablecen la RNG, solo agrega la semilla al grupo de entropía.

¿Hay alguna manera de hacer esto? (In extremis, parece que podría escribir mi propio motor de PRNG, pero me gustaría pensar que hay una opción más simple.)

+0

No necesita un PRNG. Solo necesita una secuencia predecible de valores; incluso un contador servirá. –

Respuesta

5

Puede forzar el FIPS ANSI X9.31 RNG a un modo de prueba en tiempo de ejecución, pero no al SSLeay RNG (valor predeterminado). Si recompila OpenSSL con -DPREDICT, el RNG predeterminado generará una secuencia predecible de números, pero eso no es muy conveniente.

La función RAND_pseudo_bytes genera una serie predecible de números, lo que significa que no agrega automáticamente entropía como RAND_bytes. Pero como notó, solo es posible agregar entropía a la semilla, no proporcionar la semilla explícitamente, por lo que entre las ejecuciones del programa obtendrá diferentes números. Tampoco es útil.

Pero escribir su propio motor RNG predecible no es difícil. De hecho, te llevaré a través de él al hacer un motor de rand con stdlib de rand() en su núcleo:

#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; 
} 

Cada vez que se ejecuta este programa, semillas srand() con el mismo número y por lo tanto le da la misma secuencia de números aleatorios todo el tiempo.

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

¡Gracias! La documentación de OpenSSL me dio la impresión de que anular el RNG interno era mucho más difícil que eso, y por eso era tan reacio a hacerlo. – zwol

+0

La documentación también implica fuertemente que uno debe hacer esto con ENGINEES en lugar de con RAND_METHOD, pero no entiendo cómo implementar mi propio ENGINE. ¿Puedes comentar sobre eso? – zwol

+0

@Zack: Un ENGINE es un contenedor y expone una interfaz para cargar y liberar implementaciones de algoritmo de forma dinámica. Eso es. Entonces, si tuviera que implementar un motor RNG como objeto ENGINE, primero implementaría lo que le mostré anteriormente, y luego implementaría un contenedor ENGINE para obtener todos los beneficios de la interfaz ENGINE. La interfaz del algoritmo permanece sin cambios (las funciones 'RAND_ *' y la tabla 'RAND_METHOD'). Si implementa un ENGINE, aún debe admitir a los usuarios de OpenSSL que compilaron '-DOPENSSL_NO_ENGINE' al proporcionar el acceso a la tabla. – indiv

1

Escriba un contenedor alrededor de la biblioteca. Luego sustitúyalo en el momento de la prueba por tu propio simulacro que devuelve tus valores mágicos.

Recuerde, en una prueba de unidad no está tratando de probar OpenSSL. Estás tratando de probar tu código.

+0

Esto sería tan difícil como escribir mi propio motor PRNG para OpenSSL. Hacer que el PRNG de OpenSSL haga lo que quiero, debería ser mucho más fácil. – zwol

+0

De acuerdo, luego engañe al enlazador del proyecto de prueba. Implemente sus propios RAND_bytes() y RAND_pseudo_bytes() en un módulo de origen y se vinculará antes de que entren las bibliotecas. –

+0

"Implementar su propio [PRNG criptográfico]" es lo que es tan difícil como escribir mi propio MOTOR. El objetivo de la pregunta es que no quiero tener que hacer eso si puede evitarse. – zwol

Cuestiones relacionadas