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


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

5 65

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;

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

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

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

не заботясь о качестве, Если вам нужно много чисел для чего-то вроде моделирования Монте-Карло, гораздо лучше иметь их в наличии таким образом, чтобы не вызывать блокировку 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);

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

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