Home | History | Annotate | Download | only in tool
      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