Browse Source

Improve test coverage of our strongest-rng code.

Nick Mathewson 8 years ago
parent
commit
7a5f15b6e0
3 changed files with 90 additions and 2 deletions
  1. 21 2
      src/common/crypto.c
  2. 1 0
      src/common/crypto.h
  3. 68 0
      src/test/test_crypto.c

+ 21 - 2
src/common/crypto.c

@@ -2571,6 +2571,11 @@ crypto_seed_weak_rng(tor_weak_rng_t *rng)
   tor_init_weak_random(rng, seed);
 }
 
+#ifdef TOR_UNIT_TESTS
+int break_strongest_rng_syscall = 0;
+int break_strongest_rng_fallback = 0;
+#endif
+
 /** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
  * via system calls, storing it into <b>out</b>. Return 0 on success, -1 on
  * failure.  A maximum request size of 256 bytes is imposed.
@@ -2580,6 +2585,11 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
 {
   tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE);
 
+#ifdef TOR_UNIT_TESTS
+  if (break_strongest_rng_syscall)
+    return -1;
+#endif
+
 #if defined(_WIN32)
   static int provider_set = 0;
   static HCRYPTPROV provider;
@@ -2664,6 +2674,11 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
 static int
 crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
 {
+#ifdef TOR_UNIT_TESTS
+  if (break_strongest_rng_fallback)
+    return -1;
+#endif
+
 #ifdef _WIN32
   /* Windows exclusively uses crypto_strongest_rand_syscall(). */
   (void)out;
@@ -2701,7 +2716,7 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
  * storing it into <b>out</b>. Return 0 on success, -1 on failure.  A maximum
  * request size of 256 bytes is imposed.
  */
-static int
+STATIC int
 crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
 {
   static const size_t sanity_min_size = 16;
@@ -2735,13 +2750,17 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
       return 0;
   }
 
-  /* We tried max_attempts times to fill a buffer >= 128 bits long,
+  /* LCOV_EXCL_START
+   *
+   * We tried max_attempts times to fill a buffer >= 128 bits long,
    * and each time it returned all '0's.  Either the system entropy
    * source is busted, or the user should go out and buy a ticket to
    * every lottery on the planet.
    */
   log_warn(LD_CRYPTO, "Strong OS entropy returned all zero buffer.");
+
   return -1;
+  /* LCOV_EXCL_STOP */
 }
 
 /** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,

+ 1 - 0
src/common/crypto.h

@@ -317,6 +317,7 @@ void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
 
 #ifdef CRYPTO_PRIVATE
 STATIC int crypto_force_rand_ssleay(void);
+STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len);
 #endif
 
 #endif

+ 68 - 0
src/test/test_crypto.c

@@ -190,6 +190,67 @@ test_crypto_rng_range(void *arg)
   ;
 }
 
+extern int break_strongest_rng_fallback;
+extern int break_strongest_rng_syscall;
+
+static void
+test_crypto_rng_strongest(void *arg)
+{
+  const char *how = arg;
+  int broken = 0;
+
+  if (how == NULL) {
+    ;
+  } else if (!strcmp(how, "nosyscall")) {
+    break_strongest_rng_syscall = 1;
+  } else if (!strcmp(how, "nofallback")) {
+    break_strongest_rng_fallback = 1;
+  } else if (!strcmp(how, "broken")) {
+    broken = break_strongest_rng_syscall = break_strongest_rng_fallback = 1;
+  }
+
+#define N 128
+  uint8_t combine_and[N];
+  uint8_t combine_or[N];
+  int i, j;
+
+  memset(combine_and, 0xff, N);
+  memset(combine_or, 0, N);
+
+  for (i = 0; i < 100; ++i) { /* 2^-100 chances just don't happen. */
+    uint8_t output[N];
+    memset(output, 0, N);
+    if (how == NULL) {
+      /* this one can't fail. */
+      crypto_strongest_rand(output, sizeof(output));
+    } else {
+      int r = crypto_strongest_rand_raw(output, sizeof(output));
+      if (r == -1) {
+        if (broken) {
+          goto done; /* we're fine. */
+        }
+        /* This function is allowed to break, but only if it always breaks. */
+        tt_int_op(i, OP_EQ, 0);
+        tt_skip();
+      } else {
+        tt_assert(! broken);
+      }
+    }
+    for (j = 0; j < N; ++j) {
+      combine_and[j] &= output[j];
+      combine_or[j] |= output[j];
+    }
+  }
+
+  for (j = 0; j < N; ++j) {
+    tt_int_op(combine_and[j], OP_EQ, 0);
+    tt_int_op(combine_or[j], OP_EQ, 0xff);
+  }
+ done:
+  ;
+#undef N
+}
+
 /* Test for rectifying openssl RAND engine. */
 static void
 test_crypto_rng_engine(void *arg)
@@ -2750,6 +2811,13 @@ struct testcase_t crypto_tests[] = {
   CRYPTO_LEGACY(rng),
   { "rng_range", test_crypto_rng_range, 0, NULL, NULL },
   { "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL },
+  { "rng_strongest", test_crypto_rng_strongest, TT_FORK, NULL, NULL },
+  { "rng_strongest_nosyscall", test_crypto_rng_strongest, TT_FORK,
+    &passthrough_setup, (void*)"nosyscall" },
+  { "rng_strongest_nofallback", test_crypto_rng_strongest, TT_FORK,
+    &passthrough_setup, (void*)"nofallback" },
+  { "rng_strongest_broken", test_crypto_rng_strongest, TT_FORK,
+    &passthrough_setup, (void*)"broken" },
   { "openssl_version", test_crypto_openssl_version, TT_FORK, NULL, NULL },
   { "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" },
   { "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" },