From 9cf71408859da1b963e3fdf156b65b24132f3292 Mon Sep 17 00:00:00 2001 From: rfl890 <87506407+rfl890@users.noreply.github.com> Date: Wed, 9 Oct 2024 03:20:31 -0400 Subject: [PATCH] Add FFI generator for Windows (#18) --- spec/rng_spec.lua | 19 +++++++++++++ src/uuid/rng/init.lua | 63 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/spec/rng_spec.lua b/spec/rng_spec.lua index ae8d048..a8f19c7 100644 --- a/spec/rng_spec.lua +++ b/spec/rng_spec.lua @@ -64,6 +64,25 @@ describe("rng's", function() + describe("win_ffi()", function() + + it("fails on Posix", function() + local old_package_config = package.config + finally(function() + package.config = old_package_config -- luacheck: ignore + end) + -- make it think this is Windows + package.config = "/" .. package.config:sub(2,-1) -- luacheck: ignore + + local rng, err = require("uuid").rng.win_ffi() + assert.is.falsy(rng) + assert.is.equal("win-ffi is only available on Windows", err) + end) + + end) + + + describe("urandom()", function() it("generates 1 byte", function() diff --git a/src/uuid/rng/init.lua b/src/uuid/rng/init.lua index 29db526..fdc00fb 100644 --- a/src/uuid/rng/init.lua +++ b/src/uuid/rng/init.lua @@ -80,6 +80,60 @@ end +do + local win_ffi_rng + + ---------------------------------------------------------------------------- + -- Returns an rng that implements Windows' RtlGenRandom function which is + -- a good source of randomness on all modern versions of Windows. + -- Requires LuaJIT or [`cffi-lua`](https://github.com/q66/cffi-lua). + -- @treturn function A function that returns `n` random bytes, signature: `byte_string, err = func(n)` + -- @usage + -- local uuid = require "uuid" + -- uuid.set_rng(uuid.rng.win_ffi()) + function rng.win_ffi() + if package.config:sub(1, 1) ~= "\\" then + return nil, "win-ffi is only available on Windows" + end + + -- return cahced rng function if we already created it + if win_ffi_rng then + return win_ffi_rng + end + + local ok, ffi = pcall(require, "ffi") + if not ok then + ok, ffi = pcall(require, "cffi") + end + if not ok then + return nil, "ffi not available" + end + + local ffi_defs = [[ + unsigned char SystemFunction036(void *RandomBuffer, unsigned long RandomBufferLength); + ]] + ffi.cdef(ffi_defs) + + local ok, advapi32 = pcall(ffi.load, "advapi32.dll") + if not ok then + return nil, "failed to load advapi32.dll" + end + + win_ffi_rng = function(n) + local buffer = ffi.new("unsigned char[?]", n) + local ok = advapi32.SystemFunction036(buffer, n) + if ok == 0 then + return nil, "call to RtlGenRandom failed" + end + return ffi.string(buffer, n) + end + + return win_ffi_rng + end +end + + + do local rnd = function(n) local file, err = io.open("/dev/urandom", "rb") @@ -186,6 +240,7 @@ if lua_version >= 5.4 then end + do -- create a table, on modern CPUs the ASLR will make this unique. -- we keep the table around, to prevent it being GC'ed and the address being reused @@ -230,6 +285,13 @@ do end end + do -- try RtlGenRandom() via FFI on windows + local wf = rng.win_ffi() + if wf then + return wf(40) -- this is crypto level, so good enough + end + end + -- fallback to sha1 of a combination of values local seed = { unique_table_id, @@ -298,4 +360,5 @@ do end + return rng