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

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

5 ответов

Решение

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

В последних дистрибутивах 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);
}

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

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

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

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

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

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

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

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

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

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

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

Ответ 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.

Другие вопросы по тегам