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