Add timing based generator initialization

This commit is contained in:
Miguel Oliveira 2024-05-12 18:57:43 -03:00
parent f0f9c4b940
commit 95340ad79d
2 changed files with 59 additions and 2 deletions

View file

@ -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 hoping for the best like other libraries do, CCryptoLib shifts that burden into
*you!* *you!*
### Initializing using a Trusted Web Source
If you trust the tmpim Krist node, you can fetch a socket token and use it for If you trust the tmpim Krist node, you can fetch a socket token and use it for
initialization: initialization:
```lua ```lua
@ -24,5 +25,10 @@ random.init(data.url)
http.websocket(data.url).close() http.websocket(data.url).close()
``` ```
Otherwise, you will need to find another high-quality random entropy source to ### Initializing using VM Instruction Counting
initialize the generator. **DO NOT INITIALIZE USING MATH.RANDOM.** 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()
```

View file

@ -27,6 +27,55 @@ local function init(seed)
initialized = true initialized = true
end 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. --- Mixes extra entropy into the generator state.
--- @param data string The additional entropy to mix. --- @param data string The additional entropy to mix.
local function mix(data) local function mix(data)
@ -49,6 +98,8 @@ end
return { return {
init = init, init = init,
isInit = isInit,
initWithTiming = initWithTiming,
mix = mix, mix = mix,
random = random, random = random,
} }