mirror of
https://github.com/logos-storage/nim-bearssl.git
synced 2026-01-07 07:53:06 +00:00
rand: convenience API (#28)
Helpers to generate random numbers, including simple Nim types * disable C++ tests * there are too many `const` vs `non-const` issues to run these tests - C++ never actually worked * add generateBytes to make fresh seqs Co-authored-by: Mamy Ratsimbazafy <mamy_github@numforge.co> Co-authored-by: Etan Kissling <etan@status.im>
This commit is contained in:
parent
c4aec8b664
commit
f0ea93f77e
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -163,4 +163,3 @@ jobs:
|
|||||||
nimble --version
|
nimble --version
|
||||||
nimble install -y --depsOnly
|
nimble install -y --depsOnly
|
||||||
env TEST_LANG="c" nimble test
|
env TEST_LANG="c" nimble test
|
||||||
env TEST_LANG="cpp" nimble test
|
|
||||||
|
|||||||
@ -36,6 +36,11 @@ In general, the mappings follow the conventions of the original BearSSL library
|
|||||||
* `uint` used instead of `csize_t` - these are the same type in Nim, but spelled more conveniently
|
* `uint` used instead of `csize_t` - these are the same type in Nim, but spelled more conveniently
|
||||||
* Canonical nim code will have to be careful when converting existing `int` lengths, looking out for out-of-range values
|
* Canonical nim code will have to be careful when converting existing `int` lengths, looking out for out-of-range values
|
||||||
|
|
||||||
|
In addition to the raw `C`-like api, convenience functions are added where applicable - these follow a similar set of conventions:
|
||||||
|
|
||||||
|
* named after the function they simplify, but take advantage of types and overload support in Nim
|
||||||
|
* help turn pointers and bytes into Nim types
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
You can install the developement version of the library through nimble with the following command:
|
You can install the developement version of the library through nimble with the following command:
|
||||||
|
|||||||
100
bearssl/rand.nim
100
bearssl/rand.nim
@ -1,8 +1,100 @@
|
|||||||
import
|
import
|
||||||
./abi/bearssl_rand
|
typetraits,
|
||||||
|
./abi/[bearssl_hash, bearssl_rand]
|
||||||
|
|
||||||
export bearssl_rand
|
export bearssl_rand
|
||||||
|
|
||||||
func hmacDrbgGenerate*(ctx: var HmacDrbgContext, output: var openArray[byte]) =
|
# About types used in helpers:
|
||||||
if output.len > 0:
|
# `bool` types are problematic because because they only use one bit of the
|
||||||
hmacDrbgGenerate(ctx, addr output[0], uint output.len)
|
# entire byte - a similar problem occurs with `object` types with alignment
|
||||||
|
# gaps - `supportsCopyMem` is wrong here, we should be using `supportsMemCmp` or
|
||||||
|
# something similar that takes into account these issues, but alas, there's no
|
||||||
|
# such trait as of now
|
||||||
|
|
||||||
|
proc init*[S](T: type HmacDrbgContext, seed: openArray[S]): HmacDrbgContext =
|
||||||
|
## Create a new randomness context with the given seed - typically, a single
|
||||||
|
## instance per thread should be created.
|
||||||
|
##
|
||||||
|
## The seed can later be topped up with `update`.
|
||||||
|
static: doAssert supportsCopyMem(S) and sizeof(S) > 0 and S isnot bool
|
||||||
|
|
||||||
|
if seed.len == 0:
|
||||||
|
hmacDrbgInit(result, addr bearssl_hash.sha256Vtable, nil, 0)
|
||||||
|
else:
|
||||||
|
# In theory the multiplication can overflow, but practically we can't
|
||||||
|
# allocate that much memory, so it won't
|
||||||
|
hmacDrbgInit(
|
||||||
|
result, addr sha256Vtable, unsafeAddr seed[0], uint seed.len * sizeof(S))
|
||||||
|
|
||||||
|
proc new*(T: type HmacDrbgContext): ref HmacDrbgContext =
|
||||||
|
## Create a new randomness context intended to be shared between randomness
|
||||||
|
## consumers - typically, a single instance per thread should be created.
|
||||||
|
##
|
||||||
|
## The context is seeded with randomness from the OS / system.
|
||||||
|
## Returns `nil` if the OS / system has no randomness API.
|
||||||
|
let seeder = prngSeederSystem(nil)
|
||||||
|
if seeder == nil:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
let rng = (ref HmacDrbgContext)()
|
||||||
|
hmacDrbgInit(rng[], addr sha256Vtable, nil, 0)
|
||||||
|
|
||||||
|
if seeder(addr rng.vtable) == 0:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
rng
|
||||||
|
|
||||||
|
func generate*(ctx: var HmacDrbgContext, v: var auto) =
|
||||||
|
## Fill `v` with random data - `v` must be a simple type
|
||||||
|
static: doAssert supportsCopyMem(type v)
|
||||||
|
|
||||||
|
when sizeof(v) > 0:
|
||||||
|
when v is bool:
|
||||||
|
# `bool` would result in a heavily biased value because >0 == true
|
||||||
|
var tmp: byte
|
||||||
|
hmacDrbgGenerate(ctx, addr tmp, uint sizeof(tmp))
|
||||||
|
v = (tmp and 1'u8) == 1
|
||||||
|
else:
|
||||||
|
hmacDrbgGenerate(ctx, addr v, uint sizeof(v))
|
||||||
|
|
||||||
|
func generate*[V](ctx: var HmacDrbgContext, v: var openArray[V]) =
|
||||||
|
## Fill `v` with random data - `T` must be a simple type
|
||||||
|
static: doAssert supportsCopyMem(V) and sizeof(V) > 0
|
||||||
|
|
||||||
|
when V is bool:
|
||||||
|
for b in v.mitems:
|
||||||
|
ctx.generate(b)
|
||||||
|
else:
|
||||||
|
if v.len > 0:
|
||||||
|
# In theory the multiplication can overflow, but practically we can't
|
||||||
|
# allocate that much memory, so it won't
|
||||||
|
hmacDrbgGenerate(ctx, addr v[0], uint v.len * sizeof(V))
|
||||||
|
|
||||||
|
template generate*[V](ctx: var HmacDrbgContext, v: var seq[V]) =
|
||||||
|
generate(ctx, v.toOpenArray(0, v.high()))
|
||||||
|
|
||||||
|
func generateBytes*(ctx: var HmacDrbgContext, n: int): seq[byte] =
|
||||||
|
# https://github.com/nim-lang/Nim/issues/19357
|
||||||
|
if n > 0:
|
||||||
|
result = newSeqUninitialized[byte](n)
|
||||||
|
ctx.generate(result)
|
||||||
|
|
||||||
|
func generate*(ctx: var HmacDrbgContext, T: type): T {.noinit.} =
|
||||||
|
## Create a new instance of `T` filled with random data - `T` must be
|
||||||
|
## a simple type
|
||||||
|
ctx.generate(result)
|
||||||
|
|
||||||
|
func update*[S](ctx: var HmacDrbgContext, seed: openArray[S]) =
|
||||||
|
## Update context with additional seed data
|
||||||
|
static: doAssert supportsCopyMem(S) and sizeof(S) > 0 and S isnot bool
|
||||||
|
|
||||||
|
if seed.len > 0:
|
||||||
|
# In theory the multiplication can overflow, but practically we can't
|
||||||
|
# allocate that much memory, so it won't
|
||||||
|
hmacDrbgUpdate(ctx, unsafeAddr seed[0], uint seed.len * sizeof(S))
|
||||||
|
|
||||||
|
# Convenience helpers using bearssl naming
|
||||||
|
|
||||||
|
template hmacDrbgGenerate*(
|
||||||
|
ctx: var HmacDrbgContext, output: var openArray[byte]) =
|
||||||
|
generate(ctx, output)
|
||||||
|
|||||||
62
tests/test_rand.nim
Normal file
62
tests/test_rand.nim
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import
|
||||||
|
unittest2,
|
||||||
|
../bearssl/rand
|
||||||
|
|
||||||
|
{.used.}
|
||||||
|
|
||||||
|
suite "random":
|
||||||
|
test "simple random ops":
|
||||||
|
# Some of these tests may end up triggering false fails, but given their
|
||||||
|
# probability, should be fine
|
||||||
|
|
||||||
|
let rng = HmacDrbgContext.new()
|
||||||
|
|
||||||
|
var v: array[1024, byte]
|
||||||
|
rng[].generate(v)
|
||||||
|
|
||||||
|
let v2 = rng[].generate(array[1024, byte])
|
||||||
|
check:
|
||||||
|
v != default(array[1024, byte]) # probable
|
||||||
|
v2 != default(array[1024, byte]) # probable
|
||||||
|
|
||||||
|
for i in 0..<1000:
|
||||||
|
doAssert cast[int](rng[].generate(bool)) in [0, 1]
|
||||||
|
|
||||||
|
var bools: array[64 * 1024, bool]
|
||||||
|
rng[].generate(bools)
|
||||||
|
|
||||||
|
check:
|
||||||
|
true in bools # probable
|
||||||
|
false in bools # probable
|
||||||
|
|
||||||
|
var
|
||||||
|
xxx = newSeq[int](1024)
|
||||||
|
yyy = xxx
|
||||||
|
rng[].generate(xxx)
|
||||||
|
check:
|
||||||
|
xxx != yyy # probable
|
||||||
|
|
||||||
|
test "seed":
|
||||||
|
for seed in [@[byte 0], @[byte 1], @[byte 1, 1], @[byte 42, 13, 37]]:
|
||||||
|
var
|
||||||
|
rng = HmacDrbgContext.init(seed)
|
||||||
|
rng2 = HmacDrbgContext.init(seed)
|
||||||
|
|
||||||
|
check:
|
||||||
|
rng.generate(uint64) == rng2.generate(uint64)
|
||||||
|
|
||||||
|
for seed in [@[0], @[1], @[1, 1], @[42, 1337, -5]]:
|
||||||
|
var
|
||||||
|
rng = HmacDrbgContext.init(seed)
|
||||||
|
rng2 = HmacDrbgContext.init(seed)
|
||||||
|
|
||||||
|
check:
|
||||||
|
rng.generate(uint64) == rng2.generate(uint64)
|
||||||
|
|
||||||
|
test "antiseed":
|
||||||
|
var
|
||||||
|
rng = HmacDrbgContext.init([0])
|
||||||
|
rng2 = HmacDrbgContext.init([1])
|
||||||
|
|
||||||
|
check:
|
||||||
|
rng.generate(array[1024, byte]) != rng2.generate(array[1024, byte])
|
||||||
Loading…
x
Reference in New Issue
Block a user