• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 11280486949

10 Oct 2024 07:12PM UTC coverage: 90.994% (-0.4%) from 91.389%
11280486949

push

github

web-flow
Merge pull request #4364 from randombit/jack/refactor-speed

Initial refactoring of speed cmdlet

89935 of 98836 relevant lines covered (90.99%)

8791476.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.86
/src/cli/speed.cpp
1
/*
2
* (C) 2009,2010,2014,2015,2017,2018 Jack Lloyd
3
* (C) 2015 Simon Warta (Kullo GmbH)
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "cli.h"
9
#include "perf.h"
10

11
#include <algorithm>
12
#include <chrono>
13
#include <functional>
14
#include <iomanip>
15
#include <map>
16
#include <set>
17
#include <sstream>
18

19
// Always available:
20
#include <botan/entropy_src.h>
21
#include <botan/version.h>
22
#include <botan/internal/cpuid.h>
23
#include <botan/internal/fmt.h>
24
#include <botan/internal/os_utils.h>
25
#include <botan/internal/stl_util.h>
26
#include <botan/internal/timer.h>
27

28
#if defined(BOTAN_HAS_BLOCK_CIPHER)
29
   #include <botan/block_cipher.h>
30
#endif
31

32
#if defined(BOTAN_HAS_STREAM_CIPHER)
33
   #include <botan/stream_cipher.h>
34
#endif
35

36
#if defined(BOTAN_HAS_HASH)
37
   #include <botan/hash.h>
38
#endif
39

40
#if defined(BOTAN_HAS_XOF)
41
   #include <botan/xof.h>
42
#endif
43

44
#if defined(BOTAN_HAS_CIPHER_MODES)
45
   #include <botan/cipher_mode.h>
46
#endif
47

48
#if defined(BOTAN_HAS_MAC)
49
   #include <botan/mac.h>
50
#endif
51

52
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
53
   #include <botan/pk_algs.h>
54
   #include <botan/pkcs8.h>
55
   #include <botan/pubkey.h>
56
   #include <botan/x509_key.h>
57
#endif
58

59
#if defined(BOTAN_HAS_ECC_GROUP)
60
   #include <botan/ec_group.h>
61
#endif
62

63
#if defined(BOTAN_HAS_PCURVES)
64
   #include <botan/internal/pcurves.h>
65
#endif
66

67
#if defined(BOTAN_HAS_MCELIECE)
68
   #include <botan/mceliece.h>
69
#endif
70

71
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
72
   #include <botan/kyber.h>
73
#endif
74

75
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
76
   #include <botan/dilithium.h>
77
#endif
78

79
#if defined(BOTAN_HAS_HSS_LMS)
80
   #include <botan/hss_lms.h>
81
#endif
82

83
#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
84
   #include <botan/sphincsplus.h>
85
#endif
86

87
#if defined(BOTAN_HAS_FRODOKEM)
88
   #include <botan/frodokem.h>
89
#endif
90

91
#if defined(BOTAN_HAS_ECDSA)
92
   #include <botan/ecdsa.h>
93
#endif
94

95
namespace Botan_CLI {
96

97
using Botan::Timer;
98

99
namespace {
100

101
class JSON_Output final {
2✔
102
   public:
103
      void add(const Timer& timer) { m_results.push_back(timer); }
2✔
104

105
      std::string print() const {
1✔
106
         std::ostringstream out;
1✔
107

108
         out << "[\n";
1✔
109

110
         for(size_t i = 0; i != m_results.size(); ++i) {
3✔
111
            const Timer& t = m_results[i];
2✔
112

113
            out << "{"
2✔
114
                << "\"algo\": \"" << t.get_name() << "\", "
2✔
115
                << "\"op\": \"" << t.doing() << "\", "
2✔
116
                << "\"events\": " << t.events() << ", ";
2✔
117

118
            if(t.cycles_consumed() > 0) {
4✔
119
               out << "\"cycles\": " << t.cycles_consumed() << ", ";
4✔
120
            }
121

122
            if(t.buf_size() > 0) {
2✔
123
               out << "\"bps\": " << static_cast<uint64_t>(t.events() / (t.value() / 1000000000.0)) << ", ";
2✔
124
               out << "\"buf_size\": " << t.buf_size() << ", ";
2✔
125
            }
126

127
            out << "\"nanos\": " << t.value() << "}";
2✔
128

129
            if(i != m_results.size() - 1) {
2✔
130
               out << ",";
1✔
131
            }
132

133
            out << "\n";
2✔
134
         }
135
         out << "]\n";
1✔
136

137
         return out.str();
2✔
138
      }
1✔
139

140
   private:
141
      std::vector<Timer> m_results;
142
};
143

144
class Summary final {
1✔
145
   public:
146
      Summary() = default;
1✔
147

148
      void add(const Timer& t) {
2✔
149
         if(t.buf_size() == 0) {
2✔
150
            m_ops_entries.push_back(t);
×
151
         } else {
152
            m_bps_entries[std::make_pair(t.doing(), t.get_name())].push_back(t);
4✔
153
         }
154
      }
2✔
155

156
      std::string print() {
1✔
157
         const size_t name_padding = 35;
1✔
158
         const size_t op_name_padding = 16;
1✔
159
         const size_t op_padding = 16;
1✔
160

161
         std::ostringstream result_ss;
1✔
162
         result_ss << std::fixed;
1✔
163

164
         if(!m_bps_entries.empty()) {
1✔
165
            result_ss << "\n";
1✔
166

167
            // add table header
168
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
1✔
169
                      << "operation";
1✔
170

171
            for(const Timer& t : m_bps_entries.begin()->second) {
2✔
172
               result_ss << std::setw(op_padding) << std::right << (std::to_string(t.buf_size()) + " bytes");
2✔
173
            }
174
            result_ss << "\n";
1✔
175

176
            // add table entries
177
            for(const auto& entry : m_bps_entries) {
3✔
178
               if(entry.second.empty()) {
2✔
179
                  continue;
×
180
               }
181

182
               result_ss << std::setw(name_padding) << std::left << (entry.first.second) << std::setw(op_name_padding)
2✔
183
                         << std::left << (entry.first.first);
2✔
184

185
               for(const Timer& t : entry.second) {
4✔
186
                  if(t.events() == 0) {
2✔
187
                     result_ss << std::setw(op_padding) << std::right << "N/A";
×
188
                  } else {
189
                     result_ss << std::setw(op_padding) << std::right << std::setprecision(2)
2✔
190
                               << (t.bytes_per_second() / 1000.0);
2✔
191
                  }
192
               }
193

194
               result_ss << "\n";
2✔
195
            }
196

197
            result_ss << "\n[results are the number of 1000s bytes processed per second]\n";
1✔
198
         }
199

200
         if(!m_ops_entries.empty()) {
1✔
201
            result_ss << std::setprecision(6) << "\n";
×
202

203
            // sort entries
204
            std::sort(m_ops_entries.begin(), m_ops_entries.end());
×
205

206
            // add table header
207
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
×
208
                      << "operation" << std::setw(op_padding) << std::right << "sec/op" << std::setw(op_padding)
×
209
                      << std::right << "op/sec"
×
210
                      << "\n";
×
211

212
            // add table entries
213
            for(const Timer& entry : m_ops_entries) {
×
214
               result_ss << std::setw(name_padding) << std::left << entry.get_name() << std::setw(op_name_padding)
×
215
                         << std::left << entry.doing() << std::setw(op_padding) << std::right
×
216
                         << entry.seconds_per_event() << std::setw(op_padding) << std::right
×
217
                         << entry.events_per_second() << "\n";
×
218
            }
219
         }
220

221
         return result_ss.str();
2✔
222
      }
1✔
223

224
   private:
225
      std::map<std::pair<std::string, std::string>, std::vector<Timer>> m_bps_entries;
226
      std::vector<Timer> m_ops_entries;
227
};
228

229
std::vector<size_t> unique_buffer_sizes(const std::string& cmdline_arg) {
32✔
230
   const size_t MAX_BUF_SIZE = 64 * 1024 * 1024;
32✔
231

232
   std::set<size_t> buf;
32✔
233
   for(const std::string& size_str : Command::split_on(cmdline_arg, ',')) {
62✔
234
      size_t x = 0;
33✔
235
      try {
33✔
236
         size_t converted = 0;
33✔
237
         x = static_cast<size_t>(std::stoul(size_str, &converted, 0));
33✔
238

239
         if(converted != size_str.size()) {
32✔
240
            throw CLI_Usage_Error("Invalid integer");
×
241
         }
242
      } catch(std::exception&) {
1✔
243
         throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size");
2✔
244
      }
1✔
245

246
      if(x == 0) {
32✔
247
         throw CLI_Usage_Error("Cannot have a zero-sized buffer");
2✔
248
      }
249

250
      if(x > MAX_BUF_SIZE) {
31✔
251
         throw CLI_Usage_Error("Specified buffer size is too large");
2✔
252
      }
253

254
      buf.insert(x);
30✔
255
   }
32✔
256

257
   return std::vector<size_t>(buf.begin(), buf.end());
32✔
258
}
29✔
259

260
}  // namespace
261

262
class Speed final : public Command {
×
263
   public:
264
      Speed() :
33✔
265
            Command(
266
               "speed --msec=500 --format=default --ecc-groups= --provider= --buf-size=1024 --clear-cpuid= --cpu-clock-speed=0 --cpu-clock-ratio=1.0 *algos") {
66✔
267
      }
33✔
268

269
      static std::vector<std::string> default_benchmark_list() {
×
270
         /*
271
         This is not intended to be exhaustive: it just hits the high
272
         points of the most interesting or widely used algorithms.
273
         */
274
         // clang-format off
