Как использовать /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.