Bug 1349936 - make fuzzing rng a complete rng, r=ttaubert
authorFranziskus Kiefer <franziskuskiefer@gmail.com>
Wed, 22 Mar 2017 09:59:27 +0100
changeset 13245 b4c3f3a08273d2031850c955b579c28988d09e72
parent 13244 73eac4c4656c4850d8ce0bed21fea48b2f61b0fa
child 13246 ae115cf470409ca6f01482c98e93b427974538b1
push id2113
push userfranziskuskiefer@gmail.com
push dateThu, 23 Mar 2017 14:35:36 +0000
reviewersttaubert
bugs1349936
Bug 1349936 - make fuzzing rng a complete rng, r=ttaubert Differential Revision: https://nss-review.dev.mozaws.net/D246
fuzz/tls_client_target.cc
fuzz/tls_server_target.cc
gtests/common/gtest.gypi
gtests/pk11_gtest/pk11_prng_unittest.cc
gtests/ssl_gtest/ssl_fuzz_unittest.cc
lib/freebl/blapi.h
lib/freebl/det_rng.c
lib/freebl/drbg.c
lib/freebl/freebl_base.gypi
--- a/fuzz/tls_client_target.cc
+++ b/fuzz/tls_client_target.cc
@@ -76,17 +76,17 @@ extern "C" int LLVMFuzzerTestOneInput(co
   EnableAllProtocolVersions();
   std::unique_ptr<ClientConfig> config(new ClientConfig(data, len));
 
   // Clear the cache. We never want to resume as we couldn't reproduce that.
   SSL_ClearSessionCache();
 
 #ifdef UNSAFE_FUZZER_MODE
   // Reset the RNG state.
-  assert(RNG_ResetForFuzzing() == SECSuccess);
+  assert(RNG_RandomUpdate(NULL, 0) == SECSuccess);
 #endif
 
   // Create and import dummy socket.
   std::unique_ptr<DummyPrSocket> socket(new DummyPrSocket(data, len));
   static PRDescIdentity id = PR_GetUniqueIdentity("fuzz-client");
   ScopedPRFileDesc fd(DummyIOLayerMethods::CreateFD(id, socket.get()));
   PRFileDesc* ssl_fd = SSL_ImportFD(nullptr, fd.get());
   assert(ssl_fd == fd.get());
--- a/fuzz/tls_server_target.cc
+++ b/fuzz/tls_server_target.cc
@@ -80,17 +80,17 @@ extern "C" int LLVMFuzzerTestOneInput(co
 
   std::unique_ptr<ServerConfig> config(new ServerConfig(data, len));
 
   // Clear the cache. We never want to resume as we couldn't reproduce that.
   SSL_ClearSessionCache();
 
 #ifdef UNSAFE_FUZZER_MODE
   // Reset the RNG state.
-  assert(RNG_ResetForFuzzing() == SECSuccess);
+  assert(RNG_RandomUpdate(NULL, 0) == SECSuccess);
 #endif
 
   // Create model socket.
   static ScopedPRFileDesc model(SSL_ImportFD(nullptr, PR_NewTCPSocket()));
   assert(model);
 
   // Initialize the model socket once.
   static PRCallOnceType initModelOnce;
--- a/gtests/common/gtest.gypi
+++ b/gtests/common/gtest.gypi
@@ -22,17 +22,17 @@
           '-lws2_32',
         ],
       }],
       ['OS=="android"', {
         'libraries': [
           '-lstdc++',
         ],
       }],
