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 }