1 /* Copyright (c) 2014, Google Inc. 2 * 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 14 15 #include <algorithm> 16 #include <string> 17 #include <functional> 18 #include <memory> 19 #include <vector> 20 21 #include <assert.h> 22 #include <errno.h> 23 #include <stdint.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 #include <openssl/aead.h> 28 #include <openssl/bn.h> 29 #include <openssl/curve25519.h> 30 #include <openssl/digest.h> 31 #include <openssl/err.h> 32 #include <openssl/ec.h> 33 #include <openssl/ecdsa.h> 34 #include <openssl/ec_key.h> 35 #include <openssl/evp.h> 36 #include <openssl/hrss.h> 37 #include <openssl/nid.h> 38 #include <openssl/rand.h> 39 #include <openssl/rsa.h> 40 41 #if defined(OPENSSL_WINDOWS) 42 OPENSSL_MSVC_PRAGMA(warning(push, 3)) 43 #include <windows.h> 44 OPENSSL_MSVC_PRAGMA(warning(pop)) 45 #elif defined(OPENSSL_APPLE) 46 #include <sys/time.h> 47 #else 48 #include <time.h> 49 #endif 50 51 #include "../crypto/internal.h" 52 #include "internal.h" 53 54 55 // TimeResults represents the results of benchmarking a function. 56 struct TimeResults { 57 // num_calls is the number of function calls done in the time period. 58 unsigned num_calls; 59 // us is the number of microseconds that elapsed in the time period. 60 unsigned us; 61 62 void Print(const std::string &description) { 63 printf("Did %u %s operations in %uus (%.1f ops/sec)\n", num_calls, 64 description.c_str(), us, 65 (static_cast<double>(num_calls) / us) * 1000000); 66 } 67 68 void PrintWithBytes(const std::string &description, size_t bytes_per_call) { 69 printf("Did %u %s operations in %uus (%.1f ops/sec): %.1f MB/s\n", 70 num_calls, description.c_str(), us, 71 (static_cast<double>(num_calls) / us) * 1000000, 72 static_cast<double>(bytes_per_call * num_calls) / us); 73 } 74 }; 75 76 #if defined(OPENSSL_WINDOWS) 77 static uint64_t time_now() { return GetTickCount64() * 1000; } 78 #elif defined(OPENSSL_APPLE) 79 static uint64_t time_now() { 80 struct timeval tv; 81 uint64_t ret; 82 83 gettimeofday(&tv, NULL); 84 ret = tv.tv_sec; 85 ret *= 1000000; 86 ret += tv.tv_usec; 87 return ret; 88 } 89 #else 90 static uint64_t time_now() { 91 struct timespec ts; 92 clock_gettime(CLOCK_MONOTONIC, &ts); 93 94 uint64_t ret = ts.tv_sec; 95 ret *= 1000000; 96 ret += ts.tv_nsec / 1000; 97 return ret; 98 } 99 #endif 100 101 static uint64_t g_timeout_seconds = 1; 102 static std::vector<size_t> g_chunk_lengths = {16, 256, 1350, 8192, 16384}; 103 104 static bool TimeFunction(TimeResults *results, std::function<bool()> func) { 105 // total_us is the total amount of time that we'll aim to measure a function 106 // for. 107 const uint64_t total_us = g_timeout_seconds * 1000000; 108 uint64_t start = time_now(), now, delta; 109 unsigned done = 0, iterations_between_time_checks; 110 111 if (!func()) { 112 return false; 113 } 114 now = time_now(); 115 delta = now - start; 116 if (delta == 0) { 117 iterations_between_time_checks = 250; 118 } else { 119 // Aim for about 100ms between time checks. 120 iterations_between_time_checks = 121 static_cast<double>(100000) / static_cast<double>(delta); 122 if (iterations_between_time_checks > 1000) { 123 iterations_between_time_checks = 1000; 124 } else if (iterations_between_time_checks < 1) { 125 iterations_between_time_checks = 1; 126 } 127 } 128 129 for (;;) { 130 for (unsigned i = 0; i < iterations_between_time_checks; i++) { 131 if (!func()) { 132 return false; 133 } 134 done++; 135 } 136 137 now = time_now(); 138 if (now - start > total_us) { 139 break; 140 } 141 } 142 143 results->us = now - start; 144 results->num_calls = done; 145 return true; 146 } 147 148 static bool SpeedRSA(const std::string &selected) { 149 if (!selected.empty() && selected.find("RSA") == std::string::npos) { 150 return true; 151 } 152 153 static const struct { 154 const char *name; 155 const uint8_t *key; 156 const size_t key_len; 157 } kRSAKeys[] = { 158 {"RSA 2048", kDERRSAPrivate2048, kDERRSAPrivate2048Len}, 159 {"RSA 4096", kDERRSAPrivate4096, kDERRSAPrivate4096Len}, 160 }; 161 162 for (unsigned i = 0; i < OPENSSL_ARRAY_SIZE(kRSAKeys); i++) { 163 const std::string name = kRSAKeys[i].name; 164 165 bssl::UniquePtr<RSA> key( 166 RSA_private_key_from_bytes(kRSAKeys[i].key, kRSAKeys[i].key_len)); 167 if (key == nullptr) { 168 fprintf(stderr, "Failed to parse %s key.\n", name.c_str()); 169 ERR_print_errors_fp(stderr); 170 return false; 171 } 172 173 std::unique_ptr<uint8_t[]> sig(new uint8_t[RSA_size(key.get())]); 174 const uint8_t fake_sha256_hash[32] = {0}; 175 unsigned sig_len; 176 177 TimeResults results; 178 if (!TimeFunction(&results, 179 [&key, &sig, &fake_sha256_hash, &sig_len]() -> bool { 180 // Usually during RSA signing we're using a long-lived |RSA| that has 181 // already had all of its |BN_MONT_CTX|s constructed, so it makes 182 // sense to use |key| directly here. 183 return RSA_sign(NID_sha256, fake_sha256_hash, sizeof(fake_sha256_hash), 184 sig.get(), &sig_len, key.get()); 185 })) { 186 fprintf(stderr, "RSA_sign failed.\n"); 187 ERR_print_errors_fp(stderr); 188 return false; 189 } 190 results.Print(name + " signing"); 191 192 if (!TimeFunction(&results, 193 [&key, &fake_sha256_hash, &sig, sig_len]() -> bool { 194 return RSA_verify( 195 NID_sha256, fake_sha256_hash, sizeof(fake_sha256_hash), 196 sig.get(), sig_len, key.get()); 197 })) { 198 fprintf(stderr, "RSA_verify failed.\n"); 199 ERR_print_errors_fp(stderr); 200 return false; 201 } 202 results.Print(name + " verify (same key)"); 203 204 if (!TimeFunction(&results, 205 [&key, &fake_sha256_hash, &sig, sig_len]() -> bool { 206 // Usually during RSA verification we have to parse an RSA key from a 207 // certificate or similar, in which case we'd need to construct a new 208 // RSA key, with a new |BN_MONT_CTX| for the public modulus. If we 209 // were to use |key| directly instead, then these costs wouldn't be 210 // accounted for. 211 bssl::UniquePtr<RSA> verify_key(RSA_new()); 212 if (!verify_key) { 213 return false; 214 } 215 verify_key->n = BN_dup(key->n); 216 verify_key->e = BN_dup(key->e); 217 if (!verify_key->n || 218 !verify_key->e) { 219 return false; 220 } 221 return RSA_verify(NID_sha256, fake_sha256_hash, 222 sizeof(fake_sha256_hash), sig.get(), sig_len, 223 verify_key.get()); 224 })) { 225 fprintf(stderr, "RSA_verify failed.\n"); 226 ERR_print_errors_fp(stderr); 227 return false; 228 } 229 results.Print(name + " verify (fresh key)"); 230 } 231 232 return true; 233 } 234 235 static bool SpeedRSAKeyGen(const std::string &selected) { 236 // Don't run this by default because it's so slow. 237 if (selected != "RSAKeyGen") { 238 return true; 239 } 240 241 bssl::UniquePtr<BIGNUM> e(BN_new()); 242 if (!BN_set_word(e.get(), 65537)) { 243 return false; 244 } 245 246 const std::vector<int> kSizes = {2048, 3072, 4096}; 247 for (int size : kSizes) { 248 const uint64_t start = time_now(); 249 unsigned num_calls = 0; 250 unsigned us; 251 std::vector<unsigned> durations; 252 253 for (;;) { 254 bssl::UniquePtr<RSA> rsa(RSA_new()); 255 256 const uint64_t iteration_start = time_now(); 257 if (!RSA_generate_key_ex(rsa.get(), size, e.get(), nullptr)) { 258 fprintf(stderr, "RSA_generate_key_ex failed.\n"); 259 ERR_print_errors_fp(stderr); 260 return false; 261 } 262 const uint64_t iteration_end = time_now(); 263 264 num_calls++; 265 durations.push_back(iteration_end - iteration_start); 266 267 us = iteration_end - start; 268 if (us > 30 * 1000000 /* 30 secs */) { 269 break; 270 } 271 } 272 273 std::sort(durations.begin(), durations.end()); 274 printf("Did %u RSA %d key-gen operations in %uus (%.1f ops/sec)\n", 275 num_calls, size, us, 276 (static_cast<double>(num_calls) / us) * 1000000); 277 const size_t n = durations.size(); 278 assert(n > 0); 279 280 // |min| and |max| must be stored in temporary variables to avoid an MSVC 281 // bug on x86. There, size_t is a typedef for unsigned, but MSVC's printf 282 // warning tries to retain the distinction and suggest %zu for size_t 283 // instead of %u. It gets confused if std::vector<unsigned> and 284 // std::vector<size_t> are both instantiated. Being typedefs, the two 285 // instantiations are identical, which somehow breaks the size_t vs unsigned 286 // metadata. 287 unsigned min = durations[0]; 288 unsigned median = n & 1 ? durations[n / 2] 289 : (durations[n / 2 - 1] + durations[n / 2]) / 2; 290 unsigned max = durations[n - 1]; 291 printf(" min: %uus, median: %uus, max: %uus\n", min, median, max); 292 } 293 294 return true; 295 } 296 297 static uint8_t *align(uint8_t *in, unsigned alignment) { 298 return reinterpret_cast<uint8_t *>( 299 (reinterpret_cast<uintptr_t>(in) + alignment) & 300 ~static_cast<size_t>(alignment - 1)); 301 } 302 303 static std::string ChunkLenSuffix(size_t chunk_len) { 304 char buf[32]; 305 snprintf(buf, sizeof(buf), " (%zu byte%s)", chunk_len, 306 chunk_len != 1 ? "s" : ""); 307 return buf; 308 } 309 310 static bool SpeedAEADChunk(const EVP_AEAD *aead, std::string name, 311 size_t chunk_len, size_t ad_len, 312 evp_aead_direction_t direction) { 313 static const unsigned kAlignment = 16; 314 315 name += ChunkLenSuffix(chunk_len); 316 bssl::ScopedEVP_AEAD_CTX ctx; 317 const size_t key_len = EVP_AEAD_key_length(aead); 318 const size_t nonce_len = EVP_AEAD_nonce_length(aead); 319 const size_t overhead_len = EVP_AEAD_max_overhead(aead); 320 321 std::unique_ptr<uint8_t[]> key(new uint8_t[key_len]); 322 OPENSSL_memset(key.get(), 0, key_len); 323 std::unique_ptr<uint8_t[]> nonce(new uint8_t[nonce_len]); 324 OPENSSL_memset(nonce.get(), 0, nonce_len); 325 std::unique_ptr<uint8_t[]> in_storage(new uint8_t[chunk_len + kAlignment]); 326 // N.B. for EVP_AEAD_CTX_seal_scatter the input and output buffers may be the 327 // same size. However, in the direction == evp_aead_open case we still use 328 // non-scattering seal, hence we add overhead_len to the size of this buffer. 329 std::unique_ptr<uint8_t[]> out_storage( 330 new uint8_t[chunk_len + overhead_len + kAlignment]); 331 std::unique_ptr<uint8_t[]> in2_storage( 332 new uint8_t[chunk_len + overhead_len + kAlignment]); 333 std::unique_ptr<uint8_t[]> ad(new uint8_t[ad_len]); 334 OPENSSL_memset(ad.get(), 0, ad_len); 335 std::unique_ptr<uint8_t[]> tag_storage( 336 new uint8_t[overhead_len + kAlignment]); 337 338 339 uint8_t *const in = align(in_storage.get(), kAlignment); 340 OPENSSL_memset(in, 0, chunk_len); 341 uint8_t *const out = align(out_storage.get(), kAlignment); 342 OPENSSL_memset(out, 0, chunk_len + overhead_len); 343 uint8_t *const tag = align(tag_storage.get(), kAlignment); 344 OPENSSL_memset(tag, 0, overhead_len); 345 uint8_t *const in2 = align(in2_storage.get(), kAlignment); 346 347 if (!EVP_AEAD_CTX_init_with_direction(ctx.get(), aead, key.get(), key_len, 348 EVP_AEAD_DEFAULT_TAG_LENGTH, 349 evp_aead_seal)) { 350 fprintf(stderr, "Failed to create EVP_AEAD_CTX.\n"); 351 ERR_print_errors_fp(stderr); 352 return false; 353 } 354 355 TimeResults results; 356 if (direction == evp_aead_seal) { 357 if (!TimeFunction(&results, 358 [chunk_len, nonce_len, ad_len, overhead_len, in, out, tag, 359 &ctx, &nonce, &ad]() -> bool { 360 size_t tag_len; 361 return EVP_AEAD_CTX_seal_scatter( 362 ctx.get(), out, tag, &tag_len, overhead_len, 363 nonce.get(), nonce_len, in, chunk_len, nullptr, 0, 364 ad.get(), ad_len); 365 })) { 366 fprintf(stderr, "EVP_AEAD_CTX_seal failed.\n"); 367 ERR_print_errors_fp(stderr); 368 return false; 369 } 370 } else { 371 size_t out_len; 372 EVP_AEAD_CTX_seal(ctx.get(), out, &out_len, chunk_len + overhead_len, 373 nonce.get(), nonce_len, in, chunk_len, ad.get(), ad_len); 374 375 ctx.Reset(); 376 if (!EVP_AEAD_CTX_init_with_direction(ctx.get(), aead, key.get(), key_len, 377 EVP_AEAD_DEFAULT_TAG_LENGTH, 378 evp_aead_open)) { 379 fprintf(stderr, "Failed to create EVP_AEAD_CTX.\n"); 380 ERR_print_errors_fp(stderr); 381 return false; 382 } 383 384 if (!TimeFunction(&results, 385 [chunk_len, overhead_len, nonce_len, ad_len, in2, out, 386 out_len, &ctx, &nonce, &ad]() -> bool { 387 size_t in2_len; 388 // N.B. EVP_AEAD_CTX_open_gather is not implemented for 389 // all AEADs. 390 return EVP_AEAD_CTX_open(ctx.get(), in2, &in2_len, 391 chunk_len + overhead_len, 392 nonce.get(), nonce_len, out, 393 out_len, ad.get(), ad_len); 394 })) { 395 fprintf(stderr, "EVP_AEAD_CTX_open failed.\n"); 396 ERR_print_errors_fp(stderr); 397 return false; 398 } 399 } 400 401 results.PrintWithBytes( 402 name + (direction == evp_aead_seal ? " seal" : " open"), chunk_len); 403 return true; 404 } 405 406 static bool SpeedAEAD(const EVP_AEAD *aead, const std::string &name, 407 size_t ad_len, const std::string &selected) { 408 if (!selected.empty() && name.find(selected) == std::string::npos) { 409 return true; 410 } 411 412 for (size_t chunk_len : g_chunk_lengths) { 413 if (!SpeedAEADChunk(aead, name, chunk_len, ad_len, evp_aead_seal)) { 414 return false; 415 } 416 } 417 return true; 418 } 419 420 static bool SpeedAEADOpen(const EVP_AEAD *aead, const std::string &name, 421 size_t ad_len, const std::string &selected) { 422 if (!selected.empty() && name.find(selected) == std::string::npos) { 423 return true; 424 } 425 426 for (size_t chunk_len : g_chunk_lengths) { 427 if (!SpeedAEADChunk(aead, name, chunk_len, ad_len, evp_aead_open)) { 428 return false; 429 } 430 } 431 432 return true; 433 } 434 435 static bool SpeedHashChunk(const EVP_MD *md, std::string name, 436 size_t chunk_len) { 437 bssl::ScopedEVP_MD_CTX ctx; 438 uint8_t scratch[8192]; 439 440 if (chunk_len > sizeof(scratch)) { 441 return false; 442 } 443 444 name += ChunkLenSuffix(chunk_len); 445 TimeResults results; 446 if (!TimeFunction(&results, [&ctx, md, chunk_len, &scratch]() -> bool { 447 uint8_t digest[EVP_MAX_MD_SIZE]; 448 unsigned int md_len; 449 450 return EVP_DigestInit_ex(ctx.get(), md, NULL /* ENGINE */) && 451 EVP_DigestUpdate(ctx.get(), scratch, chunk_len) && 452 EVP_DigestFinal_ex(ctx.get(), digest, &md_len); 453 })) { 454 fprintf(stderr, "EVP_DigestInit_ex failed.\n"); 455 ERR_print_errors_fp(stderr); 456 return false; 457 } 458 459 results.PrintWithBytes(name, chunk_len); 460 return true; 461 } 462 463 static bool SpeedHash(const EVP_MD *md, const std::string &name, 464 const std::string &selected) { 465 if (!selected.empty() && name.find(selected) == std::string::npos) { 466 return true; 467 } 468 469 for (size_t chunk_len : g_chunk_lengths) { 470 if (!SpeedHashChunk(md, name, chunk_len)) { 471 return false; 472 } 473 } 474 475 return true; 476 } 477 478 static bool SpeedRandomChunk(std::string name, size_t chunk_len) { 479 uint8_t scratch[8192]; 480 481 if (chunk_len > sizeof(scratch)) { 482 return false; 483 } 484 485 name += ChunkLenSuffix(chunk_len); 486 TimeResults results; 487 if (!TimeFunction(&results, [chunk_len, &scratch]() -> bool { 488 RAND_bytes(scratch, chunk_len); 489 return true; 490 })) { 491 return false; 492 } 493 494 results.PrintWithBytes(name, chunk_len); 495 return true; 496 } 497 498 static bool SpeedRandom(const std::string &selected) { 499 if (!selected.empty() && selected != "RNG") { 500 return true; 501 } 502 503 for (size_t chunk_len : g_chunk_lengths) { 504 if (!SpeedRandomChunk("RNG", chunk_len)) { 505 return false; 506 } 507 } 508 509 return true; 510 } 511 512 static bool SpeedECDHCurve(const std::string &name, int nid, 513 const std::string &selected) { 514 if (!selected.empty() && name.find(selected) == std::string::npos) { 515 return true; 516 } 517 518 bssl::UniquePtr<EC_KEY> peer_key(EC_KEY_new_by_curve_name(nid)); 519 if (!peer_key || 520 !EC_KEY_generate_key(peer_key.get())) { 521 return false; 522 } 523 524 size_t peer_value_len = EC_POINT_point2oct( 525 EC_KEY_get0_group(peer_key.get()), EC_KEY_get0_public_key(peer_key.get()), 526 POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr); 527 if (peer_value_len == 0) { 528 return false; 529 } 530 std::unique_ptr<uint8_t[]> peer_value(new uint8_t[peer_value_len]); 531 peer_value_len = EC_POINT_point2oct( 532 EC_KEY_get0_group(peer_key.get()), EC_KEY_get0_public_key(peer_key.get()), 533 POINT_CONVERSION_UNCOMPRESSED, peer_value.get(), peer_value_len, nullptr); 534 if (peer_value_len == 0) { 535 return false; 536 } 537 538 TimeResults results; 539 if (!TimeFunction(&results, [nid, peer_value_len, &peer_value]() -> bool { 540 bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid)); 541 if (!key || 542 !EC_KEY_generate_key(key.get())) { 543 return false; 544 } 545 const EC_GROUP *const group = EC_KEY_get0_group(key.get()); 546 bssl::UniquePtr<EC_POINT> point(EC_POINT_new(group)); 547 bssl::UniquePtr<EC_POINT> peer_point(EC_POINT_new(group)); 548 bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new()); 549 550 bssl::UniquePtr<BIGNUM> x(BN_new()); 551 bssl::UniquePtr<BIGNUM> y(BN_new()); 552 553 if (!point || !peer_point || !ctx || !x || !y || 554 !EC_POINT_oct2point(group, peer_point.get(), peer_value.get(), 555 peer_value_len, ctx.get()) || 556 !EC_POINT_mul(group, point.get(), NULL, peer_point.get(), 557 EC_KEY_get0_private_key(key.get()), ctx.get()) || 558 !EC_POINT_get_affine_coordinates_GFp(group, point.get(), x.get(), 559 y.get(), ctx.get())) { 560 return false; 561 } 562 563 return true; 564 })) { 565 return false; 566 } 567 568 results.Print(name); 569 return true; 570 } 571 572 static bool SpeedECDSACurve(const std::string &name, int nid, 573 const std::string &selected) { 574 if (!selected.empty() && name.find(selected) == std::string::npos) { 575 return true; 576 } 577 578 bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid)); 579 if (!key || 580 !EC_KEY_generate_key(key.get())) { 581 return false; 582 } 583 584 uint8_t signature[256]; 585 if (ECDSA_size(key.get()) > sizeof(signature)) { 586 return false; 587 } 588 uint8_t digest[20]; 589 OPENSSL_memset(digest, 42, sizeof(digest)); 590 unsigned sig_len; 591 592 TimeResults results; 593 if (!TimeFunction(&results, [&key, &signature, &digest, &sig_len]() -> bool { 594 return ECDSA_sign(0, digest, sizeof(digest), signature, &sig_len, 595 key.get()) == 1; 596 })) { 597 return false; 598 } 599 600 results.Print(name + " signing"); 601 602 if (!TimeFunction(&results, [&key, &signature, &digest, sig_len]() -> bool { 603 return ECDSA_verify(0, digest, sizeof(digest), signature, sig_len, 604 key.get()) == 1; 605 })) { 606 return false; 607 } 608 609 results.Print(name + " verify"); 610 611 return true; 612 } 613 614 static bool SpeedECDH(const std::string &selected) { 615 return SpeedECDHCurve("ECDH P-224", NID_secp224r1, selected) && 616 SpeedECDHCurve("ECDH P-256", NID_X9_62_prime256v1, selected) && 617 SpeedECDHCurve("ECDH P-384", NID_secp384r1, selected) && 618 SpeedECDHCurve("ECDH P-521", NID_secp521r1, selected); 619 } 620 621 static bool SpeedECDSA(const std::string &selected) { 622 return SpeedECDSACurve("ECDSA P-224", NID_secp224r1, selected) && 623 SpeedECDSACurve("ECDSA P-256", NID_X9_62_prime256v1, selected) && 624 SpeedECDSACurve("ECDSA P-384", NID_secp384r1, selected) && 625 SpeedECDSACurve("ECDSA P-521", NID_secp521r1, selected); 626 } 627 628 static bool Speed25519(const std::string &selected) { 629 if (!selected.empty() && selected.find("25519") == std::string::npos) { 630 return true; 631 } 632 633 TimeResults results; 634 635 uint8_t public_key[32], private_key[64]; 636 637 if (!TimeFunction(&results, [&public_key, &private_key]() -> bool { 638 ED25519_keypair(public_key, private_key); 639 return true; 640 })) { 641 return false; 642 } 643 644 results.Print("Ed25519 key generation"); 645 646 static const uint8_t kMessage[] = {0, 1, 2, 3, 4, 5}; 647 uint8_t signature[64]; 648 649 if (!TimeFunction(&results, [&private_key, &signature]() -> bool { 650 return ED25519_sign(signature, kMessage, sizeof(kMessage), 651 private_key) == 1; 652 })) { 653 return false; 654 } 655 656 results.Print("Ed25519 signing"); 657 658 if (!TimeFunction(&results, [&public_key, &signature]() -> bool { 659 return ED25519_verify(kMessage, sizeof(kMessage), signature, 660 public_key) == 1; 661 })) { 662 fprintf(stderr, "Ed25519 verify failed.\n"); 663 return false; 664 } 665 666 results.Print("Ed25519 verify"); 667 668 if (!TimeFunction(&results, []() -> bool { 669 uint8_t out[32], in[32]; 670 OPENSSL_memset(in, 0, sizeof(in)); 671 X25519_public_from_private(out, in); 672 return true; 673 })) { 674 fprintf(stderr, "Curve25519 base-point multiplication failed.\n"); 675 return false; 676 } 677 678 results.Print("Curve25519 base-point multiplication"); 679 680 if (!TimeFunction(&results, []() -> bool { 681 uint8_t out[32], in1[32], in2[32]; 682 OPENSSL_memset(in1, 0, sizeof(in1)); 683 OPENSSL_memset(in2, 0, sizeof(in2)); 684 in1[0] = 1; 685 in2[0] = 9; 686 return X25519(out, in1, in2) == 1; 687 })) { 688 fprintf(stderr, "Curve25519 arbitrary point multiplication failed.\n"); 689 return false; 690 } 691 692 results.Print("Curve25519 arbitrary point multiplication"); 693 694 return true; 695 } 696 697 static bool SpeedSPAKE2(const std::string &selected) { 698 if (!selected.empty() && selected.find("SPAKE2") == std::string::npos) { 699 return true; 700 } 701 702 TimeResults results; 703 704 static const uint8_t kAliceName[] = {'A'}; 705 static const uint8_t kBobName[] = {'B'}; 706 static const uint8_t kPassword[] = "password"; 707 bssl::UniquePtr<SPAKE2_CTX> alice(SPAKE2_CTX_new(spake2_role_alice, 708 kAliceName, sizeof(kAliceName), kBobName, 709 sizeof(kBobName))); 710 uint8_t alice_msg[SPAKE2_MAX_MSG_SIZE]; 711 size_t alice_msg_len; 712 713 if (!SPAKE2_generate_msg(alice.get(), alice_msg, &alice_msg_len, 714 sizeof(alice_msg), 715 kPassword, sizeof(kPassword))) { 716 fprintf(stderr, "SPAKE2_generate_msg failed.\n"); 717 return false; 718 } 719 720 if (!TimeFunction(&results, [&alice_msg, alice_msg_len]() -> bool { 721 bssl::UniquePtr<SPAKE2_CTX> bob(SPAKE2_CTX_new(spake2_role_bob, 722 kBobName, sizeof(kBobName), kAliceName, 723 sizeof(kAliceName))); 724 uint8_t bob_msg[SPAKE2_MAX_MSG_SIZE], bob_key[64]; 725 size_t bob_msg_len, bob_key_len; 726 if (!SPAKE2_generate_msg(bob.get(), bob_msg, &bob_msg_len, 727 sizeof(bob_msg), kPassword, 728 sizeof(kPassword)) || 729 !SPAKE2_process_msg(bob.get(), bob_key, &bob_key_len, 730 sizeof(bob_key), alice_msg, alice_msg_len)) { 731 return false; 732 } 733 734 return true; 735 })) { 736 fprintf(stderr, "SPAKE2 failed.\n"); 737 } 738 739 results.Print("SPAKE2 over Ed25519"); 740 741 return true; 742 } 743 744 static bool SpeedScrypt(const std::string &selected) { 745 if (!selected.empty() && selected.find("scrypt") == std::string::npos) { 746 return true; 747 } 748 749 TimeResults results; 750 751 static const char kPassword[] = "password"; 752 static const uint8_t kSalt[] = "NaCl"; 753 754 if (!TimeFunction(&results, [&]() -> bool { 755 uint8_t out[64]; 756 return !!EVP_PBE_scrypt(kPassword, sizeof(kPassword) - 1, kSalt, 757 sizeof(kSalt) - 1, 1024, 8, 16, 0 /* max_mem */, 758 out, sizeof(out)); 759 })) { 760 fprintf(stderr, "scrypt failed.\n"); 761 return false; 762 } 763 results.Print("scrypt (N = 1024, r = 8, p = 16)"); 764 765 if (!TimeFunction(&results, [&]() -> bool { 766 uint8_t out[64]; 767 return !!EVP_PBE_scrypt(kPassword, sizeof(kPassword) - 1, kSalt, 768 sizeof(kSalt) - 1, 16384, 8, 1, 0 /* max_mem */, 769 out, sizeof(out)); 770 })) { 771 fprintf(stderr, "scrypt failed.\n"); 772 return false; 773 } 774 results.Print("scrypt (N = 16384, r = 8, p = 1)"); 775 776 return true; 777 } 778 779 static bool SpeedHRSS(const std::string &selected) { 780 if (!selected.empty() && selected != "HRSS") { 781 return true; 782 } 783 784 TimeResults results; 785 786 if (!TimeFunction(&results, []() -> bool { 787 struct HRSS_public_key pub; 788 struct HRSS_private_key priv; 789 uint8_t entropy[HRSS_GENERATE_KEY_BYTES]; 790 RAND_bytes(entropy, sizeof(entropy)); 791 HRSS_generate_key(&pub, &priv, entropy); 792 return true; 793 })) { 794 fprintf(stderr, "Failed to time HRSS_generate_key.\n"); 795 return false; 796 } 797 798 results.Print("HRSS generate"); 799 800 struct HRSS_public_key pub; 801 struct HRSS_private_key priv; 802 uint8_t key_entropy[HRSS_GENERATE_KEY_BYTES]; 803 RAND_bytes(key_entropy, sizeof(key_entropy)); 804 HRSS_generate_key(&pub, &priv, key_entropy); 805 806 uint8_t ciphertext[HRSS_CIPHERTEXT_BYTES]; 807 if (!TimeFunction(&results, [&pub, &ciphertext]() -> bool { 808 uint8_t entropy[HRSS_ENCAP_BYTES]; 809 uint8_t shared_key[HRSS_KEY_BYTES]; 810 RAND_bytes(entropy, sizeof(entropy)); 811 HRSS_encap(ciphertext, shared_key, &pub, entropy); 812 return true; 813 })) { 814 fprintf(stderr, "Failed to time HRSS_encap.\n"); 815 return false; 816 } 817 818 results.Print("HRSS encap"); 819 820 if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool { 821 uint8_t shared_key[HRSS_KEY_BYTES]; 822 HRSS_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)); 823 return true; 824 })) { 825 fprintf(stderr, "Failed to time HRSS_encap.\n"); 826 return false; 827 } 828 829 results.Print("HRSS decap"); 830 831 return true; 832 } 833 834 static const struct argument kArguments[] = { 835 { 836 "-filter", 837 kOptionalArgument, 838 "A filter on the speed tests to run", 839 }, 840 { 841 "-timeout", 842 kOptionalArgument, 843 "The number of seconds to run each test for (default is 1)", 844 }, 845 { 846 "-chunks", 847 kOptionalArgument, 848 "A comma-separated list of input sizes to run tests at (default is " 849 "16,256,1350,8192,16384)", 850 }, 851 { 852 "", 853 kOptionalArgument, 854 "", 855 }, 856 }; 857 858 bool Speed(const std::vector<std::string> &args) { 859 std::map<std::string, std::string> args_map; 860 if (!ParseKeyValueArguments(&args_map, args, kArguments)) { 861 PrintUsage(kArguments); 862 return false; 863 } 864 865 std::string selected; 866 if (args_map.count("-filter") != 0) { 867 selected = args_map["-filter"]; 868 } 869 870 if (args_map.count("-timeout") != 0) { 871 g_timeout_seconds = atoi(args_map["-timeout"].c_str()); 872 } 873 874 if (args_map.count("-chunks") != 0) { 875 g_chunk_lengths.clear(); 876 const char *start = args_map["-chunks"].data(); 877 const char *end = start + args_map["-chunks"].size(); 878 while (start != end) { 879 errno = 0; 880 char *ptr; 881 unsigned long long val = strtoull(start, &ptr, 10); 882 if (ptr == start /* no numeric characters found */ || 883 errno == ERANGE /* overflow */ || 884 static_cast<size_t>(val) != val) { 885 fprintf(stderr, "Error parsing -chunks argument\n"); 886 return false; 887 } 888 g_chunk_lengths.push_back(static_cast<size_t>(val)); 889 start = ptr; 890 if (start != end) { 891 if (*start != ',') { 892 fprintf(stderr, "Error parsing -chunks argument\n"); 893 return false; 894 } 895 start++; 896 } 897 } 898 } 899 900 // kTLSADLen is the number of bytes of additional data that TLS passes to 901 // AEADs. 902 static const size_t kTLSADLen = 13; 903 // kLegacyADLen is the number of bytes that TLS passes to the "legacy" AEADs. 904 // These are AEADs that weren't originally defined as AEADs, but which we use 905 // via the AEAD interface. In order for that to work, they have some TLS 906 // knowledge in them and construct a couple of the AD bytes internally. 907 static const size_t kLegacyADLen = kTLSADLen - 2; 908 909 if (!SpeedRSA(selected) || 910 !SpeedAEAD(EVP_aead_aes_128_gcm(), "AES-128-GCM", kTLSADLen, selected) || 911 !SpeedAEAD(EVP_aead_aes_256_gcm(), "AES-256-GCM", kTLSADLen, selected) || 912 !SpeedAEAD(EVP_aead_chacha20_poly1305(), "ChaCha20-Poly1305", kTLSADLen, 913 selected) || 914 !SpeedAEAD(EVP_aead_des_ede3_cbc_sha1_tls(), "DES-EDE3-CBC-SHA1", 915 kLegacyADLen, selected) || 916 !SpeedAEAD(EVP_aead_aes_128_cbc_sha1_tls(), "AES-128-CBC-SHA1", 917 kLegacyADLen, selected) || 918 !SpeedAEAD(EVP_aead_aes_256_cbc_sha1_tls(), "AES-256-CBC-SHA1", 919 kLegacyADLen, selected) || 920 !SpeedAEADOpen(EVP_aead_aes_128_cbc_sha1_tls(), "AES-128-CBC-SHA1", 921 kLegacyADLen, selected) || 922 !SpeedAEADOpen(EVP_aead_aes_256_cbc_sha1_tls(), "AES-256-CBC-SHA1", 923 kLegacyADLen, selected) || 924 !SpeedAEAD(EVP_aead_aes_128_gcm_siv(), "AES-128-GCM-SIV", kTLSADLen, 925 selected) || 926 !SpeedAEAD(EVP_aead_aes_256_gcm_siv(), "AES-256-GCM-SIV", kTLSADLen, 927 selected) || 928 !SpeedAEADOpen(EVP_aead_aes_128_gcm_siv(), "AES-128-GCM-SIV", kTLSADLen, 929 selected) || 930 !SpeedAEADOpen(EVP_aead_aes_256_gcm_siv(), "AES-256-GCM-SIV", kTLSADLen, 931 selected) || 932 !SpeedAEAD(EVP_aead_aes_128_ccm_bluetooth(), "AES-128-CCM-Bluetooth", 933 kTLSADLen, selected) || 934 !SpeedHash(EVP_sha1(), "SHA-1", selected) || 935 !SpeedHash(EVP_sha256(), "SHA-256", selected) || 936 !SpeedHash(EVP_sha512(), "SHA-512", selected) || 937 !SpeedRandom(selected) || 938 !SpeedECDH(selected) || 939 !SpeedECDSA(selected) || 940 !Speed25519(selected) || 941 !SpeedSPAKE2(selected) || 942 !SpeedScrypt(selected) || 943 !SpeedRSAKeyGen(selected) || 944 !SpeedHRSS(selected)) { 945 return false; 946 } 947 948 return true; 949 } 950