275
         return {
×
276
            /* Block ciphers */
277
            "AES-128",
278
            "AES-192",
279
            "AES-256",
280
            "ARIA-128",
281
            "ARIA-192",
282
            "ARIA-256",
283
            "Blowfish",
284
            "CAST-128",
285
            "Camellia-128",
286
            "Camellia-192",
287
            "Camellia-256",
288
            "DES",
289
            "TripleDES",
290
            "GOST-28147-89",
291
            "IDEA",
292
            "Noekeon",
293
            "SHACAL2",
294
            "SM4",
295
            "Serpent",
296
            "Threefish-512",
297
            "Twofish",
298

299
            /* Cipher modes */
300
            "AES-128/CBC",
301
            "AES-128/CTR-BE",
302
            "AES-128/EAX",
303
            "AES-128/OCB",
304
            "AES-128/GCM",
305
            "AES-128/XTS",
306
            "AES-128/SIV",
307

308
            "Serpent/CBC",
309
            "Serpent/CTR-BE",
310
            "Serpent/EAX",
311
            "Serpent/OCB",
312
            "Serpent/GCM",
313
            "Serpent/XTS",
314
            "Serpent/SIV",
315

316
            "ChaCha20Poly1305",
317

318
            /* Stream ciphers */
319
            "RC4",
320
            "Salsa20",
321
            "ChaCha20",
322

323
            /* Hashes */
324
            "SHA-1",
325
            "SHA-256",
326
            "SHA-512",
327
            "SHA-3(256)",
328
            "SHA-3(512)",
329
            "RIPEMD-160",
330
            "Skein-512",
331
            "Blake2b",
332
            "Whirlpool",
333

334
            /* XOFs */
335
            "SHAKE-128",
336
            "SHAKE-256",
337

338
            /* MACs */
339
            "CMAC(AES-128)",
340
            "HMAC(SHA-256)",
341

342
            /* pubkey */
343
            "RSA",
344
            "DH",
345
            "ECDH",
346
            "ECDSA",
347
            "Ed25519",
348
            "Ed448",
349
            "X25519",
350
            "X448",
351
            "McEliece",
352
            "Kyber",
353
            "SPHINCS+",
354
            "FrodoKEM",
355
            "HSS-LMS",
356
         };
×
357
         // clang-format on
358
      }
359

360
      std::string group() const override { return "misc"; }
1✔
361

362
      std::string description() const override { return "Measures the speed of algorithms"; }
1✔
363

364
      void go() override {
32✔
365
         std::chrono::milliseconds msec(get_arg_sz("msec"));
32✔
366
         const std::string provider = get_arg("provider");
32✔
367
         std::vector<std::string> ecc_groups = Command::split_on(get_arg("ecc-groups"), ',');
67✔
368
         const std::string format = get_arg("format");
32✔
369
         const std::string clock_ratio = get_arg("cpu-clock-ratio");
35✔
370
         m_clock_speed = get_arg_sz("cpu-clock-speed");
32✔
371

372
         m_clock_cycle_ratio = std::strtod(clock_ratio.c_str(), nullptr);
32✔
373

374
         /*
375
         * This argument is intended to be the ratio between the cycle counter
376
         * and the actual machine cycles. It is extremely unlikely that there is
377
         * any machine where the cycle counter increments faster than the actual
378
         * clock.
379
         */
380
         if(m_clock_cycle_ratio < 0.0 || m_clock_cycle_ratio > 1.0) {
32✔
381
            throw CLI_Usage_Error("Unlikely CPU clock ratio of " + clock_ratio);
×
382
         }
383

384
         m_clock_cycle_ratio = 1.0 / m_clock_cycle_ratio;
32✔
385

386
         if(m_clock_speed != 0 && Botan::OS::get_cpu_cycle_counter() != 0) {
32✔
387
            error_output() << "The --cpu-clock-speed option is only intended to be used on "
×
388
                              "platforms without access to a cycle counter.\n"
389
                              "Expected incorrect results\n\n";
×
390
         }
391

392
         if(format == "table") {
32✔
393
            m_summary = std::make_unique<Summary>();
1✔
394
         } else if(format == "json") {
31✔
395
            m_json = std::make_unique<JSON_Output>();
1✔
396
         } else if(format != "default") {
30✔
397
            throw CLI_Usage_Error("Unknown --format type '" + format + "'");
×
398
         }
399

400
#if defined(BOTAN_HAS_ECC_GROUP)
401
         if(ecc_groups.empty()) {
32✔
402
            ecc_groups = {"secp256r1", "secp384r1", "secp521r1", "brainpool256r1", "brainpool384r1", "brainpool512r1"};
256✔
403
         } else if(ecc_groups.size() == 1 && ecc_groups[0] == "all") {
×
404
            auto all = Botan::EC_Group::known_named_groups();
×
405
            ecc_groups.assign(all.begin(), all.end());
×
406
         }
×
407
#endif
408

409
         std::vector<std::string> algos = get_arg_list("algos");
35✔
410

411
         const std::vector<size_t> buf_sizes = unique_buffer_sizes(get_arg("buf-size"));
67✔
412

413
         for(const std::string& cpuid_to_clear : Command::split_on(get_arg("clear-cpuid"), ',')) {
30✔
414
            auto bits = Botan::CPUID::bit_from_string(cpuid_to_clear);
1✔
415
            if(bits.empty()) {
1✔
416
               error_output() << "Warning don't know CPUID flag '" << cpuid_to_clear << "'\n";
1✔
417
            }
418

419
            for(auto bit : bits) {
1✔
420
               Botan::CPUID::clear_cpuid_bit(bit);
×
421
            }
422
         }
30✔
423

424
         if(verbose() || m_summary) {
29✔
425
            output() << Botan::version_string() << "\n"
2✔
426
                     << "CPUID: " << Botan::CPUID::to_string() << "\n\n";
3✔
427
         }
428

429
         const bool using_defaults = (algos.empty());
29✔
430
         if(using_defaults) {
29✔
431
            algos = default_benchmark_list();
×
432
         }
433

434
         class PerfConfig_Cli final : public PerfConfig {
29✔
435
            public:
436
               PerfConfig_Cli(std::chrono::milliseconds runtime,
29✔
437
                              const std::vector<size_t>& buffer_sizes,
438
                              Speed* speed) :
29✔
439
                     m_runtime(runtime), m_buffer_sizes(buffer_sizes), m_speed(speed) {}
58✔
440

441
               const std::vector<size_t>& buffer_sizes() const override { return m_buffer_sizes; }
7✔
442

443
               std::chrono::milliseconds runtime() const override { return m_runtime; }
42✔
444

445
               std::ostream& error_output() const override { return m_speed->error_output(); }
×
446

447
               Botan::RandomNumberGenerator& rng() const override { return m_speed->rng(); }
10,039✔
448

449
               void record_result(const Botan::Timer& timer) const override { m_speed->record_result(timer); }
176✔
450

451
               std::unique_ptr<Botan::Timer> make_timer(const std::string& alg,
176✔
452
                                                        uint64_t event_mult,
453
                                                        const std::string& what,
454
                                                        const std::string& provider,
455
                                                        size_t buf_size) const override {
456
                  return m_speed->make_timer(alg, event_mult, what, provider, buf_size);
352✔
457
               }
458

459
            private:
460
               std::chrono::milliseconds m_runtime;
461
               std::vector<size_t> m_buffer_sizes;
462
               Speed* m_speed;
463
         };
464

465
         PerfConfig_Cli perf_config(msec, buf_sizes, this);
29✔
466

467
         for(const auto& algo : algos) {
80✔
468
            using namespace std::placeholders;
51✔
469

470
            if(auto perf = PerfTest::get(algo)) {
51✔
471
               perf->go(perf_config);
18✔
472
            }
473
#if defined(BOTAN_HAS_HASH)
474
            else if(!Botan::HashFunction::providers(algo).empty()) {
33✔
475
               bench_providers_of<Botan::HashFunction>(
1✔
476
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_hash, this, _1, _2, _3, _4));
2✔
477
            }
478
#endif
479
#if defined(BOTAN_HAS_XOF)
480
            else if(!Botan::XOF::providers(algo).empty()) {
32✔
481
               bench_providers_of<Botan::XOF>(
×
482
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_xof, this, _1, _2, _3, _4));
×
483
            }
484
#endif
485
#if defined(BOTAN_HAS_BLOCK_CIPHER)
486
            else if(!Botan::BlockCipher::providers(algo).empty()) {
32✔
487
               bench_providers_of<Botan::BlockCipher>(
4✔
488
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_block_cipher, this, _1, _2, _3, _4));
8✔
489
            }
490
#endif
491
#if defined(BOTAN_HAS_STREAM_CIPHER)
492
            else if(!Botan::StreamCipher::providers(algo).empty()) {
28✔
493
               bench_providers_of<Botan::StreamCipher>(
1✔
494
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_stream_cipher, this, _1, _2, _3, _4));
2✔
495
            }
496
#endif
497
#if defined(BOTAN_HAS_CIPHER_MODES)
498
            else if(auto enc = Botan::Cipher_Mode::create(algo, Botan::Cipher_Dir::Encryption, provider)) {
27✔
499
               auto dec = Botan::Cipher_Mode::create_or_throw(algo, Botan::Cipher_Dir::Decryption, provider);
1✔
500
               bench_cipher_mode(*enc, *dec, msec, buf_sizes);
1✔
501
            }
1✔
502
#endif
503
#if defined(BOTAN_HAS_MAC)
504
            else if(!Botan::MessageAuthenticationCode::providers(algo).empty()) {
26✔
505
               bench_providers_of<Botan::MessageAuthenticationCode>(
1✔
506
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_mac, this, _1, _2, _3, _4));
2✔
507
            }
508
#endif
509
#if defined(BOTAN_HAS_RSA)
510
            else if(algo == "RSA") {
25✔
511
               bench_rsa(provider, msec);
1✔
512
            } else if(algo == "RSA_keygen") {
24✔
513
               bench_rsa_keygen(provider, msec);
1✔
514
            }