-      [ 'fuzz_tls==1', {
+      [ 'fuzz==1', {
         'defines': [
           'UNSAFE_FUZZER_MODE',
         ],
       }],
     ],
     'msvs_settings': {
       'VCCLCompilerTool': {
         'ExceptionHandling': 1,
--- a/gtests/pk11_gtest/pk11_prng_unittest.cc
+++ b/gtests/pk11_gtest/pk11_prng_unittest.cc
@@ -31,43 +31,86 @@ TEST_F(PK11PrngTest, Fuzz_DetPRNG) {
 }
 
 // Test that two consecutive calls to the RNG return two equal values
 // when the RNG's internal state is reset before each call.
 TEST_F(PK11PrngTest, Fuzz_DetPRNG_Reset) {
   std::vector<uint8_t> rnd1(2048, 0);
   std::vector<uint8_t> rnd2(2048, 0);
 
-  RNG_ResetForFuzzing();
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
 
   SECStatus rv = PK11_GenerateRandom(rnd1.data(), rnd1.size());
   EXPECT_EQ(rv, SECSuccess);
 
-  RNG_ResetForFuzzing();
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
 
   rv = PK11_GenerateRandom(rnd2.data(), rnd2.size());
   EXPECT_EQ(rv, SECSuccess);
 
   EXPECT_EQ(rnd1, rnd2);
 }
 
 // Test that the RNG's internal state progresses in a consistent manner.
 TEST_F(PK11PrngTest, Fuzz_DetPRNG_StatefulReset) {
   std::vector<uint8_t> rnd1(2048, 0);
   std::vector<uint8_t> rnd2(2048, 0);
 
-  RNG_ResetForFuzzing();
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
 
   SECStatus rv = PK11_GenerateRandom(rnd1.data(), rnd1.size() - 1024);
   EXPECT_EQ(rv, SECSuccess);
 
   rv = PK11_GenerateRandom(rnd1.data() + 1024, rnd1.size() - 1024);
   EXPECT_EQ(rv, SECSuccess);
 
-  RNG_ResetForFuzzing();
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
+
+  rv = PK11_GenerateRandom(rnd2.data(), rnd2.size() - 1024);
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd2.data() + 1024, rnd2.size() - 1024);
+  EXPECT_EQ(rv, SECSuccess);
+
+  EXPECT_EQ(rnd1, rnd2);
+}
+
+TEST_F(PK11PrngTest, Fuzz_DetPRNG_Seed) {
+  std::vector<uint8_t> rnd1(2048, 0);
+  std::vector<uint8_t> rnd2(2048, 0);
+  std::vector<uint8_t> seed = {0x01, 0x22, 0xAA, 0x45};
+
+  SECStatus rv = PK11_RandomUpdate(seed.data(), seed.size());
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd1.data(), rnd1.size());
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd2.data(), rnd2.size());
+  EXPECT_EQ(rv, SECSuccess);
+
+  EXPECT_NE(rnd1, rnd2);
+}
+
+TEST_F(PK11PrngTest, Fuzz_DetPRNG_StatefulReset_Seed) {
+  std::vector<uint8_t> rnd1(2048, 0);
+  std::vector<uint8_t> rnd2(2048, 0);
+  std::vector<uint8_t> seed = {0x01, 0x22, 0xAA, 0x45};
+
+  SECStatus rv = PK11_RandomUpdate(seed.data(), seed.size());
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd1.data(), rnd1.size() - 1024);
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd1.data() + 1024, rnd1.size() - 1024);
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_RandomUpdate(seed.data(), seed.size());
+  EXPECT_EQ(rv, SECSuccess);
 
   rv = PK11_GenerateRandom(rnd2.data(), rnd2.size() - 1024);
   EXPECT_EQ(rv, SECSuccess);
 
   rv = PK11_GenerateRandom(rnd2.data() + 1024, rnd2.size() - 1024);
   EXPECT_EQ(rv, SECSuccess);
 
   EXPECT_EQ(rnd1, rnd2);
