summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/lua_api.txt9
-rw-r--r--src/porting.cpp43
-rw-r--r--src/porting.h1
-rw-r--r--src/script/lua_api/l_noise.cpp115
-rw-r--r--src/script/lua_api/l_noise.h33
-rw-r--r--src/script/scripting_game.cpp1
6 files changed, 201 insertions, 1 deletions
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 63e4971e5..f82c3c2ac 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -2765,6 +2765,15 @@ It can be created via `PcgRandom(seed)` or `PcgRandom(seed, sequence)`.
* This is only a rough approximation of a normal distribution with mean=(max-min)/2 and variance=1
* Increasing num_trials improves accuracy of the approximation
+### `SecureRandom`
+Interface for the operating system's crypto-secure PRNG.
+
+It can be created via `SecureRandom()`. The constructor returns nil if a secure random device cannot be
+be found on the system.
+
+#### Methods
+* `next_bytes([count])`: return next `count` (default 1, capped at 2048) many random bytes, as a string.
+
### `PerlinNoise`
A perlin noise generator.
It can be created via `PerlinNoise(seed, octaves, persistence, scale)`
diff --git a/src/porting.cpp b/src/porting.cpp
index ced41d4fb..3e39fc813 100644
--- a/src/porting.cpp
+++ b/src/porting.cpp
@@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sys/types.h>
#include <sys/sysctl.h>
#elif defined(_WIN32)
+ #include <windows.h>
+ #include <wincrypt.h>
#include <algorithm>
#endif
#if !defined(_WIN32)
@@ -701,5 +703,44 @@ v2u32 getDisplaySize()
# endif // __ANDROID__
#endif // SERVER
-} //namespace porting
+////
+//// OS-specific Secure Random
+////
+
+#ifdef WIN32
+
+bool secure_rand_fill_buf(void *buf, size_t len)
+{
+ HCRYPTPROV wctx;
+
+ if (!CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ return false;
+
+ CryptGenRandom(wctx, len, (BYTE *)buf);
+ CryptReleaseContext(wctx, 0);
+ return true;
+}
+
+#else
+
+bool secure_rand_fill_buf(void *buf, size_t len)
+{
+ // N.B. This function checks *only* for /dev/urandom, because on most
+ // common OSes it is non-blocking, whereas /dev/random is blocking, and it
+ // is exceptionally uncommon for there to be a situation where /dev/random
+ // exists but /dev/urandom does not. This guesswork is necessary since
+ // random devices are not covered by any POSIX standard...
+ FILE *fp = fopen("/dev/urandom", "rb");
+ if (!fp)
+ return false;
+
+ bool success = fread(buf, len, 1, fp) == 1;
+
+ fclose(fp);
+ return success;
+}
+
+#endif
+
+} //namespace porting
diff --git a/src/porting.h b/src/porting.h
index a86d37fbb..1e89cd044 100644
--- a/src/porting.h
+++ b/src/porting.h
@@ -343,6 +343,7 @@ void setXorgClassHint(const video::SExposedVideoData &video_data,
// threads in the process inherit this exception handler
void setWin32ExceptionHandler();
+bool secure_rand_fill_buf(void *buf, size_t len);
} // namespace porting
#ifdef __ANDROID__
diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp
index 6dcffa31c..04dc6048f 100644
--- a/src/script/lua_api/l_noise.cpp
+++ b/src/script/lua_api/l_noise.cpp
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common/c_converter.h"
#include "common/c_content.h"
#include "log.h"
+#include "porting.h"
+#include "util/numeric.h"
///////////////////////////////////////
/*
@@ -600,3 +602,116 @@ const luaL_reg LuaPcgRandom::methods[] = {
luamethod(LuaPcgRandom, rand_normal_dist),
{0,0}
};
+
+///////////////////////////////////////
+/*
+ LuaSecureRandom
+*/
+
+bool LuaSecureRandom::fillRandBuf()
+{
+ return porting::secure_rand_fill_buf(m_rand_buf, RAND_BUF_SIZE);
+}
+
+int LuaSecureRandom::l_next_bytes(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaSecureRandom *o = checkobject(L, 1);
+ u32 count = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : 1;
+
+ // Limit count
+ count = MYMIN(RAND_BUF_SIZE, count);
+
+ // Find out whether we can pass directly from our array, or have to do some gluing
+ size_t count_remaining = RAND_BUF_SIZE - o->m_rand_idx;
+ if (count_remaining >= count) {
+ lua_pushlstring(L, o->m_rand_buf + o->m_rand_idx, count);
+ o->m_rand_idx += count;
+ } else {
+ char output_buf[RAND_BUF_SIZE];
+
+ // Copy over with what we have left from our current buffer
+ memcpy(output_buf, o->m_rand_buf + o->m_rand_idx, count_remaining);
+
+ // Refill buffer and copy over the remainder of what was requested
+ o->fillRandBuf();
+ memcpy(output_buf + count_remaining, o->m_rand_buf, count - count_remaining);
+
+ // Update index
+ o->m_rand_idx = count - count_remaining;
+
+ lua_pushlstring(L, output_buf, count);
+ }
+
+ return 1;
+}
+
+
+int LuaSecureRandom::create_object(lua_State *L)
+{
+ LuaSecureRandom *o = new LuaSecureRandom();
+
+ // Fail and return nil if we can't securely fill the buffer
+ if (!o->fillRandBuf()) {
+ delete o;
+ return 0;
+ }
+
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+ luaL_getmetatable(L, className);
+ lua_setmetatable(L, -2);
+ return 1;
+}
+
+
+int LuaSecureRandom::gc_object(lua_State *L)
+{
+ LuaSecureRandom *o = *(LuaSecureRandom **)(lua_touserdata(L, 1));
+ delete o;
+ return 0;
+}
+
+
+LuaSecureRandom *LuaSecureRandom::checkobject(lua_State *L, int narg)
+{
+ luaL_checktype(L, narg, LUA_TUSERDATA);
+ void *ud = luaL_checkudata(L, narg, className);
+ if (!ud)
+ luaL_typerror(L, narg, className);
+ return *(LuaSecureRandom **)ud;
+}
+
+
+void LuaSecureRandom::Register(lua_State *L)
+{
+ lua_newtable(L);
+ int methodtable = lua_gettop(L);
+ luaL_newmetatable(L, className);
+ int metatable = lua_gettop(L);
+
+ lua_pushliteral(L, "__metatable");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__gc");
+ lua_pushcfunction(L, gc_object);
+ lua_settable(L, metatable);
+
+ lua_pop(L, 1);
+
+ luaL_openlib(L, 0, methods, 0);
+ lua_pop(L, 1);
+
+ lua_register(L, className, create_object);
+}
+
+const char LuaSecureRandom::className[] = "SecureRandom";
+const luaL_reg LuaSecureRandom::methods[] = {
+ luamethod(LuaSecureRandom, next_bytes),
+ {0,0}
+};
diff --git a/src/script/lua_api/l_noise.h b/src/script/lua_api/l_noise.h
index e958c5a23..492eb7550 100644
--- a/src/script/lua_api/l_noise.h
+++ b/src/script/lua_api/l_noise.h
@@ -160,4 +160,37 @@ public:
static void Register(lua_State *L);
};
+
+/*
+ LuaSecureRandom
+*/
+class LuaSecureRandom : public ModApiBase {
+private:
+ static const size_t RAND_BUF_SIZE = 2048;
+ static const char className[];
+ static const luaL_reg methods[];
+
+ u32 m_rand_idx;
+ char m_rand_buf[RAND_BUF_SIZE];
+
+ // Exported functions
+
+ // garbage collector
+ static int gc_object(lua_State *L);
+
+ // next_bytes(self, count) -> get count many bytes
+ static int l_next_bytes(lua_State *L);
+
+public:
+ bool fillRandBuf();
+
+ // LuaSecureRandom()
+ // Creates an LuaSecureRandom and leaves it on top of stack
+ static int create_object(lua_State *L);
+
+ static LuaSecureRandom *checkobject(lua_State *L, int narg);
+
+ static void Register(lua_State *L);
+};
+
#endif /* L_NOISE_H_ */
diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp
index 4f0350d41..33bc5c2a7 100644
--- a/src/script/scripting_game.cpp
+++ b/src/script/scripting_game.cpp
@@ -98,6 +98,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
LuaPerlinNoiseMap::Register(L);
LuaPseudoRandom::Register(L);
LuaPcgRandom::Register(L);
+ LuaSecureRandom::Register(L);
LuaVoxelManip::Register(L);
NodeMetaRef::Register(L);
NodeTimerRef::Register(L);