From 0cb19e1617d735e815e9747a53b6d9f3b3bd568b Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Thu, 6 Feb 2025 17:56:34 +0100 Subject: refactor(random): on linux+bsd use api instead of /dev/urandom the api is faster and equally good --- src/random.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/random.c b/src/random.c index ee544c4..70bf13b 100644 --- a/src/random.c +++ b/src/random.c @@ -13,15 +13,21 @@ #include #include #else -#include -#include -#include + #include + #include + #include + #if defined(__linux__) + #include // getrandom() + #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + #include // arc4random_buf() + #endif #endif /*** Generate random bytes. -This uses `BCryptGenRandom()` on Windows, and `/dev/urandom` on other platforms. It will return the +This uses `BCryptGenRandom()` on Windows, `getrandom()` on Linux, `arc4random_buf` on BSD, +and `/dev/urandom` on other platforms. It will return the requested number of bytes, or an error, never a partial result. @function random @tparam[opt=1] int length number of bytes to get @@ -53,6 +59,7 @@ static int lua_get_random_bytes(lua_State* L) { ssize_t total_read = 0; #ifdef _WIN32 + // Use BCryptGenRandom() on Windows if (!BCRYPT_SUCCESS(BCryptGenRandom(NULL, buffer, num_bytes, BCRYPT_USE_SYSTEM_PREFERRED_RNG))) { DWORD error = GetLastError(); lua_pushnil(L); @@ -60,8 +67,25 @@ static int lua_get_random_bytes(lua_State* L) { return 2; } +#elif defined(__linux__) + // Use getrandom() on Linux (Kernel 3.17+, 2014) + while (total_read < num_bytes) { + ssize_t n = getrandom(buffer + total_read, num_bytes - total_read, 0); + if (n < 0) { + if (errno == EINTR) continue; // Retry on interrupt + lua_pushnil(L); + lua_pushfstring(L, "getrandom() failed: %s", strerror(errno)); + return 2; + } + total_read += n; + } + +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + // Use arc4random_buf() on BSD/macOS + arc4random_buf(buffer, num_bytes); + #else - // for macOS/unixes use /dev/urandom for non-blocking + // fall back to /dev/urandom for anything else int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); if (fd < 0) { lua_pushnil(L); -- cgit v1.2.3-55-g6feb