web-dev-qa-db-fra.com

Comment utiliser / dev / random ou urandom en C?

Je veux utiliser /dev/random ou /dev/urandom in C. Comment faire? Je ne sais pas comment les gérer en C, si quelqu'un le sait, dites-moi comment. Merci.

72
stojance

En général, il est préférable d'éviter d'ouvrir des fichiers pour obtenir des données aléatoires, en raison du nombre de points de défaillance de la procédure.

Sur les distributions Linux récentes, l'appel système getrandom peut être utilisé pour obtenir des nombres aléatoires crypto-sécurisés, et il ne peut pas échouer si GRND_RANDOM est pas spécifié comme indicateur et la quantité lue est au maximum de 256 octets.

Depuis octobre 2017, OpenBSD, Darwin et Linux (avec -lbsd) ont maintenant tous une implémentation de arc4random qui est crypto-sécurisé et qui ne peut pas échouer. Cela en fait une option très intéressante:

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

Sinon, vous pouvez utiliser les périphériques aléatoires comme s'il s'agissait de fichiers. Vous les lisez et vous obtenez des données aléatoires. J'utilise open/read ici, mais fopen/fread fonctionnerait tout aussi 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
    }
}

Vous pouvez lire beaucoup plus d'octets aléatoires avant de fermer le descripteur de fichier./dev/urandom ne bloque jamais et remplit toujours autant d'octets que vous avez demandé, à moins que l'appel système ne soit interrompu par un signal. Il est considéré comme cryptographiquement sécurisé et devrait être votre appareil aléatoire.

/ dev/random est plus capricieux. Sur la plupart des plates-formes, il peut retourner moins d'octets que vous ne l'auriez demandé et il peut bloquer s'il n'y a pas assez d'octets disponibles. Cela rend l'histoire de la gestion des erreurs plus complexe:

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);
}
98
zneak

Il y a d'autres réponses précises ci-dessus. Je devais utiliser un FILE* stream, cependant. Voici ce que j'ai fait ...

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

Il suffit d'ouvrir le fichier pour lire, puis de lire les données. En C++ 11, vous pouvez utiliser std::random_device qui fournit un accès multiplateforme à ces appareils.

16
Tronic

Zneak est 100% correct. Il est également très courant de lire un tampon de nombres aléatoires légèrement supérieur à ce dont vous aurez besoin au démarrage. Vous pouvez ensuite remplir un tableau en mémoire ou les écrire dans votre propre fichier pour une réutilisation ultérieure.

Une mise en œuvre typique de ce qui précède:

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

Cela ressemble plus ou moins à une bande qui avance simplement et qui peut être reconstituée comme par magie par un autre thread selon les besoins. Il y a beaucoup de services qui fournissent de gros fichiers de vidage de rien mais des nombres aléatoires qui sont générés avec des générateurs beaucoup plus puissants tels que:

  • Désintégration radioactive
  • Comportement optique (photons frappant un miroir semi-transparent)
  • Bruit atmosphérique (pas aussi fort que ci-dessus)
  • Des fermes de singes en état d'ébriété tapant sur des claviers et des souris en mouvement (blague)

N'utilisez pas d'entropie 'pré-emballée' pour les graines cryptographiques, au cas où cela ne va pas de soi . Ces ensembles sont parfaits pour les simulations, pas très bien du tout pour générer des clés et autres.

Ne vous souciant pas de la qualité, si vous avez besoin de beaucoup de chiffres pour quelque chose comme une simulation de monte-carlo, il est préférable de les avoir à disposition de manière à ne pas bloquer read ().

Cependant, rappelez-vous, le caractère aléatoire d'un nombre est aussi déterministe que la complexité impliquée dans sa génération. /dev/random et /dev/urandom sont pratiques, mais pas aussi efficaces que l'utilisation d'un HRNG (ou le téléchargement d'un grand vidage à partir d'un HRNG). A noter également que /dev/randomrecharges via entropie , il peut donc se bloquer pendant un certain temps selon les circonstances.

8
Tim Post

la réponse de zneak le couvre simplement, mais la réalité est plus compliquée que cela. Par exemple, vous devez déterminer si/dev/{u} random est vraiment le périphérique à nombres aléatoires en premier lieu. Un tel scénario peut se produire si votre machine a été compromise et les périphériques remplacés par des liens symboliques vers/dev/zero ou un fichier épars. Si cela se produit, le flux aléatoire est désormais complètement prévisible.

La manière la plus simple (au moins sur Linux et FreeBSD) est d'effectuer un appel ioctl sur le périphérique qui ne réussira que si le périphérique est un générateur aléatoire:

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

Si cela est effectué avant la première lecture de l'appareil aléatoire, il y a fort à parier que vous avez l'appareil aléatoire. Donc, la réponse de @ zneak peut être mieux étendue:

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);

Le blog Insane Coding couvert cela, et d'autres pièges il n'y a pas si longtemps; Je recommande fortement de lire l'intégralité de l'article. Je dois rendre hommage à leur origine de cette solution.

Modifié pour ajouter (2014-07-25) ...
Par coïncidence, j'ai lu hier soir que dans le cadre de LibReSSL effort , Linux semble recevoir un GetRandom () syscall. Comme au moment de la rédaction, il n'y a pas de mot de quand il sera disponible dans une version générale du noyau. Cependant, ce serait l'interface préférée pour obtenir des données aléatoires sécurisées cryptographiquement car elle supprime tous les pièges que l'accès via des fichiers fournit. Voir aussi LibReSSL implémentation possible .

4
Chris J