Skip to content

Commit

Permalink
Add LuaSecureRandom
Browse files Browse the repository at this point in the history
  • Loading branch information
est31 authored and kwolekr committed Nov 8, 2015
1 parent d506d56 commit ad5ac39
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 1 deletion.
9 changes: 9 additions & 0 deletions doc/lua_api.txt
Expand Up @@ -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.

This comment has been minimized.

Copy link
@ShadowNinja

ShadowNinja Nov 12, 2015

Member

Why the low upper limit?

This comment has been minimized.

Copy link
@est31

est31 Nov 13, 2015

Author Contributor

2048 bytes are usually okay for cryptographic purposes. Unless you want to do one time pad. For which however not even this random is enough.

This comment has been minimized.

Copy link
@ShadowNinja

ShadowNinja Nov 14, 2015

Member

"No one will need more than 637 kB of memory for a personal computer."
Why limit it so strictly if there's no technological reason to do so?

EDIT: Besides, you can get more by just chaining calls anyway.

This comment has been minimized.

Copy link
@est31

est31 Nov 14, 2015

Author Contributor

Well, RAND_BUF_SIZE is fixed, and it has to be until we get dynamically sized stack objects. Or put it on the heap and experience speed problems.

This comment has been minimized.

Copy link
@est31

est31 Nov 14, 2015

Author Contributor

Not that dynamically sized stack objects wouldn't be slower as well.

This comment has been minimized.

Copy link
@ShadowNinja

ShadowNinja Nov 16, 2015

Member

The buffer is just used as a cache. Just refill it as many times as needed if more is asked for.


### `PerlinNoise`
A perlin noise generator.
It can be created via `PerlinNoise(seed, octaves, persistence, scale)`
Expand Down
43 changes: 42 additions & 1 deletion src/porting.cpp
Expand Up @@ -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)
Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions src/porting.h
Expand Up @@ -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__
Expand Down
115 changes: 115 additions & 0 deletions src/script/lua_api/l_noise.cpp
Expand Up @@ -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"

///////////////////////////////////////
/*
Expand Down Expand Up @@ -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}
};
33 changes: 33 additions & 0 deletions src/script/lua_api/l_noise.h
Expand Up @@ -160,4 +160,37 @@ class LuaPcgRandom : public ModApiBase {
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];

This comment has been minimized.

Copy link
@ShadowNinja

ShadowNinja Nov 14, 2015

Member

Perhaps this should be static, so you don't need a new random cache for every object.

This comment has been minimized.

Copy link
@kwolekr

kwolekr Nov 14, 2015

Contributor

That would lead to race conditions and subtly broken insecure randomness.

This comment has been minimized.

Copy link
@ShadowNinja

ShadowNinja Nov 16, 2015

Member

There shouldn't be any race conditions, since this can only be called from the Lua thread. You'd just have to make sure the async threads get their own copy (which this doesn't do now, likely because it doesn't serialize well).


// 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_ */
1 change: 1 addition & 0 deletions src/script/scripting_game.cpp
Expand Up @@ -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);
Expand Down

0 comments on commit ad5ac39

Please sign in to comment.