Use NSS in crypto_rand.c

This is comparatively straightforward too, except for a couple of
twists:

   * For as long as we're building with two crypto libraries, we
     want to seed _both_ their RNGs, and use _both_ their RNGs to
     improve the output of crypto_strongest_rand()

   * The NSS prng will sometimes refuse to generate huge outputs.
     When it does, we stretch the output with SHAKE.  We only need
     this for the tests.
This commit is contained in:
Nick Mathewson 2018-07-13 14:35:51 -04:00
parent be8d497b65
commit 60705a5719
2 changed files with 107 additions and 8 deletions

View File

@ -35,9 +35,22 @@
#include "lib/testsupport/testsupport.h"
#include "lib/fs/files.h"
#ifdef ENABLE_NSS
#include "lib/crypt_ops/crypto_nss_mgt.h"
#include "lib/crypt_ops/crypto_digest.h"
#endif
#ifdef ENABLE_OPENSSL
DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/rand.h>
ENABLE_GCC_WARNING(redundant-decls)
#endif
#ifdef ENABLE_NSS
#include <pk11pub.h>
#include <secerr.h>
#include <prerror.h>
#endif
#if __GNUC__ && GCC_VERSION >= 402
#if GCC_VERSION >= 406
@ -324,14 +337,21 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
{
#define DLEN SHA512_DIGEST_LENGTH
/* We're going to hash DLEN bytes from the system RNG together with some
* bytes from the openssl PRNG, in order to yield DLEN bytes.
* bytes from the PRNGs from our crypto librar(y/ies), in order to yield
* DLEN bytes.
*/
uint8_t inp[DLEN*2];
uint8_t inp[DLEN*3];
uint8_t tmp[DLEN];
tor_assert(out);
while (out_len) {
crypto_rand((char*) inp, DLEN);
if (crypto_strongest_rand_raw(inp+DLEN, DLEN) < 0) {
memset(inp, 0, sizeof(inp));
#ifdef ENABLE_OPENSSL
RAND_bytes(inp, DLEN);
#endif
#ifdef ENABLE_NSS
PK11_GenerateRandom(inp+DLEN, DLEN);
#endif
if (crypto_strongest_rand_raw(inp+DLEN*2, DLEN) < 0) {
// LCOV_EXCL_START
log_err(LD_CRYPTO, "Failed to load strong entropy when generating an "
"important key. Exiting.");
@ -354,12 +374,13 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
#undef DLEN
}
#ifdef ENABLE_OPENSSL
/**
* Seed OpenSSL's random number generator with bytes from the operating
* system. Return 0 on success, -1 on failure.
**/
int
crypto_seed_rng(void)
static int
crypto_seed_openssl_rng(void)
{
int rand_poll_ok = 0, load_entropy_ok = 0;
uint8_t buf[ADD_ENTROPY];
@ -383,6 +404,52 @@ crypto_seed_rng(void)
else
return -1;
}
#endif
#ifdef ENABLE_NSS
/**
* Seed OpenSSL's random number generator with bytes from the operating
* system. Return 0 on success, -1 on failure.
**/
static int
crypto_seed_nss_rng(void)
{
uint8_t buf[ADD_ENTROPY];
int load_entropy_ok = !crypto_strongest_rand_raw(buf, sizeof(buf));
if (load_entropy_ok) {
if (PK11_RandomUpdate(buf, sizeof(buf)) != SECSuccess) {
load_entropy_ok = 0;
}
}
memwipe(buf, 0, sizeof(buf));
return load_entropy_ok ? 0 : -1;
}
#endif
/**
* Seed the RNG for any and all crypto libraries that we're using with bytes
* from the operating system. Return 0 on success, -1 on failure.
*/
int
crypto_seed_rng(void)
{
int seeded = 0;
#ifdef ENABLE_NSS
if (crypto_seed_nss_rng() < 0)
return -1;
++seeded;
#endif
#ifdef ENABLE_OPENSSL
if (crypto_seed_openssl_rng() < 0)
return -1;
++seeded;
#endif
tor_assert(seeded);
return 0;
}
/**
* Write <b>n</b> bytes of strong random data to <b>to</b>. Supports mocking
@ -407,17 +474,44 @@ crypto_rand, (char *to, size_t n))
void
crypto_rand_unmocked(char *to, size_t n)
{
int r;
if (n == 0)
return;
tor_assert(n < INT_MAX);
tor_assert(to);
r = RAND_bytes((unsigned char*)to, (int)n);
#ifdef ENABLE_NSS
SECStatus s = PK11_GenerateRandom((unsigned char*)to, (int)n);
if (s != SECSuccess) {
/* NSS rather sensibly might refuse to generate huge amounts of random
* data at once. Unfortunately, our unit test do this in a couple of
* places. To solve this issue, we use our XOF to stretch a shorter
* output when a longer one is needed.
*
* Yes, this is secure. */
/* This is longer than it needs to be; 1600 bits == 200 bytes is the
* state-size of SHA3. */
#define BUFLEN 512
tor_assert(PR_GetError() == SEC_ERROR_INVALID_ARGS && n > BUFLEN);
unsigned char buf[BUFLEN];
s = PK11_GenerateRandom(buf, BUFLEN);
tor_assert(s == SECSuccess);
crypto_xof_t *xof = crypto_xof_new();
crypto_xof_add_bytes(xof, buf, BUFLEN);
crypto_xof_squeeze_bytes(xof, (unsigned char *)to, n);
crypto_xof_free(xof);
memwipe(buf, 0, BUFLEN);
#undef BUFLEN
}
#else
int r = RAND_bytes((unsigned char*)to, (int)n);
/* We consider a PRNG failure non-survivable. Let's assert so that we get a
* stack trace about where it happened.
*/
tor_assert(r >= 0);
#endif
}
/**

View File

@ -9,6 +9,7 @@
#include "lib/evloop/compat_libevent.h"
#include "lib/evloop/timers.h"
#include "lib/crypt_ops/crypto_init.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/log/util_bug.h"
#include "lib/time/compat_time.h"
@ -62,6 +63,10 @@ main(int argc, char **argv)
memset(&cfg, 0, sizeof(cfg));
tor_libevent_initialize(&cfg);
timers_initialize();
init_logging(1);
if (crypto_global_init(0, NULL, NULL) < 0)
return 1;
int i;
int ret;