src: ensure that openssl's PRNG is fully seeded
authorBen Noordhuis <info@bnoordhuis.nl>
Tue, 25 Mar 2014 22:35:28 +0000 (23:35 +0100)
committerFedor Indutny <fedor.indutny@gmail.com>
Wed, 26 Mar 2014 07:31:32 +0000 (11:31 +0400)
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.

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 wait until enough entropy is available, something
that normally should never take longer than a few milliseconds.

Fixes #7338.

src/node_crypto.cc

index 702a1d1..bd0b953 100644 (file)
@@ -157,7 +157,41 @@ Handle<Value> 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<unsigned char*>(req->data_),
                           req->size_);