¿Cómo usar /dev/random o urandom en C?

9 minutos de lectura

avatar de usuario
estojancia

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.

  • Consulte este artículo muy informativo sobre algunas advertencias comunes de tomar esta ruta hacia la (pseudo) aleatoriedad: insanecoding.blogspot.fi/2014/05/…

    – appas

    17 de junio de 2015 a las 12:19

avatar de usuario
zneak

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 las distribuciones de Linux recientes, el getrandom La llamada al sistema se puede usar para obtener números aleatorios criptoseguros y no puede fallar. si GRND_RANDOM es no especificado como un indicador y la cantidad de lectura es de 256 bytes como máximo.

A partir de octubre de 2017, OpenBSD, Darwin y Linux (con -lbsd) ahora todos tienen una implementación de arc4random que es criptoseguro y que no puede fallar. Eso lo convierte en 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. Los lees 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 del archivo. /dev/urandom nunca bloquea y siempre completa tantos bytes como haya solicitado, a menos que la llamada al sistema sea interrumpida por una señal. 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 solicitó y puede bloquear si no hay suficientes bytes disponibles. Esto hace que la historia del 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);
}

  • @karim: nunca lea todos los bytes de /dev/random. Simplemente no lo hagas. Su programa probablemente no es el solamente usuario en el sistema que necesita bytes aleatorios.

    – Zan Lince

    25 de febrero de 2011 a las 22:40

  • @zneak, lo siento, olvidé que las publicaciones se pueden editar aquí. Gracias por hacérmelo saber. Su última modificación solo agregó un control de lectura, en lugar de un bucle. Lo modifiqué para usar un bucle, por lo que el código ahora se bloqueará correctamente.

    – Morrog

    26 de octubre de 2013 a las 2:31

  • Los revisores de @morrog Overeager lo rechazaron, así que hice los cambios manualmente. Lo siento, no te lo acreditan.

    – zneak

    26/10/2013 a las 22:01

  • @CodesInChaos, soy citando la página de manual de Linux: “Si no está seguro de si debe usar /dev/random o /dev/urandom, entonces probablemente quiera usar este último. Como regla general, /dev/urandom debe usarse para todo excepto GPG de larga duración/ Claves SSL/SSH”.

    – zneak

    26/10/2013 a las 22:09


  • @zneak Énfasis en longevo ya que para esos no está de más ser extra paranoico. Para uso criptográfico normal /dev/urandom está bien.

    – CodesInChaos

    26/10/2013 a las 22:51

avatar de usuario
polvo kirkland

Hay otras respuestas precisas arriba. Necesitaba usar un FILE* corriente, sin embargo. 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);

  • Un int se puede leer directamente simplemente lanzando el puntero int a un puntero char. fread((char*)(&myInt),sizeof(myInt),1,fp)

    – Azeem Bande-Ali

    25 mayo 2013 a las 18:50

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

    – Larry

    30 mayo 2014 a las 15:20

  • En ningún caso se debe usar una conversión en el código C, fread() toma un void *, así que solo haz fread(&myInt, … );

    – nos

    2 de enero de 2015 a las 21:12


  • Por qué lo necesitas byte_count? Está sin usar.

    – CalculadoraFelina

    9 de agosto de 2017 a las 23:01

  • @CalculatorFeline El byte_count aquí es un poco confuso, op tal vez inicialmente quería que cada índice tuviera la misma longitud de bytes, pero esto no pudo hacer …

    – LincolnFive

    19 de marzo de 2020 a las 6:51

avatar de usuario
Trónico

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

  • Parece que std::random_device no llegó al estándar de 2011. Sí aparece en el Borrador N3797.

    –Keith Thompson

    02/01/2015 a las 19:31

  • Parece std::random_device hizo convertirlo en C++ 11 al final.

    – leyendas2k

    26 de junio de 2015 a las 14:14

  • 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 como usar std::random_device aunque es una buena opción para usar std::random_device y tiene beneficios, simplemente no es lo que pidió el OP

    – Nfagie Yansaneh

    8 de agosto de 2017 a las 9:20


avatar de usuario
mensaje de tim