515
#endif
516

517
#if defined(BOTAN_HAS_PCURVES)
518
            else if(algo == "ECDSA-pcurves") {
23✔
519
               bench_pcurve_ecdsa(ecc_groups, msec);
×
520
            } else if(algo == "ECDH-pcurves") {
23✔
521
               bench_pcurve_ecdh(ecc_groups, msec);
×
522
            } else if(algo == "pcurves") {
23✔
523
               bench_pcurves(ecc_groups, msec);
×
524
            }
525
#endif
526

527
#if defined(BOTAN_HAS_ECDSA)
528
            else if(algo == "ECDSA") {
23✔
529
               bench_ecdsa(ecc_groups, provider, msec);
1✔
530
            } else if(algo == "ecdsa_recovery") {
22✔
531
               bench_ecdsa_recovery(ecc_groups, provider, msec);
1✔
532
            }
533
#endif
534
#if defined(BOTAN_HAS_SM2)
535
            else if(algo == "SM2") {
21✔
536
               bench_sm2(ecc_groups, provider, msec);
1✔
537
            }
538
#endif
539
#if defined(BOTAN_HAS_ECKCDSA)
540
            else if(algo == "ECKCDSA") {
20✔
541
               bench_eckcdsa(ecc_groups, provider, msec);
1✔
542
            }
543
#endif
544
#if defined(BOTAN_HAS_GOST_34_10_2001)
545
            else if(algo == "GOST-34.10") {
19✔
546
               bench_gost_3410(provider, msec);
1✔
547
            }
548
#endif
549
#if defined(BOTAN_HAS_ECGDSA)
550
            else if(algo == "ECGDSA") {
18✔
551
               bench_ecgdsa(ecc_groups, provider, msec);
1✔
552
            }
553
#endif
554
#if defined(BOTAN_HAS_ED25519)
555
            else if(algo == "Ed25519") {
17✔
556
               bench_ed25519(provider, msec);
1✔
557
            }
558
#endif
559
#if defined(BOTAN_HAS_ED448)
560
            else if(algo == "Ed448") {
16✔
561
               bench_ed448(provider, msec);
1✔
562
            }
563
#endif
564
#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
565
            else if(algo == "DH") {
15✔
566
               bench_dh(provider, msec);
1✔
567
            }
568
#endif
569
#if defined(BOTAN_HAS_DSA)
570
            else if(algo == "DSA") {
14✔
571
               bench_dsa(provider, msec);
1✔
572
            }
573
#endif
574
#if defined(BOTAN_HAS_ELGAMAL)
575
            else if(algo == "ElGamal") {
13✔
576
               bench_elgamal(provider, msec);
1✔
577
            }
578
#endif
579
#if defined(BOTAN_HAS_ECDH)
580
            else if(algo == "ECDH") {
12✔
581
               bench_ecdh(ecc_groups, provider, msec);
1✔
582
            }
583
#endif
584
#if defined(BOTAN_HAS_X25519)
585
            else if(algo == "X25519") {
11✔
586
               bench_x25519(provider, msec);
1✔
587
            }
588
#endif
589
#if defined(BOTAN_HAS_X448)
590
            else if(algo == "X448") {
10✔
591
               bench_x448(provider, msec);
1✔
592
            }
593
#endif
594
#if defined(BOTAN_HAS_MCELIECE)
595
            else if(algo == "McEliece") {
9✔
596
               bench_mceliece(provider, msec);
1✔
597
            }
598
#endif
599
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
600
            else if(algo == "Kyber") {
8✔
601
               bench_kyber(provider, msec);
1✔
602
            }
603
#endif
604
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
605
            else if(algo == "Dilithium") {
7✔
606
               bench_dilithium(provider, msec);
1✔
607
            }
608
#endif
609
#if defined(BOTAN_HAS_XMSS_RFC8391)
610
            else if(algo == "XMSS") {
6✔
611
               bench_xmss(provider, msec);
1✔
612
            }
613
#endif
614
#if defined(BOTAN_HAS_HSS_LMS)
615
            else if(algo == "HSS-LMS") {
5✔
616
               bench_hss_lms(provider, msec);
×
617
            }
618
#endif
619
#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
620
            else if(algo == "SPHINCS+") {
5✔
621
               bench_sphincs_plus(provider, msec);
1✔
622
            }
623
#endif
624
#if defined(BOTAN_HAS_FRODOKEM)
625
            else if(algo == "FrodoKEM") {
4✔
626
               bench_frodokem(provider, msec);
×
627
            }
628
#endif
629

630
#if defined(BOTAN_HAS_ECC_GROUP)
631
            else if(algo == "ecc_mult") {
4✔
632
               bench_ecc_mult(ecc_groups, msec);
1✔
633
            } else if(algo == "ecc_init") {
3✔
634
               bench_ecc_init(ecc_groups, msec);
1✔
635
            } else if(algo == "os2ecp") {
2✔
636
               bench_os2ecp(ecc_groups, msec);
1✔
637
            }
638
#endif
639
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
640
            else if(algo == "ec_h2c") {
1✔
641
               bench_ec_h2c(msec);
1✔
642
            }
643
#endif
644
            else {
645
               if(verbose() || !using_defaults) {
×
646
                  error_output() << "Unknown algorithm '" << algo << "'\n";
×
647
               }
648
            }
78✔
649
         }
650

651
         if(m_json) {
29✔
652
            output() << m_json->print();
2✔
653
         }
654
         if(m_summary) {
29✔
655
            output() << m_summary->print() << "\n";
3✔
656
         }
657

658
         if(verbose() && m_clock_speed == 0 && m_cycles_consumed > 0 && m_ns_taken > 0) {
29✔
659
            const double seconds = static_cast<double>(m_ns_taken) / 1000000000;
×
660
            const double Hz = static_cast<double>(m_cycles_consumed) / seconds;
×
661
            const double MHz = Hz / 1000000;
×
662
            output() << "\nEstimated clock speed " << MHz << " MHz\n";
×
663
         }
664
      }
131✔
665

666
   private:
667
      size_t m_clock_speed = 0;
668
      double m_clock_cycle_ratio = 0.0;
669
      uint64_t m_cycles_consumed = 0;
670
      uint64_t m_ns_taken = 0;
671
      std::unique_ptr<Summary> m_summary;
672
      std::unique_ptr<JSON_Output> m_json;
673

674
      void record_result(const Timer& t) {
435✔
675
         m_ns_taken += t.value();
435✔
676
         m_cycles_consumed += t.cycles_consumed();
435✔
677
         if(m_json) {
435✔
678
            m_json->add(t);
2✔
679
         } else {
680
            output() << t.to_string() << std::flush;
866✔
681
            if(m_summary) {
433✔
682
               m_summary->add(t);
2✔
683
            }
684
         }
685
      }
435✔
686

687
      void record_result(const std::unique_ptr<Timer>& t) { record_result(*t); }
259✔
688

689
      template <typename T>
690
      using bench_fn = std::function<void(T&, std::string, std::chrono::milliseconds, const std::vector<size_t>&)>;
691

692
      template <typename T>
693
      void bench_providers_of(const std::string& algo,
7✔
694
                              const std::string& provider, /* user request, if any */
695
                              const std::chrono::milliseconds runtime,
696
                              const std::vector<size_t>& buf_sizes,
697
                              bench_fn<T> bench_one) {
698
         for(const auto& prov : T::providers(algo)) {
14✔
699
            if(provider.empty() || provider == prov) {
7✔
700
               auto p = T::create(algo, prov);
7✔
701

702
               if(p) {
7✔
703
                  bench_one(*p, prov, runtime, buf_sizes);
14✔
704
               }
705
            }
7✔
706
         }
707
      }
7✔
708

709
      std::unique_ptr<Timer> make_timer(const std::string& name,
439✔
710
                                        uint64_t event_mult = 1,
711
                                        const std::string& what = "",
712
                                        const std::string& provider = "",
713
                                        size_t buf_size = 0) {
714
         return std::make_unique<Timer>(name, provider, what, event_mult, buf_size, m_clock_cycle_ratio, m_clock_speed);
227✔
715
      }
716

717
      std::unique_ptr<Timer> make_timer(const std::string& algo, const std::string& provider, const std::string& what) {
212✔
718
         return make_timer(algo, 1, what, provider, 0);
120✔
719
      }
720

721
#if defined(BOTAN_HAS_BLOCK_CIPHER)
722
      void bench_block_cipher(Botan::BlockCipher& cipher,
4✔
723
                              const std::string& provider,
724
                              std::chrono::milliseconds runtime,
725
                              const std::vector<size_t>& buf_sizes) {
726
         auto ks_timer = make_timer(cipher.name(), provider, "key schedule");
8✔
727

728
         const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
4✔
729
         ks_timer->run([&]() { cipher.set_key(key); });
8✔
730

731
         const size_t bs = cipher.block_size();
4✔
732
         std::set<size_t> buf_sizes_in_blocks;
4✔
733
         for(size_t buf_size : buf_sizes) {
9✔
734
            if(buf_size % bs == 0) {
5✔
735
               buf_sizes_in_blocks.insert(buf_size);
5✔
736
            } else {
737
               buf_sizes_in_blocks.insert(buf_size + bs - (buf_size % bs));
×
738
            }
739
         }
740

741
         for(size_t buf_size : buf_sizes_in_blocks) {
9✔
742
            std::vector<uint8_t> buffer(buf_size);
5✔
743
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
5✔
744
            const size_t blocks = buf_size / bs;
5✔
745

746
            auto encrypt_timer = make_timer(cipher.name(), mult * buffer.size(), "encrypt", provider, buf_size);
10✔
747
            auto decrypt_timer = make_timer(cipher.name(), mult * buffer.size(), "decrypt", provider, buf_size);
10✔
748

749
            encrypt_timer->run_until_elapsed(runtime, [&]() {
5✔
750
               for(size_t i = 0; i != mult; ++i) {
18,132✔
751
                  cipher.encrypt_n(&buffer[0], &buffer[0], blocks);
18,048✔
752
               }
753
            });
84✔
754
            record_result(encrypt_timer);
5✔
755

756
            decrypt_timer->run_until_elapsed(runtime, [&]() {
5✔
757
               for(size_t i = 0; i != mult; ++i) {
17,938✔
758
                  cipher.decrypt_n(&buffer[0], &buffer[0], blocks);
17,856✔
759
               }
760
            });
82✔
761
            record_result(decrypt_timer);
5✔
762
         }
5✔
763
      }
