[tor-commits] [tor/master] Add unit tests that check for common RNG failure modes

nickm at torproject.org nickm at torproject.org
Thu Nov 26 15:05:53 UTC 2015


commit 155fa2dbdb78be0cbf73c9b200a295740584f24e
Author: teor (Tim Wilson-Brown) <teor2345 at gmail.com>
Date:   Thu Nov 26 21:25:31 2015 +1100

    Add unit tests that check for common RNG failure modes
    
    Check that crypto_rand doesn't return all zeroes, identical values,
    or incrementing values (OpenSSL's rand_predictable feature).
---
 changes/rand-failure-modes |    5 +++
 src/test/test_crypto.c     |  105 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/changes/rand-failure-modes b/changes/rand-failure-modes
new file mode 100644
index 0000000..cc6ef47
--- /dev/null
+++ b/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".
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index dbaec61..08347b2 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -1803,6 +1803,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 }
 
@@ -1841,6 +1945,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
 };
 





More information about the tor-commits mailing list