mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 13:53:31 +01:00
Merge branch 'tor-github/pr/702'
This commit is contained in:
commit
6c173d00f5
2
.gitignore
vendored
2
.gitignore
vendored
@ -248,6 +248,7 @@ uptime-*.json
|
||||
/src/test/test-memwipe
|
||||
/src/test/test-ntor-cl
|
||||
/src/test/test-hs-ntor-cl
|
||||
/src/test/test-rng
|
||||
/src/test/test-switch-id
|
||||
/src/test/test-timers
|
||||
/src/test/test_workqueue
|
||||
@ -258,6 +259,7 @@ uptime-*.json
|
||||
/src/test/test-ntor-cl.exe
|
||||
/src/test/test-hs-ntor-cl.exe
|
||||
/src/test/test-memwipe.exe
|
||||
/src/test/test-rng.exe
|
||||
/src/test/test-switch-id.exe
|
||||
/src/test/test-timers.exe
|
||||
/src/test/test_workqueue.exe
|
||||
|
@ -605,8 +605,10 @@ AC_CHECK_FUNCS(
|
||||
llround \
|
||||
localtime_r \
|
||||
lround \
|
||||
madvise \
|
||||
memmem \
|
||||
memset_s \
|
||||
minherit \
|
||||
mmap \
|
||||
pipe \
|
||||
pipe2 \
|
||||
@ -1450,6 +1452,7 @@ AC_CHECK_HEADERS([errno.h \
|
||||
inttypes.h \
|
||||
limits.h \
|
||||
linux/types.h \
|
||||
mach/vm_inherit.h \
|
||||
machine/limits.h \
|
||||
malloc.h \
|
||||
malloc/malloc.h \
|
||||
|
@ -11,7 +11,6 @@
|
||||
* number generators, and working with randomness.
|
||||
**/
|
||||
|
||||
#ifndef CRYPTO_RAND_PRIVATE
|
||||
#define CRYPTO_RAND_PRIVATE
|
||||
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
@ -540,114 +539,6 @@ crypto_rand_u32(void)
|
||||
return rand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pseudorandom integer, chosen uniformly from the values
|
||||
* between 0 and <b>max</b>-1 inclusive. <b>max</b> must be between 1 and
|
||||
* INT_MAX+1, inclusive.
|
||||
*/
|
||||
int
|
||||
crypto_rand_int(unsigned int max)
|
||||
{
|
||||
unsigned int val;
|
||||
unsigned int cutoff;
|
||||
tor_assert(max <= ((unsigned int)INT_MAX)+1);
|
||||
tor_assert(max > 0); /* don't div by 0 */
|
||||
|
||||
/* We ignore any values that are >= 'cutoff,' to avoid biasing the
|
||||
* distribution with clipping at the upper end of unsigned int's
|
||||
* range.
|
||||
*/
|
||||
cutoff = UINT_MAX - (UINT_MAX%max);
|
||||
while (1) {
|
||||
crypto_rand((char*)&val, sizeof(val));
|
||||
if (val < cutoff)
|
||||
return val % max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pseudorandom integer, chosen uniformly from the values i such
|
||||
* that min <= i < max.
|
||||
*
|
||||
* <b>min</b> MUST be in range [0, <b>max</b>).
|
||||
* <b>max</b> MUST be in range (min, INT_MAX].
|
||||
**/
|
||||
int
|
||||
crypto_rand_int_range(unsigned int min, unsigned int max)
|
||||
{
|
||||
tor_assert(min < max);
|
||||
tor_assert(max <= INT_MAX);
|
||||
|
||||
/* The overflow is avoided here because crypto_rand_int() returns a value
|
||||
* between 0 and (max - min) inclusive. */
|
||||
return min + crypto_rand_int(max - min);
|
||||
}
|
||||
|
||||
/**
|
||||
* As crypto_rand_int_range, but supports uint64_t.
|
||||
**/
|
||||
uint64_t
|
||||
crypto_rand_uint64_range(uint64_t min, uint64_t max)
|
||||
{
|
||||
tor_assert(min < max);
|
||||
return min + crypto_rand_uint64(max - min);
|
||||
}
|
||||
|
||||
/**
|
||||
* As crypto_rand_int_range, but supports time_t.
|
||||
**/
|
||||
time_t
|
||||
crypto_rand_time_range(time_t min, time_t max)
|
||||
{
|
||||
tor_assert(min < max);
|
||||
return min + (time_t)crypto_rand_uint64(max - min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pseudorandom 64-bit integer, chosen uniformly from the values
|
||||
* between 0 and <b>max</b>-1 inclusive.
|
||||
**/
|
||||
uint64_t
|
||||
crypto_rand_uint64(uint64_t max)
|
||||
{
|
||||
uint64_t val;
|
||||
uint64_t cutoff;
|
||||
tor_assert(max < UINT64_MAX);
|
||||
tor_assert(max > 0); /* don't div by 0 */
|
||||
|
||||
/* We ignore any values that are >= 'cutoff,' to avoid biasing the
|
||||
* distribution with clipping at the upper end of unsigned int's
|
||||
* range.
|
||||
*/
|
||||
cutoff = UINT64_MAX - (UINT64_MAX%max);
|
||||
while (1) {
|
||||
crypto_rand((char*)&val, sizeof(val));
|
||||
if (val < cutoff)
|
||||
return val % max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pseudorandom double d, chosen uniformly from the range
|
||||
* 0.0 <= d < 1.0.
|
||||
**/
|
||||
double
|
||||
crypto_rand_double(void)
|
||||
{
|
||||
/* We just use an unsigned int here; we don't really care about getting
|
||||
* more than 32 bits of resolution */
|
||||
unsigned int u;
|
||||
crypto_rand((char*)&u, sizeof(u));
|
||||
#if SIZEOF_INT == 4
|
||||
#define UINT_MAX_AS_DOUBLE 4294967296.0
|
||||
#elif SIZEOF_INT == 8
|
||||
#define UINT_MAX_AS_DOUBLE 1.8446744073709552e+19
|
||||
#else
|
||||
#error SIZEOF_INT is neither 4 nor 8
|
||||
#endif /* SIZEOF_INT == 4 || ... */
|
||||
return ((double)u) / UINT_MAX_AS_DOUBLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and return a new random hostname starting with <b>prefix</b>,
|
||||
* ending with <b>suffix</b>, and containing no fewer than
|
||||
@ -738,5 +629,3 @@ crypto_force_rand_ssleay(void)
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !defined(CRYPTO_RAND_PRIVATE) */
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "lib/cc/compat_compiler.h"
|
||||
#include "lib/cc/torint.h"
|
||||
#include "lib/testsupport/testsupport.h"
|
||||
#include "lib/malloc/malloc.h"
|
||||
|
||||
/* random numbers */
|
||||
int crypto_seed_rng(void) ATTR_WUR;
|
||||
@ -24,6 +25,7 @@ void crypto_rand_unmocked(char *to, size_t n);
|
||||
void crypto_strongest_rand(uint8_t *out, size_t out_len);
|
||||
MOCK_DECL(void,crypto_strongest_rand_,(uint8_t *out, size_t out_len));
|
||||
int crypto_rand_int(unsigned int max);
|
||||
unsigned crypto_rand_uint(unsigned limit);
|
||||
int crypto_rand_int_range(unsigned int min, unsigned int max);
|
||||
uint64_t crypto_rand_uint64_range(uint64_t min, uint64_t max);
|
||||
time_t crypto_rand_time_range(time_t min, time_t max);
|
||||
@ -41,6 +43,36 @@ void *smartlist_choose(const struct smartlist_t *sl);
|
||||
void smartlist_shuffle(struct smartlist_t *sl);
|
||||
int crypto_force_rand_ssleay(void);
|
||||
|
||||
/**
|
||||
* A fast PRNG, for use when the PRNG provided by our crypto library isn't
|
||||
* fast enough. This one _should_ be cryptographically strong, but
|
||||
* has seen less auditing than the PRNGs in OpenSSL and NSS. Use with
|
||||
* caution.
|
||||
*
|
||||
* Note that this object is NOT thread-safe. If you need a thread-safe
|
||||
* prng, use crypto_rand(), or wrap this in a mutex.
|
||||
**/
|
||||
typedef struct crypto_fast_rng_t crypto_fast_rng_t;
|
||||
/**
|
||||
* Number of bytes used to seed a crypto_rand_fast_t.
|
||||
**/
|
||||
crypto_fast_rng_t *crypto_fast_rng_new(void);
|
||||
#define CRYPTO_FAST_RNG_SEED_LEN 48
|
||||
crypto_fast_rng_t *crypto_fast_rng_new_from_seed(const uint8_t *seed);
|
||||
void crypto_fast_rng_getbytes(crypto_fast_rng_t *rng, uint8_t *out, size_t n);
|
||||
void crypto_fast_rng_free_(crypto_fast_rng_t *);
|
||||
#define crypto_fast_rng_free(c) \
|
||||
FREE_AND_NULL(crypto_fast_rng_t, crypto_fast_rng_free_, (c))
|
||||
|
||||
unsigned crypto_fast_rng_get_uint(crypto_fast_rng_t *rng, unsigned limit);
|
||||
uint64_t crypto_fast_rng_get_uint64(crypto_fast_rng_t *rng, uint64_t limit);
|
||||
double crypto_fast_rng_get_double(crypto_fast_rng_t *rng);
|
||||
|
||||
#if defined(TOR_UNIT_TESTS)
|
||||
/* Used for white-box testing */
|
||||
size_t crypto_fast_rng_get_bytes_used_per_stream(void);
|
||||
#endif
|
||||
|
||||
#ifdef CRYPTO_RAND_PRIVATE
|
||||
|
||||
STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len);
|
||||
|
263
src/lib/crypt_ops/crypto_rand_fast.c
Normal file
263
src/lib/crypt_ops/crypto_rand_fast.c
Normal file
@ -0,0 +1,263 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_rand_fast.c
|
||||
*
|
||||
* \brief A fast strong PRNG for use when our underlying cryptographic
|
||||
* library's PRNG isn't fast enough.
|
||||
**/
|
||||
|
||||
/* This library is currently implemented to use the same implementation
|
||||
* technique as libottery, using AES-CTR-256 as our underlying stream cipher.
|
||||
* It's backtracking-resistant immediately, and prediction-resistant after
|
||||
* a while.
|
||||
*
|
||||
* Here's how it works:
|
||||
*
|
||||
* We generate pseudorandom bytes using AES-CTR-256. We generate BUFLEN bytes
|
||||
* at a time. When we do this, we keep the first SEED_LEN bytes as the key
|
||||
* and the IV for our next invocation of AES_CTR, and yield the remaining
|
||||
* BUFLEN - SEED_LEN bytes to the user as they invoke the PRNG. As we yield
|
||||
* bytes to the user, we clear them from the buffer.
|
||||
*
|
||||
* After we have refilled the buffer RESEED_AFTER times, we mix in an
|
||||
* additional SEED_LEN bytes from our strong PRNG into the seed.
|
||||
*
|
||||
* If the user ever asks for a huge number of bytes at once, we pull SEED_LEN
|
||||
* bytes from the PRNG and use them with our stream cipher to fill the user's
|
||||
* request.
|
||||
*/
|
||||
|
||||
#define CRYPTO_RAND_FAST_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/log/util_bug.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Alias for CRYPTO_FAST_RNG_SEED_LEN to make our code shorter.
|
||||
*/
|
||||
#define SEED_LEN (CRYPTO_FAST_RNG_SEED_LEN)
|
||||
|
||||
/* The amount of space that we mmap for a crypto_fast_rng_t.
|
||||
*/
|
||||
#define MAPLEN 4096
|
||||
|
||||
/* The number of random bytes that we can yield to the user after each
|
||||
* time we fill a crypto_fast_rng_t's buffer.
|
||||
*/
|
||||
#define BUFLEN (MAPLEN - 2*sizeof(uint16_t) - SEED_LEN)
|
||||
|
||||
/* The number of buffer refills after which we should fetch more
|
||||
* entropy from crypto_strongest_rand().
|
||||
*/
|
||||
#define RESEED_AFTER 16
|
||||
|
||||
/* The length of the stream cipher key we will use for the PRNG, in bytes.
|
||||
*/
|
||||
#define KEY_LEN (CRYPTO_FAST_RNG_SEED_LEN - CIPHER_IV_LEN)
|
||||
/* The length of the stream cipher key we will use for the PRNG, in bits.
|
||||
*/
|
||||
#define KEY_BITS (KEY_LEN * 8)
|
||||
|
||||
/* Make sure that we have a key length we can actually use with AES. */
|
||||
CTASSERT(KEY_BITS == 128 || KEY_BITS == 192 || KEY_BITS == 256);
|
||||
|
||||
struct crypto_fast_rng_t {
|
||||
/** How many more fills does this buffer have before we should mix
|
||||
* in the output of crypto_rand()? */
|
||||
uint16_t n_till_reseed;
|
||||
/** How many bytes are remaining in cbuf.bytes? */
|
||||
uint16_t bytes_left;
|
||||
struct cbuf {
|
||||
/** The seed (key and IV) that we will use the next time that we refill
|
||||
* cbuf. */
|
||||
uint8_t seed[SEED_LEN];
|
||||
/**
|
||||
* Bytes that we are yielding to the user. The next byte to be
|
||||
* yielded is at bytes[BUFLEN-bytes_left]; all other bytes in this
|
||||
* array are set to zero.
|
||||
*/
|
||||
uint8_t bytes[BUFLEN];
|
||||
} buf;
|
||||
};
|
||||
|
||||
/* alignof(uint8_t) should be 1, so there shouldn't be any padding in cbuf.
|
||||
*/
|
||||
CTASSERT(sizeof(struct cbuf) == BUFLEN+SEED_LEN);
|
||||
/* We're trying to fit all of the RNG state into a nice mmapable chunk.
|
||||
*/
|
||||
CTASSERT(sizeof(crypto_fast_rng_t) <= MAPLEN);
|
||||
|
||||
/**
|
||||
* Initialize and return a new fast PRNG, using a strong random seed.
|
||||
*
|
||||
* Note that this object is NOT thread-safe. If you need a thread-safe
|
||||
* prng, use crypto_rand(), or wrap this in a mutex.
|
||||
**/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and return a new fast PRNG, using a seed value specified
|
||||
* in <b>seed</b>. This value must be CRYPTO_FAST_RNG_SEED_LEN bytes
|
||||
* long.
|
||||
*
|
||||
* Note that this object is NOT thread-safe. If you need a thread-safe
|
||||
* prng, use crypto_rand(), or wrap this in a mutex.
|
||||
**/
|
||||
crypto_fast_rng_t *
|
||||
crypto_fast_rng_new_from_seed(const uint8_t *seed)
|
||||
{
|
||||
/* We try to allocate this object as securely as we can, to avoid
|
||||
* having it get dumped, swapped, or shared after fork.
|
||||
*/
|
||||
crypto_fast_rng_t *result = tor_mmap_anonymous(sizeof(*result),
|
||||
ANONMAP_PRIVATE | ANONMAP_NOINHERIT);
|
||||
|
||||
memcpy(result->buf.seed, seed, SEED_LEN);
|
||||
/* Causes an immediate refill once the user asks for data. */
|
||||
result->bytes_left = 0;
|
||||
result->n_till_reseed = RESEED_AFTER;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: create a crypto_cipher_t object from SEED_LEN bytes of
|
||||
* input. The first KEY_LEN bytes are used as the stream cipher's key,
|
||||
* and the remaining CIPHER_IV_LEN bytes are used as its IV.
|
||||
**/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: refill the seed bytes and output buffer of <b>rng</b>, using
|
||||
* the input seed bytes as input (key and IV) for the stream cipher.
|
||||
*
|
||||
* If the n_till_reseed counter has reached zero, mix more random bytes into
|
||||
* the seed before refilling the buffer.
|
||||
**/
|
||||
static void
|
||||
crypto_fast_rng_refill(crypto_fast_rng_t *rng)
|
||||
{
|
||||
if (rng->n_till_reseed-- == 0) {
|
||||
/* It's time to reseed the RNG. We'll do this by using our XOF to mix the
|
||||
* old value for the seed with some additional bytes from
|
||||
* crypto_strongest_rand(). */
|
||||
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);
|
||||
|
||||
rng->n_till_reseed = RESEED_AFTER;
|
||||
}
|
||||
/* Now fill rng->buf with output from our stream cipher, initialized from
|
||||
* that seed value. */
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all storage held by <b>rng</b>.
|
||||
**/
|
||||
void
|
||||
crypto_fast_rng_free_(crypto_fast_rng_t *rng)
|
||||
{
|
||||
if (!rng)
|
||||
return;
|
||||
memwipe(rng, 0, sizeof(*rng));
|
||||
tor_munmap_anonymous(rng, sizeof(*rng));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: extract bytes from the PRNG, refilling it as necessary. Does not
|
||||
* optimize the case when the user has asked for a huge output.
|
||||
**/
|
||||
static void
|
||||
crypto_fast_rng_getbytes_impl(crypto_fast_rng_t *rng, uint8_t *out,
|
||||
const size_t n)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract <b>n</b> bytes from <b>rng</b> into the buffer at <b>out</b>.
|
||||
**/
|
||||
void
|
||||
crypto_fast_rng_getbytes(crypto_fast_rng_t *rng, uint8_t *out, size_t n)
|
||||
{
|
||||
if (PREDICT_UNLIKELY(n > BUFLEN)) {
|
||||
/* The user has asked for a lot of output; generate it from a stream
|
||||
* cipher seeded by the PRNG rather than by pulling it out of the PRNG
|
||||
* directly.
|
||||
*/
|
||||
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)
|
||||
/** for white-box testing: return the number of bytes that are returned from
|
||||
* the user for each invocation of the stream cipher in this RNG. */
|
||||
size_t
|
||||
crypto_fast_rng_get_bytes_used_per_stream(void)
|
||||
{
|
||||
return BUFLEN;
|
||||
}
|
||||
#endif
|
166
src/lib/crypt_ops/crypto_rand_numeric.c
Normal file
166
src/lib/crypt_ops/crypto_rand_numeric.c
Normal file
@ -0,0 +1,166 @@
|
||||
/**
|
||||
* \file crypto_rand_numeric.c
|
||||
*
|
||||
* \brief Functions for retrieving uniformly distributed numbers
|
||||
* from our PRNGs.
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
|
||||
/**
|
||||
* Implementation macro: yields code that returns a uniform unbiased
|
||||
* random number between 0 and limit. "type" is the type of the number to
|
||||
* return; "maxval" is the largest possible value of "type"; and "fill_stmt"
|
||||
* is a code snippet that fills an object named "val" with random bits.
|
||||
**/
|
||||
#define IMPLEMENT_RAND_UNSIGNED(type, maxval, limit, fill_stmt) \
|
||||
do { \
|
||||
type val; \
|
||||
type cutoff; \
|
||||
tor_assert((limit) > 0); \
|
||||
\
|
||||
/* We ignore any values that are >= 'cutoff,' to avoid biasing */ \
|
||||
/* the distribution with clipping at the upper end of the type's */ \
|
||||
/* range. */ \
|
||||
cutoff = (maxval) - ((maxval)%(limit)); \
|
||||
while (1) { \
|
||||
fill_stmt; \
|
||||
if (val < cutoff) \
|
||||
return val % (limit); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Return a pseudorandom integer chosen uniformly from the values between 0
|
||||
* and <b>limit</b>-1 inclusive. limit must be strictly between 0 and
|
||||
* UINT_MAX. */
|
||||
unsigned
|
||||
crypto_rand_uint(unsigned limit)
|
||||
{
|
||||
tor_assert(limit < UINT_MAX);
|
||||
IMPLEMENT_RAND_UNSIGNED(unsigned, UINT_MAX, limit,
|
||||
crypto_rand((char*)&val, sizeof(val)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pseudorandom integer, chosen uniformly from the values
|
||||
* between 0 and <b>max</b>-1 inclusive. <b>max</b> must be between 1 and
|
||||
* INT_MAX+1, inclusive.
|
||||
*/
|
||||
int
|
||||
crypto_rand_int(unsigned int max)
|
||||
{
|
||||
/* We can't use IMPLEMENT_RAND_UNSIGNED directly, since we're trying
|
||||
* to return a signed type. Instead we make sure that the range is
|
||||
* reasonable for a nonnegative int, use crypto_rand_uint(), and cast.
|
||||
*/
|
||||
tor_assert(max <= ((unsigned int)INT_MAX)+1);
|
||||
|
||||
return (int)crypto_rand_uint(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pseudorandom integer, chosen uniformly from the values i such
|
||||
* that min <= i < max.
|
||||
*
|
||||
* <b>min</b> MUST be in range [0, <b>max</b>).
|
||||
* <b>max</b> MUST be in range (min, INT_MAX].
|
||||
**/
|
||||
int
|
||||
crypto_rand_int_range(unsigned int min, unsigned int max)
|
||||
{
|
||||
tor_assert(min < max);
|
||||
tor_assert(max <= INT_MAX);
|
||||
|
||||
/* The overflow is avoided here because crypto_rand_int() returns a value
|
||||
* between 0 and (max - min) inclusive. */
|
||||
return min + crypto_rand_int(max - min);
|
||||
}
|
||||
|
||||
/**
|
||||
* As crypto_rand_int_range, but supports uint64_t.
|
||||
**/
|
||||
uint64_t
|
||||
crypto_rand_uint64_range(uint64_t min, uint64_t max)
|
||||
{
|
||||
tor_assert(min < max);
|
||||
return min + crypto_rand_uint64(max - min);
|
||||
}
|
||||
|
||||
/**
|
||||
* As crypto_rand_int_range, but supports time_t.
|
||||
**/
|
||||
time_t
|
||||
crypto_rand_time_range(time_t min, time_t max)
|
||||
{
|
||||
tor_assert(min < max);
|
||||
return min + (time_t)crypto_rand_uint64(max - min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pseudorandom 64-bit integer, chosen uniformly from the values
|
||||
* between 0 and <b>max</b>-1 inclusive.
|
||||
**/
|
||||
uint64_t
|
||||
crypto_rand_uint64(uint64_t max)
|
||||
{
|
||||
tor_assert(max < UINT64_MAX);
|
||||
IMPLEMENT_RAND_UNSIGNED(uint64_t, UINT64_MAX, max,
|
||||
crypto_rand((char*)&val, sizeof(val)));
|
||||
}
|
||||
|
||||
#if SIZEOF_INT == 4
|
||||
#define UINT_MAX_AS_DOUBLE 4294967296.0
|
||||
#elif SIZEOF_INT == 8
|
||||
#define UINT_MAX_AS_DOUBLE 1.8446744073709552e+19
|
||||
#else
|
||||
#error SIZEOF_INT is neither 4 nor 8
|
||||
#endif /* SIZEOF_INT == 4 || ... */
|
||||
|
||||
/**
|
||||
* Return a pseudorandom double d, chosen uniformly from the range
|
||||
* 0.0 <= d < 1.0.
|
||||
**/
|
||||
double
|
||||
crypto_rand_double(void)
|
||||
{
|
||||
/* We just use an unsigned int here; we don't really care about getting
|
||||
* more than 32 bits of resolution */
|
||||
unsigned int u;
|
||||
crypto_rand((char*)&u, sizeof(u));
|
||||
return ((double)u) / UINT_MAX_AS_DOUBLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* As crypto_rand_uint, but extract the result from a crypto_fast_rng_t
|
||||
*/
|
||||
unsigned
|
||||
crypto_fast_rng_get_uint(crypto_fast_rng_t *rng, unsigned limit)
|
||||
{
|
||||
tor_assert(limit < UINT_MAX);
|
||||
IMPLEMENT_RAND_UNSIGNED(unsigned, UINT_MAX, limit,
|
||||
crypto_fast_rng_getbytes(rng, (void*)&val, sizeof(val)));
|
||||
}
|
||||
|
||||
/**
|
||||
* As crypto_rand_uint64, but extract the result from a crypto_fast_rng_t.
|
||||
*/
|
||||
uint64_t
|
||||
crypto_fast_rng_get_uint64(crypto_fast_rng_t *rng, uint64_t limit)
|
||||
{
|
||||
tor_assert(limit < UINT64_MAX);
|
||||
IMPLEMENT_RAND_UNSIGNED(uint64_t, UINT64_MAX, limit,
|
||||
crypto_fast_rng_getbytes(rng, (void*)&val, sizeof(val)));
|
||||
}
|
||||
|
||||
/**
|
||||
* As crypto_rand_, but extract the result from a crypto_fast_rng_t.
|
||||
*/
|
||||
double
|
||||
crypto_fast_rng_get_double(crypto_fast_rng_t *rng)
|
||||
{
|
||||
unsigned int u;
|
||||
crypto_fast_rng_getbytes(rng, (void*)&u, sizeof(u));
|
||||
return ((double)u) / UINT_MAX_AS_DOUBLE;
|
||||
}
|
@ -17,6 +17,8 @@ src_lib_libtor_crypt_ops_a_SOURCES = \
|
||||
src/lib/crypt_ops/crypto_ope.c \
|
||||
src/lib/crypt_ops/crypto_pwbox.c \
|
||||
src/lib/crypt_ops/crypto_rand.c \
|
||||
src/lib/crypt_ops/crypto_rand_fast.c \
|
||||
src/lib/crypt_ops/crypto_rand_numeric.c \
|
||||
src/lib/crypt_ops/crypto_rsa.c \
|
||||
src/lib/crypt_ops/crypto_s2k.c \
|
||||
src/lib/crypt_ops/crypto_util.c \
|
||||
|
@ -6,7 +6,8 @@ noinst_LIBRARIES += src/lib/libtor-malloc-testing.a
|
||||
endif
|
||||
|
||||
src_lib_libtor_malloc_a_SOURCES = \
|
||||
src/lib/malloc/malloc.c
|
||||
src/lib/malloc/malloc.c \
|
||||
src/lib/malloc/map_anon.c
|
||||
|
||||
if USE_OPENBSD_MALLOC
|
||||
src_lib_libtor_malloc_a_SOURCES += src/ext/OpenBSD_malloc_Linux.c
|
||||
@ -18,4 +19,5 @@ src_lib_libtor_malloc_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
|
||||
src_lib_libtor_malloc_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
|
||||
|
||||
noinst_HEADERS += \
|
||||
src/lib/malloc/malloc.h
|
||||
src/lib/malloc/malloc.h \
|
||||
src/lib/malloc/map_anon.h
|
||||
|
213
src/lib/malloc/map_anon.c
Normal file
213
src/lib/malloc/map_anon.c
Normal file
@ -0,0 +1,213 @@
|
||||
/* Copyright (c) 2003-2004, Roger Dingledine
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file map_anon.c
|
||||
* \brief Manage anonymous mappings.
|
||||
**/
|
||||
|
||||
#include "orconfig.h"
|
||||
#include "lib/malloc/map_anon.h"
|
||||
#include "lib/malloc/malloc.h"
|
||||
#include "lib/err/torerr.h"
|
||||
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_MACH_VM_INHERIT_H
|
||||
#include <mach/vm_inherit.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Macro to get the high bytes of a size_t, if there are high bytes.
|
||||
* Windows needs this; other operating systems define a size_t that does
|
||||
* what it should.
|
||||
*/
|
||||
#if SIZEOF_SIZE_T > 4
|
||||
#define HIGH_SIZE_T_BYTES(sz) ((sz) >> 32)
|
||||
#else
|
||||
#define HIGH_SIZE_T_BYTES(sz) (0)
|
||||
#endif
|
||||
|
||||
/* Here we define a MINHERIT macro that is minherit() or madvise(), depending
|
||||
* on what we actually want.
|
||||
*
|
||||
* If there's a flag that sets pages to zero after fork, we define FLAG_ZERO
|
||||
* to be that flag. If there's a flag unmaps pages after fork, we define
|
||||
* FLAG_NOINHERIT to be that flag.
|
||||
*/
|
||||
#if defined(HAVE_MINHERIT)
|
||||
#define MINHERIT minherit
|
||||
|
||||
#ifdef INHERIT_ZERO
|
||||
#define FLAG_ZERO INHERIT_ZERO
|
||||
#endif
|
||||
#ifdef INHERIT_NONE
|
||||
#define FLAG_NOINHERIT INHERIT_NONE
|
||||
#elif defined(VM_INHERIT_NONE)
|
||||
#define FLAG_NOINHERIT VM_INHERIT_NONE
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE_MADVISE)
|
||||
|
||||
#define MINHERIT madvise
|
||||
|
||||
#ifdef MADV_WIPEONFORK
|
||||
#define FLAG_ZERO MADV_WIPEONFORK
|
||||
#endif
|
||||
#ifdef MADV_DONTFORK
|
||||
#define FLAG_NOINHERIT MADV_DONTFORK
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Helper: try to prevent the <b>sz</b> bytes at <b>mem</b> from being swapped
|
||||
* to disk. Return 0 on success or if the facility is not available on this
|
||||
* OS; return -1 on failure.
|
||||
*/
|
||||
static int
|
||||
lock_mem(void *mem, size_t sz)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return VirtualLock(mem, sz) ? 0 : -1;
|
||||
#elif defined(HAVE_MLOCK)
|
||||
return mlock(mem, sz);
|
||||
#else
|
||||
(void) mem;
|
||||
(void) sz;
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: try to prevent the <b>sz</b> bytes at <b>mem</b> from appearing in
|
||||
* a core dump. Return 0 on success or if the facility is not available on
|
||||
* this OS; return -1 on failure.
|
||||
*/
|
||||
static int
|
||||
nodump_mem(void *mem, size_t sz)
|
||||
{
|
||||
#if defined(MADV_DONTDUMP)
|
||||
return madvise(mem, sz, MADV_DONTDUMP);
|
||||
#else
|
||||
(void) mem;
|
||||
(void) sz;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: try to prevent the <b>sz</b> bytes at <b>mem</b> from being
|
||||
* accessible in child processes -- ideally by having them set to 0 after a
|
||||
* fork, and if that doesn't work, by having them unmapped after a fork.
|
||||
* Return 0 on success or if the facility is not available on this OS; return
|
||||
* -1 on failure.
|
||||
*/
|
||||
static int
|
||||
noinherit_mem(void *mem, size_t sz)
|
||||
{
|
||||
#ifdef FLAG_ZERO
|
||||
int r = MINHERIT(mem, sz, FLAG_ZERO);
|
||||
if (r == 0)
|
||||
return 0;
|
||||
#endif
|
||||
#ifdef FLAG_NOINHERIT
|
||||
return MINHERIT(mem, sz, FLAG_NOINHERIT);
|
||||
#else
|
||||
(void)mem;
|
||||
(void)sz;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new anonymous memory mapping that holds <b>sz</b> bytes.
|
||||
*
|
||||
* Memory mappings are unlike the results from malloc() in that they are
|
||||
* handled separately by the operating system, and as such can have different
|
||||
* kernel-level flags set on them.
|
||||
*
|
||||
* The "flags" argument may be zero or more of ANONMAP_PRIVATE and
|
||||
* ANONMAP_NOINHERIT.
|
||||
*
|
||||
* Memory returned from this function must be released with
|
||||
* tor_munmap_anonymous().
|
||||
*
|
||||
* [Note: OS people use the word "anonymous" here to mean that the memory
|
||||
* isn't associated with any file. This has *nothing* to do with the kind of
|
||||
* anonymity that Tor is trying to provide.]
|
||||
*/
|
||||
void *
|
||||
tor_mmap_anonymous(size_t sz, unsigned flags)
|
||||
{
|
||||
void *ptr;
|
||||
#if defined(_WIN32)
|
||||
HANDLE mapping = CreateFileMapping(INVALID_HANDLE_VALUE,
|
||||
NULL, /*attributes*/
|
||||
PAGE_READWRITE,
|
||||
HIGH_SIZE_T_BYTES(sz),
|
||||
sz & 0xffffffff,
|
||||
NULL /* name */);
|
||||
raw_assert(mapping != NULL);
|
||||
ptr = MapViewOfFile(mapping, FILE_MAP_WRITE,
|
||||
0, 0, /* Offset */
|
||||
0 /* Extend to end of mapping */);
|
||||
raw_assert(ptr);
|
||||
CloseHandle(mapping); /* mapped view holds a reference */
|
||||
#elif defined(HAVE_SYS_MMAN_H)
|
||||
ptr = mmap(NULL, sz,
|
||||
PROT_READ|PROT_WRITE,
|
||||
MAP_ANON|MAP_PRIVATE,
|
||||
-1, 0);
|
||||
raw_assert(ptr != MAP_FAILED);
|
||||
raw_assert(ptr != NULL);
|
||||
#else
|
||||
ptr = tor_malloc_zero(sz);
|
||||
#endif
|
||||
|
||||
if (flags & ANONMAP_PRIVATE) {
|
||||
int lock_result = lock_mem(ptr, sz);
|
||||
raw_assert(lock_result == 0);
|
||||
int nodump_result = nodump_mem(ptr, sz);
|
||||
raw_assert(nodump_result == 0);
|
||||
}
|
||||
|
||||
if (flags & ANONMAP_NOINHERIT) {
|
||||
int noinherit_result = noinherit_mem(ptr, sz);
|
||||
raw_assert(noinherit_result == 0);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release <b>sz</b> bytes of memory that were previously mapped at
|
||||
* <b>mapping</b> by tor_mmap_anonymous().
|
||||
**/
|
||||
void
|
||||
tor_munmap_anonymous(void *mapping, size_t sz)
|
||||
{
|
||||
if (!mapping)
|
||||
return;
|
||||
|
||||
#if defined(_WIN32)
|
||||
(void)sz;
|
||||
UnmapViewOfFile(mapping);
|
||||
#elif defined(HAVE_SYS_MMAN_H)
|
||||
munmap(mapping, sz);
|
||||
#else
|
||||
(void)sz;
|
||||
tor_free(mapping);
|
||||
#endif
|
||||
}
|
37
src/lib/malloc/map_anon.h
Normal file
37
src/lib/malloc/map_anon.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* Copyright (c) 2003-2004, Roger Dingledine
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file map_anon.h
|
||||
* \brief Headers for map_anon.c
|
||||
**/
|
||||
|
||||
#ifndef TOR_MAP_ANON_H
|
||||
#define TOR_MAP_ANON_H
|
||||
|
||||
#include "lib/malloc/malloc.h"
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* When this flag is specified, try to prevent the mapping from being
|
||||
* swapped or dumped.
|
||||
*
|
||||
* In some operating systems, this flag is not implemented.
|
||||
*/
|
||||
#define ANONMAP_PRIVATE (1u<<0)
|
||||
/**
|
||||
* When this flag is specified, try to prevent the mapping from being
|
||||
* inherited after a fork(). In some operating systems, trying to access it
|
||||
* afterwards will cause its contents to be zero. In others, trying to access
|
||||
* it afterwards will cause a crash.
|
||||
*
|
||||
* In some operating systems, this flag is not implemented at all.
|
||||
*/
|
||||
#define ANONMAP_NOINHERIT (1u<<1)
|
||||
|
||||
void *tor_mmap_anonymous(size_t sz, unsigned flags);
|
||||
void tor_munmap_anonymous(void *mapping, size_t sz);
|
||||
|
||||
#endif /* !defined(TOR_MAP_ANON_H) */
|
@ -14,6 +14,8 @@
|
||||
#include "core/crypto/onion_tap.h"
|
||||
#include "core/crypto/relay_crypto.h"
|
||||
|
||||
#include "lib/intmath/weakrng.h"
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/evp.h>
|
||||
@ -335,6 +337,65 @@ bench_ed25519(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_rand_len(int len)
|
||||
{
|
||||
const int N = 100000;
|
||||
int i;
|
||||
char *buf = tor_malloc(len);
|
||||
uint64_t start,end;
|
||||
|
||||
start = perftime();
|
||||
for (i = 0; i < N; ++i) {
|
||||
crypto_rand(buf, len);
|
||||
}
|
||||
end = perftime();
|
||||
printf("crypto_rand(%d): %f nsec.\n", len, NANOCOUNT(start,end,N));
|
||||
|
||||
crypto_fast_rng_t *fr = crypto_fast_rng_new();
|
||||
start = perftime();
|
||||
for (i = 0; i < N; ++i) {
|
||||
crypto_fast_rng_getbytes(fr,(uint8_t*)buf,len);
|
||||
}
|
||||
end = perftime();
|
||||
printf("crypto_fast_rng_getbytes(%d): %f nsec.\n", len,
|
||||
NANOCOUNT(start,end,N));
|
||||
crypto_fast_rng_free(fr);
|
||||
|
||||
if (len <= 32) {
|
||||
start = perftime();
|
||||
for (i = 0; i < N; ++i) {
|
||||
crypto_strongest_rand((uint8_t*)buf, len);
|
||||
}
|
||||
end = perftime();
|
||||
printf("crypto_strongest_rand(%d): %f nsec.\n", len,
|
||||
NANOCOUNT(start,end,N));
|
||||
}
|
||||
|
||||
if (len == 4) {
|
||||
tor_weak_rng_t weak;
|
||||
tor_init_weak_random(&weak, 1337);
|
||||
|
||||
start = perftime();
|
||||
uint32_t t=0;
|
||||
for (i = 0; i < N; ++i) {
|
||||
t += tor_weak_random(&weak);
|
||||
}
|
||||
end = perftime();
|
||||
printf("weak_rand(4): %f nsec.\n", NANOCOUNT(start,end,N));
|
||||
}
|
||||
|
||||
tor_free(buf);
|
||||
}
|
||||
|
||||
static void
|
||||
bench_rand(void)
|
||||
{
|
||||
bench_rand_len(4);
|
||||
bench_rand_len(16);
|
||||
bench_rand_len(128);
|
||||
}
|
||||
|
||||
static void
|
||||
bench_cell_aes(void)
|
||||
{
|
||||
@ -695,6 +756,7 @@ static struct benchmark_t benchmarks[] = {
|
||||
ENT(onion_TAP),
|
||||
ENT(onion_ntor),
|
||||
ENT(ed25519),
|
||||
ENT(rand),
|
||||
|
||||
ENT(cell_aes),
|
||||
ENT(cell_ops),
|
||||
|
@ -68,7 +68,8 @@ noinst_PROGRAMS+= \
|
||||
src/test/test-process \
|
||||
src/test/test_workqueue \
|
||||
src/test/test-switch-id \
|
||||
src/test/test-timers
|
||||
src/test/test-timers \
|
||||
src/test/test-rng
|
||||
endif
|
||||
|
||||
src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
|
||||
@ -120,6 +121,7 @@ src_test_test_SOURCES += \
|
||||
src/test/test_controller_events.c \
|
||||
src/test/test_crypto.c \
|
||||
src/test/test_crypto_ope.c \
|
||||
src/test/test_crypto_rng.c \
|
||||
src/test/test_data.c \
|
||||
src/test/test_dir.c \
|
||||
src/test/test_dir_common.c \
|
||||
@ -257,7 +259,13 @@ src_test_test_LDADD = \
|
||||
src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS)
|
||||
src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS)
|
||||
src_test_test_slow_LDADD = $(src_test_test_LDADD)
|
||||
src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS)
|
||||
src_test_test_slow_LDFLAGS =@TOR_LDFLAGS_openssl@
|
||||
|
||||
src_test_test_rng_CPPFLAGS = $(src_test_test_CPPFLAGS)
|
||||
src_test_test_rng_CFLAGS = $(src_test_test_CFLAGS)
|
||||
src_test_test_rng_SOURCES = src/test/test_rng.c
|
||||
src_test_test_rng_LDFLAGS = $(src_test_test_LDFLAGS)
|
||||
src_test_test_rng_LDADD = $(src_test_test_LDADD)
|
||||
|
||||
src_test_test_memwipe_CPPFLAGS = $(src_test_test_CPPFLAGS)
|
||||
# Don't use bugtrap cflags here: memwipe tests require memory violations.
|
||||
|
@ -866,6 +866,7 @@ struct testgroup_t testgroups[] = {
|
||||
{ "crypto/openssl/", crypto_openssl_tests },
|
||||
#endif
|
||||
{ "crypto/pem/", pem_tests },
|
||||
{ "crypto/rng/", crypto_rng_tests },
|
||||
{ "dir/", dir_tests },
|
||||
{ "dir/md/", microdesc_tests },
|
||||
{ "dir/voting/flags/", voting_flags_tests },
|
||||
|
@ -206,6 +206,7 @@ extern struct testcase_t controller_event_tests[];
|
||||
extern struct testcase_t controller_tests[];
|
||||
extern struct testcase_t crypto_ope_tests[];
|
||||
extern struct testcase_t crypto_openssl_tests[];
|
||||
extern struct testcase_t crypto_rng_tests[];
|
||||
extern struct testcase_t crypto_tests[];
|
||||
extern struct testcase_t dir_handle_get_tests[];
|
||||
extern struct testcase_t dir_tests[];
|
||||
|
@ -254,168 +254,6 @@ test_crypto_openssl_version(void *arg)
|
||||
;
|
||||
}
|
||||
|
||||
/** Run unit tests for our random number generation function and its wrappers.
|
||||
*/
|
||||
static void
|
||||
test_crypto_rng(void *arg)
|
||||
{
|
||||
int i, j, allok;
|
||||
char data1[100], data2[100];
|
||||
double d;
|
||||
char *h=NULL;
|
||||
|
||||
/* Try out RNG. */
|
||||
(void)arg;
|
||||
tt_assert(! crypto_seed_rng());
|
||||
crypto_rand(data1, 100);
|
||||
crypto_rand(data2, 100);
|
||||
tt_mem_op(data1,OP_NE, data2,100);
|
||||
allok = 1;
|
||||
for (i = 0; i < 100; ++i) {
|
||||
uint64_t big;
|
||||
char *host;
|
||||
j = crypto_rand_int(100);
|
||||
if (j < 0 || j >= 100)
|
||||
allok = 0;
|
||||
big = crypto_rand_uint64(UINT64_C(1)<<40);
|
||||
if (big >= (UINT64_C(1)<<40))
|
||||
allok = 0;
|
||||
big = crypto_rand_uint64(UINT64_C(5));
|
||||
if (big >= 5)
|
||||
allok = 0;
|
||||
d = crypto_rand_double();
|
||||
tt_assert(d >= 0);
|
||||
tt_assert(d < 1.0);
|
||||
host = crypto_random_hostname(3,8,"www.",".onion");
|
||||
if (strcmpstart(host,"www.") ||
|
||||
strcmpend(host,".onion") ||
|
||||
strlen(host) < 13 ||
|
||||
strlen(host) > 18)
|
||||
allok = 0;
|
||||
tor_free(host);
|
||||
}
|
||||
|
||||
/* Make sure crypto_random_hostname clips its inputs properly. */
|
||||
h = crypto_random_hostname(20000, 9000, "www.", ".onion");
|
||||
tt_assert(! strcmpstart(h,"www."));
|
||||
tt_assert(! strcmpend(h,".onion"));
|
||||
tt_int_op(63+4+6, OP_EQ, strlen(h));
|
||||
|
||||
tt_assert(allok);
|
||||
done:
|
||||
tor_free(h);
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_rng_range(void *arg)
|
||||
{
|
||||
int got_smallest = 0, got_largest = 0;
|
||||
int i;
|
||||
|
||||
(void)arg;
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
int x = crypto_rand_int_range(5,9);
|
||||
tt_int_op(x, OP_GE, 5);
|
||||
tt_int_op(x, OP_LT, 9);
|
||||
if (x == 5)
|
||||
got_smallest = 1;
|
||||
if (x == 8)
|
||||
got_largest = 1;
|
||||
}
|
||||
/* These fail with probability 1/10^603. */
|
||||
tt_assert(got_smallest);
|
||||
tt_assert(got_largest);
|
||||
|
||||
got_smallest = got_largest = 0;
|
||||
const uint64_t ten_billion = 10 * ((uint64_t)1000000000000);
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
uint64_t x = crypto_rand_uint64_range(ten_billion, ten_billion+10);
|
||||
tt_u64_op(x, OP_GE, ten_billion);
|
||||
tt_u64_op(x, OP_LT, ten_billion+10);
|
||||
if (x == ten_billion)
|
||||
got_smallest = 1;
|
||||
if (x == ten_billion+9)
|
||||
got_largest = 1;
|
||||
}
|
||||
|
||||
tt_assert(got_smallest);
|
||||
tt_assert(got_largest);
|
||||
|
||||
const time_t now = time(NULL);
|
||||
for (i = 0; i < 2000; ++i) {
|
||||
time_t x = crypto_rand_time_range(now, now+60);
|
||||
tt_i64_op(x, OP_GE, now);
|
||||
tt_i64_op(x, OP_LT, now+60);
|
||||
if (x == now)
|
||||
got_smallest = 1;
|
||||
if (x == now+59)
|
||||
got_largest = 1;
|
||||
}
|
||||
|
||||
tt_assert(got_smallest);
|
||||
tt_assert(got_largest);
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_rng_strongest(void *arg)
|
||||
{
|
||||
const char *how = arg;
|
||||
int broken = 0;
|
||||
|
||||
if (how == NULL) {
|
||||
;
|
||||
} else if (!strcmp(how, "nosyscall")) {
|
||||
break_strongest_rng_syscall = 1;
|
||||
} else if (!strcmp(how, "nofallback")) {
|
||||
break_strongest_rng_fallback = 1;
|
||||
} else if (!strcmp(how, "broken")) {
|
||||
broken = break_strongest_rng_syscall = break_strongest_rng_fallback = 1;
|
||||
}
|
||||
|
||||
#define N 128
|
||||
uint8_t combine_and[N];
|
||||
uint8_t combine_or[N];
|
||||
int i, j;
|
||||
|
||||
memset(combine_and, 0xff, N);
|
||||
memset(combine_or, 0, N);
|
||||
|
||||
for (i = 0; i < 100; ++i) { /* 2^-100 chances just don't happen. */
|
||||
uint8_t output[N];
|
||||
memset(output, 0, N);
|
||||
if (how == NULL) {
|
||||
/* this one can't fail. */
|
||||
crypto_strongest_rand(output, sizeof(output));
|
||||
} else {
|
||||
int r = crypto_strongest_rand_raw(output, sizeof(output));
|
||||
if (r == -1) {
|
||||
if (broken) {
|
||||
goto done; /* we're fine. */
|
||||
}
|
||||
/* This function is allowed to break, but only if it always breaks. */
|
||||
tt_int_op(i, OP_EQ, 0);
|
||||
tt_skip();
|
||||
} else {
|
||||
tt_assert(! broken);
|
||||
}
|
||||
}
|
||||
for (j = 0; j < N; ++j) {
|
||||
combine_and[j] &= output[j];
|
||||
combine_or[j] |= output[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < N; ++j) {
|
||||
tt_int_op(combine_and[j], OP_EQ, 0);
|
||||
tt_int_op(combine_or[j], OP_EQ, 0xff);
|
||||
}
|
||||
done:
|
||||
;
|
||||
#undef N
|
||||
}
|
||||
|
||||
/** Run unit tests for our AES128 functionality */
|
||||
static void
|
||||
test_crypto_aes128(void *arg)
|
||||
@ -3140,15 +2978,6 @@ test_crypto_failure_modes(void *arg)
|
||||
|
||||
struct testcase_t crypto_tests[] = {
|
||||
CRYPTO_LEGACY(formats),
|
||||
CRYPTO_LEGACY(rng),
|
||||
{ "rng_range", test_crypto_rng_range, 0, NULL, NULL },
|
||||
{ "rng_strongest", test_crypto_rng_strongest, TT_FORK, NULL, NULL },
|
||||
{ "rng_strongest_nosyscall", test_crypto_rng_strongest, TT_FORK,
|
||||
&passthrough_setup, (void*)"nosyscall" },
|
||||
{ "rng_strongest_nofallback", test_crypto_rng_strongest, TT_FORK,
|
||||
&passthrough_setup, (void*)"nofallback" },
|
||||
{ "rng_strongest_broken", test_crypto_rng_strongest, TT_FORK,
|
||||
&passthrough_setup, (void*)"broken" },
|
||||
{ "openssl_version", test_crypto_openssl_version, TT_FORK, NULL, NULL },
|
||||
{ "aes_AES", test_crypto_aes128, TT_FORK, &passthrough_setup, (void*)"aes" },
|
||||
{ "aes_EVP", test_crypto_aes128, TT_FORK, &passthrough_setup, (void*)"evp" },
|
||||
|
324
src/test/test_crypto_rng.c
Normal file
324
src/test/test_crypto_rng.c
Normal file
@ -0,0 +1,324 @@
|
||||
/* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#include "orconfig.h"
|
||||
#define CRYPTO_RAND_PRIVATE
|
||||
#include "core/or/or.h"
|
||||
#include "test/test.h"
|
||||
#include "lib/crypt_ops/aes.h"
|
||||
#include "lib/crypt_ops/crypto_format.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
|
||||
/** Run unit tests for our random number generation function and its wrappers.
|
||||
*/
|
||||
static void
|
||||
test_crypto_rng(void *arg)
|
||||
{
|
||||
int i, j, allok;
|
||||
char data1[100], data2[100];
|
||||
double d;
|
||||
char *h=NULL;
|
||||
|
||||
/* Try out RNG. */
|
||||
(void)arg;
|
||||
tt_assert(! crypto_seed_rng());
|
||||
crypto_rand(data1, 100);
|
||||
crypto_rand(data2, 100);
|
||||
tt_mem_op(data1,OP_NE, data2,100);
|
||||
allok = 1;
|
||||
for (i = 0; i < 100; ++i) {
|
||||
uint64_t big;
|
||||
char *host;
|
||||
j = crypto_rand_int(100);
|
||||
if (j < 0 || j >= 100)
|
||||
allok = 0;
|
||||
big = crypto_rand_uint64(UINT64_C(1)<<40);
|
||||
if (big >= (UINT64_C(1)<<40))
|
||||
allok = 0;
|
||||
big = crypto_rand_uint64(UINT64_C(5));
|
||||
if (big >= 5)
|
||||
allok = 0;
|
||||
d = crypto_rand_double();
|
||||
tt_assert(d >= 0);
|
||||
tt_assert(d < 1.0);
|
||||
host = crypto_random_hostname(3,8,"www.",".onion");
|
||||
if (strcmpstart(host,"www.") ||
|
||||
strcmpend(host,".onion") ||
|
||||
strlen(host) < 13 ||
|
||||
strlen(host) > 18)
|
||||
allok = 0;
|
||||
tor_free(host);
|
||||
}
|
||||
|
||||
/* Make sure crypto_random_hostname clips its inputs properly. */
|
||||
h = crypto_random_hostname(20000, 9000, "www.", ".onion");
|
||||
tt_assert(! strcmpstart(h,"www."));
|
||||
tt_assert(! strcmpend(h,".onion"));
|
||||
tt_int_op(63+4+6, OP_EQ, strlen(h));
|
||||
|
||||
tt_assert(allok);
|
||||
done:
|
||||
tor_free(h);
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_rng_range(void *arg)
|
||||
{
|
||||
int got_smallest = 0, got_largest = 0;
|
||||
int i;
|
||||
|
||||
(void)arg;
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
int x = crypto_rand_int_range(5,9);
|
||||
tt_int_op(x, OP_GE, 5);
|
||||
tt_int_op(x, OP_LT, 9);
|
||||
if (x == 5)
|
||||
got_smallest = 1;
|
||||
if (x == 8)
|
||||
got_largest = 1;
|
||||
}
|
||||
/* These fail with probability 1/10^603. */
|
||||
tt_assert(got_smallest);
|
||||
tt_assert(got_largest);
|
||||
|
||||
got_smallest = got_largest = 0;
|
||||
const uint64_t ten_billion = 10 * ((uint64_t)1000000000000);
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
uint64_t x = crypto_rand_uint64_range(ten_billion, ten_billion+10);
|
||||
tt_u64_op(x, OP_GE, ten_billion);
|
||||
tt_u64_op(x, OP_LT, ten_billion+10);
|
||||
if (x == ten_billion)
|
||||
got_smallest = 1;
|
||||
if (x == ten_billion+9)
|
||||
got_largest = 1;
|
||||
}
|
||||
|
||||
tt_assert(got_smallest);
|
||||
tt_assert(got_largest);
|
||||
|
||||
const time_t now = time(NULL);
|
||||
for (i = 0; i < 2000; ++i) {
|
||||
time_t x = crypto_rand_time_range(now, now+60);
|
||||
tt_i64_op(x, OP_GE, now);
|
||||
tt_i64_op(x, OP_LT, now+60);
|
||||
if (x == now)
|
||||
got_smallest = 1;
|
||||
if (x == now+59)
|
||||
got_largest = 1;
|
||||
}
|
||||
|
||||
tt_assert(got_smallest);
|
||||
tt_assert(got_largest);
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_rng_strongest(void *arg)
|
||||
{
|
||||
const char *how = arg;
|
||||
int broken = 0;
|
||||
|
||||
if (how == NULL) {
|
||||
;
|
||||
} else if (!strcmp(how, "nosyscall")) {
|
||||
break_strongest_rng_syscall = 1;
|
||||
} else if (!strcmp(how, "nofallback")) {
|
||||
break_strongest_rng_fallback = 1;
|
||||
} else if (!strcmp(how, "broken")) {
|
||||
broken = break_strongest_rng_syscall = break_strongest_rng_fallback = 1;
|
||||
}
|
||||
|
||||
#define N 128
|
||||
uint8_t combine_and[N];
|
||||
uint8_t combine_or[N];
|
||||
int i, j;
|
||||
|
||||
memset(combine_and, 0xff, N);
|
||||
memset(combine_or, 0, N);
|
||||
|
||||
for (i = 0; i < 100; ++i) { /* 2^-100 chances just don't happen. */
|
||||
uint8_t output[N];
|
||||
memset(output, 0, N);
|
||||
if (how == NULL) {
|
||||
/* this one can't fail. */
|
||||
crypto_strongest_rand(output, sizeof(output));
|
||||
} else {
|
||||
int r = crypto_strongest_rand_raw(output, sizeof(output));
|
||||
if (r == -1) {
|
||||
if (broken) {
|
||||
goto done; /* we're fine. */
|
||||
}
|
||||
/* This function is allowed to break, but only if it always breaks. */
|
||||
tt_int_op(i, OP_EQ, 0);
|
||||
tt_skip();
|
||||
} else {
|
||||
tt_assert(! broken);
|
||||
}
|
||||
}
|
||||
for (j = 0; j < N; ++j) {
|
||||
combine_and[j] &= output[j];
|
||||
combine_or[j] |= output[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < N; ++j) {
|
||||
tt_int_op(combine_and[j], OP_EQ, 0);
|
||||
tt_int_op(combine_or[j], OP_EQ, 0xff);
|
||||
}
|
||||
done:
|
||||
;
|
||||
#undef N
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_rng_fast(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
crypto_fast_rng_t *rng = crypto_fast_rng_new();
|
||||
tt_assert(rng);
|
||||
|
||||
/* Rudimentary black-block test to make sure that our prng outputs
|
||||
* have all bits sometimes on and all bits sometimes off. */
|
||||
uint64_t m1 = 0, m2 = ~(uint64_t)0;
|
||||
const int N = 128;
|
||||
|
||||
for (int i=0; i < N; ++i) {
|
||||
uint64_t v;
|
||||
crypto_fast_rng_getbytes(rng, (void*)&v, sizeof(v));
|
||||
m1 |= v;
|
||||
m2 &= v;
|
||||
}
|
||||
|
||||
tt_u64_op(m1, OP_EQ, ~(uint64_t)0);
|
||||
tt_u64_op(m2, OP_EQ, 0);
|
||||
|
||||
/* Check range functions. */
|
||||
int counts[5];
|
||||
memset(counts, 0, sizeof(counts));
|
||||
for (int i=0; i < N; ++i) {
|
||||
unsigned u = crypto_fast_rng_get_uint(rng, 5);
|
||||
tt_int_op(u, OP_GE, 0);
|
||||
tt_int_op(u, OP_LT, 5);
|
||||
counts[u]++;
|
||||
|
||||
uint64_t u64 = crypto_fast_rng_get_uint64(rng, UINT64_C(1)<<40);
|
||||
tt_u64_op(u64, OP_GE, 0);
|
||||
tt_u64_op(u64, OP_LT, UINT64_C(1)<<40);
|
||||
|
||||
double d = crypto_fast_rng_get_double(rng);
|
||||
tt_assert(d >= 0.0);
|
||||
tt_assert(d < 1.0);
|
||||
}
|
||||
|
||||
/* All values should have come up once. */
|
||||
for (int i=0; i<5; ++i) {
|
||||
tt_int_op(counts[i], OP_GT, 0);
|
||||
}
|
||||
|
||||
done:
|
||||
crypto_fast_rng_free(rng);
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_rng_fast_whitebox(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
const size_t buflen = crypto_fast_rng_get_bytes_used_per_stream();
|
||||
char *buf = tor_malloc_zero(buflen);
|
||||
char *buf2 = tor_malloc_zero(buflen);
|
||||
char *buf3 = NULL, *buf4 = NULL;
|
||||
|
||||
crypto_cipher_t *cipher = NULL, *cipher2 = NULL;
|
||||
uint8_t seed[CRYPTO_FAST_RNG_SEED_LEN];
|
||||
memset(seed, 0, sizeof(seed));
|
||||
|
||||
/* Start with a prng with zero key and zero IV. */
|
||||
crypto_fast_rng_t *rng = crypto_fast_rng_new_from_seed(seed);
|
||||
tt_assert(rng);
|
||||
|
||||
/* We'll use a stream cipher to keep in sync */
|
||||
cipher = crypto_cipher_new_with_iv_and_bits(seed, seed+32, 256);
|
||||
|
||||
/* The first 48 bytes are used for the next seed -- let's make sure we have
|
||||
* them.
|
||||
*/
|
||||
memset(seed, 0, sizeof(seed));
|
||||
crypto_cipher_crypt_inplace(cipher, (char*)seed, sizeof(seed));
|
||||
|
||||
/* if we get 128 bytes, they should match the bytes from the aes256-counter
|
||||
* stream, starting at position 48.
|
||||
*/
|
||||
crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 128);
|
||||
memset(buf2, 0, 128);
|
||||
crypto_cipher_crypt_inplace(cipher, buf2, 128);
|
||||
tt_mem_op(buf, OP_EQ, buf2, 128);
|
||||
|
||||
/* Try that again, with an odd number of bytes. */
|
||||
crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 199);
|
||||
memset(buf2, 0, 199);
|
||||
crypto_cipher_crypt_inplace(cipher, buf2, 199);
|
||||
tt_mem_op(buf, OP_EQ, buf2, 199);
|
||||
|
||||
/* Make sure that refilling works as expected: skip all but the last 5 bytes
|
||||
* of this steam. */
|
||||
size_t skip = buflen - (199+128) - 5;
|
||||
crypto_fast_rng_getbytes(rng, (uint8_t*)buf, skip);
|
||||
crypto_cipher_crypt_inplace(cipher, buf2, skip);
|
||||
|
||||
/* Now get the next 128 bytes. The first 5 will come from this stream, and
|
||||
* the next 5 will come from the stream keyed by the new value of 'seed'. */
|
||||
crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 128);
|
||||
memset(buf2, 0, 128);
|
||||
crypto_cipher_crypt_inplace(cipher, buf2, 5);
|
||||
crypto_cipher_free(cipher);
|
||||
cipher = crypto_cipher_new_with_iv_and_bits(seed, seed+32, 256);
|
||||
memset(seed, 0, sizeof(seed));
|
||||
crypto_cipher_crypt_inplace(cipher, (char*)seed, sizeof(seed));
|
||||
crypto_cipher_crypt_inplace(cipher, buf2+5, 128-5);
|
||||
tt_mem_op(buf, OP_EQ, buf2, 128);
|
||||
|
||||
/* And check the next 7 bytes to make sure we didn't discard anything. */
|
||||
crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 7);
|
||||
memset(buf2, 0, 7);
|
||||
crypto_cipher_crypt_inplace(cipher, buf2, 7);
|
||||
tt_mem_op(buf, OP_EQ, buf2, 7);
|
||||
|
||||
/* Now try the optimization for long outputs. */
|
||||
buf3 = tor_malloc(65536);
|
||||
crypto_fast_rng_getbytes(rng, (uint8_t*)buf3, 65536);
|
||||
|
||||
buf4 = tor_malloc_zero(65536);
|
||||
uint8_t seed2[CRYPTO_FAST_RNG_SEED_LEN];
|
||||
memset(seed2, 0, sizeof(seed2));
|
||||
crypto_cipher_crypt_inplace(cipher, (char*)seed2, sizeof(seed2));
|
||||
cipher2 = crypto_cipher_new_with_iv_and_bits(seed2, seed2+32, 256);
|
||||
crypto_cipher_crypt_inplace(cipher2, buf4, 65536);
|
||||
tt_mem_op(buf3, OP_EQ, buf4, 65536);
|
||||
|
||||
done:
|
||||
crypto_fast_rng_free(rng);
|
||||
crypto_cipher_free(cipher);
|
||||
crypto_cipher_free(cipher2);
|
||||
tor_free(buf);
|
||||
tor_free(buf2);
|
||||
tor_free(buf3);
|
||||
tor_free(buf4);
|
||||
}
|
||||
|
||||
struct testcase_t crypto_rng_tests[] = {
|
||||
{ "rng", test_crypto_rng, 0, NULL, NULL },
|
||||
{ "rng_range", test_crypto_rng_range, 0, NULL, NULL },
|
||||
{ "rng_strongest", test_crypto_rng_strongest, TT_FORK, NULL, NULL },
|
||||
{ "rng_strongest_nosyscall", test_crypto_rng_strongest, TT_FORK,
|
||||
&passthrough_setup, (void*)"nosyscall" },
|
||||
{ "rng_strongest_nofallback", test_crypto_rng_strongest, TT_FORK,
|
||||
&passthrough_setup, (void*)"nofallback" },
|
||||
{ "rng_strongest_broken", test_crypto_rng_strongest, TT_FORK,
|
||||
&passthrough_setup, (void*)"broken" },
|
||||
{ "fast", test_crypto_rng_fast, 0, NULL, NULL },
|
||||
{ "fast_whitebox", test_crypto_rng_fast_whitebox, 0, NULL, NULL },
|
||||
END_OF_TESTCASES
|
||||
};
|
59
src/test/test_rng.c
Normal file
59
src/test/test_rng.c
Normal file
@ -0,0 +1,59 @@
|
||||
/* Copyright (c) 2016-2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/*
|
||||
* Example usage:
|
||||
*
|
||||
* ./src/test/test-rng --emit | dieharder -g 200 -a
|
||||
*
|
||||
* Remember, dieharder can tell you that your RNG is completely broken, but if
|
||||
* your RNG is not _completely_ broken, dieharder cannot tell you whether your
|
||||
* RNG is actually secure.
|
||||
*/
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
uint8_t buf[0x123];
|
||||
|
||||
if (argc != 2 || strcmp(argv[1], "--emit")) {
|
||||
fprintf(stderr, "If you want me to fill stdout with a bunch of random "
|
||||
"bytes, you need to say --emit.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (crypto_seed_rng() < 0) {
|
||||
fprintf(stderr, "Can't seed RNG.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
while (1) {
|
||||
crypto_rand(buf, sizeof(buf));
|
||||
if (write(1 /*stdout*/, buf, sizeof(buf)) != sizeof(buf)) {
|
||||
fprintf(stderr, "write() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
crypto_fast_rng_t *rng = crypto_fast_rng_new();
|
||||
while (1) {
|
||||
crypto_fast_rng_getbytes(rng, buf, sizeof(buf));
|
||||
if (write(1 /*stdout*/, buf, sizeof(buf)) != sizeof(buf)) {
|
||||
fprintf(stderr, "write() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@
|
||||
#include "lib/time/tvdiff.h"
|
||||
#include "lib/encoding/confline.h"
|
||||
#include "lib/net/socketpair.h"
|
||||
#include "lib/malloc/map_anon.h"
|
||||
|
||||
#ifdef HAVE_PWD_H
|
||||
#include <pwd.h>
|
||||
@ -59,6 +60,12 @@
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <tchar.h>
|
||||
@ -6117,6 +6124,104 @@ test_util_log_mallinfo(void *arg)
|
||||
tor_free(mem);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_map_anon(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
char *ptr = NULL;
|
||||
size_t sz = 16384;
|
||||
|
||||
/* Basic checks. */
|
||||
ptr = tor_mmap_anonymous(sz, 0);
|
||||
tt_ptr_op(ptr, OP_NE, 0);
|
||||
ptr[sz-1] = 3;
|
||||
tt_int_op(ptr[0], OP_EQ, 0);
|
||||
tt_int_op(ptr[sz-2], OP_EQ, 0);
|
||||
tt_int_op(ptr[sz-1], OP_EQ, 3);
|
||||
|
||||
/* Try again, with a private (non-swappable) mapping. */
|
||||
tor_munmap_anonymous(ptr, sz);
|
||||
ptr = tor_mmap_anonymous(sz, ANONMAP_PRIVATE);
|
||||
tt_ptr_op(ptr, OP_NE, 0);
|
||||
ptr[sz-1] = 10;
|
||||
tt_int_op(ptr[0], OP_EQ, 0);
|
||||
tt_int_op(ptr[sz/2], OP_EQ, 0);
|
||||
tt_int_op(ptr[sz-1], OP_EQ, 10);
|
||||
|
||||
/* Now let's test a drop-on-fork mapping. */
|
||||
tor_munmap_anonymous(ptr, sz);
|
||||
ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT);
|
||||
tt_ptr_op(ptr, OP_NE, 0);
|
||||
ptr[sz-1] = 10;
|
||||
tt_int_op(ptr[0], OP_EQ, 0);
|
||||
tt_int_op(ptr[sz/2], OP_EQ, 0);
|
||||
tt_int_op(ptr[sz-1], OP_EQ, 10);
|
||||
|
||||
done:
|
||||
tor_munmap_anonymous(ptr, sz);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_map_anon_nofork(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
#if !defined(HAVE_MADVISE) && !defined(HAVE_MINHERIT)
|
||||
/* The operating system doesn't support this. */
|
||||
tt_skip();
|
||||
done:
|
||||
;
|
||||
#else
|
||||
/* We have the right OS support. We're going to try marking the buffer as
|
||||
* either zero-on-fork or as drop-on-fork, whichever is supported. Then we
|
||||
* will fork and send a byte back to the parent process. This will either
|
||||
* crash, or send zero. */
|
||||
|
||||
char *ptr = NULL;
|
||||
size_t sz = 16384;
|
||||
int pipefd[2] = {-1, -1};
|
||||
|
||||
tor_munmap_anonymous(ptr, sz);
|
||||
ptr = tor_mmap_anonymous(sz, ANONMAP_NOINHERIT);
|
||||
tt_ptr_op(ptr, OP_NE, 0);
|
||||
memset(ptr, 0xd0, sz);
|
||||
|
||||
tt_int_op(0, OP_EQ, pipe(pipefd));
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
/* We're in the child. */
|
||||
close(pipefd[0]);
|
||||
ssize_t r = write(pipefd[1], &ptr[sz-1], 1); /* This may crash. */
|
||||
close(pipefd[1]);
|
||||
if (r < 0)
|
||||
exit(1);
|
||||
exit(0);
|
||||
}
|
||||
tt_int_op(child, OP_GT, 0);
|
||||
/* In the parent. */
|
||||
close(pipefd[1]);
|
||||
pipefd[1] = -1;
|
||||
char buf[1];
|
||||
ssize_t r = read(pipefd[0], buf, 1);
|
||||
#if defined(INHERIT_ZERO) || defined(MADV_WIPEONFORK)
|
||||
tt_int_op((int)r, OP_EQ, 1); // child should send us a byte.
|
||||
tt_int_op(buf[0], OP_EQ, 0);
|
||||
#else
|
||||
tt_int_op(r, OP_LE, 0); // child said nothing; it should have crashed.
|
||||
#endif
|
||||
int ws;
|
||||
waitpid(child, &ws, 0);
|
||||
|
||||
done:
|
||||
tor_munmap_anonymous(ptr, sz);
|
||||
if (pipefd[0] >= 0) {
|
||||
close(pipefd[0]);
|
||||
}
|
||||
if (pipefd[1] >= 0) {
|
||||
close(pipefd[1]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define UTIL_LEGACY(name) \
|
||||
{ #name, test_util_ ## name , 0, NULL, NULL }
|
||||
|
||||
@ -6254,5 +6359,7 @@ struct testcase_t util_tests[] = {
|
||||
UTIL_TEST(htonll, 0),
|
||||
UTIL_TEST(get_unquoted_path, 0),
|
||||
UTIL_TEST(log_mallinfo, 0),
|
||||
UTIL_TEST(map_anon, 0),
|
||||
UTIL_TEST(map_anon_nofork, 0),
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user