aboutsummaryrefslogtreecommitdiff
path: root/src/random.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/random.c')
-rw-r--r--src/random.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/random.c b/src/random.c
new file mode 100644
index 0000000..90fb3f2
--- /dev/null
+++ b/src/random.c
@@ -0,0 +1,117 @@
1/// @submodule system
2#include <lua.h>
3#include <lauxlib.h>
4#include "compat.h"
5#include <fcntl.h>
6
7#ifdef _WIN32
8#include "windows.h"
9#include "wincrypt.h"
10#else
11#include <errno.h>
12#include <unistd.h>
13#include <string.h>
14#endif
15
16
17/***
18Generate random bytes.
19This uses `CryptGenRandom()` on Windows, and `/dev/urandom` on other platforms. It will return the
20requested number of bytes, or an error, never a partial result.
21@function random
22@tparam[opt=1] int length number of bytes to get
23@treturn[1] string string of random bytes
24@treturn[2] nil
25@treturn[2] string error message
26*/
27static int lua_get_random_bytes(lua_State* L) {
28 int num_bytes = luaL_optinteger(L, 1, 1); // Number of bytes, default to 1 if not provided
29
30 if (num_bytes <= 0) {
31 if (num_bytes == 0) {
32 lua_pushliteral(L, "");
33 return 1;
34 }
35 lua_pushnil(L);
36 lua_pushstring(L, "invalid number of bytes, must not be less than 0");
37 return 2;
38 }
39
40 unsigned char* buffer = (unsigned char*)lua_newuserdata(L, num_bytes);
41 if (buffer == NULL) {
42 lua_pushnil(L);
43 lua_pushstring(L, "failed to allocate memory for random buffer");
44 return 2;
45 }
46
47 ssize_t n;
48 ssize_t total_read = 0;
49
50#ifdef _WIN32
51 HCRYPTPROV hCryptProv;
52 if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
53 DWORD error = GetLastError();
54 lua_pushnil(L);
55 lua_pushfstring(L, "failed to acquire cryptographic context: %lu", error);
56 return 2;
57 }
58
59 if (!CryptGenRandom(hCryptProv, num_bytes, buffer)) {
60 DWORD error = GetLastError();
61 lua_pushnil(L);
62 lua_pushfstring(L, "failed to get random data: %lu", error);
63 CryptReleaseContext(hCryptProv, 0);
64 return 2;
65 }
66
67 CryptReleaseContext(hCryptProv, 0);
68#else
69
70 // for macOS/unixes use /dev/urandom for non-blocking
71 int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
72 if (fd < 0) {
73 lua_pushnil(L);
74 lua_pushstring(L, "failed opening /dev/urandom");
75 return 2;
76 }
77
78 while (total_read < num_bytes) {
79 n = read(fd, buffer + total_read, num_bytes - total_read);
80
81 if (n < 0) {
82 if (errno == EINTR) {
83 continue; // Interrupted, retry
84
85 } else {
86 lua_pushnil(L);
87 lua_pushfstring(L, "failed reading /dev/urandom: %s", strerror(errno));
88 close(fd);
89 return 2;
90 }
91 }
92
93 total_read += n;
94 }
95
96 close(fd);
97#endif
98
99 lua_pushlstring(L, (const char*)buffer, num_bytes);
100 return 1;
101}
102
103
104
105static luaL_Reg func[] = {
106 { "random", lua_get_random_bytes },
107 { NULL, NULL }
108};
109
110
111
112/*-------------------------------------------------------------------------
113 * Initializes module
114 *-------------------------------------------------------------------------*/
115void random_open(lua_State *L) {
116 luaL_setfuncs(L, func, 0);
117}