Как использовать /dev/random или urandom в C?

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

Ответ 1

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

В недавних дистрибутивах Linux системный вызов getrandom может использоваться для получения криптозащищенных случайных чисел, и он не может терпеть неудачу if 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);
}

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

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

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

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

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

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

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

Ответ 5

Ответ zneak охватывает его просто, однако реальность более сложна. Например, вам нужно рассмотреть, действительно ли /dev/ {u} случайным образом является устройством случайных чисел. Такой сценарий может возникнуть, если ваш компьютер был взломан, а устройства заменены символическими ссылками на /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 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() syscall. На момент написания статьи нет ни слова о том, когда она будет доступна в общем выпуске ядра. Однако это был бы предпочтительный интерфейс для получения криптографически защищенных случайных данных, поскольку он удаляет все ловушки, доступ к которым обеспечивается через файлы. См. Также возможную реализацию LibReSSL .