Как использовать / dev / random или urandom в C? - PullRequest
71 голосов
/ 03 апреля 2010

Я хочу использовать /dev/random или /dev/urandom в C. Как я могу это сделать? Я не знаю, как я могу справиться с ними в C, если кто-то знает, пожалуйста, скажите мне, как. Спасибо.

Ответы [ 5 ]

97 голосов
/ 03 апреля 2010

В целом, лучше избегать открывать файлы для получения случайных данных из-за того, сколько точек сбоя есть в процедуре.

В последних дистрибутивах Linux системный вызов getrandom можно использовать для получения крипто-защищенных случайных чисел, и он не может завершиться ошибкой , если GRND_RANDOM равно , а не указан как флаг, а количество считанных не превышает 256 байт.

По состоянию на октябрь 2017 года, OpenBSD, Darwin и Linux (с -lbsd) теперь имеют реализацию arc4random, которая является криптобезопасной и не может дать сбой. Это делает его очень привлекательным вариантом:

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

В противном случае вы можете использовать случайные устройства, как если бы они были файлами. Вы читаете с них и получаете случайные данные. Я использую open / read здесь, но fopen / fread будет работать так же хорошо.

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

Вы можете прочитать еще много случайных байтов перед закрытием дескриптора файла. / dev / urandom никогда не блокирует и всегда заполняет столько байтов, сколько вы запросили, если только системный вызов не прерывается сигналом. Он считается криптографически безопасным и должен быть вашим случайным устройством.

/ dev / random более привередливый. На большинстве платформ он может вернуть меньше байтов, чем вы просили, и он может заблокировать, если недостаточно байт доступно. Это делает историю обработки ошибок более сложной:

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);
}
21 голосов
/ 16 августа 2012

Есть и другие точные ответы выше. Мне нужно было использовать поток FILE*. Вот что я сделал ...

int byte_count = 64;
char data[64];
FILE *fp;
fp = fopen("/dev/urandom", "r");
fread(&data, 1, byte_count, fp);
fclose(fp);
16 голосов
/ 03 апреля 2010

Просто откройте файл для чтения и затем прочитайте данные. В C ++ 11 вы можете использовать std::random_device, который обеспечивает кроссплатформенный доступ к таким устройствам.

8 голосов
/ 04 апреля 2010

Zneak является 100% правильным. Также очень распространено читать буфер случайных чисел, который немного больше, чем вам нужно при запуске. Затем вы можете заполнить массив в памяти или записать их в свой собственный файл для последующего повторного использования.

Типичная реализация вышеперечисленного:

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

Это становится более или менее похоже на ленту, которая только продвигается, и может быть волшебным образом пополнена другим потоком по мере необходимости. Существует lot из сервисов , которые предоставляют большие файловые дампы только случайных чисел, генерируемых гораздо более сильными генераторами, такими как:

  • Радиоактивный распад
  • Оптическое поведение (фотоны падают на полупрозрачное зеркало)
  • Атмосферный шум (не такой сильный, как указано выше)
  • Фермы пьяных обезьян, печатающих на клавиатурах и движущихся мышей (шучу)

Не используйте «предварительно упакованную» энтропию для криптографических семян , если это само собой разумеется. Эти наборы хороши для моделирования, совсем не подходят для генерации ключей и тому подобное.

Не заботясь о качестве, если вам нужно много чисел для чего-то вроде симуляции Монте-Карло, гораздо лучше, чтобы они были доступны таким образом, чтобы не вызвать блокировку read ().

Однако помните, что случайность числа столь же детерминистична, как и сложность его генерации. /dev/random и /dev/urandom удобны, но не так сильны, как использование HRNG (или загрузка большого дампа из HRNG). Также стоит отметить, что /dev/random пополняется через энтропию , поэтому он может блокироваться довольно долго в зависимости от обстоятельств.

4 голосов
/ 23 июля 2014

Ответ zneak охватывает это просто, однако реальность сложнее, чем это. Например, вам нужно подумать, действительно ли / dev / {u} random является устройством со случайными числами. Такой сценарий может возникнуть, если ваша машина была взломана, а устройства заменены символическими ссылками на / dev / zero или разреженным файлом. Если это происходит, случайный поток теперь полностью предсказуем.

Самый простой способ (по крайней мере, в Linux и FreeBSD) - выполнить вызов ioctl на устройстве, который будет успешным, только если устройство является генератором случайных чисел:

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

Если это выполняется до первого чтения случайного устройства, то есть справедливая ставка на то, что у вас есть случайное устройство. Так что ответ @ zneak может быть расширен:

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

Блог Insane Coding освещал эту и другие ловушки не так давно; Я настоятельно рекомендую прочитать всю статью. Я должен отдать должное их, откуда это решение было извлечено.

Отредактировано для добавления (2014-07-25) ...
Кстати, вчера вечером я читал, что в рамках LibReSSL Linux, похоже, получает системный вызов GetRandom () . На момент написания, нет ни слова о том, когда он будет доступен в общем выпуске ядра. Однако этот интерфейс предпочтителен для получения криптографически безопасных случайных данных, поскольку он устраняет все подводные камни, которые предоставляет доступ через файлы. См. Также возможную реализацию LibReSSL .

...