8✔
764
#endif
765

766
#if defined(BOTAN_HAS_STREAM_CIPHER)
767
      void bench_stream_cipher(Botan::StreamCipher& cipher,
1✔
768
                               const std::string& provider,
769
                               const std::chrono::milliseconds runtime,
770
                               const std::vector<size_t>& buf_sizes) {
771
         for(auto buf_size : buf_sizes) {
2✔
772
            const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
1✔
773
            cipher.set_key(key);
1✔
774

775
            if(cipher.valid_iv_length(12)) {
1✔
776
               const Botan::InitializationVector iv(rng(), 12);
1✔
777
               cipher.set_iv(iv.begin(), iv.size());
1✔
778
            }
1✔
779

780
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
781

782
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
783

784
            auto encrypt_timer = make_timer(cipher.name(), mult * buffer.size(), "encrypt", provider, buf_size);
2✔
785

786
            encrypt_timer->run_until_elapsed(runtime, [&]() {
1✔
787
               for(size_t i = 0; i != mult; ++i) {
975✔
788
                  cipher.encipher(buffer);
960✔
789
               }
790
            });
15✔
791

792
            record_result(encrypt_timer);
1✔
793

794
            if(verbose()) {
1✔
795
               auto ks_timer = make_timer(cipher.name(), buffer.size(), "write_keystream", provider, buf_size);
×
796

797
               while(ks_timer->under(runtime)) {
×
798
                  ks_timer->run([&]() { cipher.write_keystream(buffer.data(), buffer.size()); });
×
799
               }
800
               record_result(ks_timer);
×
801
            }
×
802
         }
3✔
803
      }
1✔
804
#endif
805

806
#if defined(BOTAN_HAS_HASH)
807
      void bench_hash(Botan::HashFunction& hash,
1✔
808
                      const std::string& provider,
809
                      const std::chrono::milliseconds runtime,
810
                      const std::vector<size_t>& buf_sizes) {
811
         std::vector<uint8_t> output(hash.output_length());
1✔
812

813
         for(auto buf_size : buf_sizes) {
2✔
814
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
815

816
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
817

818
            auto timer = make_timer(hash.name(), mult * buffer.size(), "hash", provider, buf_size);
2✔
819
            timer->run_until_elapsed(runtime, [&]() {
1✔
820
               for(size_t i = 0; i != mult; ++i) {
260✔
821
                  hash.update(buffer);
256✔
822
                  hash.final(output.data());
512✔
823
               }
824
            });
4✔
825
            record_result(timer);
1✔
826
         }
2✔
827
      }
1✔
828
#endif
829

830
#if defined(BOTAN_HAS_XOF)
831
      void bench_xof(Botan::XOF& xof,
×
832
                     const std::string& provider,
833
                     const std::chrono::milliseconds runtime,
834
                     const std::vector<size_t>& buf_sizes) {
835
         for(auto buf_size : buf_sizes) {
×
836
            Botan::secure_vector<uint8_t> in = rng().random_vec(buf_size);
×
837
            Botan::secure_vector<uint8_t> out(buf_size);
×
838

839
            auto in_timer = make_timer(xof.name(), in.size(), "input", provider, buf_size);
×
840
            in_timer->run_until_elapsed(runtime / 2, [&]() { xof.update(in); });
×
841

842
            auto out_timer = make_timer(xof.name(), out.size(), "output", provider, buf_size);
×
843
            out_timer->run_until_elapsed(runtime / 2, [&] { xof.output(out); });
×
844

845
            record_result(in_timer);
×
846
            record_result(out_timer);
×
847
         }
×
848
      }
×
849
#endif
850

851
#if defined(BOTAN_HAS_MAC)
852
      void bench_mac(Botan::MessageAuthenticationCode& mac,
1✔
853
                     const std::string& provider,
854
                     const std::chrono::milliseconds runtime,
855
                     const std::vector<size_t>& buf_sizes) {
856
         std::vector<uint8_t> output(mac.output_length());
1✔
857

858
         for(auto buf_size : buf_sizes) {
2✔
859
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
860
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
861

862
            const Botan::SymmetricKey key(rng(), mac.maximum_keylength());
1✔
863
            mac.set_key(key);
1✔
864

865
            auto timer = make_timer(mac.name(), mult * buffer.size(), "mac", provider, buf_size);
2✔
866
            timer->run_until_elapsed(runtime, [&]() {
1✔
867
               for(size_t i = 0; i != mult; ++i) {
195✔
868
                  if(mac.fresh_key_required_per_message()) {
192✔
869
                     mac.set_key(key);
×
870
                  }
871
                  mac.start(nullptr, 0);
192✔
872
                  mac.update(buffer);
192✔
873
                  mac.final(output.data());
384✔
874
               }
875
            });
3✔
876

877
            record_result(timer);
1✔
878
         }
3✔
879
      }
1✔
880
#endif
881

882
#if defined(BOTAN_HAS_CIPHER_MODES)
883
      void bench_cipher_mode(Botan::Cipher_Mode& enc,
1✔
884
                             Botan::Cipher_Mode& dec,
885
                             const std::chrono::milliseconds runtime,
886
                             const std::vector<size_t>& buf_sizes) {
887
         auto ks_timer = make_timer(enc.name(), enc.provider(), "key schedule");
2✔
888

889
         const Botan::SymmetricKey key(rng(), enc.key_spec().maximum_keylength());
1✔
890

891
         ks_timer->run([&]() { enc.set_key(key); });
2✔
892
         ks_timer->run([&]() { dec.set_key(key); });
2✔
893

894
         record_result(ks_timer);
1✔
895

896
         for(auto buf_size : buf_sizes) {
2✔
897
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
898
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
899

900
            auto encrypt_timer = make_timer(enc.name(), mult * buffer.size(), "encrypt", enc.provider(), buf_size);
2✔
901
            auto decrypt_timer = make_timer(dec.name(), mult * buffer.size(), "decrypt", dec.provider(), buf_size);
2✔
902

903
            Botan::secure_vector<uint8_t> iv = rng().random_vec(enc.default_nonce_length());
1✔
904

905
            if(buf_size >= enc.minimum_final_size()) {
1✔
906
               encrypt_timer->run_until_elapsed(runtime, [&]() {
2✔
907
                  for(size_t i = 0; i != mult; ++i) {
260✔
908
                     enc.start(iv);
256✔
909
                     enc.finish(buffer);
256✔
910
                     buffer.resize(buf_size);  // remove any tag or padding
256✔
911
                  }
912
               });
4✔
913

914
               while(decrypt_timer->under(runtime)) {
5✔
915
                  if(!iv.empty()) {
4✔
916
                     iv[iv.size() - 1] += 1;
4✔
917
                  }
918

919
                  // Create a valid ciphertext/tag for decryption to run on
920
                  buffer.resize(buf_size);
4✔
921
                  enc.start(iv);
4✔
922
                  enc.finish(buffer);
4✔
923

924
                  Botan::secure_vector<uint8_t> dbuffer;
4✔
925

926
                  decrypt_timer->run([&]() {
4✔
927
                     for(size_t i = 0; i != mult; ++i) {
260✔
928
                        dbuffer = buffer;
256✔
929
                        dec.start(iv);
256✔
930
                        dec.finish(dbuffer);
256✔
931
                     }
932
                  });
4✔
933
               }
4✔
934
            }
935
            record_result(encrypt_timer);
1✔
936
            record_result(decrypt_timer);
1✔
937
         }
2✔
938
      }
1✔
939
#endif
940

941
#if defined(BOTAN_HAS_ECC_GROUP)
942
      void bench_ecc_init(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
943
         for(std::string group_name : groups) {
7✔
944
            auto timer = make_timer(group_name + " initialization");
12✔
945

946
            while(timer->under(runtime)) {
12✔
947
               Botan::EC_Group::clear_registered_curve_data();
6✔
948
               timer->run([&]() { Botan::EC_Group::from_name(group_name); });
12✔
949
            }
950

951
            record_result(timer);
6✔
952
         }
6✔
953
      }
1✔
954

955
      void bench_ecc_mult(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
956
         for(const std::string& group_name : groups) {
7✔
957
            const auto group = Botan::EC_Group::from_name(group_name);
6✔
958

959
            auto bp_timer = make_timer(group_name + " base point");
12✔
960
            auto vp_timer = make_timer(group_name + " variable point");
12✔
961

962
            std::vector<Botan::BigInt> ws;
6✔
963

964
            auto g = Botan::EC_AffinePoint::generator(group);
6✔
965

966
            while(bp_timer->under(runtime) && vp_timer->under(runtime)) {
14✔
967
               const auto k = Botan::EC_Scalar::random(group, rng());
8✔
968

969
               const auto r1 = bp_timer->run([&]() { return Botan::EC_AffinePoint::g_mul(k, rng(), ws); });
16✔
970

971
               const auto r2 = vp_timer->run([&]() { return g.mul(k, rng(), ws); });
16✔
972

973
               BOTAN_ASSERT_EQUAL(
8✔
974
                  r1.serialize_uncompressed(), r2.serialize_uncompressed(), "Same result for multiplication");
975
            }
8✔
976

977
            record_result(bp_timer);
6✔
978
            record_result(vp_timer);
6✔
979
         }
6✔
980
      }
