#define CRYPTO_PRIVATE
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_cipher.h"
#include "lib/crypt_ops/crypto_digest.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/intmath/cmp.h"
#include "lib/cc/ctassert.h"
#include "lib/malloc/map_anon.h"
#include "lib/thread/threads.h"
#include "lib/log/util_bug.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#ifdef NOINHERIT_CAN_FAIL
#define CHECK_PID
#endif
#ifdef CHECK_PID
#define PID_FIELD_LEN sizeof(pid_t)
#else
#define PID_FIELD_LEN 0
#endif
#define SEED_LEN (CRYPTO_FAST_RNG_SEED_LEN)
#define MAPLEN 4096
#define BUFLEN (MAPLEN - 2*sizeof(uint16_t) - SEED_LEN - PID_FIELD_LEN)
#define RESEED_AFTER 16
#define KEY_LEN (CRYPTO_FAST_RNG_SEED_LEN - CIPHER_IV_LEN)
#define KEY_BITS (KEY_LEN * 8)
CTASSERT(KEY_BITS == 128 || KEY_BITS == 192 || KEY_BITS == 256);
struct crypto_fast_rng_t {
int16_t n_till_reseed;
uint16_t bytes_left;
#ifdef CHECK_PID
pid_t owner;
#endif
struct cbuf_t {
uint8_t seed[SEED_LEN];
uint8_t bytes[BUFLEN];
} buf;
};
CTASSERT(sizeof(struct cbuf_t) == BUFLEN+SEED_LEN);
CTASSERT(sizeof(crypto_fast_rng_t) <= MAPLEN);
crypto_fast_rng_t *
crypto_fast_rng_new(void)
{
uint8_t seed[SEED_LEN];
crypto_strongest_rand(seed, sizeof(seed));
crypto_fast_rng_t *result = crypto_fast_rng_new_from_seed(seed);
memwipe(seed, 0, sizeof(seed));
return result;
}
crypto_fast_rng_t *
crypto_fast_rng_new_from_seed(const uint8_t *seed)
{
unsigned inherit = INHERIT_RES_KEEP;
crypto_fast_rng_t *result = tor_mmap_anonymous(sizeof(*result),
ANONMAP_PRIVATE | ANONMAP_NOINHERIT,
&inherit);
memcpy(result->buf.seed, seed, SEED_LEN);
result->bytes_left = 0;
result->n_till_reseed = RESEED_AFTER;
#ifdef CHECK_PID
if (inherit == INHERIT_RES_KEEP) {
result->owner = getpid();
}
#elif defined(_WIN32)
#else
tor_assertf(inherit != INHERIT_RES_KEEP,
"We failed to create a non-inheritable memory region, even "
"though we believed such a failure to be impossible! This is "
"probably a bug in Tor support for your platform; please report "
"it.");
#endif
return result;
}
#ifdef TOR_UNIT_TESTS
void
crypto_fast_rng_disable_reseed(crypto_fast_rng_t *rng)
{
rng->n_till_reseed = -1;
}
#endif
static inline crypto_cipher_t *
cipher_from_seed(const uint8_t *seed)
{
return crypto_cipher_new_with_iv_and_bits(seed, seed+KEY_LEN, KEY_BITS);
}
static void
crypto_fast_rng_add_entopy(crypto_fast_rng_t *rng)
{
crypto_xof_t *xof = crypto_xof_new();
crypto_xof_add_bytes(xof, rng->buf.seed, SEED_LEN);
{
uint8_t seedbuf[SEED_LEN];
crypto_strongest_rand(seedbuf, SEED_LEN);
crypto_xof_add_bytes(xof, seedbuf, SEED_LEN);
memwipe(seedbuf, 0, SEED_LEN);
}
crypto_xof_squeeze_bytes(xof, rng->buf.seed, SEED_LEN);
crypto_xof_free(xof);
}
static void
crypto_fast_rng_refill(crypto_fast_rng_t *rng)
{
rng->n_till_reseed--;
if (rng->n_till_reseed == 0) {
crypto_fast_rng_add_entopy(rng);
rng->n_till_reseed = RESEED_AFTER;
} else if (rng->n_till_reseed < 0) {
#ifdef TOR_UNIT_TESTS
rng->n_till_reseed = -1;
#else
tor_assert_unreached();
#endif
}
crypto_cipher_t *c = cipher_from_seed(rng->buf.seed);
memset(&rng->buf, 0, sizeof(rng->buf));
crypto_cipher_crypt_inplace(c, (char*)&rng->buf, sizeof(rng->buf));
crypto_cipher_free(c);
rng->bytes_left = sizeof(rng->buf.bytes);
}
void
crypto_fast_rng_free_(crypto_fast_rng_t *rng)
{
if (!rng)
return;
memwipe(rng, 0, sizeof(*rng));
tor_munmap_anonymous(rng, sizeof(*rng));
}
static void
crypto_fast_rng_getbytes_impl(crypto_fast_rng_t *rng, uint8_t *out,
const size_t n)
{
#ifdef CHECK_PID
if (rng->owner) {
tor_assert(rng->owner == getpid());
}
#endif
size_t bytes_to_yield = n;
while (bytes_to_yield) {
if (rng->bytes_left == 0)
crypto_fast_rng_refill(rng);
const size_t to_copy = MIN(rng->bytes_left, bytes_to_yield);
tor_assert(sizeof(rng->buf.bytes) >= rng->bytes_left);
uint8_t *copy_from = rng->buf.bytes +
(sizeof(rng->buf.bytes) - rng->bytes_left);
memcpy(out, copy_from, to_copy);
memset(copy_from, 0, to_copy);
out += to_copy;
bytes_to_yield -= to_copy;
rng->bytes_left -= to_copy;
}
}
void
crypto_fast_rng_getbytes(crypto_fast_rng_t *rng, uint8_t *out, size_t n)
{
if (PREDICT_UNLIKELY(n > BUFLEN)) {
uint8_t seed[SEED_LEN];
crypto_fast_rng_getbytes_impl(rng, seed, SEED_LEN);
crypto_cipher_t *c = cipher_from_seed(seed);
memset(out, 0, n);
crypto_cipher_crypt_inplace(c, (char*)out, n);
crypto_cipher_free(c);
memwipe(seed, 0, sizeof(seed));
return;
}
crypto_fast_rng_getbytes_impl(rng, out, n);
}
#if defined(TOR_UNIT_TESTS)
size_t
crypto_fast_rng_get_bytes_used_per_stream(void)
{
return BUFLEN;
}
#endif
static tor_threadlocal_t thread_rng;
crypto_fast_rng_t *
get_thread_fast_rng(void)
{
crypto_fast_rng_t *rng = tor_threadlocal_get(&thread_rng);
if (PREDICT_UNLIKELY(rng == NULL)) {
rng = crypto_fast_rng_new();
tor_threadlocal_set(&thread_rng, rng);
}
return rng;
}
void
destroy_thread_fast_rng(void)
{
crypto_fast_rng_t *rng = tor_threadlocal_get(&thread_rng);
if (!rng)
return;
crypto_fast_rng_free(rng);
tor_threadlocal_set(&thread_rng, NULL);
}
#ifdef TOR_UNIT_TESTS
crypto_fast_rng_t *
crypto_replace_thread_fast_rng(crypto_fast_rng_t *rng)
{
crypto_fast_rng_t *old_rng = tor_threadlocal_get(&thread_rng);
tor_threadlocal_set(&thread_rng, rng);
return old_rng;
}
#endif
void
crypto_rand_fast_init(void)
{
tor_threadlocal_init(&thread_rng);
}
void
crypto_rand_fast_shutdown(void)
{
destroy_thread_fast_rng();
tor_threadlocal_destroy(&thread_rng);
}