From 95340ad79d7487ef1acc7fce868b61c292ae1d14 Mon Sep 17 00:00:00 2001 From: Miguel Oliveira Date: Sun, 12 May 2024 18:57:43 -0300 Subject: [PATCH 1/2] Add timing based generator initialization --- README.md | 10 +++++++-- ccryptolib/random.lua | 51 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c29fbc9..152f7e3 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ All functions that take secret input may query the library's random generator, hoping for the best like other libraries do, CCryptoLib shifts that burden into *you!* +### Initializing using a Trusted Web Source If you trust the tmpim Krist node, you can fetch a socket token and use it for initialization: ```lua @@ -24,5 +25,10 @@ random.init(data.url) http.websocket(data.url).close() ``` -Otherwise, you will need to find another high-quality random entropy source to -initialize the generator. **DO NOT INITIALIZE USING MATH.RANDOM.** +### Initializing using VM Instruction Counting +As of v1.2.0, you can also initialize the generator using VM instruction timing noise. +See the `random.initWithTiming` method for security risks of taking this approach. +```lua +local random = require "ccryptolib.random" +random.initWithTiming() +``` diff --git a/ccryptolib/random.lua b/ccryptolib/random.lua index f667ad1..92eb180 100644 --- a/ccryptolib/random.lua +++ b/ccryptolib/random.lua @@ -27,6 +27,55 @@ local function init(seed) initialized = true end +--- Returns whether the generator has been initialized or not. +--- @return boolean +local function isInit() + return initialized +end + +--- Initializes the generator using VM instruction timing noise. +--- +--- This function counts how many instructions the VM can execute within a single +--- millisecond, and mixes the lower bits of these values into the generator state. +--- The current implementation collects data for 512 ms and takes the lower 8 bits from +--- each count. +--- +--- Compared to fetching entropy from a trusted web source, this approach is riskier but +--- more convenient. The factors that influence instruction timing suggest that this +--- seed is unpredictable for other players, but this assumption might turn out to be +--- untrue. +local function initWithTiming() + assert(os.epoch("utc") ~= 0) + + local f = assert(load("local e=os.epoch return{" .. ("e'utc',"):rep(256) .. "}")) + + do -- Warmup. + local t = f() + while t[256] - t[1] > 1 do t = f() end + end + + -- Fill up the buffer. + local buf = {} + for i = 1, 512 do + local t = f() + while t[256] == t[1] do t = f() end + for j = 1, 256 do + if t[j] ~= t[1] then + buf[i] = j - 1 + break + end + end + end + + -- Perform a histogram check to catch faulty os.epoch implementations. + local hist = {} + for i = 0, 255 do hist[i] = 0 end + for i = 1, #buf do hist[buf[i]] = hist[buf[i]] + 1 end + for i = 0, 255 do assert(hist[i] < 20) end + + init(string.char(table.unpack(buf))) +end + --- Mixes extra entropy into the generator state. --- @param data string The additional entropy to mix. local function mix(data) @@ -49,6 +98,8 @@ end return { init = init, + isInit = isInit, + initWithTiming = initWithTiming, mix = mix, random = random, } From d1efd74ad793869cef12991b7465f02066d11389 Mon Sep 17 00:00:00 2001 From: Miguel Oliveira <41488876+migeyel@users.noreply.github.com> Date: Sun, 19 May 2024 12:24:33 -0300 Subject: [PATCH 2/2] Fix unsupported ingame epoch calls in random.lua --- ccryptolib/random.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ccryptolib/random.lua b/ccryptolib/random.lua index 92eb180..29bcea4 100644 --- a/ccryptolib/random.lua +++ b/ccryptolib/random.lua @@ -9,7 +9,8 @@ local lassert = util.lassert local ctx = { "ccryptolib 2023-04-11T19:43Z random.lua initialization context", os.epoch("utc"), - os.epoch("ingame"), + os.day(), + os.time(), math.random(0, 2 ^ 24 - 1), math.random(0, 2 ^ 24 - 1), tostring({}),