1✔
981

982
      void bench_os2ecp(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
983
         for(const std::string& group_name : groups) {
7✔
984
            auto uncmp_timer = make_timer("OS2ECP uncompressed " + group_name);
12✔
985
            auto cmp_timer = make_timer("OS2ECP compressed " + group_name);
12✔
986

987
            const auto ec_group = Botan::EC_Group::from_name(group_name);
6✔
988

989
            while(uncmp_timer->under(runtime) && cmp_timer->under(runtime)) {
128✔
990
               const Botan::BigInt k(rng(), 256);
122✔
991
               const Botan::EC_Point p = ec_group.get_base_point() * k;
122✔
992
               const std::vector<uint8_t> os_cmp = p.encode(Botan::EC_Point_Format::Compressed);
122✔
993
               const std::vector<uint8_t> os_uncmp = p.encode(Botan::EC_Point_Format::Uncompressed);
122✔
994

995
               uncmp_timer->run([&]() { ec_group.OS2ECP(os_uncmp); });
244✔
996
               cmp_timer->run([&]() { ec_group.OS2ECP(os_cmp); });
244✔
997
            }
244✔
998

999
            record_result(uncmp_timer);
6✔
1000
            record_result(cmp_timer);
6✔
1001
         }
6✔
1002
      }
1✔
1003

1004
#endif
1005

1006
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
1007
      void bench_ec_h2c(const std::chrono::milliseconds runtime) {
1✔
1008
         for(std::string group_name : {"secp256r1", "secp384r1", "secp521r1"}) {
4✔
1009
            auto h2c_ro_timer = make_timer(group_name + "-RO", "", "hash to curve");
6✔
1010
            auto h2c_nu_timer = make_timer(group_name + "-NU", "", "hash to curve");
6✔
1011

1012
            const auto group = Botan::EC_Group::from_name(group_name);
3✔
1013

1014
            const std::string hash_fn = "SHA-256";
3✔
1015

1016
            while(h2c_ro_timer->under(runtime)) {
11✔
1017
               const auto input = rng().random_array<32>();
8✔
1018
               const auto domain_sep = rng().random_array<32>();
8✔
1019

1020
               h2c_ro_timer->run(
8✔
1021
                  [&]() { return Botan::EC_AffinePoint::hash_to_curve_ro(group, hash_fn, input, domain_sep); });
8✔
1022

1023
               h2c_nu_timer->run(
8✔
1024
                  [&]() { return Botan::EC_AffinePoint::hash_to_curve_nu(group, hash_fn, input, domain_sep); });
8✔
1025
            }
1026

1027
            record_result(h2c_ro_timer);
3✔
1028
            record_result(h2c_nu_timer);
3✔
1029
         }
3✔
1030
      }
1✔
1031
#endif
1032

1033
#if defined(BOTAN_HAS_PCURVES)
1034

1035
      void bench_pcurves(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
×
1036
         for(const auto& group_name : groups) {
×
1037
            if(auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name)) {
×
1038
               auto base_timer = make_timer(group_name + " pcurve base mul");
×
1039
               auto var_timer = make_timer(group_name + " pcurve var mul");
×
1040
               auto mul2_setup_timer = make_timer(group_name + " pcurve mul2 setup");
×
1041
               auto mul2_timer = make_timer(group_name + " pcurve mul2");
×
1042

1043
               auto scalar_invert = make_timer(group_name + " pcurve scalar invert");
×
1044
               auto to_affine = make_timer(group_name + " pcurve proj->affine");
×
1045

1046
               auto g = curve->generator();
×
1047
               auto h = curve->mul(g, curve->random_scalar(rng()), rng()).to_affine();
×
1048
               auto gh_tab = curve->mul2_setup(g, h);
×
1049

1050
               while(base_timer->under(runtime)) {
×
1051
                  const auto scalar = curve->random_scalar(rng());
×
1052
                  base_timer->run([&]() { return curve->mul_by_g(scalar, rng()).to_affine(); });
×
1053
               }
×
1054

1055
               while(var_timer->under(runtime)) {
×
1056
                  const auto scalar = curve->random_scalar(rng());
×
1057
                  var_timer->run([&]() { return curve->mul(h, scalar, rng()).to_affine(); });
×
1058
               }
×
1059

1060
               while(mul2_setup_timer->under(runtime)) {
×
1061
                  mul2_setup_timer->run([&]() { return curve->mul2_setup(g, h); });
×
1062
               }
1063

1064
               while(mul2_timer->under(runtime)) {
×
1065
                  const auto scalar = curve->random_scalar(rng());
×
1066
                  const auto scalar2 = curve->random_scalar(rng());
×
1067
                  mul2_timer->run([&]() -> std::optional<Botan::PCurve::PrimeOrderCurve::AffinePoint> {
×
1068
                     if(auto pt = curve->mul2_vartime(*gh_tab, scalar, scalar2)) {
×
1069
                        return pt->to_affine();
×
1070
                     } else {
1071
                        return {};
×
1072
                     }
×
1073
                  });
1074
               }
×
1075

1076
               auto pt = curve->mul(g, curve->random_scalar(rng()), rng());
×
1077
               to_affine->run_until_elapsed(runtime, [&]() { pt.to_affine(); });
×
1078

1079
               while(scalar_invert->under(runtime)) {
×
1080
                  const auto scalar = curve->random_scalar(rng());
×
1081
                  scalar_invert->run([&]() { scalar.invert(); });
×
1082
               }
×
1083

1084
               record_result(base_timer);
×
1085
               record_result(var_timer);
×
1086
               record_result(mul2_setup_timer);
×
1087
               record_result(mul2_timer);
×
1088
               record_result(to_affine);
×
1089
               record_result(scalar_invert);
×
1090
            }
×
1091
         }
1092
      }
×
1093

1094
      void bench_pcurve_ecdsa(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
×
1095
         for(const auto& group_name : groups) {
×
1096
            auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name);
×
1097
            if(!curve) {
×
1098
               continue;
×
1099
            }
1100

1101
            // Setup (not timed)
1102
            const auto g = curve->generator();
×
1103
            const auto x = curve->random_scalar(rng());
×
1104
            const auto y = curve->mul_by_g(x, rng()).to_affine();
×
1105
            const auto e = curve->random_scalar(rng());
×
1106

1107
            const auto gy_tab = curve->mul2_setup(g, y);
×
1108

1109
            auto b = curve->random_scalar(rng());
×
1110
            auto b_inv = b.invert();
×
1111

1112
            auto sign_timer = make_timer("ECDSA sign pcurves " + group_name);
×
1113
            auto verify_timer = make_timer("ECDSA verify pcurves " + group_name);
×
1114

1115
            while(sign_timer->under(runtime)) {
×
1116
               sign_timer->start();
×
1117

1118
               const auto signature = [&]() {
×
1119
                  const auto k = curve->random_scalar(rng());
×
1120
                  const auto r = curve->base_point_mul_x_mod_order(k, rng());
×
1121
                  const auto k_inv = (b * k).invert() * b;
×
1122
                  b = b.square();
×
1123
                  b_inv = b_inv.square();
×
1124
                  const auto be = b * e;
×
1125
                  const auto bx = b * x;
×
1126
                  const auto bxr_e = (bx * r) + be;
×
1127
                  const auto s = (k_inv * bxr_e) * b_inv;
×
1128

1129
                  return Botan::concat(r.serialize(), s.serialize());
×
1130
               }();
×
1131

1132
               sign_timer->stop();
×
1133

1134
               verify_timer->start();
×
1135

1136
               auto result = [&](std::span<const uint8_t> sig) {
×
1137
                  const size_t scalar_bytes = curve->scalar_bytes();
×
1138
                  if(sig.size() != 2 * scalar_bytes) {
×
1139
                     return false;
1140
                  }
1141

1142
                  const auto r = curve->deserialize_scalar(sig.first(scalar_bytes));
×
1143
                  const auto s = curve->deserialize_scalar(sig.last(scalar_bytes));
×
1144

1145
                  if(r && s) {
×
1146
                     if(r->is_zero() || s->is_zero()) {
×
1147
                        return false;
×
1148
                     }
1149

1150
                     auto w = s->invert();
×
1151

1152
                     auto u1 = e * w;
×
1153
                     auto u2 = *r * w;
×
1154

1155
                     return curve->mul2_vartime_x_mod_order_eq(*gy_tab, *r, u1, u2);
×
1156
                  }
×
1157

1158
                  return false;
1159
               }(signature);
×
1160

1161
               BOTAN_ASSERT(result, "ECDSA-pcurves signature ok");
×
1162

1163
               verify_timer->stop();
×
1164
            }
×
1165

1166
            record_result(sign_timer);
×
1167
            record_result(verify_timer);
×
1168
         }
×
1169
      }
×
1170

