mirror of
https://github.com/GrapheneOS/hardened_malloc.git
synced 2024-12-23 22:49:35 -05:00
dcd969ae04
The stdint.h types don't cover 128-bit integers and the underscore makes them ill suited to usage in function suffixes. Instead, use the common naming style in the Linux kernel and elsewhere including the ChaCha8 implementation included here.
111 lines
3.0 KiB
C
111 lines
3.0 KiB
C
#include <errno.h>
|
|
#include <string.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
|
|
|
|
#include "chacha.h"
|
|
|
|
static void get_random_seed(void *buf, size_t size) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
u16 get_random_u16(struct random_state *state) {
|
|
u16 value;
|
|
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);
|
|
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;
|
|
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);
|
|
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;
|
|
}
|