hardened_malloc/random.c

118 lines
2.8 KiB
C
Raw Normal View History

2018-08-21 19:23:22 +00:00
#include <errno.h>
2018-08-23 20:30:44 +00:00
#include <string.h>
2018-08-21 19:23:22 +00:00
#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
2018-08-26 03:00:00 +00:00
#include "chacha.h"
static void get_random_seed(void *buf, size_t size) {
2018-08-21 19:23:22 +00: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-26 03:00:00 +00:00
#define KEY_SIZE 32
#define IV_SIZE 8
2018-08-23 20:30:44 +00:00
void random_state_init(struct random_state *state) {
2018-08-26 03:00:00 +00:00
uint8_t rnd[KEY_SIZE + IV_SIZE];
get_random_seed(rnd, sizeof(rnd));
chacha_keysetup(&state->ctx, rnd, KEY_SIZE * 8);
chacha_ivsetup(&state->ctx, rnd + KEY_SIZE);
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
state->index = 0;
state->reseed = 0;
2018-08-21 19:23:22 +00:00
}
2018-08-23 20:30:44 +00:00
void get_random_bytes(struct random_state *state, void *buf, size_t size) {
if (size > RANDOM_CACHE_SIZE / 2) {
2018-08-26 03:00:00 +00:00
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
2018-08-23 20:30:44 +00:00
return;
}
while (size) {
if (state->index == RANDOM_CACHE_SIZE) {
2018-08-26 03:00:00 +00:00
if (state->reseed >= RANDOM_RESEED_SIZE) {
random_state_init(state);
} else {
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
state->index = 0;
state->reseed += RANDOM_CACHE_SIZE;
}
2018-08-23 20:30:44 +00:00
}
size_t remaining = RANDOM_CACHE_SIZE - state->index;
size_t copy_size = size < remaining ? size : remaining;
memcpy(buf, state->cache + state->index, copy_size);
state->index += copy_size;
size -= copy_size;
}
2018-08-21 19:23:22 +00:00
}
uint16_t get_random_u16(struct random_state *state) {
uint16_t value;
get_random_bytes(state, &value, sizeof(value));
return value;
2018-08-21 19:23:22 +00:00
}
// based on OpenBSD arc4random_uniform
uint16_t get_random_u16_uniform(struct random_state *state, uint16_t bound) {
2018-08-21 19:23:22 +00:00
if (bound < 2) {
return 0;
}
uint16_t min = (uint16_t)-bound % bound;
2018-08-21 19:23:22 +00:00
uint16_t r;
2018-08-21 19:23:22 +00:00
do {
r = get_random_u16(state);
} while (r < min);
return r % bound;
}
uint64_t get_random_u64(struct random_state *state) {
uint64_t value;
get_random_bytes(state, &value, sizeof(value));
return value;
}
// based on OpenBSD arc4random_uniform
uint64_t get_random_u64_uniform(struct random_state *state, uint64_t bound) {
if (bound < 2) {
return 0;
}
uint64_t min = -bound % bound;
uint64_t r;
do {
r = get_random_u64(state);
2018-08-21 19:23:22 +00:00
} while (r < min);
return r % bound;
}