1171
      void bench_pcurve_ecdh(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
×
1172
         for(const auto& group_name : groups) {
×
1173
            auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name);
×
1174
            if(!curve) {
×
1175
               continue;
×
1176
            }
1177

1178
            auto ka_timer = make_timer("ECDH agree pcurves " + group_name);
×
1179

1180
            auto agree = [&](const Botan::PCurve::PrimeOrderCurve::Scalar& sk, std::span<const uint8_t> pt_bytes) {
×
1181
               const auto pt = curve->deserialize_point(pt_bytes);
×
1182
               if(pt) {
×
1183
                  return curve->mul(*pt, sk, rng()).to_affine().serialize();
×
1184
               } else {
1185
                  return std::vector<uint8_t>();
×
1186
               }
1187
            };
×
1188

1189
            while(ka_timer->under(runtime)) {
×
1190
               const auto g = curve->generator();
×
1191
               const auto x1 = curve->random_scalar(rng());
×
1192
               const auto x2 = curve->random_scalar(rng());
×
1193

1194
               const auto y1 = curve->mul_by_g(x1, rng()).to_affine().serialize();
×
1195
               const auto y2 = curve->mul_by_g(x2, rng()).to_affine().serialize();
×
1196

1197
               ka_timer->start();
×
1198
               const auto ss1 = agree(x1, y2);
×
1199
               ka_timer->stop();
×
1200

1201
               ka_timer->start();
×
1202
               const auto ss2 = agree(x1, y2);
×
1203
               ka_timer->stop();
×
1204

1205
               BOTAN_ASSERT(ss1 == ss2, "Key agreement worked");
×
1206
            }
×
1207

1208
            record_result(ka_timer);
×
1209
         }
×
1210
      }
×
1211

1212
#endif
1213

1214
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
1215
      void bench_pk_enc(const Botan::Private_Key& key,
4✔
1216
                        const std::string& nm,
1217
                        const std::string& provider,
1218
                        const std::string& padding,
1219
                        std::chrono::milliseconds msec) {
1220
         std::vector<uint8_t> plaintext, ciphertext;
4✔
1221

1222
         Botan::PK_Encryptor_EME enc(key, rng(), padding, provider);
4✔
1223
         Botan::PK_Decryptor_EME dec(key, rng(), padding, provider);
4✔
1224

1225
         auto enc_timer = make_timer(nm + " " + padding, provider, "encrypt");
8✔
1226
         auto dec_timer = make_timer(nm + " " + padding, provider, "decrypt");
8✔
1227

1228
         while(enc_timer->under(msec) || dec_timer->under(msec)) {
12✔
1229
            // Generate a new random ciphertext to decrypt
1230
            if(ciphertext.empty() || enc_timer->under(msec)) {
4✔
1231
               rng().random_vec(plaintext, enc.maximum_input_size());
4✔
1232
               ciphertext = enc_timer->run([&]() { return enc.encrypt(plaintext, rng()); });
8✔
1233
            }
1234

1235
            if(dec_timer->under(msec)) {
4✔
1236
               const auto dec_pt = dec_timer->run([&]() { return dec.decrypt(ciphertext); });
12✔
1237

1238
               if(!(Botan::unlock(dec_pt) == plaintext))  // sanity check
4✔
1239
               {
1240
                  error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n";
×
1241
               }
1242
            }
4✔
1243
         }
1244

1245
         record_result(enc_timer);
4✔
1246
         record_result(dec_timer);
4✔
1247
      }
4✔
1248

1249
      void bench_pk_ka(const std::string& algo,
13✔
1250
                       const std::string& nm,
1251
                       const std::string& params,
1252
                       const std::string& provider,
1253
                       std::chrono::milliseconds msec) {
1254
         const std::string kdf = "KDF2(SHA-256)";  // arbitrary choice
13✔
1255

1256
         auto keygen_timer = make_timer(nm, provider, "keygen");
26✔
1257

1258
         std::unique_ptr<Botan::Private_Key> key1(
13✔
1259
            keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); }));
26✔
1260
         std::unique_ptr<Botan::Private_Key> key2(
13✔
1261
            keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); }));
26✔
1262

1263
         record_result(keygen_timer);
13✔
1264

1265
         const Botan::PK_Key_Agreement_Key& ka_key1 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key1);
13✔
1266
         const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key2);
13✔
1267

1268
         Botan::PK_Key_Agreement ka1(ka_key1, rng(), kdf, provider);
13✔
1269
         Botan::PK_Key_Agreement ka2(ka_key2, rng(), kdf, provider);
13✔
1270

1271
         const std::vector<uint8_t> ka1_pub = ka_key1.public_value();
13✔
1272
         const std::vector<uint8_t> ka2_pub = ka_key2.public_value();
13✔
1273

1274
         auto ka_timer = make_timer(nm, provider, "key agreements");
26✔
1275

1276
         while(ka_timer->under(msec)) {
31✔
1277
            Botan::SymmetricKey symkey1 = ka_timer->run([&]() { return ka1.derive_key(32, ka2_pub); });
36✔
1278
            Botan::SymmetricKey symkey2 = ka_timer->run([&]() { return ka2.derive_key(32, ka1_pub); });
36✔
1279

1280
            if(symkey1 != symkey2) {
18✔
1281
               error_output() << "Key agreement mismatch in PK bench\n";
×
1282
            }
1283
         }
36✔
1284

1285
         record_result(ka_timer);
13✔
1286
      }
39✔
1287

1288
      void bench_pk_kem(const Botan::Private_Key& key,
11✔
1289
                        const std::string& nm,
1290
                        const std::string& provider,
1291
                        const std::string& kdf,
1292
                        std::chrono::milliseconds msec) {
1293
         Botan::PK_KEM_Decryptor dec(key, rng(), kdf, provider);
11✔
1294
         Botan::PK_KEM_Encryptor enc(key, kdf, provider);
11✔
1295

1296
         auto kem_enc_timer = make_timer(nm, provider, "KEM encrypt");
22✔
1297
         auto kem_dec_timer = make_timer(nm, provider, "KEM decrypt");
22✔
1298

1299
         while(kem_enc_timer->under(msec) && kem_dec_timer->under(msec)) {
57✔
1300
            Botan::secure_vector<uint8_t> salt = rng().random_vec(16);
46✔
1301

1302
            kem_enc_timer->start();
46✔
1303
            const auto kem_result = enc.encrypt(rng(), 64, salt);
46✔
1304
            kem_enc_timer->stop();
46✔
1305

1306
            kem_dec_timer->start();
46✔
1307
            Botan::secure_vector<uint8_t> dec_shared_key = dec.decrypt(kem_result.encapsulated_shared_key(), 64, salt);
46✔
1308
            kem_dec_timer->stop();
46✔
1309

1310
            if(kem_result.shared_key() != dec_shared_key) {
46✔
1311
               error_output() << "KEM mismatch in PK bench\n";
×
1312
            }
1313
         }
92✔
1314

1315
         record_result(kem_enc_timer);
11✔
1316
         record_result(kem_dec_timer);
11✔
1317
      }
11✔
1318

1319
      void bench_pk_sig_ecc(const std::string& algo,
7✔
1320
                            const std::string& emsa,
1321
                            const std::string& provider,
1322
                            const std::vector<std::string>& params,
1323
                            std::chrono::milliseconds msec) {
1324
         for(std::string grp : params) {
34✔
1325
            const std::string nm = grp.empty() ? algo : Botan::fmt("{}-{}", algo, grp);
27✔
1326

1327
            auto keygen_timer = make_timer(nm, provider, "keygen");
54✔
1328

1329
            std::unique_ptr<Botan::Private_Key> key(
27✔
1330
               keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), grp); }));
54✔
1331

1332
            record_result(keygen_timer);
27✔
1333
            bench_pk_sig(*key, nm, provider, emsa, msec);
27✔
1334
         }
27✔
1335
      }
7✔
1336

1337
      size_t bench_pk_sig(const Botan::Private_Key& key,
42✔
1338
                          const std::string& nm,
1339
                          const std::string& provider,
1340
                          const std::string& padding,
1341
                          std::chrono::milliseconds msec) {
1342
         std::vector<uint8_t> message, signature, bad_signature;
42✔
1343

1344
         Botan::PK_Signer sig(key, rng(), padding, Botan::Signature_Format::Standard, provider);
42✔
1345
         Botan::PK_Verifier ver(key, padding, Botan::Signature_Format::Standard, provider);
42✔
1346

1347
         auto sig_timer = make_timer(nm + " " + padding, provider, "sign");
84✔
1348
         auto ver_timer = make_timer(nm + " " + padding, provider, "verify");
84✔
1349

1350
         size_t invalid_sigs = 0;
42✔
1351

1352
         while(ver_timer->under(msec) || sig_timer->under(msec)) {
245✔
1353
            if(signature.empty() || sig_timer->under(msec)) {
161✔
1354
               /*
1355
               Length here is kind of arbitrary, but 48 bytes fits into a single
1356
               hash block so minimizes hashing overhead versus the PK op itself.
1357
               */
1358
               rng().random_vec(message, 48);
107✔
1359

1360
               signature = sig_timer->run([&]() { return sig.sign_message(message, rng()); });
214✔
1361

1362
               bad_signature = signature;
107✔
1363
               bad_signature[rng().next_byte() % bad_signature.size()] ^= rng().next_nonzero_byte();
107✔
1364
            }
1365

1366
            if(ver_timer->under(msec)) {
161✔
1367
               const bool verified = ver_timer->run([&] { return ver.verify_message(message, signature); });
210✔
1368

1369
               if(!verified) {
105✔
1370
                  invalid_sigs += 1;
×
1371
               }
1372

1373
               const bool verified_bad = ver_timer->run([&] { return ver.verify_message(message, bad_signature); });
210✔
1374

1375
               if(verified_bad) {
105✔
1376
                  error_output() << "Bad signature accepted in " << nm << " signature bench\n";
×
1377
               }
1378
            }
1379
         }
1380

1381
         if(invalid_sigs > 0) {
42✔
1382
            error_output() << invalid_sigs << " generated signatures rejected in " << nm << " signature bench\n";
×
1383
         }
1384

1385
         const size_t events = static_cast<size_t>(std::min(sig_timer->events(), ver_timer->events()));
42✔
1386

1387
         record_result(sig_timer);
42✔
1388
         record_result(ver_timer);
42✔
1389

1390
         return events;
42✔
1391
      }
42✔
1392
#endif
1393

