tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

test_crypto_rng.c (9437B)


      1 /* Copyright (c) 2001-2004, Roger Dingledine.
      2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      4 /* See LICENSE for licensing information */
      5 
      6 #include "orconfig.h"
      7 #define CRYPTO_RAND_PRIVATE
      8 #include "core/or/or.h"
      9 #include "test/test.h"
     10 #include "lib/crypt_ops/aes.h"
     11 #include "lib/crypt_ops/crypto_format.h"
     12 #include "lib/crypt_ops/crypto_rand.h"
     13 
     14 /** Run unit tests for our random number generation function and its wrappers.
     15 */
     16 static void
     17 test_crypto_rng(void *arg)
     18 {
     19  int i, j, allok;
     20  char data1[100], data2[100];
     21  double d;
     22  char *h=NULL;
     23 
     24  /* Try out RNG. */
     25  (void)arg;
     26  tt_assert(! crypto_seed_rng());
     27  crypto_rand(data1, 100);
     28  crypto_rand(data2, 100);
     29  tt_mem_op(data1,OP_NE, data2,100);
     30  allok = 1;
     31  for (i = 0; i < 100; ++i) {
     32    uint64_t big;
     33    char *host;
     34    j = crypto_rand_int(100);
     35    if (j < 0 || j >= 100)
     36      allok = 0;
     37    big = crypto_rand_uint64(UINT64_C(1)<<40);
     38    if (big >= (UINT64_C(1)<<40))
     39      allok = 0;
     40    big = crypto_rand_uint64(UINT64_C(5));
     41    if (big >= 5)
     42      allok = 0;
     43    d = crypto_rand_double();
     44    tt_assert(d >= 0);
     45    tt_assert(d < 1.0);
     46    host = crypto_random_hostname(3,8,"www.",".onion");
     47    if (strcmpstart(host,"www.") ||
     48        strcmpend(host,".onion") ||
     49        strlen(host) < 13 ||
     50        strlen(host) > 18)
     51      allok = 0;
     52    tor_free(host);
     53  }
     54 
     55  /* Make sure crypto_random_hostname clips its inputs properly. */
     56  h = crypto_random_hostname(20000, 9000, "www.", ".onion");
     57  tt_assert(! strcmpstart(h,"www."));
     58  tt_assert(! strcmpend(h,".onion"));
     59  tt_int_op(63+4+6, OP_EQ, strlen(h));
     60 
     61  tt_assert(allok);
     62 done:
     63  tor_free(h);
     64 }
     65 
     66 static void
     67 test_crypto_rng_range(void *arg)
     68 {
     69  int got_smallest = 0, got_largest = 0;
     70  int i;
     71 
     72  (void)arg;
     73  for (i = 0; i < 1000; ++i) {
     74    int x = crypto_rand_int_range(5,9);
     75    tt_int_op(x, OP_GE, 5);
     76    tt_int_op(x, OP_LT, 9);
     77    if (x == 5)
     78      got_smallest = 1;
     79    if (x == 8)
     80      got_largest = 1;
     81  }
     82  /* These fail with probability 1/10^603. */
     83  tt_assert(got_smallest);
     84  tt_assert(got_largest);
     85 
     86  got_smallest = got_largest = 0;
     87  const uint64_t ten_billion = 10 * ((uint64_t)1000000000000);
     88  for (i = 0; i < 1000; ++i) {
     89    uint64_t x = crypto_rand_uint64_range(ten_billion, ten_billion+10);
     90    tt_u64_op(x, OP_GE, ten_billion);
     91    tt_u64_op(x, OP_LT, ten_billion+10);
     92    if (x == ten_billion)
     93      got_smallest = 1;
     94    if (x == ten_billion+9)
     95      got_largest = 1;
     96  }
     97 
     98  tt_assert(got_smallest);
     99  tt_assert(got_largest);
    100 
    101  const time_t now = time(NULL);
    102  for (i = 0; i < 2000; ++i) {
    103    time_t x = crypto_rand_time_range(now, now+60);
    104    tt_i64_op(x, OP_GE, now);
    105    tt_i64_op(x, OP_LT, now+60);
    106    if (x == now)
    107      got_smallest = 1;
    108    if (x == now+59)
    109      got_largest = 1;
    110  }
    111 
    112  tt_assert(got_smallest);
    113  tt_assert(got_largest);
    114 done:
    115  ;
    116 }
    117 
    118 static void
    119 test_crypto_rng_strongest(void *arg)
    120 {
    121  const char *how = arg;
    122  int broken = 0;
    123 
    124  if (how == NULL) {
    125    ;
    126  } else if (!strcmp(how, "nosyscall")) {
    127    break_strongest_rng_syscall = 1;
    128  } else if (!strcmp(how, "nofallback")) {
    129    break_strongest_rng_fallback = 1;
    130  } else if (!strcmp(how, "broken")) {
    131    broken = break_strongest_rng_syscall = break_strongest_rng_fallback = 1;
    132  }
    133 
    134 #define N 128
    135  uint8_t combine_and[N];
    136  uint8_t combine_or[N];
    137  int i, j;
    138 
    139  memset(combine_and, 0xff, N);
    140  memset(combine_or, 0, N);
    141 
    142  for (i = 0; i < 100; ++i) { /* 2^-100 chances just don't happen. */
    143    uint8_t output[N];
    144    memset(output, 0, N);
    145    if (how == NULL) {
    146      /* this one can't fail. */
    147      crypto_strongest_rand(output, sizeof(output));
    148    } else {
    149      int r = crypto_strongest_rand_raw(output, sizeof(output));
    150      if (r == -1) {
    151        if (broken) {
    152          goto done; /* we're fine. */
    153        }
    154        /* This function is allowed to break, but only if it always breaks. */
    155        tt_int_op(i, OP_EQ, 0);
    156        tt_skip();
    157      } else {
    158        tt_assert(! broken);
    159      }
    160    }
    161    for (j = 0; j < N; ++j) {
    162      combine_and[j] &= output[j];
    163      combine_or[j] |= output[j];
    164    }
    165  }
    166 
    167  for (j = 0; j < N; ++j) {
    168    tt_int_op(combine_and[j], OP_EQ, 0);
    169    tt_int_op(combine_or[j], OP_EQ, 0xff);
    170  }
    171 done:
    172  ;
    173 #undef N
    174 }
    175 
    176 static void
    177 test_crypto_rng_fast(void *arg)
    178 {
    179  (void)arg;
    180  crypto_fast_rng_t *rng = crypto_fast_rng_new();
    181  tt_assert(rng);
    182 
    183  /* Rudimentary black-block test to make sure that our prng outputs
    184   * have all bits sometimes on and all bits sometimes off. */
    185  uint64_t m1 = 0, m2 = ~(uint64_t)0;
    186  const int N = 128;
    187 
    188  for (int i=0; i < N; ++i) {
    189    uint64_t v;
    190    crypto_fast_rng_getbytes(rng, (void*)&v, sizeof(v));
    191    m1 |= v;
    192    m2 &= v;
    193  }
    194 
    195  tt_u64_op(m1, OP_EQ, ~(uint64_t)0);
    196  tt_u64_op(m2, OP_EQ, 0);
    197 
    198  /* Check range functions. */
    199  int counts[5];
    200  memset(counts, 0, sizeof(counts));
    201  for (int i=0; i < N; ++i) {
    202    unsigned u = crypto_fast_rng_get_uint(rng, 5);
    203    tt_int_op(u, OP_GE, 0);
    204    tt_int_op(u, OP_LT, 5);
    205    counts[u]++;
    206 
    207    uint64_t u64 = crypto_fast_rng_get_uint64(rng, UINT64_C(1)<<40);
    208    tt_u64_op(u64, OP_GE, 0);
    209    tt_u64_op(u64, OP_LT, UINT64_C(1)<<40);
    210 
    211    double d = crypto_fast_rng_get_double(rng);
    212    tt_assert(d >= 0.0);
    213    tt_assert(d < 1.0);
    214  }
    215 
    216  /* All values should have come up once. */
    217  for (int i=0; i<5; ++i) {
    218    tt_int_op(counts[i], OP_GT, 0);
    219  }
    220 
    221  /* per-thread rand_fast shouldn't crash or leak. */
    222  crypto_fast_rng_t *t_rng = get_thread_fast_rng();
    223  for (int i = 0; i < N; ++i) {
    224    uint64_t u64 = crypto_fast_rng_get_uint64(t_rng, UINT64_C(1)<<40);
    225    tt_u64_op(u64, OP_GE, 0);
    226    tt_u64_op(u64, OP_LT, UINT64_C(1)<<40);
    227  }
    228 
    229 done:
    230  crypto_fast_rng_free(rng);
    231 }
    232 
    233 static void
    234 test_crypto_rng_fast_whitebox(void *arg)
    235 {
    236  (void)arg;
    237  const size_t buflen = crypto_fast_rng_get_bytes_used_per_stream();
    238  char *buf = tor_malloc_zero(buflen);
    239  char *buf2 = tor_malloc_zero(buflen);
    240  char *buf3 = NULL, *buf4 = NULL;
    241 
    242  crypto_cipher_t *cipher = NULL, *cipher2 = NULL;
    243  uint8_t seed[CRYPTO_FAST_RNG_SEED_LEN];
    244  memset(seed, 0, sizeof(seed));
    245 
    246  /* Start with a prng with zero key and zero IV. */
    247  crypto_fast_rng_t *rng = crypto_fast_rng_new_from_seed(seed);
    248  tt_assert(rng);
    249 
    250  /* We'll use a stream cipher to keep in sync */
    251  cipher = crypto_cipher_new_with_iv_and_bits(seed, seed+32, 256);
    252 
    253  /* The first 48 bytes are used for the next seed -- let's make sure we have
    254   * them.
    255   */
    256  memset(seed, 0, sizeof(seed));
    257  crypto_cipher_crypt_inplace(cipher, (char*)seed, sizeof(seed));
    258 
    259  /* if we get 128 bytes, they should match the bytes from the aes256-counter
    260   * stream, starting at position 48.
    261   */
    262  crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 128);
    263  memset(buf2, 0, 128);
    264  crypto_cipher_crypt_inplace(cipher, buf2, 128);
    265  tt_mem_op(buf, OP_EQ, buf2, 128);
    266 
    267  /* Try that again, with an odd number of bytes. */
    268  crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 199);
    269  memset(buf2, 0, 199);
    270  crypto_cipher_crypt_inplace(cipher, buf2, 199);
    271  tt_mem_op(buf, OP_EQ, buf2, 199);
    272 
    273  /* Make sure that refilling works as expected: skip all but the last 5 bytes
    274   * of this steam. */
    275  size_t skip = buflen - (199+128) - 5;
    276  crypto_fast_rng_getbytes(rng, (uint8_t*)buf, skip);
    277  crypto_cipher_crypt_inplace(cipher, buf2, skip);
    278 
    279  /* Now get the next 128 bytes. The first 5 will come from this stream, and
    280   * the next 5 will come from the stream keyed by the new value of 'seed'. */
    281  crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 128);
    282  memset(buf2, 0, 128);
    283  crypto_cipher_crypt_inplace(cipher, buf2, 5);
    284  crypto_cipher_free(cipher);
    285  cipher = crypto_cipher_new_with_iv_and_bits(seed, seed+32, 256);
    286  memset(seed, 0, sizeof(seed));
    287  crypto_cipher_crypt_inplace(cipher, (char*)seed, sizeof(seed));
    288  crypto_cipher_crypt_inplace(cipher, buf2+5, 128-5);
    289  tt_mem_op(buf, OP_EQ, buf2, 128);
    290 
    291  /* And check the next 7 bytes to make sure we didn't discard anything. */
    292  crypto_fast_rng_getbytes(rng, (uint8_t*)buf, 7);
    293  memset(buf2, 0, 7);
    294  crypto_cipher_crypt_inplace(cipher, buf2, 7);
    295  tt_mem_op(buf, OP_EQ, buf2, 7);
    296 
    297  /* Now try the optimization for long outputs. */
    298  buf3 = tor_malloc(65536);
    299  crypto_fast_rng_getbytes(rng, (uint8_t*)buf3, 65536);
    300 
    301  buf4 = tor_malloc_zero(65536);
    302  uint8_t seed2[CRYPTO_FAST_RNG_SEED_LEN];
    303  memset(seed2, 0, sizeof(seed2));
    304  crypto_cipher_crypt_inplace(cipher, (char*)seed2, sizeof(seed2));
    305  cipher2 = crypto_cipher_new_with_iv_and_bits(seed2, seed2+32, 256);
    306  crypto_cipher_crypt_inplace(cipher2, buf4, 65536);
    307  tt_mem_op(buf3, OP_EQ, buf4, 65536);
    308 
    309 done:
    310  crypto_fast_rng_free(rng);
    311  crypto_cipher_free(cipher);
    312  crypto_cipher_free(cipher2);
    313  tor_free(buf);
    314  tor_free(buf2);
    315  tor_free(buf3);
    316  tor_free(buf4);
    317 }
    318 
    319 struct testcase_t crypto_rng_tests[] = {
    320  { "rng", test_crypto_rng, 0, NULL, NULL },
    321  { "rng_range", test_crypto_rng_range, 0, NULL, NULL },
    322  { "rng_strongest", test_crypto_rng_strongest, TT_FORK, NULL, NULL },
    323  { "rng_strongest_nosyscall", test_crypto_rng_strongest, TT_FORK,
    324    &passthrough_setup, (void*)"nosyscall" },
    325  { "rng_strongest_nofallback", test_crypto_rng_strongest, TT_FORK,
    326    &passthrough_setup, (void*)"nofallback" },
    327  { "rng_strongest_broken", test_crypto_rng_strongest, TT_FORK,
    328    &passthrough_setup, (void*)"broken" },
    329  { "fast", test_crypto_rng_fast, 0, NULL, NULL },
    330  { "fast_whitebox", test_crypto_rng_fast_whitebox, 0, NULL, NULL },
    331  END_OF_TESTCASES
    332 };