--- a/gtests/ssl_gtest/ssl_fuzz_unittest.cc
+++ b/gtests/ssl_gtest/ssl_fuzz_unittest.cc
@@ -12,19 +12,16 @@
 namespace nss_test {
 
 #ifdef UNSAFE_FUZZER_MODE
 #define FUZZ_F(c, f) TEST_F(c, Fuzz_##f)
 #define FUZZ_P(c, f) TEST_P(c, Fuzz_##f)
 #else
 #define FUZZ_F(c, f) TEST_F(c, DISABLED_Fuzz_##f)
 #define FUZZ_P(c, f) TEST_P(c, DISABLED_Fuzz_##f)
-// RNG_ResetForFuzzing() isn't exported from the shared libraries, rather than
-// fail to link to it, make it fail (we're not running it anyway).
-#define RNG_ResetForFuzzing() SECFailure
 #endif
 
 const uint8_t kShortEmptyFinished[8] = {0};
 const uint8_t kLongEmptyFinished[128] = {0};
 
 class TlsFuzzTest : public ::testing::Test {};
 
 // Record the application data stream.
@@ -86,31 +83,31 @@ FUZZ_P(TlsConnectGeneric, DeterministicE
   // Make sure we have RSA blinding params.
   Connect();
 
   Reset();
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   DisableECDHEServerKeyReuse();
 
   // Reset the RNG state.
-  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
   Connect();
 
   // Export a key derived from the MS and nonces.
   SECStatus rv =
       SSL_ExportKeyingMaterial(client_->ssl_fd(), kLabel, strlen(kLabel), false,
                                NULL, 0, out1.data(), out1.size());
   EXPECT_EQ(SECSuccess, rv);
 
   Reset();
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   DisableECDHEServerKeyReuse();
 
   // Reset the RNG state.
-  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
   Connect();
 
   // Export another key derived from the MS and nonces.
   rv = SSL_ExportKeyingMaterial(client_->ssl_fd(), kLabel, strlen(kLabel),
                                 false, NULL, 0, out2.data(), out2.size());
   EXPECT_EQ(SECSuccess, rv);
 
   // The two exported keys should be the same.
@@ -130,17 +127,17 @@ FUZZ_P(TlsConnectGeneric, DeterministicT
     ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
     DisableECDHEServerKeyReuse();
 
     DataBuffer buffer;
     client_->SetPacketFilter(std::make_shared<TlsConversationRecorder>(buffer));
     server_->SetPacketFilter(std::make_shared<TlsConversationRecorder>(buffer));
 
     // Reset the RNG state.
-    EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
+    EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
     Connect();
 
     // Ensure the filters go away before |buffer| does.
     client_->DeletePacketFilter();
     server_->DeletePacketFilter();
 
     if (last.len() > 0) {
       EXPECT_EQ(last, buffer);
--- a/lib/freebl/blapi.h
+++ b/lib/freebl/blapi.h
@@ -1424,18 +1424,16 @@ extern SECStatus RNG_RNGInit(void);
 extern SECStatus RNG_RandomUpdate(const void *data, size_t bytes);
 
 /*
 ** Generate some random bytes, using the global random number generator
 ** object.
 */
 extern SECStatus RNG_GenerateGlobalRandomBytes(void *dest, size_t len);
 
-extern SECStatus RNG_ResetForFuzzing(void);
-
 /* Destroy the global RNG context.  After a call to RNG_RNGShutdown()
 ** a call to RNG_RNGInit() is required in order to use the generator again,
 ** along with seed data (see the comment above RNG_RNGInit()).
 */
 extern void RNG_RNGShutdown(void);
 
 extern void RNG_SystemInfoForRNG(void);
 
--- a/lib/freebl/det_rng.c
+++ b/lib/freebl/det_rng.c
@@ -4,41 +4,67 @@
 
 #include "blapi.h"
 #include "blapit.h"
 #include "chacha20.h"
 #include "nssilock.h"
 #include "seccomon.h"
 #include "secerr.h"
 
+#define GLOBAL_BYTES_SIZE 100
+static PRUint8 globalBytes[GLOBAL_BYTES_SIZE];
 static unsigned long globalNumCalls = 0;
+static PZLock *rng_lock = NULL;
 
 SECStatus
-prng_ResetForFuzzing(PZLock *rng_lock)
+RNG_RNGInit(void)
+{
+    rng_lock = PZ_NewLock(nssILockOther);
+    if (!rng_lock) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    /* --- LOCKED --- */
+    PZ_Lock(rng_lock);
+    memset(globalBytes, 0, GLOBAL_BYTES_SIZE);
+    PZ_Unlock(rng_lock);
+    /* --- UNLOCKED --- */
+
+    return SECSuccess;
+}
+
+/* Take min(size, GLOBAL_BYTES_SIZE) bytes from data and use as seed and reset
+ * the rng state. */
+SECStatus
+RNG_RandomUpdate(const void *data, size_t bytes)
 {
     /* Check for a valid RNG lock. */
     PORT_Assert(rng_lock != NULL);
     if (rng_lock == NULL) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     /* --- LOCKED --- */
     PZ_Lock(rng_lock);
+    memset(globalBytes, 0, GLOBAL_BYTES_SIZE);
     globalNumCalls = 0;
+    if (data) {
+        memcpy(globalBytes, (PRUint8 *)data, PR_MIN(bytes, GLOBAL_BYTES_SIZE));
+    }
     PZ_Unlock(rng_lock);
     /* --- UNLOCKED --- */
 
     return SECSuccess;
 }
 
 SECStatus
-prng_GenerateDeterministicRandomBytes(PZLock *rng_lock, void *dest, size_t len)
+RNG_GenerateGlobalRandomBytes(void *dest, size_t len)
 {
-    static const uint8_t key[32];
+    static const uint8_t key[32] = { 0 };
     uint8_t nonce[12] = { 0 };
 
     /* Check for a valid RNG lock. */
     PORT_Assert(rng_lock != NULL);
     if (rng_lock == NULL) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
@@ -53,15 +79,65 @@ prng_GenerateDeterministicRandomBytes(PZ
         ChaCha20Poly1305_CreateContext(key, sizeof(key), 16);
     if (!cx) {
         PORT_SetError(SEC_ERROR_NO_MEMORY);
         PZ_Unlock(rng_lock);
         return SECFailure;
     }
 
     memset(dest, 0, len);
+    memcpy(dest, globalBytes, PR_MIN(len, GLOBAL_BYTES_SIZE));
     ChaCha20XOR(dest, dest, len, key, nonce, 0);
     ChaCha20Poly1305_DestroyContext(cx, PR_TRUE);
 
     PZ_Unlock(rng_lock);
     /* --- UNLOCKED --- */
+
     return SECSuccess;
 }
+
+void
+RNG_RNGShutdown(void)
+{
+    PZ_DestroyLock(rng_lock);
+    rng_lock = NULL;
+}
+
+/* Test functions are not implemented! */
+SECStatus
+PRNGTEST_Instantiate(const PRUint8 *entropy, unsigned int entropy_len,
+                     const PRUint8 *nonce, unsigned int nonce_len,
+                     const PRUint8 *personal_string, unsigned int ps_len)
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_Reseed(const PRUint8 *entropy, unsigned int entropy_len,
+                const PRUint8 *additional, unsigned int additional_len)
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_Generate(PRUint8 *bytes, unsigned int bytes_len,
+                  const PRUint8 *additional, unsigned int additional_len)
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_Uninstantiate()
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_RunHealthTests()
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_Instantiate_Kat()
+{
+    return SECFailure;
+}
--- a/lib/freebl/drbg.c
+++ b/lib/freebl/drbg.c
@@ -15,20 +15,16 @@
 #include "blapii.h"
 #include "nssilock.h"
 #include "secitem.h"
 #include "sha_fast.h"
 #include "sha256.h"
 #include "secrng.h" /* for RNG_SystemRNG() */
 #include "secmpi.h"
 
-#ifdef UNSAFE_FUZZER_MODE
-#include "det_rng.h"
-#endif
-
 /* PRNG_SEEDLEN defined in NIST SP 800-90 section 10.1
  * for SHA-1, SHA-224, and SHA-256 it's 440 bits.
  * for SHA-384 and SHA-512 it's 888 bits */
 #define PRNG_SEEDLEN (440 / PR_BITS_PER_BYTE)
 #define PRNG_MAX_ADDITIONAL_BYTES PR_INT64(0x100000000)
 /* 2^35 bits or 2^32 bytes */
 #define PRNG_MAX_REQUEST_SIZE 0x10000             /* 2^19 bits or 2^16 bytes */
 #define PRNG_ADDITONAL_DATA_CACHE_SIZE (8 * 1024) /* must be less than          \
@@ -396,37 +392,34 @@ prng_generateNewBytes(RNGContext *rng,
  * threads, creating a race condition.
  */
 static const PRCallOnceType pristineCallOnce;
 static PRCallOnceType coRNGInit;
 static PRStatus
 rng_init(void)
 {
     PRUint8 bytes[PRNG_SEEDLEN * 2]; /* entropy + nonce */
-#ifndef UNSAFE_RNG_NO_URANDOM_SEED
     unsigned int numBytes;
     SECStatus rv = SECSuccess;
-#endif
 
     if (globalrng == NULL) {
         /* bytes needs to have enough space to hold
      * a SHA256 hash value. Blow up at compile time if this isn't true */
         PR_STATIC_ASSERT(sizeof(bytes) >= SHA256_LENGTH);
         /* create a new global RNG context */
         globalrng = &theGlobalRng;
         PORT_Assert(NULL == globalrng->lock);
         /* create a lock for it */
         globalrng->lock = PZ_NewLock(nssILockOther);
         if (globalrng->lock == NULL) {
             globalrng = NULL;
             PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
             return PR_FAILURE;
         }
 
-#ifndef UNSAFE_RNG_NO_URANDOM_SEED
         /* Try to get some seed data for the RNG */
         numBytes = (unsigned int)RNG_SystemRNG(bytes, sizeof bytes);
         PORT_Assert(numBytes == 0 || numBytes == sizeof bytes);
         if (numBytes != 0) {
             /* if this is our first call,  instantiate, otherwise reseed
              * prng_instantiate gets a new clean state, we want to mix
              * any previous entropy we may have collected */
             if (V(globalrng)[0] == 0) {
@@ -439,17 +432,16 @@ rng_init(void)
             PZ_DestroyLock(globalrng->lock);
             globalrng->lock = NULL;
             globalrng = NULL;
             return PR_FAILURE;
         }
         if (rv != SECSuccess) {
             return PR_FAILURE;
         }
-#endif
 
         /* the RNG is in a valid state */
         globalrng->isValid = PR_TRUE;
         globalrng->isKatTest = PR_FALSE;
 
         /* fetch one random value so that we can populate rng->oldV for our
          * continous random number test. */
         prng_generateNewBytes(globalrng, bytes, SHA256_LENGTH, NULL, 0);
@@ -657,31 +649,17 @@ prng_GenerateGlobalRandomBytes(RNGContex
 
 /*
 ** Generate some random bytes, using the global random number generator
 ** object.
 */
 SECStatus
 RNG_GenerateGlobalRandomBytes(void *dest, size_t len)
 {
-#ifdef UNSAFE_FUZZER_MODE
-    return prng_GenerateDeterministicRandomBytes(globalrng->lock, dest, len);
-#else
     return prng_GenerateGlobalRandomBytes(globalrng, dest, len);
-#endif
-}
-
-SECStatus
-RNG_ResetForFuzzing(void)
-{
-#ifdef UNSAFE_FUZZER_MODE
-    return prng_ResetForFuzzing(globalrng->lock);
-#else
-    return SECFailure;
-#endif
 }
 
 void
 RNG_RNGShutdown(void)
 {
     /* check for a valid global RNG context */
     PORT_Assert(globalrng != NULL);
     if (globalrng == NULL) {
--- a/lib/freebl/freebl_base.gypi
+++ b/lib/freebl/freebl_base.gypi
@@ -153,25 +153,21 @@
           # not x64
           'sources': [
             'chacha20.c',
             'poly1305.c',
           ],
         }],
       ],
     }],
-    [ 'fuzz_oss==1', {
-      'defines': [
-        'UNSAFE_RNG_NO_URANDOM_SEED',
-      ],
+    [ 'fuzz==1', {
+      'sources!': [ 'drbg.c' ],
+      'sources': [ 'det_rng.c' ],
     }],
     [ 'fuzz_tls==1', {
-      'sources': [
-        'det_rng.c',
-      ],
       'defines': [
         'UNSAFE_FUZZER_MODE',
       ],
     }],
     [ 'ct_verif==1', {
       'defines': [
         'CT_VERIF',
       ],