Add LuaSecureRandom
authorest31 <MTest31@outlook.com>
Thu, 6 Aug 2015 06:57:13 +0000 (08:57 +0200)
committerkwolekr <kwolekr@minetest.net>
Sun, 8 Nov 2015 20:57:15 +0000 (15:57 -0500)
doc/lua_api.txt
src/porting.cpp
src/porting.h
src/script/lua_api/l_noise.cpp
src/script/lua_api/l_noise.h
src/script/scripting_game.cpp

index 63e4971e53b9a40be9ae8d13eb1f5655d78621f5..f82c3c2ac59cfcb66ce9b5c7ebe9ddfe649a6134 100644 (file)
@@ -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)`
index ced41d4fb748083e0886440661620ece2498bf89..3e39fc8131ff672df2f6e3bd27a542d15de80bf1 100644 (file)
@@ -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
index a86d37fbb91a50dcc1fc1381959ad34a8640f7c1..1e89cd04482d5083a1fe694054fd3a0175fa6be8 100644 (file)
@@ -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__
index 6dcffa31ca6282dc4807f3c0f9ff5359f9e48765..04dc6048fbc8c5ea3713eace4e9a648f33b677c9 100644 (file)
@@ -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}
+};
index e958c5a2388b3005e69e992677e0c5af7fa70a92..492eb7550651a8ebc236bacb6df51b8b6763d71a 100644 (file)
@@ -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_ */
index 4f0350d418b3ba752fba3429a6bb7a4d2beec095..33bc5c2a776ad5e6eebb1588153159333c802a2d 100644 (file)
@@ -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);