getrandom(2) возвращает серию произвольных байт

ОБЗОР

#include <linux/random.h>

int getrandom(void *buf, size_t buflen, unsigned int flags);

ОПИСАНИЕ

Системный вызов getrandom() заполняет буфер, указанный в buf, произвольными байтами в количестве до buflen. Эти байты можно использовать как начальные значения в генераторах произвольных чисел пространства пользователя или с целями шифрования.

Механизм getrandom() полагается на энтропию, полученную от драйверов устройств и другие источники окружающего шума. Чрезмерное чтение больших порций данных приведёт к негативному воздействию на других пользователей устройств /dev/random и /dev/urandom. Поэтому getrandom() не должен использоваться для симуляции Monte Carlo или других программ/алгоритмов, которые выполняют вероятностные выборки.

По умолчанию, getrandom() забирает энтропию из пула /dev/urandom. Это поведение можно изменить через параметр flags. Если пул /dev/urandom инициализирован, то из него можно прочитать не более 256 байт, но всегда возвращается столько байт, сколько запрошено и это не прерывается сигналами. Для буферов большего размера это не гарантируется. Например, если вызов прерывается обработчиком сигнала, то он может вернуть частично заполненный буфер или завершиться с ошибкой EINTR. Если пул ещё не инициализирован, то вызов блокируется, если в flags не указано значение GRND_NONBLOCK.

Аргумент flags является битовой маской, которая может содержать ноль или более следующих флагов:

GRND_RANDOM
Если этот бит установлен, то произвольные байты берутся из пула /dev/random, а не из пула /dev/urandom. Ограничение пула /dev/random следует из энтропии, которую можно получить из окружающего шума. Если количество доступных байт в /dev/random меньше запрашиваемых в buflen, то вызов завершается сразу после выдачи всех доступных произвольных байт. Если произвольных байт нет, то поведение зависит от наличия флага GRND_NONBLOCK в параметре flags.
GRND_NONBLOCK
По умолчанию, при чтении из /dev/random getrandom() блокируется, если произвольные байты недоступны, и а при чтении из /dev/urandom блокируется, если ещё не инициализирован пул энтропии. Если указан флаг GRND_NONBLOCK, то getrandom() не блокируется в этих случаях, а сразу возвращает -1 и присваивает errno значение EAGAIN.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении getrandom() возвращает количество скопированных в буфер buf байт. Это значение может быть меньше, чем количество запрашиваемых в buflen байт, если в flags был указан GRND_RANDOM и нет достаточного количества энтропии в пуле /dev/random, или если системный вызов был прерван сигналом.

В случае ошибки возвращается -1 и значение errno устанавливается соответствующим образом.

ОШИБКИ

EINVAL
В flags указан неверный флаг.
EFAULT
Адрес, указанный в buf, лежит вне доступного адресного пространства.
EAGAIN
Запрошенное количество энтропии недоступно, и getrandom() заблокировался бы, если бы отсутствовал флаг GRND_NONBLOCK.
EINTR
Вызов был прерван обработчиком сигнала; смотрите описание о прерывании вызовов read(2) при работе с «медленными» устройствами и при отсутствии флага SA_RESTART в справочной странице signal(7).

ВЕРСИИ

Вызов getrandom() появился в версии 3.17 ядра Linux.

СООТВЕТСТВИЕ СТАНДАРТАМ

Данный вызов есть только в Linux.

ЗАМЕЧАНИЯ

Возвращается максимальное количество байтов

На момент Linux 3.19 существуют следующие ограничения:
*
При чтении из /dev/urandom в системе, где размер int равен 32 битам, один вызов getrandom() возвращает максимум 33554431 байт.
*
При чтении из /dev/random возвращается максимум 512 байт.

Инициализация пула энтропии

Ядро собирает биты энтропии из окружения. Когда собрано достаточное количество произвольных бит, пул энтропии /dev/urandom считается инициализированным. Обычно, это состояние достигается на ранних стадиях загрузки системы.

Прерывание обработчиком сигнала

При чтении из /dev/urandom (GRND_RANDOM не указан), getrandom() блокируется до тех пор, пока не специализируется пул энтропии (если не указан флаг GRND_NONBLOCK). Если запрос требует большого количества байт (больше 256), getrandom() будет заблокирован до тех пор, пока байты не будут сгенерированы и переданы из памяти ядра в buf. При чтении из /dev/random (указан GRND_RANDOM), getrandom() будет заблокирован до тех пор, пока какое-то количество произвольных байт не станет доступно (если не указан флаг GRND_NONBLOCK).

Что происходит когда вызов блокированный чтением /dev/urandom вызов getrandom() прерывается обработчиком сигнала зависит от состояния инициализации буфера энтропии и от запрашиваемого объёма buflen. Если энтропия ещё не инициализирована, то вызов завершится с ошибкой EINTR. Если пул энтропии инициализирован и запрашиваемый объём большой (buflen > 256), то вызов или завершится успешно, вернув частично заполненный буфер, или завершится с ошибкой EINTR. Если пул энтропии инициализирован и запрашиваемый объём мал (buflen <= 256), то getrandom() завершится без ошибки EINTR. Вместо этого, он вернёт все запрашиваемый байты.

При чтении из /dev/random блокирующие запросы на любой объём могут быть прерваны сигналом (вызов завершается с ошибкой EINTR).

Вызов getrandom() для чтения из /dev/urandom малого объёма (<= 256) в buflen — предпочтительный способ использования.

Специальный режим для маленького объёма buflen был разработан для совместимости с системным вызовом getentropy() из OpenBSD.

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

Выбор устройства генератора произвольных чисел

Если вы не генерируете долгосрочные ключи (и возможно даже при этом), то, вероятно, не должны использовать GRND_RANDOM. Алгоритмы шифрования, в которых используется /dev/urandom, довольно консервативны и поэтому его достаточно для любых задач. Недостаток GRND_RANDOM в том, что он может заблокироваться. Кроме того, из-за получения частично заполненного getrandom() буфера в запросе, что может возникнуть при использовании GRND_RANDOM, увеличивается сложность кода.

Эмуляция getentropy() из OpenBSD

Системный вызов getentropy() из OpenBSD можно эмулировать с помощью следующей функции:

int
getentropy(void *buf, size_t buflen)
{
    int ret;
    if (buflen > 256)
        goto failure;
    ret = getrandom(buf, buflen, 0);
    if (ret < 0)
        return ret;
    if (ret == buflen)
        return 0;
failure:
    errno = EIO;
    return -1;
}

ДЕФЕКТЫ

В Linux 3.19 существуют следующие дефекты:
*
В зависимости от загруженности ЦП, getrandom() не реагирует на прерывания, пока не прочитает все запрашиваемые байты.