Zneak es 100% correcto. También es muy común leer un búfer de números aleatorios que es un poco más grande que lo que necesitará al inicio. Luego puede llenar una matriz en la memoria o escribirlos en su propio archivo para reutilizarlos más adelante.

Una implementación típica de lo anterior:

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

Esto se vuelve más o menos como una cinta que simplemente avanza y que mágicamente se puede reponer con otro hilo según sea necesario. Hay un lote de servicios que proporcionan volcados de archivos grandes de nada más que números aleatorios que se generan con generadores mucho más potentes como:

  • Desintegración radioactiva
  • Comportamiento óptico (fotones que golpean un espejo semitransparente)
  • Ruido atmosférico (no tan fuerte como el anterior)
  • Granjas de monos intoxicados tecleando en teclados y moviendo ratones (es broma)

No use entropía ‘preempaquetada’ para semillas criptográficas, en caso de que eso no sea evidente. Esos conjuntos están bien para simulaciones, no está bien en absoluto para generar claves y tal.

Sin preocuparse por la calidad, si necesita muchos números para algo como una simulación de monte carlo, es mucho mejor tenerlos disponibles de una manera que no provoque que read() se bloquee.

Sin embargo, recuerde, la aleatoriedad de un número es tan determinista como la complejidad que implica generarlo. /dev/random y /dev/urandom son convenientes, pero no tan fuertes como usar un HRNG (o descargar un volcado grande de un HRNG). También vale la pena señalar que /dev/random recargas a través de la entropíapor lo que puede bloquearse durante bastante tiempo dependiendo de las circunstancias.

avatar de usuario
cris j

La respuesta de zneak lo cubre de manera simple, sin embargo, la realidad es más complicada que eso. Por ejemplo, debe considerar si /dev/{u}random realmente es el dispositivo de números aleatorios en primer lugar. Tal escenario puede ocurrir si su máquina se ha visto comprometida y los dispositivos se reemplazaron con enlaces simbólicos a /dev/zero o un archivo disperso. Si esto sucede, el flujo aleatorio ahora es completamente predecible.

La forma más sencilla (al menos en Linux y FreeBSD) es realizar una llamada ioctl en el dispositivo que solo 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 realiza antes de la primera lectura del dispositivo aleatorio, entonces hay una apuesta justa de que tiene el dispositivo aleatorio. Entonces, la respuesta de @ zneak se puede ampliar mejor para 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);

El blog de codificación insana cubrió esto y otras trampas no hace tanto tiempo; Recomiendo encarecidamente leer el artículo completo. Tengo que dar crédito a su lugar de donde se extrajo esta solución.

Editado para agregar (2014-07-25)…

Coincidentemente, leí anoche que como parte de la Esfuerzo de LibReSSLLinux parece estar recibiendo una ObtenerAleatorio() llamada al sistema. En el momento de escribir este artículo, no se sabe 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. Véase también LibReSSL posible implementación.

  • Un atacante con suficiente poder para reemplazar /dev/random o /dev/urandom con otra cosa, normalmente también tiene suficiente poder para cargar un módulo del kernel para arruinar cada intento que haga para determinar si es un dispositivo aleatorio o no.

    – zneak

    02/01/2015 a las 20:39

  • los página man dice getrandom() se introdujo en el núcleo 3.17. Entonces, el stock Ubuntu 16.04 no lo tiene a partir del 2018-01-17. Correr uname -a en una terminal para verificar la versión de su kernel.

    – eraperto

    17 de enero de 2018 a las 18:01

  • Un atacante con suficiente poder para reemplazar /dev/random o /dev/urandom con otra cosa, normalmente también tiene suficiente poder para cargar un módulo del kernel para arruinar cada intento que haga para determinar si es un dispositivo aleatorio o no.

    – zneak

    02/01/2015 a las 20:39

  • los página man dice getrandom() se introdujo en el núcleo 3.17. Entonces, el stock Ubuntu 16.04 no lo tiene a partir del 2018-01-17. Correr uname -a en una terminal para verificar la versión de su kernel.

    – eraperto

    17 de enero de 2018 a las 18:01

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad