2010-04-03 9 views
61

Quiero usar /dev/random o /dev/urandom en C. ¿Cómo puedo hacerlo? No sé cómo puedo manejarlos en C, si alguien sabe, por favor dígame cómo. Gracias.¿Cómo usar/dev/random o urandom en C?

+0

Echa un vistazo a este artículo muy informativo sobre algunas advertencias comunes de tomar esta ruta a (pseudo-) aleatoriedad: http://insanecoding.blogspot.fi/2014/05/a-good-idea-with-bad-usage- devurandom.html – appas

Respuesta

84

En general, es una mejor idea evitar abrir archivos para obtener datos aleatorios, debido a la cantidad de puntos de falla que hay en el procedimiento.

En los últimos distribuciones de Linux, la llamada getrandom sistema puede ser utilizado para obtener números aleatorios cripto-seguro, y no podemos dejar siGRND_RANDOM es no especificado como una bandera y la cantidad de lectura es como máximo de 256 bytes.

A partir de octubre de 2017, OpenBSD, Darwin y Linux (con -lbsd) ahora todos tienen una implementación de arc4random que es cripto-segura y que no puede fallar. Eso lo hace una opción muy atractiva:

char myRandomData[50]; 
arc4random_buf(myRandomData, sizeof myRandomData); // done! 

De lo contrario, puede usar los dispositivos aleatorios como si fueran archivos. Lees de ellos y obtienes datos aleatorios. Estoy usando open/read aquí, pero fopen/fread funcionaría igual de bien.

int randomData = open("/dev/urandom", O_RDONLY); 
if (randomData < 0) 
{ 
    // something went wrong 
} 
else 
{ 
    char myRandomData[50]; 
    ssize_t result = read(randomData, myRandomData, sizeof myRandomData); 
    if (result < 0) 
    { 
     // something went wrong 
    } 
} 

Puede leer muchos más bytes aleatorios antes de cerrar el descriptor de archivo./dev/urandom nunca bloquea y siempre rellena todos los bytes que haya solicitado, a menos que la señal del sistema interrumpa la llamada. Se considera criptográficamente seguro y debe ser su dispositivo aleatorio de acceso.

/dev/random es más meticuloso. En la mayoría de las plataformas, puede devolver menos bytes de los que ha pedido y puede bloquear si no hay suficientes bytes disponibles. Esto hace que la historia de manejo de errores sea más compleja:

int randomData = open("/dev/random", O_RDONLY); 
if (randomData < 0) 
{ 
    // something went wrong 
} 
else 
{ 
    char myRandomData[50]; 
    size_t randomDataLen = 0; 
    while (randomDataLen < sizeof myRandomData) 
    { 
     ssize_t result = read(randomData, myRandomData + randomDataLen, (sizeof myRandomData) - randomDataLen); 
     if (result < 0) 
     { 
      // something went wrong 
     } 
     randomDataLen += result; 
    } 
    close(randomData); 
} 
+0

bien, buena respuesta. ¿Y qué pasa si quiero configurarlo para que lea varios números? ¿Lo haría mientras (algo) {read (..)} o debería abrirlo/cerrarlo cada vez que el ciclo comienza de nuevo? – stojance

+0

Sí, lo leería varias veces. Editaré mi respuesta. – zneak

+3

O simplemente lea más para completar una matriz de números enteros. – mark4o

14

Simplemente abra el archivo para leer y luego leer datos. En C++ 11 es posible que desee utilizar std::random_device que proporciona acceso multiplataforma a dichos dispositivos.

+0

