Browse Source

Merge remote-tracking branch 'teor/rand-failure-modes-v2'

Nick Mathewson 8 years ago
parent
commit
09e0ae0588
2 changed files with 110 additions and 0 deletions
  1. 5 0
      changes/rand-failure-modes
  2. 105 0
      src/test/test_crypto.c

+ 5 - 0
changes/rand-failure-modes

@@ -0,0 +1,5 @@
+  o Minor features (unit tests, random number generation):
+    - Add unit tests that check for common RNG failure modes, such as
+      returning all zeroes, identical values, or incrementing values
+      (OpenSSL's rand_predictable feature).
+      Patch by "teor".

+ 105 - 0
src/test/test_crypto.c

@@ -1831,6 +1831,110 @@ test_crypto_siphash(void *arg)
   ;
 }
 
+/* We want the likelihood that the random buffer exhibits any regular pattern
+ * to be far less than the memory bit error rate in the int return value.
+ * Using 2048 bits provides a failure rate of 1/(3 * 10^616), and we call
+ * 3 functions, leading to an overall error rate of 1/10^616.
+ * This is comparable with the 1/10^603 failure rate of test_crypto_rng_range.
+ */
+#define FAILURE_MODE_BUFFER_SIZE (2048/8)
+
+/** Check crypto_rand for a failure mode where it does nothing to the buffer,
+ * or it sets the buffer to all zeroes. Return 0 when the check passes,
+ * or -1 when it fails. */
+static int
+crypto_rand_check_failure_mode_zero(void)
+{
+  char buf[FAILURE_MODE_BUFFER_SIZE];
+
+  memset(buf, 0, FAILURE_MODE_BUFFER_SIZE);
+  crypto_rand(buf, FAILURE_MODE_BUFFER_SIZE);
+
+  for (size_t i = 0; i < FAILURE_MODE_BUFFER_SIZE; i++) {
+    if (buf[i] != 0) {
+      return 0;
+    }
+  }
+
+  return -1;
+}
+
+/** Check crypto_rand for a failure mode where every int64_t in the buffer is
+ * the same. Return 0 when the check passes, or -1 when it fails. */
+static int
+crypto_rand_check_failure_mode_identical(void)
+{
+  /* just in case the buffer size isn't a multiple of sizeof(int64_t) */
+#define FAILURE_MODE_BUFFER_SIZE_I64 \
+  (FAILURE_MODE_BUFFER_SIZE/SIZEOF_INT64_T)
+#define FAILURE_MODE_BUFFER_SIZE_I64_BYTES \
+  (FAILURE_MODE_BUFFER_SIZE_I64*SIZEOF_INT64_T)
+
+#if FAILURE_MODE_BUFFER_SIZE_I64 < 2
+#error FAILURE_MODE_BUFFER_SIZE needs to be at least 2*SIZEOF_INT64_T
+#endif
+
+  int64_t buf[FAILURE_MODE_BUFFER_SIZE_I64];
+
+  memset(buf, 0, FAILURE_MODE_BUFFER_SIZE_I64_BYTES);
+  crypto_rand((char *)buf, FAILURE_MODE_BUFFER_SIZE_I64_BYTES);
+
+  for (size_t i = 1; i < FAILURE_MODE_BUFFER_SIZE_I64; i++) {
+    if (buf[i] != buf[i-1]) {
+      return 0;
+    }
+  }
+
+  return -1;
+}
+
+/** Check crypto_rand for a failure mode where it increments the "random"
+ * value by 1 for every byte in the buffer. (This is OpenSSL's PREDICT mode.)
+ * Return 0 when the check passes, or -1 when it fails. */
+static int
+crypto_rand_check_failure_mode_predict(void)
+{
+  unsigned char buf[FAILURE_MODE_BUFFER_SIZE];
+
+  memset(buf, 0, FAILURE_MODE_BUFFER_SIZE);
+  crypto_rand((char *)buf, FAILURE_MODE_BUFFER_SIZE);
+
+  for (size_t i = 1; i < FAILURE_MODE_BUFFER_SIZE; i++) {
+    /* check if the last byte was incremented by 1, including integer
+     * wrapping */
+    if (buf[i] - buf[i-1] != 1 && buf[i-1] - buf[i] != 255) {
+      return 0;
+    }
+  }
+
+  return -1;
+}
+
+#undef FAILURE_MODE_BUFFER_SIZE
+
+static void
+test_crypto_failure_modes(void *arg)
+{
+  int rv = 0;
+  (void)arg;
+
+  rv = crypto_early_init();
+  tt_assert(rv == 0);
+
+  /* Check random works */
+  rv = crypto_rand_check_failure_mode_zero();
+  tt_assert(rv == 0);
+
+  rv = crypto_rand_check_failure_mode_identical();
+  tt_assert(rv == 0);
+
+  rv = crypto_rand_check_failure_mode_predict();
+  tt_assert(rv == 0);
+
+ done:
+  ;
+}
+
 #define CRYPTO_LEGACY(name)                                            \
   { #name, test_crypto_ ## name , 0, NULL, NULL }
 
@@ -1869,6 +1973,7 @@ struct testcase_t crypto_tests[] = {
   { "ed25519_fuzz_donna", test_crypto_ed25519_fuzz_donna, TT_FORK, NULL,
     NULL },
   { "siphash", test_crypto_siphash, 0, NULL, NULL },
+  { "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL },
   END_OF_TESTCASES
 };