a13db3fc68
This avoids making a huge number of getrandom system calls during initialization. The init CSPRNG is unmapped before initialization finishes and these are still reseeded from the OS. The purpose of the independent CSPRNGs is simply to avoid the massive performance hit of synchronization and there's no harm in doing it this way. Keeping around the init CSPRNG and reseeding from it would defeat the purpose of reseeding, and it isn't a measurable performance issue since it can just be tuned to reseed less often.
142 lines
4.0 KiB
C
142 lines
4.0 KiB
C
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include "chacha.h"
|
|
#include "random.h"
|
|
#include "util.h"
|
|
|
|
#if __has_include(<sys/random.h>)
|
|
// glibc 2.25 and later
|
|
#include <sys/random.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/syscall.h>
|
|
|
|
static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) {
|
|
return syscall(SYS_getrandom, buf, buflen, flags);
|
|
}
|
|
#endif
|
|
|
|
static void get_random_seed(void *buf, size_t size) {
|
|
while (size) {
|
|
ssize_t r;
|
|
|
|
do {
|
|
r = getrandom(buf, size, 0);
|
|
} while (r == -1 && errno == EINTR);
|
|
|
|
if (r <= 0) {
|
|
fatal_error("getrandom failed");
|
|
}
|
|
|
|
buf = (char *)buf + r;
|
|
size -= r;
|
|
}
|
|
}
|
|
|
|
void random_state_init(struct random_state *state) {
|
|
u8 rnd[CHACHA_KEY_SIZE + CHACHA_IV_SIZE];
|
|
get_random_seed(rnd, sizeof(rnd));
|
|
chacha_keysetup(&state->ctx, rnd);
|
|
chacha_ivsetup(&state->ctx, rnd + CHACHA_KEY_SIZE);
|
|
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
|
|
state->index = 0;
|
|
state->reseed = 0;
|
|
}
|
|
|
|
void random_state_init_from_random_state(struct random_state *state, struct random_state *source) {
|
|
u8 rnd[CHACHA_KEY_SIZE + CHACHA_IV_SIZE];
|
|
get_random_bytes(source, rnd, sizeof(rnd));
|
|
chacha_keysetup(&state->ctx, rnd);
|
|
chacha_ivsetup(&state->ctx, rnd + CHACHA_KEY_SIZE);
|
|
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
|
|
state->index = 0;
|
|
state->reseed = 0;
|
|
}
|
|
|
|
static void refill(struct random_state *state) {
|
|
if (state->reseed < RANDOM_RESEED_SIZE) {
|
|
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
|
|
state->index = 0;
|
|
state->reseed += RANDOM_CACHE_SIZE;
|
|
} else {
|
|
random_state_init(state);
|
|
}
|
|
}
|
|
|
|
void get_random_bytes(struct random_state *state, void *buf, size_t size) {
|
|
// avoid needless copying to and from the cache as an optimization
|
|
if (size > RANDOM_CACHE_SIZE / 2) {
|
|
chacha_keystream_bytes(&state->ctx, buf, size);
|
|
return;
|
|
}
|
|
|
|
while (size) {
|
|
if (state->index == RANDOM_CACHE_SIZE) {
|
|
refill(state);
|
|
}
|
|
|
|
size_t remaining = RANDOM_CACHE_SIZE - state->index;
|
|
size_t copy_size = min(size, remaining);
|
|
memcpy(buf, state->cache + state->index, copy_size);
|
|
state->index += copy_size;
|
|
|
|
buf = (char *)buf + copy_size;
|
|
size -= copy_size;
|
|
}
|
|
}
|
|
|
|
u16 get_random_u16(struct random_state *state) {
|
|
u16 value;
|
|
unsigned remaining = RANDOM_CACHE_SIZE - state->index;
|
|
if (remaining < sizeof(value)) {
|
|
refill(state);
|
|
}
|
|
memcpy(&value, state->cache + state->index, sizeof(value));
|
|
state->index += sizeof(value);
|
|
return value;
|
|
}
|
|
|
|
// See Fast Random Integer Generation in an Interval by Daniel Lemire
|
|
u16 get_random_u16_uniform(struct random_state *state, u16 bound) {
|
|
u32 random = get_random_u16(state);
|
|
u32 multiresult = random * bound;
|
|
u16 leftover = multiresult;
|
|
if (leftover < bound) {
|
|
u16 threshold = -bound % bound;
|
|
while (leftover < threshold) {
|
|
random = get_random_u16(state);
|
|
multiresult = random * bound;
|
|
leftover = (u16)multiresult;
|
|
}
|
|
}
|
|
return multiresult >> 16;
|
|
}
|
|
|
|
u64 get_random_u64(struct random_state *state) {
|
|
u64 value;
|
|
unsigned remaining = RANDOM_CACHE_SIZE - state->index;
|
|
if (remaining < sizeof(value)) {
|
|
refill(state);
|
|
}
|
|
memcpy(&value, state->cache + state->index, sizeof(value));
|
|
state->index += sizeof(value);
|
|
return value;
|
|
}
|
|
|
|
// See Fast Random Integer Generation in an Interval by Daniel Lemire
|
|
u64 get_random_u64_uniform(struct random_state *state, u64 bound) {
|
|
u128 random = get_random_u64(state);
|
|
u128 multiresult = random * bound;
|
|
u64 leftover = multiresult;
|
|
if (leftover < bound) {
|
|
u64 threshold = -bound % bound;
|
|
while (leftover < threshold) {
|
|
random = get_random_u64(state);
|
|
multiresult = random * bound;
|
|
leftover = multiresult;
|
|
}
|
|
}
|
|
return multiresult >> 64;
|
|
}
|