Parece que 'std :: random_device' no entró en el estándar de 2011. Sí aparece en el [borrador N3797] (https://isocpp.org/files/papers/N3797.pdf). –

+2

Parece que ['std :: random_device'] (http://en.cppreference.com/w/cpp/numeric/random/random_device) _did_ lo convierte en C++ 11 al final. – legends2k

+1

El problema es que '' std :: random_device'' está en C++ y no en C, y el OP preguntó cómo usar ''/dev/random'' o ''/dev/urandom'' no cómo usar '' std :: random_device'' aunque es una buena opción usar '' std :: random_device'' y tiene beneficios, simplemente no es lo que solicitó el OP –

7

Zneak es 100% correcto. También es muy común leer un buffer de números aleatorios que es un poco más grande que lo que necesitarás al inicio. A continuación, puede completar una matriz en la memoria, o escribirlos en su propio archivo para su posterior reutilización.

Una aplicación típica de los anteriores:

typedef struct prandom { 
    struct prandom *prev; 
    int64_t number; 
    struct prandom *next; 
} prandom_t; 

Esto se hace más o menos como una cinta que acaba avanza que puede ser mágicamente repuesta por otro hilo, según sea necesario.Hay un lot de services que proporcionan grandes vertederos de archivo de nada más que números aleatorios que se generan con los generadores mucho más fuertes tales como:

  • La desintegración radiactiva
  • comportamiento óptico (fotones que golpean un espejo semi transparente)
  • el ruido atmosférico (no tan fuerte como el anterior)
  • granjas de monos intoxicados escribir en los teclados y ratones en movimiento (es broma)

No utilice entropía 'pre-envasados' para las semillas criptográficas, en caso de que no se va sin decir. Esos conjuntos están bien para las simulaciones, no está bien para la generación de llaves y tal.

No se preocupe por la calidad, si necesita muchos números para algo parecido a una simulación de monte carlo, es mucho mejor tenerlos disponibles de forma que no se bloquee la lectura().

Sin embargo, recuerde que la aleatoriedad de un número es tan determinista como la complejidad involucrada en su generación. /dev/random/dev/urandom y son convenientes, pero no tan fuerte como el uso de un HRNG (o la descarga de un gran vertedero de una HRNG). También vale la pena señalar que /dev/randomrefills via entropy, por lo que puede bloquear por un tiempo dependiendo de las circunstancias.

+2

La descarga de "volcados de archivos grandes de nada más que números aleatorios" es terrible consejo para propósitos criptográficos. Le está pidiendo a alguien que proporcione la semilla para sus funciones, y esos servicios parecen transferir esa información sin cifrar a través de Internet. Por favor no hagas eso. – dequis

+0

@dequis, aclaré. No veo ningún problema con su uso para ejecutar simulaciones grandes, _kinda_ pensó que sería de sentido común no usarlas para keygen/etc, pero vale la pena ser extrañamente específico al punto. La pregunta era agnóstica, así que realmente no se me ocurrió que fuera tan específica, pero sí buena idea. –

18

Existen otras respuestas precisas anteriormente. Sin embargo, necesitaba usar una secuencia FILE*. Esto es lo que hice ...

int byte_count = 64; 
char data[64]; 
FILE *fp; 
fp = fopen("/dev/urandom", "r"); 
fread(&data, 1, byte_count, fp); 
fclose(fp); 
+1

Un int se puede leer directamente simplemente colocando el puntero int en un puntero char. 'fread ((char *) (& myInt), sizeof (myInt), 1, fp)' –

+0

@ AzeemBande-Ali: ¿Por qué no usa fread ((int *) (& myInt), sizeof (myInt), 1, fp) en su lugar? Me refiero a un elenco a int *? – Larry

+4

En ninguno de los casos se debe utilizar un molde en el código C, fread() toma un vacío *, así que simplemente haga clic en fread (& myInt, ...); – nos

3

La respuesta de zneak es simple, pero la realidad es más complicada que eso. Por ejemplo, debe considerar si/dev/{u} al azar realmente es el dispositivo de número aleatorio en primer lugar. Tal escenario puede ocurrir si su máquina se ha visto comprometida y los dispositivos han sido reemplazados por enlaces simbólicos a/dev/zero o un archivo disperso. Si esto sucede, la secuencia aleatoria ahora es completamente predecible.

La manera más simple (al menos en Linux y FreeBSD) es realizar una llamada ioctl en el dispositivo que sólo tendrá éxito si el dispositivo es un generador aleatorio:

int data; 
int result = ioctl(fd, RNDGETENTCNT, &data); 
// Upon success data now contains amount of entropy available in bits 

Si esto se lleva a cabo antes de la primera lea sobre el dispositivo aleatorio, entonces hay una apuesta justa de que tiene el dispositivo aleatorio. Así que la respuesta de @ zneak mejor puede extenderse a ser:

int randomData = open("/dev/random", O_RDONLY); 
int entropy; 
int result = ioctl(randomData, RNDGETENTCNT, &entropy); 

if (!result) { 
    // Error - /dev/random isn't actually a random device 
    return; 
} 

if (entropy < sizeof(int) * 8) { 
    // Error - there's not enough bits of entropy in the random device to fill the buffer 
    return; 
} 

int myRandomInteger; 
size_t randomDataLen = 0; 
while (randomDataLen < sizeof myRandomInteger) 
{ 
    ssize_t result = read(randomData, ((char*)&myRandomInteger) + randomDataLen, (sizeof myRandomInteger) - randomDataLen); 
    if (result < 0) 
    { 
     // error, unable to read /dev/random 
    } 
    randomDataLen += result; 
} 
close(randomData); 

los locos Codificación del blog covered this, and other pitfalls no hace mucho tiempo; Recomiendo leer el artículo completo. Tengo que dar crédito a su origen de donde se extrajo esta solución.

Editado para añadir (25/07/2014) ...
coincidentemente, leí anoche que como parte de la LibReSSL effort, Linux parece estar recibiendo una llamada al sistema GetRandom(). Como al momento de escribir, no hay noticias de cuándo estará disponible en una versión general del kernel. Sin embargo, esta sería la interfaz preferida para obtener datos aleatorios criptográficamente seguros, ya que elimina todas las trampas que proporciona el acceso a través de archivos. Consulte también LibReSSL possible implementation.

+2

Un atacante con suficiente poder para reemplazar/dev/random o/dev/urandom con algo más normalmente también tiene suficiente poder para cargar un módulo de kernel para arruinar cada intento que haces para determinar si es un dispositivo aleatorio o no. – zneak

+0

La [página man] (http://man7.org/linux/man-pages/man2/getrandom.2.html) dice 'getrandom()' se introdujo en kernel 3.17. Así que stock Ubuntu 16.04 no lo tiene a partir de 2018-01-17. Ejecute 'uname -a' en un terminal para verificar la versión de su kernel. – erapert