1394
#if defined(BOTAN_HAS_RSA)
1395
      void bench_rsa_keygen(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1396
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
1397
            const std::string nm = "RSA-" + std::to_string(keylen);
8✔
1398
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1399

1400
            while(keygen_timer->under(msec)) {
8✔
1401
               std::unique_ptr<Botan::Private_Key> key(
4✔
1402
                  keygen_timer->run([&] { return Botan::create_private_key("RSA", rng(), std::to_string(keylen)); }));
8✔
1403

1404
               BOTAN_ASSERT(key->check_key(rng(), true), "Key is ok");
4✔
1405
            }
4✔
1406

1407
            record_result(keygen_timer);
4✔
1408
         }
4✔
1409
      }
1✔
1410

1411
      void bench_rsa(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1412
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
1413
            const std::string nm = "RSA-" + std::to_string(keylen);
8✔
1414

1415
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1416

1417
            std::unique_ptr<Botan::Private_Key> key(
4✔
1418
               keygen_timer->run([&] { return Botan::create_private_key("RSA", rng(), std::to_string(keylen)); }));
8✔
1419

1420
            record_result(keygen_timer);
4✔
1421

1422
            // Using PKCS #1 padding so OpenSSL provider can play along
1423
            bench_pk_sig(*key, nm, provider, "EMSA-PKCS1-v1_5(SHA-256)", msec);
8✔
1424

1425
            //bench_pk_sig(*key, nm, provider, "PSSR(SHA-256)", msec);
1426
            //bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
1427
            //bench_pk_enc(*key, nm, provider, "OAEP(SHA-1)", msec);
1428
         }
4✔
1429
      }
1✔
1430
#endif
1431

1432
#if defined(BOTAN_HAS_ECDSA)
1433
      void bench_ecdsa(const std::vector<std::string>& groups,
1✔
1434
                       const std::string& provider,
1435
                       std::chrono::milliseconds msec) {
1436
         return bench_pk_sig_ecc("ECDSA", "SHA-256", provider, groups, msec);
2✔
1437
      }
1438

1439
      void bench_ecdsa_recovery(const std::vector<std::string>& groups,
1✔
1440
                                const std::string& /*unused*/,
1441
                                std::chrono::milliseconds msec) {
1442
         for(const std::string& group_name : groups) {
7✔
1443
            const auto group = Botan::EC_Group::from_name(group_name);
6✔
1444
            auto recovery_timer = make_timer("ECDSA recovery " + group_name);
12✔
1445

1446
            while(recovery_timer->under(msec)) {
14✔
1447
               Botan::ECDSA_PrivateKey key(rng(), group);
8✔
1448

1449
               std::vector<uint8_t> message(group.get_order_bits() / 8);
8✔
1450
               rng().randomize(message.data(), message.size());
8✔
1451

1452
               Botan::PK_Signer signer(key, rng(), "Raw");
8✔
1453
               signer.update(message);
8✔
1454
               std::vector<uint8_t> signature = signer.signature(rng());
8✔
1455

1456
               Botan::PK_Verifier verifier(key, "Raw", Botan::Signature_Format::Standard, "base");
8✔
1457
               verifier.update(message);
8✔
1458
               BOTAN_ASSERT(verifier.check_signature(signature), "Valid signature");
8✔
1459

1460
               Botan::BigInt r(signature.data(), signature.size() / 2);
8✔
1461
               Botan::BigInt s(signature.data() + signature.size() / 2, signature.size() / 2);
8✔
1462

1463
               const uint8_t v = key.recovery_param(message, r, s);
8✔
1464

1465
               recovery_timer->run([&]() {
8✔
1466
                  Botan::ECDSA_PublicKey pubkey(group, message, r, s, v);
8✔
1467
                  BOTAN_ASSERT(pubkey.public_point() == key.public_point(), "Recovered public key");
8✔
1468
               });
8✔
1469
            }
16✔
1470

1471
            record_result(recovery_timer);
6✔
1472
         }
6✔
1473
      }
1✔
1474

1475
#endif
1476

1477
#if defined(BOTAN_HAS_ECKCDSA)
1478
      void bench_eckcdsa(const std::vector<std::string>& groups,
1✔
1479
                         const std::string& provider,
1480
                         std::chrono::milliseconds msec) {
1481
         return bench_pk_sig_ecc("ECKCDSA", "SHA-256", provider, groups, msec);
2✔
1482
      }
1483
#endif
1484

1485
#if defined(BOTAN_HAS_GOST_34_10_2001)
1486
      void bench_gost_3410(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1487
         return bench_pk_sig_ecc("GOST-34.10", "GOST-34.11", provider, {"gost_256A"}, msec);
2✔
1488
      }
1489
#endif
1490

1491
#if defined(BOTAN_HAS_SM2)
1492
      void bench_sm2(const std::vector<std::string>& groups,
1✔
1493
                     const std::string& provider,
1494
                     std::chrono::milliseconds msec) {
1495
         return bench_pk_sig_ecc("SM2_Sig", "SM3", provider, groups, msec);
2✔
1496
      }
1497
#endif
1498

1499
#if defined(BOTAN_HAS_ECGDSA)
1500
      void bench_ecgdsa(const std::vector<std::string>& groups,
1✔
1501
                        const std::string& provider,
1502
                        std::chrono::milliseconds msec) {
1503
         return bench_pk_sig_ecc("ECGDSA", "SHA-256", provider, groups, msec);
2✔
1504
      }
1505
#endif
1506

1507
#if defined(BOTAN_HAS_ED25519)
1508
      void bench_ed25519(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1509
         return bench_pk_sig_ecc("Ed25519", "Pure", provider, std::vector<std::string>{""}, msec);
3✔
1510
      }
2✔
1511
#endif
1512

1513
#if defined(BOTAN_HAS_ED448)
1514
      void bench_ed448(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1515
         return bench_pk_sig_ecc("Ed448", "Pure", provider, std::vector<std::string>{""}, msec);
3✔
1516
      }
2✔
1517
#endif
1518

1519
#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
1520
      void bench_dh(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1521
         for(size_t bits : {2048, 3072, 4096, 6144, 8192}) {
6✔
1522
            bench_pk_ka("DH", "DH-" + std::to_string(bits), "ffdhe/ietf/" + std::to_string(bits), provider, msec);
20✔
1523
         }
1524
      }
1✔
1525
#endif
1526

1527
#if defined(BOTAN_HAS_DSA)
1528
      void bench_dsa(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1529
         for(size_t bits : {1024, 2048, 3072}) {
4✔
1530
            const std::string nm = "DSA-" + std::to_string(bits);
6✔
1531

1532
            const std::string params = (bits == 1024) ? "dsa/jce/1024" : ("dsa/botan/" + std::to_string(bits));
5✔
1533

1534
            auto keygen_timer = make_timer(nm, provider, "keygen");
6✔
1535

1536
            std::unique_ptr<Botan::Private_Key> key(
3✔
1537
               keygen_timer->run([&] { return Botan::create_private_key("DSA", rng(), params); }));
6✔
1538

1539
            record_result(keygen_timer);
3✔
1540

1541
            bench_pk_sig(*key, nm, provider, "SHA-256", msec);
6✔
1542
         }
3✔
1543
      }
1✔
1544
#endif
1545

1546
#if defined(BOTAN_HAS_ELGAMAL)
1547
      void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1548
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
1549
            const std::string nm = "ElGamal-" + std::to_string(keylen);
8✔
1550

1551
            const std::string params = "modp/ietf/" + std::to_string(keylen);
8✔
1552

1553
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1554

1555
            std::unique_ptr<Botan::Private_Key> key(
4✔
1556
               keygen_timer->run([&] { return Botan::create_private_key("ElGamal", rng(), params); }));
8✔
1557

1558
            record_result(keygen_timer);
4✔
1559

1560
            bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
8✔
1561
         }
4✔
1562
      }
1✔
1563
#endif
1564

1565
#if defined(BOTAN_HAS_ECDH)
1566
      void bench_ecdh(const std::vector<std::string>& groups,
1✔
1567
                      const std::string& provider,
1568
                      std::chrono::milliseconds msec) {
1569
         for(const std::string& grp : groups) {
7✔
1570
            bench_pk_ka("ECDH", "ECDH-" + grp, grp, provider, msec);
12✔
1571
         }
1572
      }
1✔
1573
#endif
1574

1575
#if defined(BOTAN_HAS_X25519)
1576
      void bench_x25519(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1577
         bench_pk_ka("X25519", "X25519", "", provider, msec);
2✔
1578
      }
1✔
1579
#endif
1580

1581
#if defined(BOTAN_HAS_X448)
1582
      void bench_x448(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1583
         bench_pk_ka("X448", "X448", "", provider, msec);
2✔
1584
      }
1✔
1585
#endif
1586

1587
#if defined(BOTAN_HAS_MCELIECE)
1588
      void bench_mceliece(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1589
         /*
1590
         SL=80 n=1632 t=33 - 59 KB pubkey 140 KB privkey
1591
         SL=107 n=2480 t=45 - 128 KB pubkey 300 KB privkey
1592
         SL=128 n=2960 t=57 - 195 KB pubkey 459 KB privkey
1593
         SL=147 n=3408 t=67 - 265 KB pubkey 622 KB privkey
1594
         SL=191 n=4624 t=95 - 516 KB pubkey 1234 KB privkey
1595
         SL=256 n=6624 t=115 - 942 KB pubkey 2184 KB privkey
1596
         */
1597

1598
         const std::vector<std::pair<size_t, size_t>> mce_params = {
1✔
1599
            {2480, 45}, {2960, 57}, {3408, 67}, {4624, 95}, {6624, 115}};
1✔
1600

1601
         for(auto params : mce_params) {
6✔
1602
            size_t n = params.first;
5✔
1603
            size_t t = params.second;
5✔
1604

1605
            const std::string nm = "McEliece-" + std::to_string(n) + "," + std::to_string(t) +
20✔
1606
                                   " (WF=" + std::to_string(Botan::mceliece_work_factor(n, t)) + ")";
15✔
1607

1608
            auto keygen_timer = make_timer(nm, provider, "keygen");
10✔
1609

1610
            std::unique_ptr<Botan::Private_Key> key =
5✔
1611
               keygen_timer->run([&] { return std::make_unique<Botan::McEliece_PrivateKey>(rng(), n, t); });
10✔
1612

1613
            record_result(keygen_timer);
5✔
1614
            bench_pk_kem(*key, nm, provider, "KDF2(SHA-256)", msec);
10✔
1615
         }
5✔
1616
      }
