Merge remote-tracking branch 'nickm/thread_rng'

This commit is contained in:
George Kadianakis 2019-02-22 16:07:57 +02:00
commit f229a33685
5 changed files with 99 additions and 1 deletions

9
changes/ticket29536 Normal file
View File

@ -0,0 +1,9 @@
o Minor features (performance, RNG):
- Tor now constructs a fast secure pseudorandom number generator for
each thread, to use for cases where performance is critical. This PRNG
is based on AES-CTR, using a buffering construction similar to
libottery and the (newer) OpenBSD arc4random() code. It outperforms
OpenSSL 1.1.1a's CSPRNG by roughly a factor of 100 for small outputs.
Although we believe it to be cryptographically strong, we are only
using it when necessary for reasonable performance. Implements tickets
29023 and 29536.

View File

@ -12,6 +12,8 @@
#include "orconfig.h" #include "orconfig.h"
#define CRYPTO_PRIVATE
#include "lib/crypt_ops/crypto_init.h" #include "lib/crypt_ops/crypto_init.h"
#include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_curve25519.h"
@ -69,6 +71,8 @@ crypto_early_init(void)
if (crypto_init_siphash_key() < 0) if (crypto_init_siphash_key() < 0)
return -1; return -1;
crypto_rand_fast_init();
curve25519_init(); curve25519_init();
ed25519_init(); ed25519_init();
} }
@ -111,6 +115,7 @@ crypto_thread_cleanup(void)
#ifdef ENABLE_OPENSSL #ifdef ENABLE_OPENSSL
crypto_openssl_thread_cleanup(); crypto_openssl_thread_cleanup();
#endif #endif
destroy_thread_fast_rng();
} }
/** /**
@ -129,6 +134,8 @@ crypto_global_cleanup(void)
crypto_nss_global_cleanup(); crypto_nss_global_cleanup();
#endif #endif
crypto_rand_fast_shutdown();
crypto_early_initialized_ = 0; crypto_early_initialized_ = 0;
crypto_global_initialized_ = 0; crypto_global_initialized_ = 0;
have_seeded_siphash = 0; have_seeded_siphash = 0;

View File

@ -68,6 +68,15 @@ 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); 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); double crypto_fast_rng_get_double(crypto_fast_rng_t *rng);
crypto_fast_rng_t *get_thread_fast_rng(void);
#ifdef CRYPTO_PRIVATE
/* These are only used from crypto_init.c */
void destroy_thread_fast_rng(void);
void crypto_rand_fast_init(void);
void crypto_rand_fast_shutdown(void);
#endif
#if defined(TOR_UNIT_TESTS) #if defined(TOR_UNIT_TESTS)
/* Used for white-box testing */ /* Used for white-box testing */
size_t crypto_fast_rng_get_bytes_used_per_stream(void); size_t crypto_fast_rng_get_bytes_used_per_stream(void);

View File

@ -33,6 +33,7 @@
*/ */
#define CRYPTO_RAND_FAST_PRIVATE #define CRYPTO_RAND_FAST_PRIVATE
#define CRYPTO_PRIVATE
#include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_cipher.h"
@ -41,6 +42,7 @@
#include "lib/intmath/cmp.h" #include "lib/intmath/cmp.h"
#include "lib/cc/ctassert.h" #include "lib/cc/ctassert.h"
#include "lib/malloc/map_anon.h" #include "lib/malloc/map_anon.h"
#include "lib/thread/threads.h"
#include "lib/log/util_bug.h" #include "lib/log/util_bug.h"
@ -122,7 +124,8 @@ crypto_fast_rng_new(void)
* long. * long.
* *
* Note that this object is NOT thread-safe. If you need a thread-safe * Note that this object is NOT thread-safe. If you need a thread-safe
* prng, use crypto_rand(), or wrap this in a mutex. * prng, you should probably look at get_thread_fast_rng(). Alternatively,
* use crypto_rand(), wrap this in a mutex.
**/ **/
crypto_fast_rng_t * crypto_fast_rng_t *
crypto_fast_rng_new_from_seed(const uint8_t *seed) crypto_fast_rng_new_from_seed(const uint8_t *seed)
@ -261,3 +264,65 @@ crypto_fast_rng_get_bytes_used_per_stream(void)
return BUFLEN; return BUFLEN;
} }
#endif #endif
/**
* Thread-local instance for our fast RNG.
**/
static tor_threadlocal_t thread_rng;
/**
* Return a per-thread fast RNG, initializing it if necessary.
*
* You do not need to free this yourself.
*
* It is NOT safe to share this value across threads.
**/
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;
}
/**
* Used when a thread is exiting: free the per-thread fast RNG if needed.
* Invoked from the crypto subsystem's thread-cleanup code.
**/
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);
}
/**
* Initialize the global thread-local key that will be used to keep track
* of per-thread fast RNG instances. Called from the crypto subsystem's
* initialization code.
**/
void
crypto_rand_fast_init(void)
{
tor_threadlocal_init(&thread_rng);
}
/**
* Initialize the global thread-local key that will be used to keep track
* of per-thread fast RNG instances. Called from the crypto subsystem's
* shutdown code.
**/
void
crypto_rand_fast_shutdown(void)
{
destroy_thread_fast_rng();
tor_threadlocal_destroy(&thread_rng);
}

View File

@ -218,6 +218,14 @@ test_crypto_rng_fast(void *arg)
tt_int_op(counts[i], OP_GT, 0); tt_int_op(counts[i], OP_GT, 0);
} }
/* per-thread rand_fast shouldn't crash or leak. */
crypto_fast_rng_t *t_rng = get_thread_fast_rng();
for (int i = 0; i < N; ++i) {
uint64_t u64 = crypto_fast_rng_get_uint64(t_rng, UINT64_C(1)<<40);
tt_u64_op(u64, OP_GE, 0);
tt_u64_op(u64, OP_LT, UINT64_C(1)<<40);
}
done: done:
crypto_fast_rng_free(rng); crypto_fast_rng_free(rng);
} }