Skip to content

Commit

Permalink
Merge tag 'v1.2.1' (closes #2)
Browse files Browse the repository at this point in the history
  • Loading branch information
MCJack123 committed May 31, 2024
2 parents cb8af71 + d1efd74 commit 0232170
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 2 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -34,5 +35,10 @@ local random = require "ccryptolib.random"
random.init(os.time("nano"))
```

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()
```
51 changes: 51 additions & 0 deletions ccryptolib/random.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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.time() ~= 0)

local f = assert(load("local e=os.time return{" .. ("e(),"):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)
Expand All @@ -49,6 +98,8 @@ end

return {
init = init,
isInit = isInit,
initWithTiming = initWithTiming,
mix = mix,
random = random,
}

0 comments on commit 0232170

Please sign in to comment.