1✔
1617
#endif
1618

1619
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
1620
      void bench_kyber(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1621
         const Botan::KyberMode::Mode all_modes[] = {
1✔
1622
            Botan::KyberMode::Kyber512_R3,
1623
            Botan::KyberMode::Kyber512_90s,
1624
            Botan::KyberMode::Kyber768_R3,
1625
            Botan::KyberMode::Kyber768_90s,
1626
            Botan::KyberMode::Kyber1024_R3,
1627
            Botan::KyberMode::Kyber1024_90s,
1628
         };
1629

1630
         for(auto modet : all_modes) {
7✔
1631
            Botan::KyberMode mode(modet);
6✔
1632

1633
   #if !defined(BOTAN_HAS_KYBER)
1634
            if(mode.is_modern())
1635
               continue;
1636
   #endif
1637

1638
   #if !defined(BOTAN_HAS_KYBER_90S)
1639
            if(mode.is_90s())
1640
               continue;
1641
   #endif
1642

1643
            auto keygen_timer = make_timer(mode.to_string(), provider, "keygen");
12✔
1644

1645
            auto key = keygen_timer->run([&] { return Botan::Kyber_PrivateKey(rng(), mode); });
12✔
1646

1647
            record_result(keygen_timer);
6✔
1648

1649
            bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec);
12✔
1650
         }
6✔
1651
      }
1✔
1652
#endif
1653

1654
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
1655
      void bench_dilithium(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1656
         const Botan::DilithiumMode::Mode all_modes[] = {Botan::DilithiumMode::Dilithium4x4,
1✔
1657
                                                         Botan::DilithiumMode::Dilithium4x4_AES,
1658
                                                         Botan::DilithiumMode::Dilithium6x5,
1659
                                                         Botan::DilithiumMode::Dilithium6x5_AES,
1660
                                                         Botan::DilithiumMode::Dilithium8x7,
1661
                                                         Botan::DilithiumMode::Dilithium8x7_AES};
1662

1663
         for(auto modet : all_modes) {
7✔
1664
            Botan::DilithiumMode mode(modet);
6✔
1665

1666
   #if !defined(BOTAN_HAS_DILITHIUM)
1667
            if(mode.is_modern())
1668
               continue;
1669
   #endif
1670

1671
   #if !defined(BOTAN_HAS_DILITHIUM_AES)
1672
            if(mode.is_aes())
1673
               continue;
1674
   #endif
1675

1676
            auto keygen_timer = make_timer(mode.to_string(), provider, "keygen");
12✔
1677

1678
            auto key = keygen_timer->run([&] { return Botan::Dilithium_PrivateKey(rng(), mode); });
12✔
1679

1680
            record_result(keygen_timer);
6✔
1681

1682
            bench_pk_sig(key, mode.to_string(), provider, "", msec);
12✔
1683
         }
6✔
1684
      }
1✔
1685
#endif
1686

1687
#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
1688
      void bench_sphincs_plus(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1689
         // Sphincs_Parameter_Set set, Sphincs_Hash_Type hash
1690
         std::vector<std::string> sphincs_params{"SphincsPlus-sha2-128s-r3.1",
1✔
1691
                                                 "SphincsPlus-sha2-128f-r3.1",
1692
                                                 "SphincsPlus-sha2-192s-r3.1",
1693
                                                 "SphincsPlus-sha2-192f-r3.1",
1694
                                                 "SphincsPlus-sha2-256s-r3.1",
1695
                                                 "SphincsPlus-sha2-256f-r3.1",
1696
                                                 "SphincsPlus-shake-128s-r3.1",
1697
                                                 "SphincsPlus-shake-128f-r3.1",
1698
                                                 "SphincsPlus-shake-192s-r3.1",
1699
                                                 "SphincsPlus-shake-192f-r3.1",
1700
                                                 "SphincsPlus-shake-256s-r3.1",
1701
                                                 "SphincsPlus-shake-256f-r3.1"};
14✔
1702

1703
         for(auto params : sphincs_params) {
1✔
1704
            try {
1✔
1705
               auto keygen_timer = make_timer(params, provider, "keygen");
2✔
1706

1707
               std::unique_ptr<Botan::Private_Key> key(
1✔
1708
                  keygen_timer->run([&] { return Botan::create_private_key("SPHINCS+", rng(), params); }));
2✔
1709

1710
               record_result(keygen_timer);
1✔
1711
               if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
1✔
1712
                  break;
1713
               }
1714
            } catch(Botan::Not_Implemented&) {
1✔
1715
               continue;
×
1716
            }
×
1717
         }
1✔
1718
      }
3✔
1719
#endif
1720

1721
#if defined(BOTAN_HAS_FRODOKEM)
1722
      void bench_frodokem(const std::string& provider, std::chrono::milliseconds msec) {
×
1723
         std::vector<Botan::FrodoKEMMode> frodo_modes{
×
1724
            Botan::FrodoKEMMode::FrodoKEM640_SHAKE,
1725
            Botan::FrodoKEMMode::FrodoKEM976_SHAKE,
1726
            Botan::FrodoKEMMode::FrodoKEM1344_SHAKE,
1727
            Botan::FrodoKEMMode::eFrodoKEM640_SHAKE,
1728
            Botan::FrodoKEMMode::eFrodoKEM976_SHAKE,
1729
            Botan::FrodoKEMMode::eFrodoKEM1344_SHAKE,
1730
            Botan::FrodoKEMMode::FrodoKEM640_AES,
1731
            Botan::FrodoKEMMode::FrodoKEM976_AES,
1732
            Botan::FrodoKEMMode::FrodoKEM1344_AES,
1733
            Botan::FrodoKEMMode::eFrodoKEM640_AES,
1734
            Botan::FrodoKEMMode::eFrodoKEM976_AES,
1735
            Botan::FrodoKEMMode::eFrodoKEM1344_AES,
1736
         };
×
1737

1738
         for(auto modet : frodo_modes) {
×
1739
            if(!modet.is_available()) {
×
1740
               continue;
×
1741
            }
1742

1743
            Botan::FrodoKEMMode mode(modet);
×
1744

1745
            auto keygen_timer = make_timer(mode.to_string(), provider, "keygen");
×
1746

1747
            auto key = keygen_timer->run([&] { return Botan::FrodoKEM_PrivateKey(rng(), mode); });
×
1748

1749
            record_result(keygen_timer);
×
1750

1751
            bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec);
×
1752
         }
×
1753
      }
×
1754
#endif
1755

1756
#if defined(BOTAN_HAS_XMSS_RFC8391)
1757
      void bench_xmss(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1758
         /*
1759
         We only test H10 signatures here since already they are quite slow (a
1760
         few seconds per signature). On a fast machine, H16 signatures take 1-2
1761
         minutes to generate and H20 signatures take 5-10 minutes to generate
1762
         */
1763
         std::vector<std::string> xmss_params{
1✔
1764
            "XMSS-SHA2_10_256",
1765
            "XMSS-SHAKE_10_256",
1766
            "XMSS-SHA2_10_512",
1767
            "XMSS-SHAKE_10_512",
1768
         };
6✔
1769

1770
         for(std::string params : xmss_params) {
1✔
1771
            auto keygen_timer = make_timer(params, provider, "keygen");
2✔
1772

1773
            std::unique_ptr<Botan::Private_Key> key(
1✔
1774
               keygen_timer->run([&] { return Botan::create_private_key("XMSS", rng(), params); }));
2✔
1775

1776
            record_result(keygen_timer);
1✔
1777
            if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
1✔
1778
               break;
1779
            }
1780
         }
1✔
1781
      }
3✔
1782
#endif
1783

1784
#if defined(BOTAN_HAS_HSS_LMS)
1785
      void bench_hss_lms(const std::string& provider, std::chrono::milliseconds msec) {
×
1786
         // At first we compare instances with multiple hash functions. LMS trees with
1787
         // height 10 are suitable, since they can be used for enough signatures and are
1788
         // fast enough for speed testing.
1789
         // Afterward, setups with multiple HSS layers are tested
1790
         std::vector<std::string> hss_lms_instances{"SHA-256,HW(10,1)",
×
1791
                                                    "SHAKE-256(256),HW(10,1)",
1792
                                                    "SHAKE-256(192),HW(10,1)",
1793
                                                    "Truncated(SHA-256,192),HW(10,1)",
1794
                                                    "SHA-256,HW(10,1),HW(10,1)",
1795
                                                    "SHA-256,HW(10,1),HW(10,1),HW(10,1)"};
×
1796

1797
         for(const auto& params : hss_lms_instances) {
×
1798
            auto keygen_timer = make_timer(params, provider, "keygen");
×
1799

1800
            std::unique_ptr<Botan::Private_Key> key(
×
1801
               keygen_timer->run([&] { return Botan::create_private_key("HSS-LMS", rng(), params); }));
×
1802

1803
            record_result(keygen_timer);
×
1804
            if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
×
1805
               break;
1806
            }
1807
         }
×
1808
      }
×
1809
#endif
1810
};
1811

1812
BOTAN_REGISTER_COMMAND("speed", Speed);
33✔
1813

1814
}  // namespace Botan_CLI
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc