Skip to content

Commit

Permalink
refactor(random): on linux+bsd use api instead of /dev/urandom
Browse files Browse the repository at this point in the history
the api is faster and equally good
  • Loading branch information
Tieske committed Feb 6, 2025
1 parent 6f4b532 commit 6cd431d
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ The scope of what is covered by the version number excludes:
### unreleased

- Fix: NetBSD fix compilation, undeclared directives
- Refactor: random bytes; remove deprecated API usage on Windows, move to
binary api instead of /dev/urandom file on linux and bsd

### version 0.4.5, released 18-Dec-2024

Expand Down
34 changes: 29 additions & 5 deletions src/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,21 @@
#include <windows.h>
#include <bcrypt.h>
#else
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#if defined(__linux__)
#include <sys/random.h> // getrandom()
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
#include <stdlib.h> // 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
Expand Down Expand Up @@ -53,15 +59,33 @@ 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);
lua_pushfstring(L, "failed to get random data: %lu", error);
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);
Expand Down

0 comments on commit 6cd431d

Please sign in to comment.