• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2014 The BoringSSL Authors
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 <functional>
17 #include <memory>
18 #include <string>
19 #include <vector>
20 
21 #include <assert.h>
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <openssl/aead.h>
29 #include <openssl/aes.h>
30 #include <openssl/base64.h>
31 #include <openssl/bn.h>
32 #include <openssl/bytestring.h>
33 #include <openssl/crypto.h>
34 #include <openssl/curve25519.h>
35 #include <openssl/digest.h>
36 #include <openssl/ec.h>
37 #include <openssl/ec_key.h>
38 #include <openssl/ecdsa.h>
39 #include <openssl/err.h>
40 #include <openssl/evp.h>
41 #define OPENSSL_UNSTABLE_EXPERIMENTAL_KYBER
42 #include <openssl/experimental/kyber.h>
43 #include <openssl/hrss.h>
44 #include <openssl/mem.h>
45 #include <openssl/mldsa.h>
46 #include <openssl/mlkem.h>
47 #include <openssl/nid.h>
48 #include <openssl/rand.h>
49 #include <openssl/rsa.h>
50 #include <openssl/siphash.h>
51 #include <openssl/slhdsa.h>
52 #include <openssl/trust_token.h>
53 
54 #if defined(OPENSSL_WINDOWS)
55 OPENSSL_MSVC_PRAGMA(warning(push, 3))
56 #include <windows.h>
57 OPENSSL_MSVC_PRAGMA(warning(pop))
58 #elif defined(OPENSSL_APPLE)
59 #include <sys/time.h>
60 #else
61 #include <time.h>
62 #endif
63 
64 #if defined(OPENSSL_THREADS)
65 #include <condition_variable>
66 #include <mutex>
67 #include <thread>
68 #endif
69 
70 #include "../crypto/ec_extra/internal.h"
71 #include "../crypto/fipsmodule/ec/internal.h"
72 #include "../crypto/internal.h"
73 #include "../crypto/trust_token/internal.h"
74 #include "internal.h"
75 
76 // g_print_json is true if printed output is JSON formatted.
77 static bool g_print_json = false;
78 
79 // TimeResults represents the results of benchmarking a function.
80 struct TimeResults {
81   // num_calls is the number of function calls done in the time period.
82   uint64_t num_calls;
83   // us is the number of microseconds that elapsed in the time period.
84   uint64_t us;
85 
PrintTimeResults86   void Print(const std::string &description) const {
87     if (g_print_json) {
88       PrintJSON(description);
89     } else {
90       printf(
91           "Did %" PRIu64 " %s operations in %" PRIu64 "us (%.1f ops/sec)\n",
92           num_calls, description.c_str(), us,
93           (static_cast<double>(num_calls) / static_cast<double>(us)) * 1000000);
94     }
95   }
96 
PrintWithBytesTimeResults97   void PrintWithBytes(const std::string &description,
98                       size_t bytes_per_call) const {
99     if (g_print_json) {
100       PrintJSON(description, bytes_per_call);
101     } else {
102       printf(
103           "Did %" PRIu64 " %s operations in %" PRIu64
104           "us (%.1f ops/sec): %.1f MB/s\n",
105           num_calls, description.c_str(), us,
106           (static_cast<double>(num_calls) / static_cast<double>(us)) * 1000000,
107           static_cast<double>(bytes_per_call * num_calls) /
108               static_cast<double>(us));
109     }
110   }
111 
112  private:
PrintJSONTimeResults113   void PrintJSON(const std::string &description,
114                  size_t bytes_per_call = 0) const {
115     if (first_json_printed) {
116       puts(",");
117     }
118 
119     printf("{\"description\": \"%s\", \"numCalls\": %" PRIu64
120            ", \"microseconds\": %" PRIu64,
121            description.c_str(), num_calls, us);
122 
123     if (bytes_per_call > 0) {
124       printf(", \"bytesPerCall\": %zu", bytes_per_call);
125     }
126 
127     printf("}");
128     first_json_printed = true;
129   }
130 
131   // first_json_printed is true if |g_print_json| is true and the first item in
132   // the JSON results has been printed already. This is used to handle the
133   // commas between each item in the result list.
134   static bool first_json_printed;
135 };
136 
137 bool TimeResults::first_json_printed = false;
138 
139 #if defined(OPENSSL_WINDOWS)
time_now()140 static uint64_t time_now() { return GetTickCount64() * 1000; }
141 #elif defined(OPENSSL_APPLE)
time_now()142 static uint64_t time_now() {
143   struct timeval tv;
144   uint64_t ret;
145 
146   gettimeofday(&tv, NULL);
147   ret = tv.tv_sec;
148   ret *= 1000000;
149   ret += tv.tv_usec;
150   return ret;
151 }
152 #else
time_now()153 static uint64_t time_now() {
154   struct timespec ts;
155   clock_gettime(CLOCK_MONOTONIC, &ts);
156 
157   uint64_t ret = ts.tv_sec;
158   ret *= 1000000;
159   ret += ts.tv_nsec / 1000;
160   return ret;
161 }
162 #endif
163 
164 static uint64_t g_timeout_seconds = 1;
165 static std::vector<size_t> g_chunk_lengths = {16, 256, 1350, 8192, 16384};
166 
167 // IterationsBetweenTimeChecks returns the number of iterations of |func| to run
168 // in between checking the time, or zero on error.
IterationsBetweenTimeChecks(std::function<bool ()> func)169 static uint32_t IterationsBetweenTimeChecks(std::function<bool()> func) {
170   uint64_t start = time_now();
171   if (!func()) {
172     return 0;
173   }
174   uint64_t delta = time_now() - start;
175   if (delta == 0) {
176     return 250;
177   }
178 
179   // Aim for about 100ms between time checks.
180   uint32_t ret = static_cast<double>(100000) / static_cast<double>(delta);
181   if (ret > 1000) {
182     ret = 1000;
183   } else if (ret < 1) {
184     ret = 1;
185   }
186   return ret;
187 }
188 
TimeFunctionImpl(TimeResults * results,std::function<bool ()> func,uint32_t iterations_between_time_checks)189 static bool TimeFunctionImpl(TimeResults *results, std::function<bool()> func,
190                              uint32_t iterations_between_time_checks) {
191   // total_us is the total amount of time that we'll aim to measure a function
192   // for.
193   const uint64_t total_us = g_timeout_seconds * 1000000;
194   uint64_t start = time_now(), now;
195   uint64_t done = 0;
196   for (;;) {
197     for (uint32_t i = 0; i < iterations_between_time_checks; i++) {
198       if (!func()) {
199         return false;
200       }
201       done++;
202     }
203 
204     now = time_now();
205     if (now - start > total_us) {
206       break;
207     }
208   }
209 
210   results->us = now - start;
211   results->num_calls = done;
212   return true;
213 }
214 
TimeFunction(TimeResults * results,std::function<bool ()> func)215 static bool TimeFunction(TimeResults *results, std::function<bool()> func) {
216   uint32_t iterations_between_time_checks = IterationsBetweenTimeChecks(func);
217   if (iterations_between_time_checks == 0) {
218     return false;
219   }
220 
221   return TimeFunctionImpl(results, std::move(func),
222                           iterations_between_time_checks);
223 }
224 
225 #if defined(OPENSSL_THREADS)
226 // g_threads is the number of threads to run in parallel benchmarks.
227 static int g_threads = 1;
228 
229 // Latch behaves like C++20 std::latch.
230 class Latch {
231  public:
Latch(int expected)232   explicit Latch(int expected) : expected_(expected) {}
233   Latch(const Latch &) = delete;
234   Latch &operator=(const Latch &) = delete;
235 
ArriveAndWait()236   void ArriveAndWait() {
237     std::unique_lock<std::mutex> lock(lock_);
238     expected_--;
239     if (expected_ > 0) {
240       cond_.wait(lock, [&] { return expected_ == 0; });
241     } else {
242       cond_.notify_all();
243     }
244   }
245 
246  private:
247   int expected_;
248   std::mutex lock_;
249   std::condition_variable cond_;
250 };
251 
TimeFunctionParallel(TimeResults * results,std::function<bool ()> func)252 static bool TimeFunctionParallel(TimeResults *results,
253                                  std::function<bool()> func) {
254   if (g_threads <= 1) {
255     return TimeFunction(results, std::move(func));
256   }
257 
258   uint32_t iterations_between_time_checks = IterationsBetweenTimeChecks(func);
259   if (iterations_between_time_checks == 0) {
260     return false;
261   }
262 
263   struct ThreadResult {
264     TimeResults time_result;
265     bool ok = false;
266   };
267   std::vector<ThreadResult> thread_results(g_threads);
268   Latch latch(g_threads);
269   std::vector<std::thread> threads;
270   for (int i = 0; i < g_threads; i++) {
271     threads.emplace_back([&, i] {
272       // Wait for all the threads to be ready before running the benchmark.
273       latch.ArriveAndWait();
274       thread_results[i].ok = TimeFunctionImpl(
275           &thread_results[i].time_result, func, iterations_between_time_checks);
276     });
277   }
278 
279   for (auto &thread : threads) {
280     thread.join();
281   }
282 
283   results->num_calls = 0;
284   results->us = 0;
285   for (const auto &pair : thread_results) {
286     if (!pair.ok) {
287       return false;
288     }
289     results->num_calls += pair.time_result.num_calls;
290     results->us += pair.time_result.us;
291   }
292   return true;
293 }
294 
295 #else
TimeFunctionParallel(TimeResults * results,std::function<bool ()> func)296 static bool TimeFunctionParallel(TimeResults *results,
297                                  std::function<bool()> func) {
298   return TimeFunction(results, std::move(func));
299 }
300 #endif
301 
SpeedRSA(const std::string & selected)302 static bool SpeedRSA(const std::string &selected) {
303   if (!selected.empty() && selected.find("RSA") == std::string::npos) {
304     return true;
305   }
306 
307   static const struct {
308     const char *name;
309     const uint8_t *key;
310     const size_t key_len;
311   } kRSAKeys[] = {
312       {"RSA 2048", kDERRSAPrivate2048, kDERRSAPrivate2048Len},
313       {"RSA 3072", kDERRSAPrivate3072, kDERRSAPrivate3072Len},
314       {"RSA 4096", kDERRSAPrivate4096, kDERRSAPrivate4096Len},
315   };
316 
317   for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kRSAKeys); i++) {
318     const std::string name = kRSAKeys[i].name;
319 
320     bssl::UniquePtr<RSA> key(
321         RSA_private_key_from_bytes(kRSAKeys[i].key, kRSAKeys[i].key_len));
322     if (key == nullptr) {
323       fprintf(stderr, "Failed to parse %s key.\n", name.c_str());
324       ERR_print_errors_fp(stderr);
325       return false;
326     }
327 
328     static constexpr size_t kMaxSignature = 512;
329     if (RSA_size(key.get()) > kMaxSignature) {
330       abort();
331     }
332     const uint8_t fake_sha256_hash[32] = {0};
333 
334     TimeResults results;
335     if (!TimeFunctionParallel(&results, [&key, &fake_sha256_hash]() -> bool {
336           // Usually during RSA signing we're using a long-lived |RSA| that
337           // has already had all of its |BN_MONT_CTX|s constructed, so it
338           // makes sense to use |key| directly here.
339           uint8_t out[kMaxSignature];
340           unsigned out_len;
341           return RSA_sign(NID_sha256, fake_sha256_hash,
342                           sizeof(fake_sha256_hash), out, &out_len, key.get());
343         })) {
344       fprintf(stderr, "RSA_sign failed.\n");
345       ERR_print_errors_fp(stderr);
346       return false;
347     }
348     results.Print(name + " signing");
349 
350     uint8_t sig[kMaxSignature];
351     unsigned sig_len;
352     if (!RSA_sign(NID_sha256, fake_sha256_hash, sizeof(fake_sha256_hash), sig,
353                   &sig_len, key.get())) {
354       return false;
355     }
356     if (!TimeFunctionParallel(
357             &results, [&key, &fake_sha256_hash, &sig, sig_len]() -> bool {
358               return RSA_verify(NID_sha256, fake_sha256_hash,
359                                 sizeof(fake_sha256_hash), sig, sig_len,
360                                 key.get());
361             })) {
362       fprintf(stderr, "RSA_verify failed.\n");
363       ERR_print_errors_fp(stderr);
364       return false;
365     }
366     results.Print(name + " verify (same key)");
367 
368     if (!TimeFunctionParallel(
369             &results, [&key, &fake_sha256_hash, &sig, sig_len]() -> bool {
370               // Usually during RSA verification we have to parse an RSA key
371               // from a certificate or similar, in which case we'd need to
372               // construct a new RSA key, with a new |BN_MONT_CTX| for the
373               // public modulus. If we were to use |key| directly instead, then
374               // these costs wouldn't be accounted for.
375               bssl::UniquePtr<RSA> verify_key(RSA_new_public_key(
376                   RSA_get0_n(key.get()), RSA_get0_e(key.get())));
377               if (!verify_key) {
378                 return false;
379               }
380               return RSA_verify(NID_sha256, fake_sha256_hash,
381                                 sizeof(fake_sha256_hash), sig, sig_len,
382                                 verify_key.get());
383             })) {
384       fprintf(stderr, "RSA_verify failed.\n");
385       ERR_print_errors_fp(stderr);
386       return false;
387     }
388     results.Print(name + " verify (fresh key)");
389 
390     if (!TimeFunctionParallel(&results, [&]() -> bool {
391           return bssl::UniquePtr<RSA>(RSA_private_key_from_bytes(
392                      kRSAKeys[i].key, kRSAKeys[i].key_len)) != nullptr;
393         })) {
394       fprintf(stderr, "Failed to parse %s key.\n", name.c_str());
395       ERR_print_errors_fp(stderr);
396       return false;
397     }
398     results.Print(name + " private key parse");
399   }
400 
401   return true;
402 }
403 
SpeedRSAKeyGen(const std::string & selected)404 static bool SpeedRSAKeyGen(const std::string &selected) {
405   // Don't run this by default because it's so slow.
406   if (selected != "RSAKeyGen") {
407     return true;
408   }
409 
410   bssl::UniquePtr<BIGNUM> e(BN_new());
411   if (!BN_set_word(e.get(), 65537)) {
412     return false;
413   }
414 
415   const std::vector<int> kSizes = {2048, 3072, 4096};
416   for (int size : kSizes) {
417     const uint64_t start = time_now();
418     uint64_t num_calls = 0;
419     uint64_t us;
420     std::vector<uint64_t> durations;
421 
422     for (;;) {
423       bssl::UniquePtr<RSA> rsa(RSA_new());
424 
425       const uint64_t iteration_start = time_now();
426       if (!RSA_generate_key_ex(rsa.get(), size, e.get(), nullptr)) {
427         fprintf(stderr, "RSA_generate_key_ex failed.\n");
428         ERR_print_errors_fp(stderr);
429         return false;
430       }
431       const uint64_t iteration_end = time_now();
432 
433       num_calls++;
434       durations.push_back(iteration_end - iteration_start);
435 
436       us = iteration_end - start;
437       if (us > 30 * 1000000 /* 30 secs */) {
438         break;
439       }
440     }
441 
442     std::sort(durations.begin(), durations.end());
443     const std::string description =
444         std::string("RSA ") + std::to_string(size) + std::string(" key-gen");
445     const TimeResults results = {num_calls, us};
446     results.Print(description);
447     const size_t n = durations.size();
448     assert(n > 0);
449 
450     // Distribution information is useful, but doesn't fit into the standard
451     // format used by |g_print_json|.
452     if (!g_print_json) {
453       uint64_t min = durations[0];
454       uint64_t median = n & 1 ? durations[n / 2]
455                               : (durations[n / 2 - 1] + durations[n / 2]) / 2;
456       uint64_t max = durations[n - 1];
457       printf("  min: %" PRIu64 "us, median: %" PRIu64 "us, max: %" PRIu64
458              "us\n",
459              min, median, max);
460     }
461   }
462 
463   return true;
464 }
465 
ChunkLenSuffix(size_t chunk_len)466 static std::string ChunkLenSuffix(size_t chunk_len) {
467   char buf[32];
468   snprintf(buf, sizeof(buf), " (%zu byte%s)", chunk_len,
469            chunk_len != 1 ? "s" : "");
470   return buf;
471 }
472 
SpeedAEADChunk(const EVP_AEAD * aead,std::string name,size_t chunk_len,size_t ad_len,evp_aead_direction_t direction)473 static bool SpeedAEADChunk(const EVP_AEAD *aead, std::string name,
474                            size_t chunk_len, size_t ad_len,
475                            evp_aead_direction_t direction) {
476   static const unsigned kAlignment = 16;
477 
478   name += ChunkLenSuffix(chunk_len);
479   bssl::ScopedEVP_AEAD_CTX ctx;
480   const size_t key_len = EVP_AEAD_key_length(aead);
481   const size_t nonce_len = EVP_AEAD_nonce_length(aead);
482   const size_t overhead_len = EVP_AEAD_max_overhead(aead);
483 
484   auto key = std::make_unique<uint8_t[]>(key_len);
485   OPENSSL_memset(key.get(), 0, key_len);
486   auto nonce = std::make_unique<uint8_t[]>(nonce_len);
487   OPENSSL_memset(nonce.get(), 0, nonce_len);
488   auto in_storage = std::make_unique<uint8_t[]>(chunk_len + kAlignment);
489   // N.B. for EVP_AEAD_CTX_seal_scatter the input and output buffers may be the
490   // same size. However, in the direction == evp_aead_open case we still use
491   // non-scattering seal, hence we add overhead_len to the size of this buffer.
492   auto out_storage =
493       std::make_unique<uint8_t[]>(chunk_len + overhead_len + kAlignment);
494   auto in2_storage =
495       std::make_unique<uint8_t[]>(chunk_len + overhead_len + kAlignment);
496   auto ad = std::make_unique<uint8_t[]>(ad_len);
497   OPENSSL_memset(ad.get(), 0, ad_len);
498   auto tag_storage = std::make_unique<uint8_t[]>(overhead_len + kAlignment);
499 
500   uint8_t *const in =
501       static_cast<uint8_t *>(align_pointer(in_storage.get(), kAlignment));
502   OPENSSL_memset(in, 0, chunk_len);
503   uint8_t *const out =
504       static_cast<uint8_t *>(align_pointer(out_storage.get(), kAlignment));
505   OPENSSL_memset(out, 0, chunk_len + overhead_len);
506   uint8_t *const tag =
507       static_cast<uint8_t *>(align_pointer(tag_storage.get(), kAlignment));
508   OPENSSL_memset(tag, 0, overhead_len);
509   uint8_t *const in2 =
510       static_cast<uint8_t *>(align_pointer(in2_storage.get(), kAlignment));
511 
512   if (!EVP_AEAD_CTX_init_with_direction(ctx.get(), aead, key.get(), key_len,
513                                         EVP_AEAD_DEFAULT_TAG_LENGTH,
514                                         evp_aead_seal)) {
515     fprintf(stderr, "Failed to create EVP_AEAD_CTX.\n");
516     ERR_print_errors_fp(stderr);
517     return false;
518   }
519 
520   // TODO(davidben): In most cases, this can be |TimeFunctionParallel|, but a
521   // few stateful AEADs must be run serially.
522   TimeResults results;
523   if (direction == evp_aead_seal) {
524     if (!TimeFunction(&results,
525                       [chunk_len, nonce_len, ad_len, overhead_len, in, out, tag,
526                        &ctx, &nonce, &ad]() -> bool {
527                         size_t tag_len;
528                         return EVP_AEAD_CTX_seal_scatter(
529                             ctx.get(), out, tag, &tag_len, overhead_len,
530                             nonce.get(), nonce_len, in, chunk_len, nullptr, 0,
531                             ad.get(), ad_len);
532                       })) {
533       fprintf(stderr, "EVP_AEAD_CTX_seal failed.\n");
534       ERR_print_errors_fp(stderr);
535       return false;
536     }
537   } else {
538     size_t out_len;
539     EVP_AEAD_CTX_seal(ctx.get(), out, &out_len, chunk_len + overhead_len,
540                       nonce.get(), nonce_len, in, chunk_len, ad.get(), ad_len);
541 
542     ctx.Reset();
543     if (!EVP_AEAD_CTX_init_with_direction(ctx.get(), aead, key.get(), key_len,
544                                           EVP_AEAD_DEFAULT_TAG_LENGTH,
545                                           evp_aead_open)) {
546       fprintf(stderr, "Failed to create EVP_AEAD_CTX.\n");
547       ERR_print_errors_fp(stderr);
548       return false;
549     }
550 
551     if (!TimeFunction(&results,
552                       [chunk_len, overhead_len, nonce_len, ad_len, in2, out,
553                        out_len, &ctx, &nonce, &ad]() -> bool {
554                         size_t in2_len;
555                         // N.B. EVP_AEAD_CTX_open_gather is not implemented for
556                         // all AEADs.
557                         return EVP_AEAD_CTX_open(ctx.get(), in2, &in2_len,
558                                                  chunk_len + overhead_len,
559                                                  nonce.get(), nonce_len, out,
560                                                  out_len, ad.get(), ad_len);
561                       })) {
562       fprintf(stderr, "EVP_AEAD_CTX_open failed.\n");
563       ERR_print_errors_fp(stderr);
564       return false;
565     }
566   }
567 
568   results.PrintWithBytes(
569       name + (direction == evp_aead_seal ? " seal" : " open"), chunk_len);
570   return true;
571 }
572 
SpeedAEAD(const EVP_AEAD * aead,const std::string & name,size_t ad_len,const std::string & selected)573 static bool SpeedAEAD(const EVP_AEAD *aead, const std::string &name,
574                       size_t ad_len, const std::string &selected) {
575   if (!selected.empty() && name.find(selected) == std::string::npos) {
576     return true;
577   }
578 
579   for (size_t chunk_len : g_chunk_lengths) {
580     if (!SpeedAEADChunk(aead, name, chunk_len, ad_len, evp_aead_seal)) {
581       return false;
582     }
583   }
584   return true;
585 }
586 
SpeedAEADOpen(const EVP_AEAD * aead,const std::string & name,size_t ad_len,const std::string & selected)587 static bool SpeedAEADOpen(const EVP_AEAD *aead, const std::string &name,
588                           size_t ad_len, const std::string &selected) {
589   if (!selected.empty() && name.find(selected) == std::string::npos) {
590     return true;
591   }
592 
593   for (size_t chunk_len : g_chunk_lengths) {
594     if (!SpeedAEADChunk(aead, name, chunk_len, ad_len, evp_aead_open)) {
595       return false;
596     }
597   }
598 
599   return true;
600 }
601 
SpeedAESBlock(const std::string & name,unsigned bits,const std::string & selected)602 static bool SpeedAESBlock(const std::string &name, unsigned bits,
603                           const std::string &selected) {
604   if (!selected.empty() && name.find(selected) == std::string::npos) {
605     return true;
606   }
607 
608   static const uint8_t kZero[32] = {0};
609 
610   {
611     TimeResults results;
612     if (!TimeFunctionParallel(&results, [&]() -> bool {
613           AES_KEY key;
614           return AES_set_encrypt_key(kZero, bits, &key) == 0;
615         })) {
616       fprintf(stderr, "AES_set_encrypt_key failed.\n");
617       return false;
618     }
619     results.Print(name + " encrypt setup");
620   }
621 
622   {
623     AES_KEY key;
624     if (AES_set_encrypt_key(kZero, bits, &key) != 0) {
625       return false;
626     }
627     uint8_t block[16] = {0};
628     TimeResults results;
629     if (!TimeFunctionParallel(&results, [&]() -> bool {
630           AES_encrypt(block, block, &key);
631           return true;
632         })) {
633       fprintf(stderr, "AES_encrypt failed.\n");
634       return false;
635     }
636     results.Print(name + " encrypt");
637   }
638 
639   {
640     TimeResults results;
641     if (!TimeFunctionParallel(&results, [&]() -> bool {
642           AES_KEY key;
643           return AES_set_decrypt_key(kZero, bits, &key) == 0;
644         })) {
645       fprintf(stderr, "AES_set_decrypt_key failed.\n");
646       return false;
647     }
648     results.Print(name + " decrypt setup");
649   }
650 
651   {
652     AES_KEY key;
653     if (AES_set_decrypt_key(kZero, bits, &key) != 0) {
654       return false;
655     }
656     uint8_t block[16] = {0};
657     TimeResults results;
658     if (!TimeFunctionParallel(&results, [&]() -> bool {
659           AES_decrypt(block, block, &key);
660           return true;
661         })) {
662       fprintf(stderr, "AES_decrypt failed.\n");
663       return false;
664     }
665     results.Print(name + " decrypt");
666   }
667 
668   return true;
669 }
670 
SpeedHashChunk(const EVP_MD * md,std::string name,size_t chunk_len)671 static bool SpeedHashChunk(const EVP_MD *md, std::string name,
672                            size_t chunk_len) {
673   uint8_t input[16384] = {0};
674 
675   if (chunk_len > sizeof(input)) {
676     return false;
677   }
678 
679   name += ChunkLenSuffix(chunk_len);
680   TimeResults results;
681   if (!TimeFunctionParallel(&results, [md, chunk_len, &input]() -> bool {
682         uint8_t digest[EVP_MAX_MD_SIZE];
683         unsigned int md_len;
684 
685         bssl::ScopedEVP_MD_CTX ctx;
686         return EVP_DigestInit_ex(ctx.get(), md, NULL /* ENGINE */) &&
687                EVP_DigestUpdate(ctx.get(), input, chunk_len) &&
688                EVP_DigestFinal_ex(ctx.get(), digest, &md_len);
689       })) {
690     fprintf(stderr, "EVP_DigestInit_ex failed.\n");
691     ERR_print_errors_fp(stderr);
692     return false;
693   }
694 
695   results.PrintWithBytes(name, chunk_len);
696   return true;
697 }
698 
SpeedHash(const EVP_MD * md,const std::string & name,const std::string & selected)699 static bool SpeedHash(const EVP_MD *md, const std::string &name,
700                       const std::string &selected) {
701   if (!selected.empty() && name.find(selected) == std::string::npos) {
702     return true;
703   }
704 
705   for (size_t chunk_len : g_chunk_lengths) {
706     if (!SpeedHashChunk(md, name, chunk_len)) {
707       return false;
708     }
709   }
710 
711   return true;
712 }
713 
SpeedRandomChunk(std::string name,size_t chunk_len)714 static bool SpeedRandomChunk(std::string name, size_t chunk_len) {
715   static constexpr size_t kMaxChunk = 16384;
716   if (chunk_len > kMaxChunk) {
717     return false;
718   }
719 
720   name += ChunkLenSuffix(chunk_len);
721   TimeResults results;
722   if (!TimeFunctionParallel(&results, [chunk_len]() -> bool {
723         uint8_t scratch[kMaxChunk];
724         RAND_bytes(scratch, chunk_len);
725         return true;
726       })) {
727     return false;
728   }
729 
730   results.PrintWithBytes(name, chunk_len);
731   return true;
732 }
733 
SpeedRandom(const std::string & selected)734 static bool SpeedRandom(const std::string &selected) {
735   if (!selected.empty() && selected != "RNG") {
736     return true;
737   }
738 
739   for (size_t chunk_len : g_chunk_lengths) {
740     if (!SpeedRandomChunk("RNG", chunk_len)) {
741       return false;
742     }
743   }
744 
745   return true;
746 }
747 
SpeedECDHCurve(const std::string & name,const EC_GROUP * group,const std::string & selected)748 static bool SpeedECDHCurve(const std::string &name, const EC_GROUP *group,
749                            const std::string &selected) {
750   if (!selected.empty() && name.find(selected) == std::string::npos) {
751     return true;
752   }
753 
754   bssl::UniquePtr<EC_KEY> peer_key(EC_KEY_new());
755   if (!peer_key || !EC_KEY_set_group(peer_key.get(), group) ||
756       !EC_KEY_generate_key(peer_key.get())) {
757     return false;
758   }
759 
760   size_t peer_value_len = EC_POINT_point2oct(
761       EC_KEY_get0_group(peer_key.get()), EC_KEY_get0_public_key(peer_key.get()),
762       POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
763   if (peer_value_len == 0) {
764     return false;
765   }
766   auto peer_value = std::make_unique<uint8_t[]>(peer_value_len);
767   peer_value_len = EC_POINT_point2oct(
768       EC_KEY_get0_group(peer_key.get()), EC_KEY_get0_public_key(peer_key.get()),
769       POINT_CONVERSION_UNCOMPRESSED, peer_value.get(), peer_value_len, nullptr);
770   if (peer_value_len == 0) {
771     return false;
772   }
773 
774   TimeResults results;
775   if (!TimeFunctionParallel(
776           &results, [group, peer_value_len, &peer_value]() -> bool {
777             bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
778             if (!key || !EC_KEY_set_group(key.get(), group) ||
779                 !EC_KEY_generate_key(key.get())) {
780               return false;
781             }
782             bssl::UniquePtr<EC_POINT> point(EC_POINT_new(group));
783             bssl::UniquePtr<EC_POINT> peer_point(EC_POINT_new(group));
784             bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
785             bssl::UniquePtr<BIGNUM> x(BN_new());
786             if (!point || !peer_point || !ctx || !x ||
787                 !EC_POINT_oct2point(group, peer_point.get(), peer_value.get(),
788                                     peer_value_len, ctx.get()) ||
789                 !EC_POINT_mul(group, point.get(), nullptr, peer_point.get(),
790                               EC_KEY_get0_private_key(key.get()), ctx.get()) ||
791                 !EC_POINT_get_affine_coordinates_GFp(
792                     group, point.get(), x.get(), nullptr, ctx.get())) {
793               return false;
794             }
795 
796             return true;
797           })) {
798     return false;
799   }
800 
801   results.Print(name);
802   return true;
803 }
804 
SpeedECDSACurve(const std::string & name,const EC_GROUP * group,const std::string & selected)805 static bool SpeedECDSACurve(const std::string &name, const EC_GROUP *group,
806                             const std::string &selected) {
807   if (!selected.empty() && name.find(selected) == std::string::npos) {
808     return true;
809   }
810 
811   bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
812   if (!key || !EC_KEY_set_group(key.get(), group) ||
813       !EC_KEY_generate_key(key.get())) {
814     return false;
815   }
816 
817   static constexpr size_t kMaxSignature = 256;
818   if (ECDSA_size(key.get()) > kMaxSignature) {
819     abort();
820   }
821   uint8_t digest[20];
822   OPENSSL_memset(digest, 42, sizeof(digest));
823 
824   TimeResults results;
825   if (!TimeFunctionParallel(&results, [&key, &digest]() -> bool {
826         uint8_t out[kMaxSignature];
827         unsigned out_len;
828         return ECDSA_sign(0, digest, sizeof(digest), out, &out_len,
829                           key.get()) == 1;
830       })) {
831     return false;
832   }
833 
834   results.Print(name + " signing");
835 
836   uint8_t signature[kMaxSignature];
837   unsigned sig_len;
838   if (!ECDSA_sign(0, digest, sizeof(digest), signature, &sig_len, key.get())) {
839     return false;
840   }
841 
842   if (!TimeFunctionParallel(
843           &results, [&key, &signature, &digest, sig_len]() -> bool {
844             return ECDSA_verify(0, digest, sizeof(digest), signature, sig_len,
845                                 key.get()) == 1;
846           })) {
847     return false;
848   }
849 
850   results.Print(name + " verify");
851 
852   return true;
853 }
854 
SpeedECDH(const std::string & selected)855 static bool SpeedECDH(const std::string &selected) {
856   return SpeedECDHCurve("ECDH P-224", EC_group_p224(), selected) &&
857          SpeedECDHCurve("ECDH P-256", EC_group_p256(), selected) &&
858          SpeedECDHCurve("ECDH P-384", EC_group_p384(), selected) &&
859          SpeedECDHCurve("ECDH P-521", EC_group_p521(), selected);
860 }
861 
SpeedECDSA(const std::string & selected)862 static bool SpeedECDSA(const std::string &selected) {
863   return SpeedECDSACurve("ECDSA P-224", EC_group_p224(), selected) &&
864          SpeedECDSACurve("ECDSA P-256", EC_group_p256(), selected) &&
865          SpeedECDSACurve("ECDSA P-384", EC_group_p384(), selected) &&
866          SpeedECDSACurve("ECDSA P-521", EC_group_p521(), selected);
867 }
868 
Speed25519(const std::string & selected)869 static bool Speed25519(const std::string &selected) {
870   if (!selected.empty() && selected.find("25519") == std::string::npos) {
871     return true;
872   }
873 
874   TimeResults results;
875   if (!TimeFunctionParallel(&results, []() -> bool {
876         uint8_t public_key[32], private_key[64];
877         ED25519_keypair(public_key, private_key);
878         return true;
879       })) {
880     return false;
881   }
882 
883   results.Print("Ed25519 key generation");
884 
885   uint8_t public_key[32], private_key[64];
886   ED25519_keypair(public_key, private_key);
887   static const uint8_t kMessage[] = {0, 1, 2, 3, 4, 5};
888 
889   if (!TimeFunctionParallel(&results, [&private_key]() -> bool {
890         uint8_t out[64];
891         return ED25519_sign(out, kMessage, sizeof(kMessage), private_key) == 1;
892       })) {
893     return false;
894   }
895 
896   results.Print("Ed25519 signing");
897 
898   uint8_t signature[64];
899   if (!ED25519_sign(signature, kMessage, sizeof(kMessage), private_key)) {
900     return false;
901   }
902 
903   if (!TimeFunctionParallel(&results, [&public_key, &signature]() -> bool {
904         return ED25519_verify(kMessage, sizeof(kMessage), signature,
905                               public_key) == 1;
906       })) {
907     fprintf(stderr, "Ed25519 verify failed.\n");
908     return false;
909   }
910 
911   results.Print("Ed25519 verify");
912 
913   if (!TimeFunctionParallel(&results, []() -> bool {
914         uint8_t out[32], in[32];
915         OPENSSL_memset(in, 0, sizeof(in));
916         X25519_public_from_private(out, in);
917         return true;
918       })) {
919     fprintf(stderr, "Curve25519 base-point multiplication failed.\n");
920     return false;
921   }
922 
923   results.Print("Curve25519 base-point multiplication");
924 
925   if (!TimeFunctionParallel(&results, []() -> bool {
926         uint8_t out[32], in1[32], in2[32];
927         OPENSSL_memset(in1, 0, sizeof(in1));
928         OPENSSL_memset(in2, 0, sizeof(in2));
929         in1[0] = 1;
930         in2[0] = 9;
931         return X25519(out, in1, in2) == 1;
932       })) {
933     fprintf(stderr, "Curve25519 arbitrary point multiplication failed.\n");
934     return false;
935   }
936 
937   results.Print("Curve25519 arbitrary point multiplication");
938 
939   return true;
940 }
941 
SpeedSPAKE2(const std::string & selected)942 static bool SpeedSPAKE2(const std::string &selected) {
943   if (!selected.empty() && selected.find("SPAKE2") == std::string::npos) {
944     return true;
945   }
946 
947   TimeResults results;
948 
949   static const uint8_t kAliceName[] = {'A'};
950   static const uint8_t kBobName[] = {'B'};
951   static const uint8_t kPassword[] = "password";
952   bssl::UniquePtr<SPAKE2_CTX> alice(
953       SPAKE2_CTX_new(spake2_role_alice, kAliceName, sizeof(kAliceName),
954                      kBobName, sizeof(kBobName)));
955   uint8_t alice_msg[SPAKE2_MAX_MSG_SIZE];
956   size_t alice_msg_len;
957 
958   if (!SPAKE2_generate_msg(alice.get(), alice_msg, &alice_msg_len,
959                            sizeof(alice_msg), kPassword, sizeof(kPassword))) {
960     fprintf(stderr, "SPAKE2_generate_msg failed.\n");
961     return false;
962   }
963 
964   if (!TimeFunctionParallel(&results, [&alice_msg, alice_msg_len]() -> bool {
965         bssl::UniquePtr<SPAKE2_CTX> bob(
966             SPAKE2_CTX_new(spake2_role_bob, kBobName, sizeof(kBobName),
967                            kAliceName, sizeof(kAliceName)));
968         uint8_t bob_msg[SPAKE2_MAX_MSG_SIZE], bob_key[64];
969         size_t bob_msg_len, bob_key_len;
970         if (!SPAKE2_generate_msg(bob.get(), bob_msg, &bob_msg_len,
971                                  sizeof(bob_msg), kPassword,
972                                  sizeof(kPassword)) ||
973             !SPAKE2_process_msg(bob.get(), bob_key, &bob_key_len,
974                                 sizeof(bob_key), alice_msg, alice_msg_len)) {
975           return false;
976         }
977 
978         return true;
979       })) {
980     fprintf(stderr, "SPAKE2 failed.\n");
981   }
982 
983   results.Print("SPAKE2 over Ed25519");
984 
985   return true;
986 }
987 
SpeedScrypt(const std::string & selected)988 static bool SpeedScrypt(const std::string &selected) {
989   if (!selected.empty() && selected.find("scrypt") == std::string::npos) {
990     return true;
991   }
992 
993   TimeResults results;
994 
995   static const char kPassword[] = "password";
996   static const uint8_t kSalt[] = "NaCl";
997 
998   if (!TimeFunctionParallel(&results, [&]() -> bool {
999         uint8_t out[64];
1000         return !!EVP_PBE_scrypt(kPassword, sizeof(kPassword) - 1, kSalt,
1001                                 sizeof(kSalt) - 1, 1024, 8, 16, 0 /* max_mem */,
1002                                 out, sizeof(out));
1003       })) {
1004     fprintf(stderr, "scrypt failed.\n");
1005     return false;
1006   }
1007   results.Print("scrypt (N = 1024, r = 8, p = 16)");
1008 
1009   if (!TimeFunctionParallel(&results, [&]() -> bool {
1010         uint8_t out[64];
1011         return !!EVP_PBE_scrypt(kPassword, sizeof(kPassword) - 1, kSalt,
1012                                 sizeof(kSalt) - 1, 16384, 8, 1, 0 /* max_mem */,
1013                                 out, sizeof(out));
1014       })) {
1015     fprintf(stderr, "scrypt failed.\n");
1016     return false;
1017   }
1018   results.Print("scrypt (N = 16384, r = 8, p = 1)");
1019 
1020   return true;
1021 }
1022 
SpeedHRSS(const std::string & selected)1023 static bool SpeedHRSS(const std::string &selected) {
1024   if (!selected.empty() && selected != "HRSS") {
1025     return true;
1026   }
1027 
1028   TimeResults results;
1029 
1030   if (!TimeFunctionParallel(&results, []() -> bool {
1031         struct HRSS_public_key pub;
1032         struct HRSS_private_key priv;
1033         uint8_t entropy[HRSS_GENERATE_KEY_BYTES];
1034         RAND_bytes(entropy, sizeof(entropy));
1035         return HRSS_generate_key(&pub, &priv, entropy);
1036       })) {
1037     fprintf(stderr, "Failed to time HRSS_generate_key.\n");
1038     return false;
1039   }
1040 
1041   results.Print("HRSS generate");
1042 
1043   struct HRSS_public_key pub;
1044   struct HRSS_private_key priv;
1045   uint8_t key_entropy[HRSS_GENERATE_KEY_BYTES];
1046   RAND_bytes(key_entropy, sizeof(key_entropy));
1047   if (!HRSS_generate_key(&pub, &priv, key_entropy)) {
1048     return false;
1049   }
1050 
1051   if (!TimeFunctionParallel(&results, [&pub]() -> bool {
1052         uint8_t entropy[HRSS_ENCAP_BYTES];
1053         uint8_t shared_key[HRSS_KEY_BYTES];
1054         uint8_t ciphertext[HRSS_CIPHERTEXT_BYTES];
1055         RAND_bytes(entropy, sizeof(entropy));
1056         return HRSS_encap(ciphertext, shared_key, &pub, entropy);
1057       })) {
1058     fprintf(stderr, "Failed to time HRSS_encap.\n");
1059     return false;
1060   }
1061   results.Print("HRSS encap");
1062 
1063   uint8_t entropy[HRSS_ENCAP_BYTES];
1064   uint8_t shared_key[HRSS_KEY_BYTES];
1065   uint8_t ciphertext[HRSS_CIPHERTEXT_BYTES];
1066   RAND_bytes(entropy, sizeof(entropy));
1067   if (!HRSS_encap(ciphertext, shared_key, &pub, entropy)) {
1068     return false;
1069   }
1070 
1071   if (!TimeFunctionParallel(&results, [&priv, &ciphertext]() -> bool {
1072         uint8_t shared_key2[HRSS_KEY_BYTES];
1073         return HRSS_decap(shared_key2, &priv, ciphertext, sizeof(ciphertext));
1074       })) {
1075     fprintf(stderr, "Failed to time HRSS_encap.\n");
1076     return false;
1077   }
1078 
1079   results.Print("HRSS decap");
1080 
1081   return true;
1082 }
1083 
SpeedKyber(const std::string & selected)1084 static bool SpeedKyber(const std::string &selected) {
1085   if (!selected.empty() && selected != "Kyber") {
1086     return true;
1087   }
1088 
1089   TimeResults results;
1090 
1091   uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES];
1092   // This ciphertext is nonsense, but Kyber decap is constant-time so, for the
1093   // purposes of timing, it's fine.
1094   memset(ciphertext, 42, sizeof(ciphertext));
1095   if (!TimeFunctionParallel(&results, [&]() -> bool {
1096         KYBER_private_key priv;
1097         uint8_t encoded_public_key[KYBER_PUBLIC_KEY_BYTES];
1098         KYBER_generate_key(encoded_public_key, &priv);
1099         uint8_t shared_secret[KYBER_SHARED_SECRET_BYTES];
1100         KYBER_decap(shared_secret, ciphertext, &priv);
1101         return true;
1102       })) {
1103     fprintf(stderr, "Failed to time KYBER_generate_key + KYBER_decap.\n");
1104     return false;
1105   }
1106 
1107   results.Print("Kyber generate + decap");
1108 
1109   KYBER_private_key priv;
1110   uint8_t encoded_public_key[KYBER_PUBLIC_KEY_BYTES];
1111   KYBER_generate_key(encoded_public_key, &priv);
1112   KYBER_public_key pub;
1113   if (!TimeFunctionParallel(&results, [&]() -> bool {
1114         CBS encoded_public_key_cbs;
1115         CBS_init(&encoded_public_key_cbs, encoded_public_key,
1116                  sizeof(encoded_public_key));
1117         if (!KYBER_parse_public_key(&pub, &encoded_public_key_cbs)) {
1118           return false;
1119         }
1120         uint8_t shared_secret[KYBER_SHARED_SECRET_BYTES];
1121         KYBER_encap(ciphertext, shared_secret, &pub);
1122         return true;
1123       })) {
1124     fprintf(stderr, "Failed to time KYBER_encap.\n");
1125     return false;
1126   }
1127 
1128   results.Print("Kyber parse + encap");
1129 
1130   return true;
1131 }
1132 
SpeedMLDSA(const std::string & selected)1133 static bool SpeedMLDSA(const std::string &selected) {
1134   if (!selected.empty() && selected != "ML-DSA") {
1135     return true;
1136   }
1137 
1138   TimeResults results;
1139 
1140   auto encoded_public_key =
1141       std::make_unique<uint8_t[]>(MLDSA65_PUBLIC_KEY_BYTES);
1142   auto priv = std::make_unique<MLDSA65_private_key>();
1143   if (!TimeFunctionParallel(&results, [&]() -> bool {
1144         uint8_t seed[MLDSA_SEED_BYTES];
1145         if (!MLDSA65_generate_key(encoded_public_key.get(), seed, priv.get())) {
1146           fprintf(stderr, "Failure in MLDSA65_generate_key.\n");
1147           return false;
1148         }
1149         return true;
1150       })) {
1151     fprintf(stderr, "Failed to time MLDSA65_generate_key.\n");
1152     return false;
1153   }
1154 
1155   results.Print("MLDSA key generation");
1156 
1157   const char *message = "Hello world";
1158   size_t message_len = strlen(message);
1159   auto out_encoded_signature =
1160       std::make_unique<uint8_t[]>(MLDSA65_SIGNATURE_BYTES);
1161   if (!TimeFunctionParallel(&results, [&]() -> bool {
1162         if (!MLDSA65_sign(out_encoded_signature.get(), priv.get(),
1163                           (const uint8_t *)message, message_len, nullptr, 0)) {
1164           fprintf(stderr, "Failure in MLDSA65_sign.\n");
1165           return false;
1166         }
1167         return true;
1168       })) {
1169     fprintf(stderr, "Failed to time MLDSA65_sign.\n");
1170     return false;
1171   }
1172 
1173   results.Print("MLDSA sign (randomized)");
1174 
1175   auto pub = std::make_unique<MLDSA65_public_key>();
1176 
1177   if (!TimeFunctionParallel(&results, [&]() -> bool {
1178         CBS cbs;
1179         CBS_init(&cbs, encoded_public_key.get(), MLDSA65_PUBLIC_KEY_BYTES);
1180         if (!MLDSA65_parse_public_key(pub.get(), &cbs)) {
1181           fprintf(stderr, "Failure in MLDSA65_parse_public_key.\n");
1182           return false;
1183         }
1184         return true;
1185       })) {
1186     fprintf(stderr, "Failed to time MLDSA65_parse_public_key.\n");
1187     return false;
1188   }
1189 
1190   results.Print("MLDSA parse (valid) public key");
1191 
1192   if (!TimeFunctionParallel(&results, [&]() -> bool {
1193         if (!MLDSA65_verify(pub.get(), out_encoded_signature.get(),
1194                             MLDSA65_SIGNATURE_BYTES, (const uint8_t *)message,
1195                             message_len, nullptr, 0)) {
1196           fprintf(stderr, "Failed to verify MLDSA signature.\n");
1197           return false;
1198         }
1199         return true;
1200       })) {
1201     fprintf(stderr, "Failed to time MLDSA65_verify.\n");
1202     return false;
1203   }
1204 
1205   results.Print("MLDSA verify (valid signature)");
1206 
1207   out_encoded_signature[42] ^= 0x42;
1208   if (!TimeFunctionParallel(&results, [&]() -> bool {
1209         if (MLDSA65_verify(pub.get(), out_encoded_signature.get(),
1210                            MLDSA65_SIGNATURE_BYTES, (const uint8_t *)message,
1211                            message_len, nullptr, 0)) {
1212           fprintf(stderr, "MLDSA signature unexpectedly verified.\n");
1213           return false;
1214         }
1215         return true;
1216       })) {
1217     fprintf(stderr, "Failed to time MLDSA65_verify.\n");
1218     return false;
1219   }
1220 
1221   results.Print("MLDSA verify (invalid signature)");
1222 
1223   return true;
1224 }
1225 
SpeedMLKEM(const std::string & selected)1226 static bool SpeedMLKEM(const std::string &selected) {
1227   if (!selected.empty() && selected != "ML-KEM-768") {
1228     return true;
1229   }
1230 
1231   TimeResults results;
1232 
1233   uint8_t ciphertext[MLKEM768_CIPHERTEXT_BYTES];
1234   // This ciphertext is nonsense, but decap is constant-time so, for the
1235   // purposes of timing, it's fine.
1236   memset(ciphertext, 42, sizeof(ciphertext));
1237   if (!TimeFunctionParallel(&results, [&]() -> bool {
1238         MLKEM768_private_key priv;
1239         uint8_t encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
1240         MLKEM768_generate_key(encoded_public_key, nullptr, &priv);
1241         uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
1242         MLKEM768_decap(shared_secret, ciphertext, sizeof(ciphertext), &priv);
1243         return true;
1244       })) {
1245     fprintf(stderr, "Failed to time MLKEM768_generate_key + MLKEM768_decap.\n");
1246     return false;
1247   }
1248 
1249   results.Print("ML-KEM-768 generate + decap");
1250 
1251   MLKEM768_private_key priv;
1252   uint8_t encoded_public_key[MLKEM768_PUBLIC_KEY_BYTES];
1253   MLKEM768_generate_key(encoded_public_key, nullptr, &priv);
1254   MLKEM768_public_key pub;
1255   if (!TimeFunctionParallel(&results, [&]() -> bool {
1256         CBS encoded_public_key_cbs;
1257         CBS_init(&encoded_public_key_cbs, encoded_public_key,
1258                  sizeof(encoded_public_key));
1259         if (!MLKEM768_parse_public_key(&pub, &encoded_public_key_cbs)) {
1260           return false;
1261         }
1262         uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
1263         MLKEM768_encap(ciphertext, shared_secret, &pub);
1264         return true;
1265       })) {
1266     fprintf(stderr, "Failed to time MLKEM768_encap.\n");
1267     return false;
1268   }
1269 
1270   results.Print("ML-KEM-768 parse + encap");
1271 
1272   return true;
1273 }
1274 
SpeedMLKEM1024(const std::string & selected)1275 static bool SpeedMLKEM1024(const std::string &selected) {
1276   if (!selected.empty() && selected != "ML-KEM-1024") {
1277     return true;
1278   }
1279 
1280   TimeResults results;
1281 
1282   uint8_t ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
1283   auto priv = std::make_unique<MLKEM1024_private_key>();
1284   // This ciphertext is nonsense, but decap is constant-time so, for the
1285   // purposes of timing, it's fine.
1286   memset(ciphertext, 42, sizeof(ciphertext));
1287   if (!TimeFunctionParallel(&results, [&]() -> bool {
1288         uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
1289         MLKEM1024_generate_key(encoded_public_key, nullptr, priv.get());
1290         uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
1291         MLKEM1024_decap(shared_secret, ciphertext, sizeof(ciphertext),
1292                         priv.get());
1293         return true;
1294       })) {
1295     fprintf(stderr, "Failed to time MLKEM768_generate_key + MLKEM768_decap.\n");
1296     return false;
1297   }
1298 
1299   results.Print("ML-KEM-1024 generate + decap");
1300 
1301   uint8_t encoded_public_key[MLKEM1024_PUBLIC_KEY_BYTES];
1302   MLKEM1024_generate_key(encoded_public_key, nullptr, priv.get());
1303   MLKEM1024_public_key pub;
1304   if (!TimeFunctionParallel(&results, [&]() -> bool {
1305         CBS encoded_public_key_cbs;
1306         CBS_init(&encoded_public_key_cbs, encoded_public_key,
1307                  sizeof(encoded_public_key));
1308         if (!MLKEM1024_parse_public_key(&pub, &encoded_public_key_cbs)) {
1309           return false;
1310         }
1311         uint8_t shared_secret[MLKEM_SHARED_SECRET_BYTES];
1312         MLKEM1024_encap(ciphertext, shared_secret, &pub);
1313         return true;
1314       })) {
1315     fprintf(stderr, "Failed to time MLKEM768_encap.\n");
1316     return false;
1317   }
1318 
1319   results.Print("ML-KEM-1024 parse + encap");
1320 
1321   return true;
1322 }
1323 
SpeedSLHDSA(const std::string & selected)1324 static bool SpeedSLHDSA(const std::string &selected) {
1325   if (!selected.empty() && selected.find("SLH-DSA") == std::string::npos) {
1326     return true;
1327   }
1328 
1329   TimeResults results;
1330   if (!TimeFunctionParallel(&results, []() -> bool {
1331         uint8_t public_key[SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES],
1332             private_key[SLHDSA_SHA2_128S_PRIVATE_KEY_BYTES];
1333         SLHDSA_SHA2_128S_generate_key(public_key, private_key);
1334         return true;
1335       })) {
1336     return false;
1337   }
1338 
1339   results.Print("SLHDSA-SHA2-128s key generation");
1340 
1341   uint8_t public_key[SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES],
1342       private_key[SLHDSA_SHA2_128S_PRIVATE_KEY_BYTES];
1343   SLHDSA_SHA2_128S_generate_key(public_key, private_key);
1344   static const uint8_t kMessage[] = {0, 1, 2, 3, 4, 5};
1345 
1346   if (!TimeFunctionParallel(&results, [&private_key]() -> bool {
1347         uint8_t out[SLHDSA_SHA2_128S_SIGNATURE_BYTES];
1348         SLHDSA_SHA2_128S_sign(out, private_key, kMessage, sizeof(kMessage),
1349                               nullptr, 0);
1350         return true;
1351       })) {
1352     return false;
1353   }
1354 
1355   results.Print("SLHDSA-SHA2-128s signing");
1356 
1357   uint8_t signature[SLHDSA_SHA2_128S_SIGNATURE_BYTES];
1358   SLHDSA_SHA2_128S_sign(signature, private_key, kMessage, sizeof(kMessage),
1359                         nullptr, 0);
1360 
1361   if (!TimeFunctionParallel(&results, [&public_key, &signature]() -> bool {
1362         return SLHDSA_SHA2_128S_verify(signature, sizeof(signature), public_key,
1363                                        kMessage, sizeof(kMessage), nullptr,
1364                                        0) == 1;
1365       })) {
1366     fprintf(stderr, "SLHDSA-SHA2-128s verify failed.\n");
1367     return false;
1368   }
1369 
1370   results.Print("SLHDSA-SHA2-128s verify");
1371 
1372   return true;
1373 }
1374 
SpeedHashToCurve(const std::string & selected)1375 static bool SpeedHashToCurve(const std::string &selected) {
1376   if (!selected.empty() && selected.find("hashtocurve") == std::string::npos) {
1377     return true;
1378   }
1379 
1380   uint8_t input[64];
1381   RAND_bytes(input, sizeof(input));
1382 
1383   static const uint8_t kLabel[] = "label";
1384 
1385   TimeResults results;
1386   {
1387     if (!TimeFunctionParallel(&results, [&]() -> bool {
1388           EC_JACOBIAN out;
1389           return ec_hash_to_curve_p256_xmd_sha256_sswu(EC_group_p256(), &out,
1390                                                        kLabel, sizeof(kLabel),
1391                                                        input, sizeof(input));
1392         })) {
1393       fprintf(stderr, "hash-to-curve failed.\n");
1394       return false;
1395     }
1396     results.Print("hash-to-curve P256_XMD:SHA-256_SSWU_RO_");
1397 
1398     if (!TimeFunctionParallel(&results, [&]() -> bool {
1399           EC_JACOBIAN out;
1400           return ec_hash_to_curve_p384_xmd_sha384_sswu(EC_group_p384(), &out,
1401                                                        kLabel, sizeof(kLabel),
1402                                                        input, sizeof(input));
1403         })) {
1404       fprintf(stderr, "hash-to-curve failed.\n");
1405       return false;
1406     }
1407     results.Print("hash-to-curve P384_XMD:SHA-384_SSWU_RO_");
1408 
1409     if (!TimeFunctionParallel(&results, [&]() -> bool {
1410           EC_SCALAR out;
1411           return ec_hash_to_scalar_p384_xmd_sha512_draft07(
1412               EC_group_p384(), &out, kLabel, sizeof(kLabel), input,
1413               sizeof(input));
1414         })) {
1415       fprintf(stderr, "hash-to-scalar failed.\n");
1416       return false;
1417     }
1418     results.Print("hash-to-scalar P384_XMD:SHA-512");
1419   }
1420 
1421   return true;
1422 }
1423 
SpeedBase64(const std::string & selected)1424 static bool SpeedBase64(const std::string &selected) {
1425   if (!selected.empty() && selected.find("base64") == std::string::npos) {
1426     return true;
1427   }
1428 
1429   static const char kInput[] =
1430       "MIIDtTCCAp2gAwIBAgIJALW2IrlaBKUhMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV"
1431       "BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX"
1432       "aWRnaXRzIFB0eSBMdGQwHhcNMTYwNzA5MDQzODA5WhcNMTYwODA4MDQzODA5WjBF"
1433       "MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50"
1434       "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB"
1435       "CgKCAQEAugvahBkSAUF1fC49vb1bvlPrcl80kop1iLpiuYoz4Qptwy57+EWssZBc"
1436       "HprZ5BkWf6PeGZ7F5AX1PyJbGHZLqvMCvViP6pd4MFox/igESISEHEixoiXCzepB"
1437       "rhtp5UQSjHD4D4hKtgdMgVxX+LRtwgW3mnu/vBu7rzpr/DS8io99p3lqZ1Aky+aN"
1438       "lcMj6MYy8U+YFEevb/V0lRY9oqwmW7BHnXikm/vi6sjIS350U8zb/mRzYeIs2R65"
1439       "LUduTL50+UMgat9ocewI2dv8aO9Dph+8NdGtg8LFYyTTHcUxJoMr1PTOgnmET19W"
1440       "JH4PrFwk7ZE1QJQQ1L4iKmPeQistuQIDAQABo4GnMIGkMB0GA1UdDgQWBBT5m6Vv"
1441       "zYjVYHG30iBE+j2XDhUE8jB1BgNVHSMEbjBsgBT5m6VvzYjVYHG30iBE+j2XDhUE"
1442       "8qFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV"
1443       "BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJALW2IrlaBKUhMAwGA1UdEwQF"
1444       "MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAD7Jg68SArYWlcoHfZAB90Pmyrt5H6D8"
1445       "LRi+W2Ri1fBNxREELnezWJ2scjl4UMcsKYp4Pi950gVN+62IgrImcCNvtb5I1Cfy"
1446       "/MNNur9ffas6X334D0hYVIQTePyFk3umI+2mJQrtZZyMPIKSY/sYGQHhGGX6wGK+"
1447       "GO/og0PQk/Vu6D+GU2XRnDV0YZg1lsAsHd21XryK6fDmNkEMwbIWrts4xc7scRrG"
1448       "HWy+iMf6/7p/Ak/SIicM4XSwmlQ8pPxAZPr+E2LoVd9pMpWUwpW2UbtO5wsGTrY5"
1449       "sO45tFNN/y+jtUheB1C2ijObG/tXELaiyCdM+S/waeuv0MXtI4xnn1A=";
1450 
1451   TimeResults results;
1452   if (!TimeFunctionParallel(&results, [&]() -> bool {
1453         uint8_t out[sizeof(kInput)];
1454         size_t len;
1455         return EVP_DecodeBase64(out, &len, sizeof(out),
1456                                 reinterpret_cast<const uint8_t *>(kInput),
1457                                 strlen(kInput));
1458       })) {
1459     fprintf(stderr, "base64 decode failed.\n");
1460     return false;
1461   }
1462   results.PrintWithBytes("base64 decode", strlen(kInput));
1463   return true;
1464 }
1465 
SpeedSipHash(const std::string & selected)1466 static bool SpeedSipHash(const std::string &selected) {
1467   if (!selected.empty() && selected.find("siphash") == std::string::npos) {
1468     return true;
1469   }
1470 
1471   uint64_t key[2] = {0};
1472   for (size_t len : g_chunk_lengths) {
1473     std::vector<uint8_t> input(len);
1474     TimeResults results;
1475     if (!TimeFunctionParallel(&results, [&]() -> bool {
1476           SIPHASH_24(key, input.data(), input.size());
1477           return true;
1478         })) {
1479       fprintf(stderr, "SIPHASH_24 failed.\n");
1480       ERR_print_errors_fp(stderr);
1481       return false;
1482     }
1483     results.PrintWithBytes("SipHash-2-4" + ChunkLenSuffix(len), len);
1484   }
1485 
1486   return true;
1487 }
1488 
trust_token_pretoken_dup(const TRUST_TOKEN_PRETOKEN * in)1489 static TRUST_TOKEN_PRETOKEN *trust_token_pretoken_dup(
1490     const TRUST_TOKEN_PRETOKEN *in) {
1491   return static_cast<TRUST_TOKEN_PRETOKEN *>(
1492       OPENSSL_memdup(in, sizeof(TRUST_TOKEN_PRETOKEN)));
1493 }
1494 
SpeedTrustToken(std::string name,const TRUST_TOKEN_METHOD * method,size_t batchsize,const std::string & selected)1495 static bool SpeedTrustToken(std::string name, const TRUST_TOKEN_METHOD *method,
1496                             size_t batchsize, const std::string &selected) {
1497   if (!selected.empty() && selected.find("trusttoken") == std::string::npos) {
1498     return true;
1499   }
1500 
1501   TimeResults results;
1502   if (!TimeFunction(&results, [&]() -> bool {
1503         uint8_t priv_key[TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE];
1504         uint8_t pub_key[TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE];
1505         size_t priv_key_len, pub_key_len;
1506         return TRUST_TOKEN_generate_key(
1507             method, priv_key, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE,
1508             pub_key, &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0);
1509       })) {
1510     fprintf(stderr, "TRUST_TOKEN_generate_key failed.\n");
1511     return false;
1512   }
1513   results.Print(name + " generate_key");
1514 
1515   bssl::UniquePtr<TRUST_TOKEN_CLIENT> client(
1516       TRUST_TOKEN_CLIENT_new(method, batchsize));
1517   bssl::UniquePtr<TRUST_TOKEN_ISSUER> issuer(
1518       TRUST_TOKEN_ISSUER_new(method, batchsize));
1519   uint8_t priv_key[TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE];
1520   uint8_t pub_key[TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE];
1521   size_t priv_key_len, pub_key_len, key_index;
1522   if (!client || !issuer ||
1523       !TRUST_TOKEN_generate_key(
1524           method, priv_key, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE,
1525           pub_key, &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0) ||
1526       !TRUST_TOKEN_CLIENT_add_key(client.get(), &key_index, pub_key,
1527                                   pub_key_len) ||
1528       !TRUST_TOKEN_ISSUER_add_key(issuer.get(), priv_key, priv_key_len)) {
1529     fprintf(stderr, "failed to generate trust token key.\n");
1530     return false;
1531   }
1532 
1533   uint8_t public_key[32], private_key[64];
1534   ED25519_keypair(public_key, private_key);
1535   bssl::UniquePtr<EVP_PKEY> priv(
1536       EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, private_key, 32));
1537   bssl::UniquePtr<EVP_PKEY> pub(
1538       EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, public_key, 32));
1539   if (!priv || !pub) {
1540     fprintf(stderr, "failed to generate trust token SRR key.\n");
1541     return false;
1542   }
1543 
1544   TRUST_TOKEN_CLIENT_set_srr_key(client.get(), pub.get());
1545   TRUST_TOKEN_ISSUER_set_srr_key(issuer.get(), priv.get());
1546   uint8_t metadata_key[32];
1547   RAND_bytes(metadata_key, sizeof(metadata_key));
1548   if (!TRUST_TOKEN_ISSUER_set_metadata_key(issuer.get(), metadata_key,
1549                                            sizeof(metadata_key))) {
1550     fprintf(stderr, "failed to generate trust token metadata key.\n");
1551     return false;
1552   }
1553 
1554   if (!TimeFunction(&results, [&]() -> bool {
1555         uint8_t *issue_msg = NULL;
1556         size_t msg_len;
1557         int ok = TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg,
1558                                                    &msg_len, batchsize);
1559         OPENSSL_free(issue_msg);
1560         // Clear pretokens.
1561         sk_TRUST_TOKEN_PRETOKEN_pop_free(client->pretokens,
1562                                          TRUST_TOKEN_PRETOKEN_free);
1563         client->pretokens = sk_TRUST_TOKEN_PRETOKEN_new_null();
1564         return ok;
1565       })) {
1566     fprintf(stderr, "TRUST_TOKEN_CLIENT_begin_issuance failed.\n");
1567     return false;
1568   }
1569   results.Print(name + " begin_issuance");
1570 
1571   uint8_t *issue_msg = NULL;
1572   size_t msg_len;
1573   if (!TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg, &msg_len,
1574                                          batchsize)) {
1575     fprintf(stderr, "TRUST_TOKEN_CLIENT_begin_issuance failed.\n");
1576     return false;
1577   }
1578   bssl::UniquePtr<uint8_t> free_issue_msg(issue_msg);
1579 
1580   bssl::UniquePtr<STACK_OF(TRUST_TOKEN_PRETOKEN)> pretokens(
1581       sk_TRUST_TOKEN_PRETOKEN_deep_copy(client->pretokens,
1582                                         trust_token_pretoken_dup,
1583                                         TRUST_TOKEN_PRETOKEN_free));
1584 
1585   if (!TimeFunction(&results, [&]() -> bool {
1586         uint8_t *issue_resp = NULL;
1587         size_t resp_len, tokens_issued;
1588         int ok = TRUST_TOKEN_ISSUER_issue(issuer.get(), &issue_resp, &resp_len,
1589                                           &tokens_issued, issue_msg, msg_len,
1590                                           /*public_metadata=*/0,
1591                                           /*private_metadata=*/0,
1592                                           /*max_issuance=*/batchsize);
1593         OPENSSL_free(issue_resp);
1594         return ok;
1595       })) {
1596     fprintf(stderr, "TRUST_TOKEN_ISSUER_issue failed.\n");
1597     return false;
1598   }
1599   results.Print(name + " issue");
1600 
1601   uint8_t *issue_resp = NULL;
1602   size_t resp_len, tokens_issued;
1603   if (!TRUST_TOKEN_ISSUER_issue(issuer.get(), &issue_resp, &resp_len,
1604                                 &tokens_issued, issue_msg, msg_len,
1605                                 /*public_metadata=*/0, /*private_metadata=*/0,
1606                                 /*max_issuance=*/batchsize)) {
1607     fprintf(stderr, "TRUST_TOKEN_ISSUER_issue failed.\n");
1608     return false;
1609   }
1610   bssl::UniquePtr<uint8_t> free_issue_resp(issue_resp);
1611 
1612   if (!TimeFunction(&results, [&]() -> bool {
1613         size_t key_index2;
1614         bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
1615             TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index2,
1616                                                issue_resp, resp_len));
1617 
1618         // Reset pretokens.
1619         client->pretokens = sk_TRUST_TOKEN_PRETOKEN_deep_copy(
1620             pretokens.get(), trust_token_pretoken_dup,
1621             TRUST_TOKEN_PRETOKEN_free);
1622         return !!tokens;
1623       })) {
1624     fprintf(stderr, "TRUST_TOKEN_CLIENT_finish_issuance failed.\n");
1625     return false;
1626   }
1627   results.Print(name + " finish_issuance");
1628 
1629   bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
1630       TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, issue_resp,
1631                                          resp_len));
1632   if (!tokens || sk_TRUST_TOKEN_num(tokens.get()) < 1) {
1633     fprintf(stderr, "TRUST_TOKEN_CLIENT_finish_issuance failed.\n");
1634     return false;
1635   }
1636 
1637   const TRUST_TOKEN *token = sk_TRUST_TOKEN_value(tokens.get(), 0);
1638 
1639   const uint8_t kClientData[] = "\x70TEST CLIENT DATA";
1640   uint64_t kRedemptionTime = 13374242;
1641 
1642   if (!TimeFunction(&results, [&]() -> bool {
1643         uint8_t *redeem_msg = NULL;
1644         size_t redeem_msg_len;
1645         int ok = TRUST_TOKEN_CLIENT_begin_redemption(
1646             client.get(), &redeem_msg, &redeem_msg_len, token, kClientData,
1647             sizeof(kClientData) - 1, kRedemptionTime);
1648         OPENSSL_free(redeem_msg);
1649         return ok;
1650       })) {
1651     fprintf(stderr, "TRUST_TOKEN_CLIENT_begin_redemption failed.\n");
1652     return false;
1653   }
1654   results.Print(name + " begin_redemption");
1655 
1656   uint8_t *redeem_msg = NULL;
1657   size_t redeem_msg_len;
1658   if (!TRUST_TOKEN_CLIENT_begin_redemption(
1659           client.get(), &redeem_msg, &redeem_msg_len, token, kClientData,
1660           sizeof(kClientData) - 1, kRedemptionTime)) {
1661     fprintf(stderr, "TRUST_TOKEN_CLIENT_begin_redemption failed.\n");
1662     return false;
1663   }
1664   bssl::UniquePtr<uint8_t> free_redeem_msg(redeem_msg);
1665 
1666   if (!TimeFunction(&results, [&]() -> bool {
1667         uint32_t public_value;
1668         uint8_t private_value;
1669         TRUST_TOKEN *rtoken;
1670         uint8_t *client_data = NULL;
1671         size_t client_data_len;
1672         int ok = TRUST_TOKEN_ISSUER_redeem(
1673             issuer.get(), &public_value, &private_value, &rtoken, &client_data,
1674             &client_data_len, redeem_msg, redeem_msg_len);
1675         OPENSSL_free(client_data);
1676         TRUST_TOKEN_free(rtoken);
1677         return ok;
1678       })) {
1679     fprintf(stderr, "TRUST_TOKEN_ISSUER_redeem failed.\n");
1680     return false;
1681   }
1682   results.Print(name + " redeem");
1683 
1684   uint32_t public_value;
1685   uint8_t private_value;
1686   TRUST_TOKEN *rtoken;
1687   uint8_t *client_data = NULL;
1688   size_t client_data_len;
1689   if (!TRUST_TOKEN_ISSUER_redeem(issuer.get(), &public_value, &private_value,
1690                                  &rtoken, &client_data, &client_data_len,
1691                                  redeem_msg, redeem_msg_len)) {
1692     fprintf(stderr, "TRUST_TOKEN_ISSUER_redeem failed.\n");
1693     return false;
1694   }
1695   bssl::UniquePtr<uint8_t> free_client_data(client_data);
1696   bssl::UniquePtr<TRUST_TOKEN> free_rtoken(rtoken);
1697 
1698   return true;
1699 }
1700 
1701 #if defined(BORINGSSL_FIPS)
SpeedSelfTest(const std::string & selected)1702 static bool SpeedSelfTest(const std::string &selected) {
1703   if (!selected.empty() && selected.find("self-test") == std::string::npos) {
1704     return true;
1705   }
1706 
1707   TimeResults results;
1708   if (!TimeFunction(&results, []() -> bool { return BORINGSSL_self_test(); })) {
1709     fprintf(stderr, "BORINGSSL_self_test faileid.\n");
1710     ERR_print_errors_fp(stderr);
1711     return false;
1712   }
1713 
1714   results.Print("self-test");
1715   return true;
1716 }
1717 #endif
1718 
1719 static const struct argument kArguments[] = {
1720     {
1721         "-filter",
1722         kOptionalArgument,
1723         "A filter on the speed tests to run",
1724     },
1725     {
1726         "-timeout",
1727         kOptionalArgument,
1728         "The number of seconds to run each test for (default is 1)",
1729     },
1730     {
1731         "-chunks",
1732         kOptionalArgument,
1733         "A comma-separated list of input sizes to run tests at (default is "
1734         "16,256,1350,8192,16384)",
1735     },
1736     {
1737         "-json",
1738         kBooleanArgument,
1739         "If this flag is set, speed will print the output of each benchmark in "
1740         "JSON format as follows: \"{\"description\": "
1741         "\"descriptionOfOperation\", \"numCalls\": 1234, "
1742         "\"timeInMicroseconds\": 1234567, \"bytesPerCall\": 1234}\". When "
1743         "there is no information about the bytes per call for an  operation, "
1744         "the JSON field for bytesPerCall will be omitted.",
1745     },
1746 #if defined(OPENSSL_THREADS)
1747     {
1748         "-threads",
1749         kOptionalArgument,
1750         "The number of threads to benchmark in parallel (default is 1)",
1751     },
1752 #endif
1753     {
1754         "",
1755         kOptionalArgument,
1756         "",
1757     },
1758 };
1759 
Speed(const std::vector<std::string> & args)1760 bool Speed(const std::vector<std::string> &args) {
1761   std::map<std::string, std::string> args_map;
1762   if (!ParseKeyValueArguments(&args_map, args, kArguments)) {
1763     PrintUsage(kArguments);
1764     return false;
1765   }
1766 
1767   std::string selected;
1768   if (args_map.count("-filter") != 0) {
1769     selected = args_map["-filter"];
1770   }
1771 
1772   if (args_map.count("-json") != 0) {
1773     g_print_json = true;
1774   }
1775 
1776   if (args_map.count("-timeout") != 0) {
1777     g_timeout_seconds = atoi(args_map["-timeout"].c_str());
1778   }
1779 
1780 #if defined(OPENSSL_THREADS)
1781   if (args_map.count("-threads") != 0) {
1782     g_threads = atoi(args_map["-threads"].c_str());
1783   }
1784 #endif
1785 
1786   if (args_map.count("-chunks") != 0) {
1787     g_chunk_lengths.clear();
1788     const char *start = args_map["-chunks"].data();
1789     const char *end = start + args_map["-chunks"].size();
1790     while (start != end) {
1791       errno = 0;
1792       char *ptr;
1793       unsigned long long val = strtoull(start, &ptr, 10);
1794       if (ptr == start /* no numeric characters found */ ||
1795           errno == ERANGE /* overflow */ || static_cast<size_t>(val) != val) {
1796         fprintf(stderr, "Error parsing -chunks argument\n");
1797         return false;
1798       }
1799       g_chunk_lengths.push_back(static_cast<size_t>(val));
1800       start = ptr;
1801       if (start != end) {
1802         if (*start != ',') {
1803           fprintf(stderr, "Error parsing -chunks argument\n");
1804           return false;
1805         }
1806         start++;
1807       }
1808     }
1809   }
1810 
1811   // kTLSADLen is the number of bytes of additional data that TLS passes to
1812   // AEADs.
1813   static const size_t kTLSADLen = 13;
1814   // kLegacyADLen is the number of bytes that TLS passes to the "legacy" AEADs.
1815   // These are AEADs that weren't originally defined as AEADs, but which we use
1816   // via the AEAD interface. In order for that to work, they have some TLS
1817   // knowledge in them and construct a couple of the AD bytes internally.
1818   static const size_t kLegacyADLen = kTLSADLen - 2;
1819 
1820   if (g_print_json) {
1821     puts("[");
1822   }
1823   if (!SpeedRSA(selected) ||
1824       !SpeedAEAD(EVP_aead_aes_128_gcm(), "AES-128-GCM", kTLSADLen, selected) ||
1825       !SpeedAEAD(EVP_aead_aes_256_gcm(), "AES-256-GCM", kTLSADLen, selected) ||
1826       !SpeedAEAD(EVP_aead_chacha20_poly1305(), "ChaCha20-Poly1305", kTLSADLen,
1827                  selected) ||
1828       !SpeedAEAD(EVP_aead_des_ede3_cbc_sha1_tls(), "DES-EDE3-CBC-SHA1",
1829                  kLegacyADLen, selected) ||
1830       !SpeedAEAD(EVP_aead_aes_128_cbc_sha1_tls(), "AES-128-CBC-SHA1",
1831                  kLegacyADLen, selected) ||
1832       !SpeedAEAD(EVP_aead_aes_256_cbc_sha1_tls(), "AES-256-CBC-SHA1",
1833                  kLegacyADLen, selected) ||
1834       !SpeedAEADOpen(EVP_aead_aes_128_cbc_sha1_tls(), "AES-128-CBC-SHA1",
1835                      kLegacyADLen, selected) ||
1836       !SpeedAEADOpen(EVP_aead_aes_256_cbc_sha1_tls(), "AES-256-CBC-SHA1",
1837                      kLegacyADLen, selected) ||
1838       !SpeedAEAD(EVP_aead_aes_128_gcm_siv(), "AES-128-GCM-SIV", kTLSADLen,
1839                  selected) ||
1840       !SpeedAEAD(EVP_aead_aes_256_gcm_siv(), "AES-256-GCM-SIV", kTLSADLen,
1841                  selected) ||
1842       !SpeedAEADOpen(EVP_aead_aes_128_gcm_siv(), "AES-128-GCM-SIV", kTLSADLen,
1843                      selected) ||
1844       !SpeedAEADOpen(EVP_aead_aes_256_gcm_siv(), "AES-256-GCM-SIV", kTLSADLen,
1845                      selected) ||
1846       !SpeedAEAD(EVP_aead_aes_128_ccm_bluetooth(), "AES-128-CCM-Bluetooth",
1847                  kTLSADLen, selected) ||
1848       !SpeedAESBlock("AES-128", 128, selected) ||
1849       !SpeedAESBlock("AES-256", 256, selected) ||
1850       !SpeedHash(EVP_sha1(), "SHA-1", selected) ||
1851       !SpeedHash(EVP_sha256(), "SHA-256", selected) ||
1852       !SpeedHash(EVP_sha512(), "SHA-512", selected) ||
1853       !SpeedHash(EVP_blake2b256(), "BLAKE2b-256", selected) ||
1854       !SpeedRandom(selected) ||       //
1855       !SpeedECDH(selected) ||         //
1856       !SpeedECDSA(selected) ||        //
1857       !Speed25519(selected) ||        //
1858       !SpeedSPAKE2(selected) ||       //
1859       !SpeedScrypt(selected) ||       //
1860       !SpeedRSAKeyGen(selected) ||    //
1861       !SpeedHRSS(selected) ||         //
1862       !SpeedKyber(selected) ||        //
1863       !SpeedMLDSA(selected) ||        //
1864       !SpeedMLKEM(selected) ||        //
1865       !SpeedMLKEM1024(selected) ||    //
1866       !SpeedSLHDSA(selected) ||       //
1867       !SpeedHashToCurve(selected) ||  //
1868       !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1,
1869                        selected) ||
1870       !SpeedTrustToken("TrustToken-Exp1-Batch10", TRUST_TOKEN_experiment_v1(),
1871                        10, selected) ||
1872       !SpeedTrustToken("TrustToken-Exp2VOPRF-Batch1",
1873                        TRUST_TOKEN_experiment_v2_voprf(), 1, selected) ||
1874       !SpeedTrustToken("TrustToken-Exp2VOPRF-Batch10",
1875                        TRUST_TOKEN_experiment_v2_voprf(), 10, selected) ||
1876       !SpeedTrustToken("TrustToken-Exp2PMB-Batch1",
1877                        TRUST_TOKEN_experiment_v2_pmb(), 1, selected) ||
1878       !SpeedTrustToken("TrustToken-Exp2PMB-Batch10",
1879                        TRUST_TOKEN_experiment_v2_pmb(), 10, selected) ||
1880       !SpeedBase64(selected) ||  //
1881       !SpeedSipHash(selected)) {
1882     return false;
1883   }
1884 #if defined(BORINGSSL_FIPS)
1885   if (!SpeedSelfTest(selected)) {
1886     return false;
1887   }
1888 #endif
1889   if (g_print_json) {
1890     puts("\n]");
1891   }
1892 
1893   return true;
1894 }
1895