2018-08-21 21:23:22 +02:00
|
|
|
#include <errno.h>
|
2018-08-23 22:30:44 +02:00
|
|
|
#include <string.h>
|
2018-08-21 21:23:22 +02:00
|
|
|
|
|
|
|
#include "random.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
2018-08-25 00:44:49 +02:00
|
|
|
#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
|
|
|
|
|
2018-08-26 05:00:00 +02:00
|
|
|
#include "chacha.h"
|
|
|
|
|
2018-08-26 00:32:44 +02:00
|
|
|
static void get_random_seed(void *buf, size_t size) {
|
2018-08-21 21:23:22 +02:00
|
|
|
while (size > 0) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-23 22:30:44 +02:00
|
|
|
void random_state_init(struct random_state *state) {
|
2018-10-04 20:25:16 +02:00
|
|
|
u8 rnd[CHACHA_KEY_SIZE + CHACHA_IV_SIZE];
|
2018-08-26 05:00:00 +02:00
|
|
|
get_random_seed(rnd, sizeof(rnd));
|
2018-09-11 16:22:28 +02:00
|
|
|
chacha_keysetup(&state->ctx, rnd);
|
|
|
|
chacha_ivsetup(&state->ctx, rnd + CHACHA_KEY_SIZE);
|
2018-08-26 05:00:00 +02:00
|
|
|
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
|
|
|
|
state->index = 0;
|
|
|
|
state->reseed = 0;
|
2018-08-21 21:23:22 +02:00
|
|
|
}
|
|
|
|
|
2018-08-26 06:02:35 +02:00
|
|
|
static void refill(struct random_state *state) {
|
|
|
|
if (state->reseed < RANDOM_RESEED_SIZE) {
|
2018-08-26 05:00:00 +02:00
|
|
|
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
|
2018-08-26 06:02:35 +02:00
|
|
|
state->index = 0;
|
|
|
|
state->reseed += RANDOM_CACHE_SIZE;
|
|
|
|
} else {
|
|
|
|
random_state_init(state);
|
2018-08-23 22:30:44 +02:00
|
|
|
}
|
2018-08-21 21:23:22 +02:00
|
|
|
}
|
|
|
|
|
2018-10-04 20:25:16 +02:00
|
|
|
u16 get_random_u16(struct random_state *state) {
|
|
|
|
u16 value;
|
2018-08-26 06:02:35 +02:00
|
|
|
size_t remaining = RANDOM_CACHE_SIZE - state->index;
|
|
|
|
if (remaining < sizeof(value)) {
|
|
|
|
refill(state);
|
|
|
|
}
|
|
|
|
memcpy(&value, state->cache + state->index, sizeof(value));
|
|
|
|
state->index += sizeof(value);
|
2018-08-26 00:32:44 +02:00
|
|
|
return value;
|
2018-08-21 21:23:22 +02:00
|
|
|
}
|
|
|
|
|
2018-08-30 07:16:58 +02:00
|
|
|
// See Fast Random Integer Generation in an Interval by Daniel Lemire
|
2018-10-04 20:25:16 +02:00
|
|
|
u16 get_random_u16_uniform(struct random_state *state, u16 bound) {
|
|
|
|
u32 random = get_random_u16(state);
|
|
|
|
u32 multiresult = random * bound;
|
|
|
|
u16 leftover = multiresult;
|
2018-08-30 07:16:58 +02:00
|
|
|
if (leftover < bound) {
|
2018-10-04 20:25:16 +02:00
|
|
|
u16 threshold = -bound % bound;
|
2018-08-30 07:16:58 +02:00
|
|
|
while (leftover < threshold) {
|
|
|
|
random = get_random_u16(state);
|
|
|
|
multiresult = random * bound;
|
2018-10-04 20:25:16 +02:00
|
|
|
leftover = (u16)multiresult;
|
2018-08-30 07:16:58 +02:00
|
|
|
}
|
2018-08-21 21:23:22 +02:00
|
|
|
}
|
2018-08-30 07:16:58 +02:00
|
|
|
return multiresult >> 16;
|
2018-08-26 00:32:44 +02:00
|
|
|
}
|
|
|
|
|
2018-10-04 20:25:16 +02:00
|
|
|
u64 get_random_u64(struct random_state *state) {
|
|
|
|
u64 value;
|
2018-08-26 06:02:35 +02:00
|
|
|
size_t remaining = RANDOM_CACHE_SIZE - state->index;
|
|
|
|
if (remaining < sizeof(value)) {
|
|
|
|
refill(state);
|
|
|
|
}
|
|
|
|
memcpy(&value, state->cache + state->index, sizeof(value));
|
|
|
|
state->index += sizeof(value);
|
2018-08-26 00:32:44 +02:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2018-08-30 07:16:58 +02:00
|
|
|
// See Fast Random Integer Generation in an Interval by Daniel Lemire
|
2018-10-04 20:25:16 +02:00
|
|
|
u64 get_random_u64_uniform(struct random_state *state, u64 bound) {
|
|
|
|
u128 random = get_random_u64(state);
|
|
|
|
u128 multiresult = random * bound;
|
|
|
|
u64 leftover = multiresult;
|
2018-08-30 07:16:58 +02:00
|
|
|
if (leftover < bound) {
|
2018-10-04 20:25:16 +02:00
|
|
|
u64 threshold = -bound % bound;
|
2018-08-30 07:16:58 +02:00
|
|
|
while (leftover < threshold) {
|
|
|
|
random = get_random_u64(state);
|
|
|
|
multiresult = random * bound;
|
|
|
|
leftover = multiresult;
|
|
|
|
}
|
2018-08-26 00:32:44 +02:00
|
|
|
}
|
2018-08-30 07:16:58 +02:00
|
|
|
return multiresult >> 64;
|
2018-08-21 21:23:22 +02:00
|
|
|
}
|