diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 702a1d13ffe5fb..bd0b953a8b2788 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -157,7 +157,41 @@ Handle ThrowCryptoTypeError(unsigned long err) { } +// Ensure that OpenSSL has enough entropy (at least 256 bits) for its PRNG. +// The entropy pool starts out empty and needs to fill up before the PRNG +// can be used securely. Once the pool is filled, it never dries up again; +// its contents is stirred and reused when necessary. +// +// OpenSSL normally fills the pool automatically but not when someone starts +// generating random numbers before the pool is full: in that case OpenSSL +// keeps lowering the entropy estimate to thwart attackers trying to guess +// the initial state of the PRNG. +// +// When that happens, we will have to wait until enough entropy is available. +// That should normally never take longer than a few milliseconds. +// +// OpenSSL draws from /dev/random and /dev/urandom. While /dev/random may +// block pending "true" randomness, /dev/urandom is a CSPRNG that doesn't +// block under normal circumstances. +// +// The only time when /dev/urandom may conceivably block is right after boot, +// when the whole system is still low on entropy. That's not something we can +// do anything about. +inline void CheckEntropy() { + for (;;) { + int status = RAND_status(); + assert(status >= 0); // Cannot fail. + if (status != 0) + break; + if (RAND_poll() == 0) // Give up, RAND_poll() not supported. + break; + } +} + + bool EntropySource(unsigned char* buffer, size_t length) { + // Ensure that OpenSSL's PRNG is properly seeded. + CheckEntropy(); // RAND_bytes() can return 0 to indicate that the entropy data is not truly // random. That's okay, it's still better than V8's stock source of entropy, // which is /dev/urandom on UNIX platforms and the current time on Windows. @@ -3994,6 +4028,9 @@ void RandomBytesWork(uv_work_t* work_req) { work_req_); int r; + // Ensure that OpenSSL's PRNG is properly seeded. + CheckEntropy(); + if (pseudoRandom == true) { r = RAND_pseudo_bytes(reinterpret_cast(req->data_), req->size_);