diff --git a/changes/bug17694_strongest b/changes/bug17694_strongest new file mode 100644 index 0000000000..0a8954a25e --- /dev/null +++ b/changes/bug17694_strongest @@ -0,0 +1,6 @@ + o Minor features (security): + - Never use the system entropy output directly for anything besides + seeding the PRNG. When we want to generate important keys, instead + of using system entropy directly, hash it with the PRNG stream. + This may help resist certain attacks based on broken OS entropy + implementations. Closes part of ticket 17694. \ No newline at end of file diff --git a/src/common/crypto.c b/src/common/crypto.c index 9669493a83..675fe7a8f0 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -2280,10 +2280,13 @@ crypto_seed_weak_rng(tor_weak_rng_t *rng) } /** Try to get out_len bytes of the strongest entropy we can generate, - * storing it into out. + * storing it into out. Return 0 on success, -1 on failure. + * + * (You should almost never call this directly unless you are seeding a PRNG; + * use crypto_strongest_rand() instead.) */ -int -crypto_strongest_rand(uint8_t *out, size_t out_len) +static int +crypto_strongest_rand_raw(uint8_t *out, size_t out_len) { #ifdef _WIN32 static int provider_set = 0; @@ -2334,6 +2337,41 @@ crypto_strongest_rand(uint8_t *out, size_t out_len) #endif } +/** Try to get out_len bytes of the strongest entropy we can generate, + * storing it into out. + */ +void +crypto_strongest_rand(uint8_t *out, size_t out_len) +{ + const unsigned DLEN = SHA512_DIGEST_LENGTH; + uint8_t inp[DLEN*2]; + uint8_t tmp[DLEN]; + tor_assert(out); + while (out_len) { + crypto_rand((char*) inp+DLEN, DLEN); + if (crypto_strongest_rand_raw(inp, DLEN) < 0) { + log_err(LD_CRYPTO, "Failed to load strong entropy when generating an " + "important key. Exiting."); + tor_assert(0); + } + if (out_len >= DLEN) { + SHA512(inp, sizeof(inp), out); + out += DLEN; + out_len -= DLEN; + } else { + SHA512(inp, sizeof(inp), tmp); + memcpy(out, tmp, out_len); + out += DLEN; + out_len -= DLEN; + break; + } + } + memwipe(tmp, 0, sizeof(tmp)); + memwipe(inp, 0, sizeof(inp)); + +} + + /** Seed OpenSSL's random number generator with bytes from the operating * system. startup should be true iff we have just started Tor and * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure. @@ -2351,7 +2389,7 @@ crypto_seed_rng(void) if (rand_poll_ok == 0) log_warn(LD_CRYPTO, "RAND_poll() failed."); - load_entropy_ok = !crypto_strongest_rand(buf, sizeof(buf)); + load_entropy_ok = !crypto_strongest_rand_raw(buf, sizeof(buf)); if (load_entropy_ok) { RAND_seed(buf, sizeof(buf)); } diff --git a/src/common/crypto.h b/src/common/crypto.h index 3b471c2956..1e0bbb426f 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -262,7 +262,7 @@ int crypto_expand_key_material_rfc5869_sha256( int crypto_seed_rng(void) ATTR_WUR; MOCK_DECL(void,crypto_rand,(char *to, size_t n)); void crypto_rand_unmocked(char *to, size_t n); -int crypto_strongest_rand(uint8_t *out, size_t out_len); +void crypto_strongest_rand(uint8_t *out, size_t out_len); int crypto_rand_int(unsigned int max); int crypto_rand_int_range(unsigned int min, unsigned int max); uint64_t crypto_rand_uint64_range(uint64_t min, uint64_t max); diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c index 00302a2ff0..2002483265 100644 --- a/src/common/crypto_curve25519.c +++ b/src/common/crypto_curve25519.c @@ -111,18 +111,11 @@ curve25519_public_key_is_ok(const curve25519_public_key_t *key) int curve25519_rand_seckey_bytes(uint8_t *out, int extra_strong) { - uint8_t k_tmp[CURVE25519_SECKEY_LEN]; + if (extra_strong) + crypto_strongest_rand(out, CURVE25519_SECKEY_LEN); + else + crypto_rand((char*)out, CURVE25519_SECKEY_LEN); - crypto_rand((char*)out, CURVE25519_SECKEY_LEN); - if (extra_strong && !crypto_strongest_rand(k_tmp, CURVE25519_SECKEY_LEN)) { - /* If they asked for extra-strong entropy and we have some, use it as an - * HMAC key to improve not-so-good entropy rather than using it directly, - * just in case the extra-strong entropy is less amazing than we hoped. */ - crypto_hmac_sha256((char*) out, - (const char *)k_tmp, sizeof(k_tmp), - (const char *)out, CURVE25519_SECKEY_LEN); - } - memwipe(k_tmp, 0, sizeof(k_tmp)); return 0; } diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c index 1749efc34c..41ec486f0a 100644 --- a/src/common/crypto_ed25519.c +++ b/src/common/crypto_ed25519.c @@ -107,7 +107,9 @@ ed25519_secret_key_generate(ed25519_secret_key_t *seckey_out, { int r; uint8_t seed[32]; - if (! extra_strong || crypto_strongest_rand(seed, sizeof(seed)) < 0) + if (extra_strong) + crypto_strongest_rand(seed, sizeof(seed)); + else crypto_rand((char*)seed, sizeof(seed)); r = get_ed_impl()->seckey_expand(seckey_out->seckey, seed); diff --git a/src/ext/ed25519/donna/ed25519_tor.c b/src/ext/ed25519/donna/ed25519_tor.c index 12493f7d14..ac726ba045 100644 --- a/src/ext/ed25519/donna/ed25519_tor.c +++ b/src/ext/ed25519/donna/ed25519_tor.c @@ -148,8 +148,7 @@ ed25519_donna_seckey(unsigned char *sk) { ed25519_secret_key seed; - if (crypto_strongest_rand(seed, 32)) - return -1; + crypto_strongest_rand(seed, 32); ed25519_extsk(sk, seed); diff --git a/src/ext/ed25519/ref10/randombytes.h b/src/ext/ed25519/ref10/randombytes.h index fc709fcefc..8bf31631f0 100644 --- a/src/ext/ed25519/ref10/randombytes.h +++ b/src/ext/ed25519/ref10/randombytes.h @@ -1,4 +1,4 @@ /* Added for Tor. */ #include "crypto.h" #define randombytes(b, n) \ - (crypto_strongest_rand((b), (n))) + (crypto_strongest_rand((b), (n)), 0)