tor

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

bench.c (25315B)


      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 /**
      7 * \file bench.c
      8 * \brief Benchmarks for lower level Tor modules.
      9 **/
     10 
     11 #include "orconfig.h"
     12 
     13 #include "core/or/or.h"
     14 #include "core/crypto/relay_crypto.h"
     15 
     16 #include "lib/intmath/weakrng.h"
     17 
     18 #ifdef ENABLE_OPENSSL
     19 #include <openssl/opensslv.h>
     20 #include <openssl/evp.h>
     21 #include <openssl/ec.h>
     22 #include <openssl/ecdh.h>
     23 #include <openssl/obj_mac.h>
     24 #endif /* defined(ENABLE_OPENSSL) */
     25 
     26 #include <math.h>
     27 
     28 #include "ext/polyval/polyval.h"
     29 #include "core/or/circuitlist.h"
     30 #include "app/config/config.h"
     31 #include "app/main/subsysmgr.h"
     32 #include "lib/crypt_ops/crypto_curve25519.h"
     33 #include "lib/crypt_ops/crypto_dh.h"
     34 #include "core/crypto/onion_ntor.h"
     35 #include "lib/crypt_ops/crypto_ed25519.h"
     36 #include "lib/crypt_ops/crypto_rand.h"
     37 #include "feature/dircommon/consdiff.h"
     38 #include "lib/compress/compress.h"
     39 #include "core/crypto/relay_crypto_cgo.h"
     40 
     41 #include "core/or/cell_st.h"
     42 #include "core/or/or_circuit_st.h"
     43 
     44 #include "lib/crypt_ops/digestset.h"
     45 #include "lib/crypt_ops/crypto_init.h"
     46 
     47 #include "feature/dirparse/microdesc_parse.h"
     48 #include "feature/nodelist/microdesc.h"
     49 
     50 #if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) \
     51  || defined(_M_X64) || defined(_M_IX86) || defined(__i486)       \
     52  || defined(__i386__)
     53 #define INTEL
     54 #endif
     55 
     56 #ifdef INTEL
     57 #include "x86intrin.h"
     58 
     59 static inline uint64_t
     60 cycles(void)
     61 {
     62  return __rdtsc();
     63 }
     64 #define cpb(start, end, bytes) \
     65  (((double)(end - start)) / (bytes))
     66 #else
     67 #define cycles() 0
     68 #define cpb(start,end,bytes) ((void)(start+end+bytes), (double)NAN)
     69 #endif
     70 
     71 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
     72 static uint64_t nanostart;
     73 static inline uint64_t
     74 timespec_to_nsec(const struct timespec *ts)
     75 {
     76  return ((uint64_t)ts->tv_sec)*1000000000 + ts->tv_nsec;
     77 }
     78 
     79 static void
     80 reset_perftime(void)
     81 {
     82  struct timespec ts;
     83  int r;
     84  r = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
     85  tor_assert(r == 0);
     86  nanostart = timespec_to_nsec(&ts);
     87 }
     88 
     89 static uint64_t
     90 perftime(void)
     91 {
     92  struct timespec ts;
     93  int r;
     94  r = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
     95  tor_assert(r == 0);
     96  return timespec_to_nsec(&ts) - nanostart;
     97 }
     98 
     99 #else /* !(defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)) */
    100 static struct timeval tv_start = { 0, 0 };
    101 static void
    102 reset_perftime(void)
    103 {
    104  tor_gettimeofday(&tv_start);
    105 }
    106 static uint64_t
    107 perftime(void)
    108 {
    109  struct timeval now, out;
    110  tor_gettimeofday(&now);
    111  timersub(&now, &tv_start, &out);
    112  return ((uint64_t)out.tv_sec)*1000000000 + out.tv_usec*1000;
    113 }
    114 #endif /* defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) */
    115 
    116 #define NANOCOUNT(start,end,iters) \
    117  ( ((double)((end)-(start))) / (iters) )
    118 
    119 #define MICROCOUNT(start,end,iters) \
    120  ( NANOCOUNT((start), (end), (iters)) / 1000.0 )
    121 
    122 /** Run AES performance benchmarks. */
    123 static void
    124 bench_aes(void)
    125 {
    126  int len, i;
    127  char *b1, *b2;
    128  crypto_cipher_t *c;
    129  uint64_t start, end;
    130  const int bytes_per_iter = (1<<24);
    131  reset_perftime();
    132  char key[CIPHER_KEY_LEN];
    133  crypto_rand(key, sizeof(key));
    134  c = crypto_cipher_new(key);
    135 
    136  for (len = 1; len <= 8192; len *= 2) {
    137    int iters = bytes_per_iter / len;
    138    b1 = tor_malloc_zero(len);
    139    b2 = tor_malloc_zero(len);
    140    start = perftime();
    141    for (i = 0; i < iters; ++i) {
    142      crypto_cipher_encrypt(c, b1, b2, len);
    143    }
    144    end = perftime();
    145    tor_free(b1);
    146    tor_free(b2);
    147    printf("%d bytes: %.2f nsec per byte\n", len,
    148           NANOCOUNT(start, end, iters*len));
    149  }
    150  crypto_cipher_free(c);
    151 }
    152 
    153 static void
    154 bench_onion_ntor_impl(void)
    155 {
    156  const int iters = 1<<10;
    157  int i;
    158  curve25519_keypair_t keypair1, keypair2;
    159  uint64_t start, end;
    160  uint8_t os[NTOR_ONIONSKIN_LEN];
    161  uint8_t or[NTOR_REPLY_LEN];
    162  ntor_handshake_state_t *state = NULL;
    163  uint8_t nodeid[DIGEST_LEN];
    164  di_digest256_map_t *keymap = NULL;
    165 
    166  curve25519_secret_key_generate(&keypair1.seckey, 0);
    167  curve25519_public_key_generate(&keypair1.pubkey, &keypair1.seckey);
    168  curve25519_secret_key_generate(&keypair2.seckey, 0);
    169  curve25519_public_key_generate(&keypair2.pubkey, &keypair2.seckey);
    170  dimap_add_entry(&keymap, keypair1.pubkey.public_key, &keypair1);
    171  dimap_add_entry(&keymap, keypair2.pubkey.public_key, &keypair2);
    172  crypto_rand((char *)nodeid, sizeof(nodeid));
    173 
    174  reset_perftime();
    175  start = perftime();
    176  for (i = 0; i < iters; ++i) {
    177    onion_skin_ntor_create(nodeid, &keypair1.pubkey, &state, os);
    178    ntor_handshake_state_free(state);
    179    state = NULL;
    180  }
    181  end = perftime();
    182  printf("Client-side, part 1: %f usec.\n", NANOCOUNT(start, end, iters)/1e3);
    183 
    184  state = NULL;
    185  onion_skin_ntor_create(nodeid, &keypair1.pubkey, &state, os);
    186  start = perftime();
    187  for (i = 0; i < iters; ++i) {
    188    uint8_t key_out[CPATH_KEY_MATERIAL_LEN];
    189    onion_skin_ntor_server_handshake(os, keymap, NULL, nodeid, or,
    190                                key_out, sizeof(key_out));
    191  }
    192  end = perftime();
    193  printf("Server-side: %f usec\n",
    194         NANOCOUNT(start, end, iters)/1e3);
    195 
    196  start = perftime();
    197  for (i = 0; i < iters; ++i) {
    198    uint8_t key_out[CPATH_KEY_MATERIAL_LEN];
    199    int s;
    200    s = onion_skin_ntor_client_handshake(state, or, key_out, sizeof(key_out),
    201                                         NULL);
    202    tor_assert(s == 0);
    203  }
    204  end = perftime();
    205  printf("Client-side, part 2: %f usec.\n",
    206         NANOCOUNT(start, end, iters)/1e3);
    207 
    208  ntor_handshake_state_free(state);
    209  dimap_free(keymap, NULL);
    210 }
    211 
    212 static void
    213 bench_onion_ntor(void)
    214 {
    215  int ed;
    216 
    217  for (ed = 0; ed <= 1; ++ed) {
    218    printf("Ed25519-based basepoint multiply = %s.\n",
    219           (ed == 0) ? "disabled" : "enabled");
    220    curve25519_set_impl_params(ed);
    221    bench_onion_ntor_impl();
    222  }
    223 }
    224 
    225 static void
    226 bench_ed25519_impl(void)
    227 {
    228  uint64_t start, end;
    229  const int iters = 1<<12;
    230  int i;
    231  const uint8_t msg[] = "but leaving, could not tell what they had heard";
    232  ed25519_signature_t sig;
    233  ed25519_keypair_t kp;
    234  curve25519_keypair_t curve_kp;
    235  ed25519_public_key_t pubkey_tmp;
    236 
    237  ed25519_secret_key_generate(&kp.seckey, 0);
    238  start = perftime();
    239  for (i = 0; i < iters; ++i) {
    240    ed25519_public_key_generate(&kp.pubkey, &kp.seckey);
    241  }
    242  end = perftime();
    243  printf("Generate public key: %.2f usec\n",
    244         MICROCOUNT(start, end, iters));
    245 
    246  start = perftime();
    247  for (i = 0; i < iters; ++i) {
    248    ed25519_sign(&sig, msg, sizeof(msg), &kp);
    249  }
    250  end = perftime();
    251  printf("Sign a short message: %.2f usec\n",
    252         MICROCOUNT(start, end, iters));
    253 
    254  start = perftime();
    255  for (i = 0; i < iters; ++i) {
    256    ed25519_checksig(&sig, msg, sizeof(msg), &kp.pubkey);
    257  }
    258  end = perftime();
    259  printf("Verify signature: %.2f usec\n",
    260         MICROCOUNT(start, end, iters));
    261 
    262  curve25519_keypair_generate(&curve_kp, 0);
    263  start = perftime();
    264  for (i = 0; i < iters; ++i) {
    265    ed25519_public_key_from_curve25519_public_key(&pubkey_tmp,
    266                                                  &curve_kp.pubkey, 1);
    267  }
    268  end = perftime();
    269  printf("Convert public point from curve25519: %.2f usec\n",
    270         MICROCOUNT(start, end, iters));
    271 
    272  curve25519_keypair_generate(&curve_kp, 0);
    273  start = perftime();
    274  for (i = 0; i < iters; ++i) {
    275    ed25519_public_blind(&pubkey_tmp, &kp.pubkey, msg);
    276  }
    277  end = perftime();
    278  printf("Blind a public key: %.2f usec\n",
    279         MICROCOUNT(start, end, iters));
    280 }
    281 
    282 static void
    283 bench_ed25519(void)
    284 {
    285  int donna;
    286 
    287  for (donna = 0; donna <= 1; ++donna) {
    288    printf("Ed25519-donna = %s.\n",
    289           (donna == 0) ? "disabled" : "enabled");
    290    ed25519_set_impl_params(donna);
    291    bench_ed25519_impl();
    292  }
    293 }
    294 
    295 static void
    296 bench_rand_len(int len)
    297 {
    298  const int N = 100000;
    299  int i;
    300  char *buf = tor_malloc(len);
    301  uint64_t start,end;
    302 
    303  start = perftime();
    304  for (i = 0; i < N; ++i) {
    305    crypto_rand(buf, len);
    306  }
    307  end = perftime();
    308  printf("crypto_rand(%d): %f nsec.\n", len, NANOCOUNT(start,end,N));
    309 
    310  crypto_fast_rng_t *fr = crypto_fast_rng_new();
    311  start = perftime();
    312  for (i = 0; i < N; ++i) {
    313    crypto_fast_rng_getbytes(fr,(uint8_t*)buf,len);
    314  }
    315  end = perftime();
    316  printf("crypto_fast_rng_getbytes(%d): %f nsec.\n", len,
    317         NANOCOUNT(start,end,N));
    318  crypto_fast_rng_free(fr);
    319 
    320  if (len <= 32) {
    321    start = perftime();
    322    for (i = 0; i < N; ++i) {
    323      crypto_strongest_rand((uint8_t*)buf, len);
    324    }
    325    end = perftime();
    326    printf("crypto_strongest_rand(%d): %f nsec.\n", len,
    327           NANOCOUNT(start,end,N));
    328  }
    329 
    330  if (len == 4) {
    331    tor_weak_rng_t weak;
    332    tor_init_weak_random(&weak, 1337);
    333 
    334    start = perftime();
    335    uint32_t t=0;
    336    for (i = 0; i < N; ++i) {
    337      t += tor_weak_random(&weak);
    338      (void) t;
    339    }
    340    end = perftime();
    341    printf("weak_rand(4): %f nsec.\n", NANOCOUNT(start,end,N));
    342  }
    343 
    344  tor_free(buf);
    345 }
    346 
    347 static void
    348 bench_rand(void)
    349 {
    350  bench_rand_len(4);
    351  bench_rand_len(16);
    352  bench_rand_len(128);
    353 }
    354 
    355 static void
    356 bench_cell_aes(void)
    357 {
    358  uint64_t start, end;
    359  const int len = 509;
    360  const int iters = (1<<16);
    361  const int max_misalign = 15;
    362  char *b = tor_malloc(len+max_misalign);
    363  crypto_cipher_t *c;
    364  int i, misalign;
    365  char key[CIPHER_KEY_LEN];
    366  crypto_rand(key, sizeof(key));
    367  c = crypto_cipher_new(key);
    368 
    369  reset_perftime();
    370  for (misalign = 0; misalign <= max_misalign; ++misalign) {
    371    start = perftime();
    372    for (i = 0; i < iters; ++i) {
    373      crypto_cipher_crypt_inplace(c, b+misalign, len);
    374    }
    375    end = perftime();
    376    printf("%d bytes, misaligned by %d: %.2f nsec per byte\n", len, misalign,
    377           NANOCOUNT(start, end, iters*len));
    378  }
    379 
    380  crypto_cipher_free(c);
    381  tor_free(b);
    382 }
    383 
    384 /** Run digestmap_t performance benchmarks. */
    385 static void
    386 bench_dmap(void)
    387 {
    388  smartlist_t *sl = smartlist_new();
    389  smartlist_t *sl2 = smartlist_new();
    390  uint64_t start, end, pt2, pt3, pt4;
    391  int iters = 8192;
    392  const int elts = 4000;
    393  const int fpostests = 100000;
    394  char d[20];
    395  int i,n=0, fp = 0;
    396  digestmap_t *dm = digestmap_new();
    397  digestset_t *ds = digestset_new(elts);
    398 
    399  for (i = 0; i < elts; ++i) {
    400    crypto_rand(d, 20);
    401    smartlist_add(sl, tor_memdup(d, 20));
    402  }
    403  for (i = 0; i < elts; ++i) {
    404    crypto_rand(d, 20);
    405    smartlist_add(sl2, tor_memdup(d, 20));
    406  }
    407  //printf("nbits=%d\n", ds->mask+1);
    408 
    409  reset_perftime();
    410 
    411  start = perftime();
    412  for (i = 0; i < iters; ++i) {
    413    SMARTLIST_FOREACH(sl, const char *, cp, digestmap_set(dm, cp, (void*)1));
    414  }
    415  pt2 = perftime();
    416  printf("digestmap_set: %.2f ns per element\n",
    417         NANOCOUNT(start, pt2, iters*elts));
    418 
    419  for (i = 0; i < iters; ++i) {
    420    SMARTLIST_FOREACH(sl, const char *, cp, digestmap_get(dm, cp));
    421    SMARTLIST_FOREACH(sl2, const char *, cp, digestmap_get(dm, cp));
    422  }
    423  pt3 = perftime();
    424  printf("digestmap_get: %.2f ns per element\n",
    425         NANOCOUNT(pt2, pt3, iters*elts*2));
    426 
    427  for (i = 0; i < iters; ++i) {
    428    SMARTLIST_FOREACH(sl, const char *, cp, digestset_add(ds, cp));
    429  }
    430  pt4 = perftime();
    431  printf("digestset_add: %.2f ns per element\n",
    432         NANOCOUNT(pt3, pt4, iters*elts));
    433 
    434  for (i = 0; i < iters; ++i) {
    435    SMARTLIST_FOREACH(sl, const char *, cp,
    436                      n += digestset_probably_contains(ds, cp));
    437    SMARTLIST_FOREACH(sl2, const char *, cp,
    438                      n += digestset_probably_contains(ds, cp));
    439  }
    440  end = perftime();
    441  printf("digestset_probably_contains: %.2f ns per element.\n",
    442         NANOCOUNT(pt4, end, iters*elts*2));
    443  /* We need to use this, or else the whole loop gets optimized out. */
    444  printf("Hits == %d\n", n);
    445 
    446  for (i = 0; i < fpostests; ++i) {
    447    crypto_rand(d, 20);
    448    if (digestset_probably_contains(ds, d)) ++fp;
    449  }
    450  printf("False positive rate on digestset: %.2f%%\n",
    451         (fp/(double)fpostests)*100);
    452 
    453  digestmap_free(dm, NULL);
    454  digestset_free(ds);
    455  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
    456  SMARTLIST_FOREACH(sl2, char *, cp, tor_free(cp));
    457  smartlist_free(sl);
    458  smartlist_free(sl2);
    459 }
    460 
    461 static void
    462 bench_siphash(void)
    463 {
    464  char buf[128];
    465  int lens[] = { 7, 8, 15, 16, 20, 32, 111, 128, -1 };
    466  int i, j;
    467  uint64_t start, end;
    468  const int N = 300000;
    469  crypto_rand(buf, sizeof(buf));
    470 
    471  for (i = 0; lens[i] > 0; ++i) {
    472    reset_perftime();
    473    start = perftime();
    474    for (j = 0; j < N; ++j) {
    475      siphash24g(buf, lens[i]);
    476    }
    477    end = perftime();
    478    printf("siphash24g(%d): %.2f ns per call\n",
    479           lens[i], NANOCOUNT(start,end,N));
    480  }
    481 }
    482 
    483 static void
    484 bench_digest(void)
    485 {
    486  char buf[8192];
    487  char out[DIGEST512_LEN];
    488  const int lens[] = { 1, 16, 32, 64, 128, 512, 1024, 2048, -1 };
    489  const int N = 300000;
    490  uint64_t start, end;
    491  crypto_rand(buf, sizeof(buf));
    492 
    493  for (int alg = 0; alg < N_DIGEST_ALGORITHMS; alg++) {
    494    for (int i = 0; lens[i] > 0; ++i) {
    495      reset_perftime();
    496      start = perftime();
    497      int failures = 0;
    498      for (int j = 0; j < N; ++j) {
    499        switch (alg) {
    500          case DIGEST_SHA1:
    501            failures += crypto_digest(out, buf, lens[i]) < 0;
    502            break;
    503          case DIGEST_SHA256:
    504          case DIGEST_SHA3_256:
    505            failures += crypto_digest256(out, buf, lens[i], alg) < 0;
    506            break;
    507          case DIGEST_SHA512:
    508          case DIGEST_SHA3_512:
    509            failures += crypto_digest512(out, buf, lens[i], alg) < 0;
    510            break;
    511          default:
    512            tor_assert(0);
    513        }
    514      }
    515      end = perftime();
    516      printf("%s(%d): %.2f ns per call\n",
    517             crypto_digest_algorithm_get_name(alg),
    518             lens[i], NANOCOUNT(start,end,N));
    519      if (failures)
    520        printf("ERROR: crypto_digest failed %d times.\n", failures);
    521    }
    522  }
    523 }
    524 
    525 static void
    526 bench_cell_ops_tor1(void)
    527 {
    528  const int iters = 1<<20;
    529  int i;
    530 
    531  /* benchmarks for cell ops at relay. */
    532  or_circuit_t *or_circ = tor_malloc_zero(sizeof(or_circuit_t));
    533  cell_t *cell = tor_malloc(sizeof(cell_t));
    534  int outbound;
    535  uint64_t start, end;
    536  uint64_t cstart, cend;
    537 
    538  // TODO CGO: use constant after this is merged or rebased.
    539  const unsigned payload_len = 498;
    540 
    541  crypto_rand((char*)cell->payload, sizeof(cell->payload));
    542 
    543  /* Mock-up or_circuit_t */
    544  or_circ->base_.magic = OR_CIRCUIT_MAGIC;
    545  or_circ->base_.purpose = CIRCUIT_PURPOSE_OR;
    546 
    547  /* Initialize crypto */
    548  char keys[CPATH_KEY_MATERIAL_LEN];
    549  crypto_rand(keys, sizeof(keys));
    550  size_t keylen = sizeof(keys);
    551  relay_crypto_init(RELAY_CRYPTO_ALG_TOR1,
    552                    &or_circ->crypto, keys, keylen);
    553 
    554  reset_perftime();
    555 
    556  for (outbound = 0; outbound <= 1; ++outbound) {
    557    cell_direction_t d = outbound ? CELL_DIRECTION_OUT : CELL_DIRECTION_IN;
    558    start = perftime();
    559    cstart = cycles();
    560    for (i = 0; i < iters; ++i) {
    561      char recognized = 0;
    562      crypt_path_t *layer_hint = NULL;
    563      relay_decrypt_cell(TO_CIRCUIT(or_circ), cell, d,
    564                         &layer_hint, &recognized);
    565    }
    566    cend = cycles();
    567    end = perftime();
    568    printf("%sbound cells: %.2f ns per cell. "
    569           "(%.2f ns per byte of payload, %.2f cpb)\n",
    570           outbound?"Out":" In",
    571           NANOCOUNT(start,end,iters),
    572           NANOCOUNT(start,end,iters * payload_len),
    573           cpb(cstart, cend, iters * payload_len));
    574  }
    575 
    576  start = perftime();
    577  cstart = cycles();
    578  for (i = 0; i < iters; ++i) {
    579    relay_encrypt_cell_inbound(cell, or_circ);
    580  }
    581  cend = cycles();
    582  end = perftime();
    583  printf("originate inbound : %.2f ns per cell. "
    584         "(%.2f ns per payload byte, %.2f cpb)\n",
    585         NANOCOUNT(start, end, iters),
    586         NANOCOUNT(start, end, iters * payload_len),
    587         cpb(cstart, cend, iters*payload_len));
    588 
    589  relay_crypto_clear(&or_circ->crypto);
    590  tor_free(or_circ);
    591  tor_free(cell);
    592 }
    593 
    594 static void
    595 bench_polyval(void)
    596 {
    597  polyval_t pv;
    598  polyvalx_t pvx;
    599  uint8_t key[16];
    600  uint8_t input[512];
    601  uint64_t start, end, cstart, cend;
    602  crypto_rand((char*) key, sizeof(key));
    603  crypto_rand((char*) input, sizeof(input));
    604 
    605  const int iters = 1<<20;
    606 
    607  polyval_init(&pv, key);
    608  start = perftime();
    609  cstart = cycles();
    610  for (int i = 0; i < iters; ++i) {
    611    polyval_add_block(&pv, input);
    612  }
    613  cend = cycles();
    614  end = perftime();
    615  printf("polyval (add 16): %.2f ns; %.2f cpb\n",
    616         NANOCOUNT(start, end, iters),
    617         cpb(cstart, cend, iters * 16));
    618 
    619  start = perftime();
    620  cstart = cycles();
    621  for (int i = 0; i < iters; ++i) {
    622    polyval_add_zpad(&pv, input, 512);
    623  }
    624  cend = cycles();
    625  end = perftime();
    626  printf("polyval (add 512): %.2f ns; %.2f cpb\n",
    627         NANOCOUNT(start, end, iters),
    628         cpb(cstart, cend, iters * 512));
    629 
    630  polyvalx_init(&pvx, key);
    631  start = perftime();
    632  cstart = cycles();
    633  for (int i = 0; i < iters; ++i) {
    634    polyvalx_add_zpad(&pvx, input, 512);
    635  }
    636  cend = cycles();
    637  end = perftime();
    638  printf("polyval (add 512, pre-expanded key): %.2f ns; %.2f cpb\n",
    639         NANOCOUNT(start, end, iters),
    640         cpb(cstart, cend, iters * 512));
    641 }
    642 
    643 static void
    644 bench_cell_ops_cgo(void)
    645 {
    646  const int iters = 1<<20;
    647 
    648  /* benchmarks for cell ops at relay. */
    649  cell_t *cell = tor_malloc(sizeof(cell_t));
    650 
    651  uint64_t start, end;
    652  uint64_t cstart, cend;
    653 
    654  const uint8_t *tag = NULL;
    655  size_t  keylen = cgo_key_material_len(128);
    656  uint8_t *keys = tor_malloc(keylen);
    657  crypto_rand((char*) keys, keylen);
    658 
    659  // We're using the version of this constant that _does_ include
    660  // stream IDs, for an apples-to-apples comparison with tor1.
    661  //
    662  // TODO CGO: use constant after this is merged or rebased.
    663  const unsigned payload_len = 488;
    664 
    665  memset(cell, 0, sizeof(*cell));
    666 
    667 #define SHOW(operation) \
    668  printf("%s: %.2f per cell (%.2f cpb)\n",              \
    669         (operation),                                   \
    670         NANOCOUNT(start,end,iters),                    \
    671         cpb(cstart, cend, (double)iters * payload_len))
    672 
    673  // Initialize crypto
    674  cgo_crypt_t *r_f = cgo_crypt_new(CGO_MODE_RELAY_FORWARD, 128, keys, keylen);
    675  cgo_crypt_t *r_b = cgo_crypt_new(CGO_MODE_RELAY_BACKWARD, 128, keys, keylen);
    676 
    677  reset_perftime();
    678 
    679  start = perftime();
    680  cstart = cycles();
    681  for (int i=0; i < iters; ++i) {
    682    cgo_crypt_relay_forward(r_f, cell, &tag);
    683  }
    684  cend = cycles();
    685  end = perftime();
    686  SHOW("CGO outbound at relay");
    687 
    688  start = perftime();
    689  cstart = cycles();
    690  for (int i=0; i < iters; ++i) {
    691    cgo_crypt_relay_backward(r_b, cell);
    692  }
    693  cend = cycles();
    694  end = perftime();
    695  SHOW("CGO inbound at relay");
    696 
    697  start = perftime();
    698  cstart = cycles();
    699  for (int i=0; i < iters; ++i) {
    700    cgo_crypt_relay_originate(r_b, cell, &tag);
    701  }
    702  cend = cycles();
    703  end = perftime();
    704  SHOW("CGO originate at relay");
    705 
    706  tor_free(cell);
    707  tor_free(keys);
    708  cgo_crypt_free(r_f);
    709  cgo_crypt_free(r_b);
    710 
    711 #undef SHOW
    712 }
    713 
    714 static void
    715 bench_dh(void)
    716 {
    717  const int iters = 1<<10;
    718  int i;
    719  uint64_t start, end;
    720 
    721  reset_perftime();
    722  start = perftime();
    723  for (i = 0; i < iters; ++i) {
    724    char dh_pubkey_a[DH1024_KEY_LEN], dh_pubkey_b[DH1024_KEY_LEN];
    725    char secret_a[DH1024_KEY_LEN], secret_b[DH1024_KEY_LEN];
    726    ssize_t slen_a, slen_b;
    727    crypto_dh_t *dh_a = crypto_dh_new(DH_TYPE_TLS);
    728    crypto_dh_t *dh_b = crypto_dh_new(DH_TYPE_TLS);
    729    crypto_dh_generate_public(dh_a);
    730    crypto_dh_generate_public(dh_b);
    731    crypto_dh_get_public(dh_a, dh_pubkey_a, sizeof(dh_pubkey_a));
    732    crypto_dh_get_public(dh_b, dh_pubkey_b, sizeof(dh_pubkey_b));
    733    slen_a = crypto_dh_compute_secret(LOG_NOTICE,
    734                                      dh_a, dh_pubkey_b, sizeof(dh_pubkey_b),
    735                                      secret_a, sizeof(secret_a));
    736    slen_b = crypto_dh_compute_secret(LOG_NOTICE,
    737                                      dh_b, dh_pubkey_a, sizeof(dh_pubkey_a),
    738                                      secret_b, sizeof(secret_b));
    739    tor_assert(slen_a == slen_b);
    740    tor_assert(fast_memeq(secret_a, secret_b, slen_a));
    741    crypto_dh_free(dh_a);
    742    crypto_dh_free(dh_b);
    743  }
    744  end = perftime();
    745  printf("Complete DH handshakes (1024 bit, public and private ops):\n"
    746         "      %f millisec each.\n", NANOCOUNT(start, end, iters)/1e6);
    747 }
    748 
    749 #ifdef ENABLE_OPENSSL
    750 static void
    751 bench_ecdh_impl(int nid, const char *name)
    752 {
    753  const int iters = 1<<10;
    754  int i;
    755  uint64_t start, end;
    756 
    757  reset_perftime();
    758  start = perftime();
    759  for (i = 0; i < iters; ++i) {
    760    char secret_a[DH1024_KEY_LEN], secret_b[DH1024_KEY_LEN];
    761    ssize_t slen_a, slen_b;
    762    EC_KEY *dh_a = EC_KEY_new_by_curve_name(nid);
    763    EC_KEY *dh_b = EC_KEY_new_by_curve_name(nid);
    764    if (!dh_a || !dh_b) {
    765      puts("Skipping.  (No implementation?)");
    766      return;
    767    }
    768 
    769    EC_KEY_generate_key(dh_a);
    770    EC_KEY_generate_key(dh_b);
    771    slen_a = ECDH_compute_key(secret_a, DH1024_KEY_LEN,
    772                              EC_KEY_get0_public_key(dh_b), dh_a,
    773                              NULL);
    774    slen_b = ECDH_compute_key(secret_b, DH1024_KEY_LEN,
    775                              EC_KEY_get0_public_key(dh_a), dh_b,
    776                              NULL);
    777 
    778    tor_assert(slen_a == slen_b);
    779    tor_assert(fast_memeq(secret_a, secret_b, slen_a));
    780    EC_KEY_free(dh_a);
    781    EC_KEY_free(dh_b);
    782  }
    783  end = perftime();
    784  printf("Complete ECDH %s handshakes (2 public and 2 private ops):\n"
    785         "      %f millisec each.\n", name, NANOCOUNT(start, end, iters)/1e6);
    786 }
    787 
    788 static void
    789 bench_ecdh_p256(void)
    790 {
    791  bench_ecdh_impl(NID_X9_62_prime256v1, "P-256");
    792 }
    793 
    794 static void
    795 bench_ecdh_p224(void)
    796 {
    797  bench_ecdh_impl(NID_secp224r1, "P-224");
    798 }
    799 #endif /* defined(ENABLE_OPENSSL) */
    800 
    801 static void
    802 bench_md_parse(void)
    803 {
    804  uint64_t start, end;
    805  const int N = 100000;
    806  // selected arbitrarily
    807  const char md_text[] =
    808    "@last-listed 2018-12-14 18:14:14\n"
    809    "onion-key\n"
    810    "-----BEGIN RSA PUBLIC KEY-----\n"
    811    "MIGJAoGBAMHkZeXNDX/49JqM2BVLmh1Fnb5iMVnatvZZTLJyedqDLkbXZ1WKP5oh\n"
    812    "7ec14dj/k3ntpwHD4s2o3Lb6nfagWbug4+F/rNJ7JuFru/PSyOvDyHGNAuegOXph\n"
    813    "3gTGjdDpv/yPoiadGebbVe8E7n6hO+XxM2W/4dqheKimF0/s9B7HAgMBAAE=\n"
    814    "-----END RSA PUBLIC KEY-----\n"
    815    "ntor-onion-key QgF/EjqlNG1wRHLIop/nCekEH+ETGZSgYOhu26eiTF4=\n"
    816    "family $00E9A86E7733240E60D8435A7BBD634A23894098 "
    817    "$329BD7545DEEEBBDC8C4285F243916F248972102 "
    818    "$69E06EBB2573A4F89330BDF8BC869794A3E10E4D "
    819    "$DCA2A3FAE50B3729DAA15BC95FB21AF03389818B\n"
    820    "p accept 53,80,443,5222-5223,25565\n"
    821    "id ed25519 BzffzY99z6Q8KltcFlUTLWjNTBU7yKK+uQhyi1Ivb3A\n";
    822 
    823  reset_perftime();
    824  start = perftime();
    825  for (int i = 0; i < N; ++i) {
    826    smartlist_t *s = microdescs_parse_from_string(md_text, NULL, 1,
    827                                                  SAVED_IN_CACHE, NULL);
    828    SMARTLIST_FOREACH(s, microdesc_t *, md, microdesc_free(md));
    829    smartlist_free(s);
    830  }
    831 
    832  end = perftime();
    833  printf("Microdesc parse: %f nsec\n", NANOCOUNT(start, end, N));
    834 }
    835 
    836 typedef void (*bench_fn)(void);
    837 
    838 typedef struct benchmark_t {
    839  const char *name;
    840  bench_fn fn;
    841  int enabled;
    842 } benchmark_t;
    843 
    844 #define ENT(s) { #s , bench_##s, 0 }
    845 
    846 static struct benchmark_t benchmarks[] = {
    847  ENT(dmap),
    848  ENT(siphash),
    849  ENT(digest),
    850  ENT(polyval),
    851  ENT(aes),
    852  ENT(onion_ntor),
    853  ENT(ed25519),
    854  ENT(rand),
    855 
    856  ENT(cell_aes),
    857  ENT(cell_ops_tor1),
    858  ENT(cell_ops_cgo),
    859  ENT(dh),
    860 
    861 #ifdef ENABLE_OPENSSL
    862  ENT(ecdh_p256),
    863  ENT(ecdh_p224),
    864 #endif
    865 
    866  ENT(md_parse),
    867  {NULL,NULL,0}
    868 };
    869 
    870 static benchmark_t *
    871 find_benchmark(const char *name)
    872 {
    873  benchmark_t *b;
    874  for (b = benchmarks; b->name; ++b) {
    875    if (!strcmp(name, b->name)) {
    876      return b;
    877    }
    878  }
    879  return NULL;
    880 }
    881 
    882 /** Main entry point for benchmark code: parse the command line, and run
    883 * some benchmarks. */
    884 int
    885 main(int argc, const char **argv)
    886 {
    887  int i;
    888  int list=0, n_enabled=0;
    889  char *errmsg;
    890  or_options_t *options;
    891 
    892  subsystems_init_upto(SUBSYS_LEVEL_LIBS);
    893  flush_log_messages_from_startup();
    894 
    895  tor_compress_init();
    896 
    897  if (argc == 4 && !strcmp(argv[1], "diff")) {
    898    const int N = 200;
    899    char *f1 = read_file_to_str(argv[2], RFTS_BIN, NULL);
    900    char *f2 = read_file_to_str(argv[3], RFTS_BIN, NULL);
    901    if (! f1 || ! f2) {
    902      perror("X");
    903      return 1;
    904    }
    905    size_t f1len = strlen(f1);
    906    size_t f2len = strlen(f2);
    907    for (i = 0; i < N; ++i) {
    908      char *diff = consensus_diff_generate(f1, f1len, f2, f2len);
    909      tor_free(diff);
    910    }
    911    char *diff = consensus_diff_generate(f1, f1len, f2, f2len);
    912    printf("%s", diff);
    913    tor_free(f1);
    914    tor_free(f2);
    915    tor_free(diff);
    916    return 0;
    917  }
    918 
    919  for (i = 1; i < argc; ++i) {
    920    if (!strcmp(argv[i], "--list")) {
    921      list = 1;
    922    } else {
    923      benchmark_t *benchmark = find_benchmark(argv[i]);
    924      ++n_enabled;
    925      if (benchmark) {
    926        benchmark->enabled = 1;
    927      } else {
    928        printf("No such benchmark as %s\n", argv[i]);
    929      }
    930    }
    931  }
    932 
    933  reset_perftime();
    934 
    935  if (crypto_global_init(0, NULL, NULL) < 0) {
    936    printf("Couldn't seed RNG; exiting.\n");
    937    return 1;
    938  }
    939 
    940  init_protocol_warning_severity_level();
    941  options = options_new();
    942  options->command = CMD_RUN_UNITTESTS;
    943  options->DataDirectory = tor_strdup("");
    944  options->KeyDirectory = tor_strdup("");
    945  options->CacheDirectory = tor_strdup("");
    946  options_init(options);
    947  if (set_options(options, &errmsg) < 0) {
    948    printf("Failed to set initial options: %s\n", errmsg);
    949    tor_free(errmsg);
    950    return 1;
    951  }
    952 
    953  for (benchmark_t *b = benchmarks; b->name; ++b) {
    954    if (b->enabled || n_enabled == 0) {
    955      printf("===== %s =====\n", b->name);
    956      if (!list)
    957        b->fn();
    958    }
    959  }
    960 
    961  return 0;
    962 }