Skip to content

Commit

Permalink
Add FFI generator for Windows (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
rfl890 authored Oct 9, 2024
1 parent 47ba6a3 commit 9cf7140
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
19 changes: 19 additions & 0 deletions spec/rng_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
63 changes: 63 additions & 0 deletions src/uuid/rng/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -298,4 +360,5 @@ do
end



return rng

0 comments on commit 9cf7140

Please sign in to comment.