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

randombit / botan / 10113230739

26 Jul 2024 02:49PM UTC coverage: 91.656% (-0.001%) from 91.657%
10113230739

push

github

web-flow
Merge pull request #4263 from randombit/jack/h2c-refactor

Refactor hash2curve

87250 of 95193 relevant lines covered (91.66%)

9417742.41 hits per line

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

82.56
/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 "../tests/test_rng.h"  // FIXME
9
#include "cli.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_BIGINT)
29
   #include <botan/bigint.h>
30
   #include <botan/internal/divide.h>
31
#endif
32

33
#if defined(BOTAN_HAS_BLOCK_CIPHER)
34
   #include <botan/block_cipher.h>
35
#endif
36

37
#if defined(BOTAN_HAS_STREAM_CIPHER)
38
   #include <botan/stream_cipher.h>
39
#endif
40

41
#if defined(BOTAN_HAS_HASH)
42
   #include <botan/hash.h>
43
#endif
44

45
#if defined(BOTAN_HAS_XOF)
46
   #include <botan/xof.h>
47
#endif
48

49
#if defined(BOTAN_HAS_CIPHER_MODES)
50
   #include <botan/cipher_mode.h>
51
#endif
52

53
#if defined(BOTAN_HAS_MAC)
54
   #include <botan/mac.h>
55
#endif
56

57
#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
58
   #include <botan/auto_rng.h>
59
#endif
60

61
#if defined(BOTAN_HAS_SYSTEM_RNG)
62
   #include <botan/system_rng.h>
63
#endif
64

65
#if defined(BOTAN_HAS_HMAC_DRBG)
66
   #include <botan/hmac_drbg.h>
67
#endif
68

69
#if defined(BOTAN_HAS_PROCESSOR_RNG)
70
   #include <botan/processor_rng.h>
71
#endif
72

73
#if defined(BOTAN_HAS_CHACHA_RNG)
74
   #include <botan/chacha_rng.h>
75
#endif
76

77
#if defined(BOTAN_HAS_FPE_FE1)
78
   #include <botan/fpe_fe1.h>
79
#endif
80

81
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
82
   #include <botan/rfc3394.h>
83
#endif
84

85
#if defined(BOTAN_HAS_COMPRESSION)
86
   #include <botan/compression.h>
87
#endif
88

89
#if defined(BOTAN_HAS_POLY_DBL)
90
   #include <botan/internal/poly_dbl.h>
91
#endif
92

93
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
94
   #include <botan/pk_algs.h>
95
   #include <botan/pkcs8.h>
96
   #include <botan/pubkey.h>
97
   #include <botan/x509_key.h>
98
   #include <botan/internal/workfactor.h>
99
#endif
100

101
#if defined(BOTAN_HAS_NUMBERTHEORY)
102
   #include <botan/numthry.h>
103
   #include <botan/reducer.h>
104
   #include <botan/internal/curve_nistp.h>
105
   #include <botan/internal/primality.h>
106
#endif
107

108
#if defined(BOTAN_HAS_ECC_GROUP)
109
   #include <botan/ec_group.h>
110
#endif
111

112
#if defined(BOTAN_HAS_PCURVES)
113
   #include <botan/internal/pcurves.h>
114
#endif
115

116
#if defined(BOTAN_HAS_DL_GROUP)
117
   #include <botan/dl_group.h>
118
#endif
119

120
#if defined(BOTAN_HAS_MCELIECE)
121
   #include <botan/mceliece.h>
122
#endif
123

124
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
125
   #include <botan/kyber.h>
126
#endif
127

128
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
129
   #include <botan/dilithium.h>
130
#endif
131

132
#if defined(BOTAN_HAS_HSS_LMS)
133
   #include <botan/hss_lms.h>
134
#endif
135

136
#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
137
   #include <botan/sphincsplus.h>
138
#endif
139

140
#if defined(BOTAN_HAS_FRODOKEM)
141
   #include <botan/frodokem.h>
142
#endif
143

144
#if defined(BOTAN_HAS_ECDSA)
145
   #include <botan/ecdsa.h>
146
#endif
147

148
#if defined(BOTAN_HAS_BCRYPT)
149
   #include <botan/bcrypt.h>
150
#endif
151

152
#if defined(BOTAN_HAS_PASSHASH9)
153
   #include <botan/passhash9.h>
154
#endif
155

156
#if defined(BOTAN_HAS_PASSWORD_HASHING)
157
   #include <botan/pwdhash.h>
158
#endif
159

160
#if defined(BOTAN_HAS_ZFEC)
161
   #include <botan/zfec.h>
162
#endif
163

164
namespace Botan_CLI {
165

166
using Botan::Timer;
167

168
namespace {
169

170
class JSON_Output final {
2✔
171
   public:
172
      void add(const Timer& timer) { m_results.push_back(timer); }
2✔
173

174
      std::string print() const {
1✔
175
         std::ostringstream out;
1✔
176

177
         out << "[\n";
1✔
178

179
         for(size_t i = 0; i != m_results.size(); ++i) {
3✔
180
            const Timer& t = m_results[i];
2✔
181

182
            out << "{"
2✔
183
                << "\"algo\": \"" << t.get_name() << "\", "
2✔
184
                << "\"op\": \"" << t.doing() << "\", "
2✔
185
                << "\"events\": " << t.events() << ", ";
2✔
186

187
            if(t.cycles_consumed() > 0) {
4✔
188
               out << "\"cycles\": " << t.cycles_consumed() << ", ";
4✔
189
            }
190

191
            if(t.buf_size() > 0) {
2✔
192
               out << "\"bps\": " << static_cast<uint64_t>(t.events() / (t.value() / 1000000000.0)) << ", ";
2✔
193
               out << "\"buf_size\": " << t.buf_size() << ", ";
2✔
194
            }
195

196
            out << "\"nanos\": " << t.value() << "}";
2✔
197

198
            if(i != m_results.size() - 1) {
2✔
199
               out << ",";
1✔
200
            }
201

202
            out << "\n";
2✔
203
         }
204
         out << "]\n";
1✔
205

206
         return out.str();
2✔
207
      }
1✔
208

209
   private:
210
      std::vector<Timer> m_results;
211
};
212

213
class Summary final {
1✔
214
   public:
215
      Summary() = default;
1✔
216

217
      void add(const Timer& t) {
2✔
218
         if(t.buf_size() == 0) {
2✔
219
            m_ops_entries.push_back(t);
×
220
         } else {
221
            m_bps_entries[std::make_pair(t.doing(), t.get_name())].push_back(t);
4✔
222
         }
223
      }
2✔
224

225
      std::string print() {
1✔
226
         const size_t name_padding = 35;
1✔
227
         const size_t op_name_padding = 16;
1✔
228
         const size_t op_padding = 16;
1✔
229

230
         std::ostringstream result_ss;
1✔
231
         result_ss << std::fixed;
1✔
232

233
         if(!m_bps_entries.empty()) {
1✔
234
            result_ss << "\n";
1✔
235

236
            // add table header
237
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
1✔
238
                      << "operation";
1✔
239

240
            for(const Timer& t : m_bps_entries.begin()->second) {
2✔
241
               result_ss << std::setw(op_padding) << std::right << (std::to_string(t.buf_size()) + " bytes");
2✔
242
            }
243
            result_ss << "\n";
1✔
244

245
            // add table entries
246
            for(const auto& entry : m_bps_entries) {
3✔
247
               if(entry.second.empty()) {
2✔
248
                  continue;
×
249
               }
250

251
               result_ss << std::setw(name_padding) << std::left << (entry.first.second) << std::setw(op_name_padding)
2✔
252
                         << std::left << (entry.first.first);
2✔
253

254
               for(const Timer& t : entry.second) {
4✔
255
                  if(t.events() == 0) {
2✔
256
                     result_ss << std::setw(op_padding) << std::right << "N/A";
×
257
                  } else {
258
                     result_ss << std::setw(op_padding) << std::right << std::setprecision(2)
2✔
259
                               << (t.bytes_per_second() / 1000.0);
2✔
260
                  }
261
               }
262

263
               result_ss << "\n";
2✔
264
            }
265

266
            result_ss << "\n[results are the number of 1000s bytes processed per second]\n";
1✔
267
         }
268

269
         if(!m_ops_entries.empty()) {
1✔
270
            result_ss << std::setprecision(6) << "\n";
×
271

272
            // sort entries
273
            std::sort(m_ops_entries.begin(), m_ops_entries.end());
×
274

275
            // add table header
276
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
×
277
                      << "operation" << std::setw(op_padding) << std::right << "sec/op" << std::setw(op_padding)
×
278
                      << std::right << "op/sec"
×
279
                      << "\n";
×
280

281
            // add table entries
282
            for(const Timer& entry : m_ops_entries) {
×
283
               result_ss << std::setw(name_padding) << std::left << entry.get_name() << std::setw(op_name_padding)
×
284
                         << std::left << entry.doing() << std::setw(op_padding) << std::right
×
285
                         << entry.seconds_per_event() << std::setw(op_padding) << std::right
×
286
                         << entry.events_per_second() << "\n";
×
287
            }
288
         }
289

290
         return result_ss.str();
2✔
291
      }
1✔
292

293
   private:
294
      std::map<std::pair<std::string, std::string>, std::vector<Timer>> m_bps_entries;
295
      std::vector<Timer> m_ops_entries;
296
};
297

298
std::vector<size_t> unique_buffer_sizes(const std::string& cmdline_arg) {
33✔
299
   const size_t MAX_BUF_SIZE = 64 * 1024 * 1024;
33✔
300

301
   std::set<size_t> buf;
33✔
302
   for(const std::string& size_str : Command::split_on(cmdline_arg, ',')) {
64✔
303
      size_t x = 0;
34✔
304
      try {
34✔
305
         size_t converted = 0;
34✔
306
         x = static_cast<size_t>(std::stoul(size_str, &converted, 0));
34✔
307

308
         if(converted != size_str.size()) {
33✔
309
            throw CLI_Usage_Error("Invalid integer");
×
310
         }
311
      } catch(std::exception&) {
1✔
312
         throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size");
2✔
313
      }
1✔
314

315
      if(x == 0) {
33✔
316
         throw CLI_Usage_Error("Cannot have a zero-sized buffer");
2✔
317
      }
318

319
      if(x > MAX_BUF_SIZE) {
32✔
320
         throw CLI_Usage_Error("Specified buffer size is too large");
2✔
321
      }
322

323
      buf.insert(x);
31✔
324
   }
33✔
325

326
   return std::vector<size_t>(buf.begin(), buf.end());
33✔
327
}
30✔
328

329
}  // namespace
330

331
class Speed final : public Command {
×
332
   public:
333
      Speed() :
34✔
334
            Command(
335
               "speed --msec=500 --format=default --ecc-groups= --provider= --buf-size=1024 --clear-cpuid= --cpu-clock-speed=0 --cpu-clock-ratio=1.0 *algos") {
68✔
336
      }
34✔
337

338
      static std::vector<std::string> default_benchmark_list() {
×
339
         /*
340
         This is not intended to be exhaustive: it just hits the high
341
         points of the most interesting or widely used algorithms.
342
         */
343
         // clang-format off
344
         return {
×
345
            /* Block ciphers */
346
            "AES-128",
347
            "AES-192",
348
            "AES-256",
349
            "ARIA-128",
350
            "ARIA-192",
351
            "ARIA-256",
352
            "Blowfish",
353
            "CAST-128",
354
            "Camellia-128",
355
            "Camellia-192",
356
            "Camellia-256",
357
            "DES",
358
            "TripleDES",
359
            "GOST-28147-89",
360
            "IDEA",
361
            "Noekeon",
362
            "SHACAL2",
363
            "SM4",
364
            "Serpent",
365
            "Threefish-512",
366
            "Twofish",
367

368
            /* Cipher modes */
369
            "AES-128/CBC",
370
            "AES-128/CTR-BE",
371
            "AES-128/EAX",
372
            "AES-128/OCB",
373
            "AES-128/GCM",
374
            "AES-128/XTS",
375
            "AES-128/SIV",
376

377
            "Serpent/CBC",
378
            "Serpent/CTR-BE",
379
            "Serpent/EAX",
380
            "Serpent/OCB",
381
            "Serpent/GCM",
382
            "Serpent/XTS",
383
            "Serpent/SIV",
384

385
            "ChaCha20Poly1305",
386

387
            /* Stream ciphers */
388
            "RC4",
389
            "Salsa20",
390
            "ChaCha20",
391

392
            /* Hashes */
393
            "SHA-1",
394
            "SHA-256",
395
            "SHA-512",
396
            "SHA-3(256)",
397
            "SHA-3(512)",
398
            "RIPEMD-160",
399
            "Skein-512",
400
            "Blake2b",
401
            "Whirlpool",
402

403
            /* XOFs */
404
            "SHAKE-128",
405
            "SHAKE-256",
406

407
            /* MACs */
408
            "CMAC(AES-128)",
409
            "HMAC(SHA-256)",
410

411
            /* pubkey */
412
            "RSA",
413
            "DH",
414
            "ECDH",
415
            "ECDSA",
416
            "Ed25519",
417
            "Ed448",
418
            "X25519",
419
            "X448",
420
            "McEliece",
421
            "Kyber",
422
            "SPHINCS+",
423
            "FrodoKEM",
424
            "HSS-LMS",
425
         };
×
426
         // clang-format on
427
      }
428

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

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

433
      void go() override {
33✔
434
         std::chrono::milliseconds msec(get_arg_sz("msec"));
33✔
435
         const std::string provider = get_arg("provider");
33✔
436
         std::vector<std::string> ecc_groups = Command::split_on(get_arg("ecc-groups"), ',');
69✔
437
         const std::string format = get_arg("format");
33✔
438
         const std::string clock_ratio = get_arg("cpu-clock-ratio");
36✔
439
         m_clock_speed = get_arg_sz("cpu-clock-speed");
33✔
440

441
         m_clock_cycle_ratio = std::strtod(clock_ratio.c_str(), nullptr);
33✔
442

443
         /*
444
         * This argument is intended to be the ratio between the cycle counter
445
         * and the actual machine cycles. It is extremely unlikely that there is
446
         * any machine where the cycle counter increments faster than the actual
447
         * clock.
448
         */
449
         if(m_clock_cycle_ratio < 0.0 || m_clock_cycle_ratio > 1.0) {
33✔
450
            throw CLI_Usage_Error("Unlikely CPU clock ratio of " + clock_ratio);
×
451
         }
452

453
         m_clock_cycle_ratio = 1.0 / m_clock_cycle_ratio;
33✔
454

455
         if(m_clock_speed != 0 && Botan::OS::get_cpu_cycle_counter() != 0) {
33✔
456
            error_output() << "The --cpu-clock-speed option is only intended to be used on "
×
457
                              "platforms without access to a cycle counter.\n"
458
                              "Expected incorrect results\n\n";
×
459
         }
460

461
         if(format == "table") {
33✔
462
            m_summary = std::make_unique<Summary>();
1✔
463
         } else if(format == "json") {
32✔
464
            m_json = std::make_unique<JSON_Output>();
1✔
465
         } else if(format != "default") {
31✔
466
            throw CLI_Usage_Error("Unknown --format type '" + format + "'");
×
467
         }
468

469
#if defined(BOTAN_HAS_ECC_GROUP)
470
         if(ecc_groups.empty()) {
33✔
471
            ecc_groups = {"secp256r1", "secp384r1", "secp521r1", "brainpool256r1", "brainpool384r1", "brainpool512r1"};
264✔
472
         } else if(ecc_groups.size() == 1 && ecc_groups[0] == "all") {
×
473
            auto all = Botan::EC_Group::known_named_groups();
×
474
            ecc_groups.assign(all.begin(), all.end());
×
475
         }
×
476
#endif
477

478
         std::vector<std::string> algos = get_arg_list("algos");
36✔
479

480
         const std::vector<size_t> buf_sizes = unique_buffer_sizes(get_arg("buf-size"));
69✔
481

482
         for(const std::string& cpuid_to_clear : Command::split_on(get_arg("clear-cpuid"), ',')) {
31✔
483
            auto bits = Botan::CPUID::bit_from_string(cpuid_to_clear);
1✔
484
            if(bits.empty()) {
1✔
485
               error_output() << "Warning don't know CPUID flag '" << cpuid_to_clear << "'\n";
1✔
486
            }
487

488
            for(auto bit : bits) {
1✔
489
               Botan::CPUID::clear_cpuid_bit(bit);
×
490
            }
491
         }
31✔
492

493
         if(verbose() || m_summary) {
30✔
494
            output() << Botan::version_string() << "\n"
2✔
495
                     << "CPUID: " << Botan::CPUID::to_string() << "\n\n";
3✔
496
         }
497

498
         const bool using_defaults = (algos.empty());
30✔
499
         if(using_defaults) {
30✔
500
            algos = default_benchmark_list();
×
501
         }
502

503
         for(const auto& algo : algos) {
82✔
504
            using namespace std::placeholders;
52✔
505

506
            if(false) {
52✔
507
               // Since everything might be disabled, need a block to else if from
508
            }
509
#if defined(BOTAN_HAS_HASH)
510
            else if(!Botan::HashFunction::providers(algo).empty()) {
52✔
511
               bench_providers_of<Botan::HashFunction>(
1✔
512
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_hash, this, _1, _2, _3, _4));
2✔
513
            }
514
#endif
515
#if defined(BOTAN_HAS_XOF)
516
            else if(!Botan::XOF::providers(algo).empty()) {
51✔
517
               bench_providers_of<Botan::XOF>(
×
518
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_xof, this, _1, _2, _3, _4));
×
519
            }
520
#endif
521
#if defined(BOTAN_HAS_BLOCK_CIPHER)
522
            else if(!Botan::BlockCipher::providers(algo).empty()) {
51✔
523
               bench_providers_of<Botan::BlockCipher>(
4✔
524
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_block_cipher, this, _1, _2, _3, _4));
8✔
525
            }
526
#endif
527
#if defined(BOTAN_HAS_STREAM_CIPHER)
528
            else if(!Botan::StreamCipher::providers(algo).empty()) {
47✔
529
               bench_providers_of<Botan::StreamCipher>(
1✔
530
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_stream_cipher, this, _1, _2, _3, _4));
2✔
531
            }
532
#endif
533
#if defined(BOTAN_HAS_CIPHER_MODES)
534
            else if(auto enc = Botan::Cipher_Mode::create(algo, Botan::Cipher_Dir::Encryption, provider)) {
46✔
535
               auto dec = Botan::Cipher_Mode::create_or_throw(algo, Botan::Cipher_Dir::Decryption, provider);
1✔
536
               bench_cipher_mode(*enc, *dec, msec, buf_sizes);
1✔
537
            }
1✔
538
#endif
539
#if defined(BOTAN_HAS_MAC)
540
            else if(!Botan::MessageAuthenticationCode::providers(algo).empty()) {
45✔
541
               bench_providers_of<Botan::MessageAuthenticationCode>(
1✔
542
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_mac, this, _1, _2, _3, _4));
2✔
543
            }
544
#endif
545
#if defined(BOTAN_HAS_RSA)
546
            else if(algo == "RSA") {
44✔
547
               bench_rsa(provider, msec);
1✔
548
            } else if(algo == "RSA_keygen") {
43✔
549
               bench_rsa_keygen(provider, msec);
1✔
550
            }
551
#endif
552

553
#if defined(BOTAN_HAS_PCURVES)
554
            else if(algo == "ECDSA-pcurves") {
42✔
555
               bench_pcurve_ecdsa(ecc_groups, msec);
×
556
            } else if(algo == "ECDH-pcurves") {
42✔
557
               bench_pcurve_ecdh(ecc_groups, msec);
×
558
            } else if(algo == "pcurves") {
42✔
559
               bench_pcurves(ecc_groups, msec);
×
560
            }
561
#endif
562

563
#if defined(BOTAN_HAS_ECDSA)
564
            else if(algo == "ECDSA") {
42✔
565
               bench_ecdsa(ecc_groups, provider, msec);
1✔
566
            } else if(algo == "ecdsa_recovery") {
41✔
567
               bench_ecdsa_recovery(ecc_groups, provider, msec);
1✔
568
            }
569
#endif
570
#if defined(BOTAN_HAS_SM2)
571
            else if(algo == "SM2") {
40✔
572
               bench_sm2(ecc_groups, provider, msec);
1✔
573
            }
574
#endif
575
#if defined(BOTAN_HAS_ECKCDSA)
576
            else if(algo == "ECKCDSA") {
39✔
577
               bench_eckcdsa(ecc_groups, provider, msec);
1✔
578
            }
579
#endif
580
#if defined(BOTAN_HAS_GOST_34_10_2001)
581
            else if(algo == "GOST-34.10") {
38✔
582
               bench_gost_3410(provider, msec);
1✔
583
            }
584
#endif
585
#if defined(BOTAN_HAS_ECGDSA)
586
            else if(algo == "ECGDSA") {
37✔
587
               bench_ecgdsa(ecc_groups, provider, msec);
1✔
588
            }
589
#endif
590
#if defined(BOTAN_HAS_ED25519)
591
            else if(algo == "Ed25519") {
36✔
592
               bench_ed25519(provider, msec);
1✔
593
            }
594
#endif
595
#if defined(BOTAN_HAS_ED448)
596
            else if(algo == "Ed448") {
35✔
597
               bench_ed448(provider, msec);
1✔
598
            }
599
#endif
600
#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
601
            else if(algo == "DH") {
34✔
602
               bench_dh(provider, msec);
1✔
603
            }
604
#endif
605
#if defined(BOTAN_HAS_DSA)
606
            else if(algo == "DSA") {
33✔
607
               bench_dsa(provider, msec);
1✔
608
            }
609
#endif
610
#if defined(BOTAN_HAS_ELGAMAL)
611
            else if(algo == "ElGamal") {
32✔
612
               bench_elgamal(provider, msec);
1✔
613
            }
614
#endif
615
#if defined(BOTAN_HAS_ECDH)
616
            else if(algo == "ECDH") {
31✔
617
               bench_ecdh(ecc_groups, provider, msec);
1✔
618
            }
619
#endif
620
#if defined(BOTAN_HAS_X25519)
621
            else if(algo == "X25519") {
30✔
622
               bench_x25519(provider, msec);
1✔
623
            }
624
#endif
625
#if defined(BOTAN_HAS_X448)
626
            else if(algo == "X448") {
29✔
627
               bench_x448(provider, msec);
1✔
628
            }
629
#endif
630
#if defined(BOTAN_HAS_MCELIECE)
631
            else if(algo == "McEliece") {
28✔
632
               bench_mceliece(provider, msec);
1✔
633
            }
634
#endif
635
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
636
            else if(algo == "Kyber") {
27✔
637
               bench_kyber(provider, msec);
1✔
638
            }
639
#endif
640
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
641
            else if(algo == "Dilithium") {
26✔
642
               bench_dilithium(provider, msec);
1✔
643
            }
644
#endif
645
#if defined(BOTAN_HAS_XMSS_RFC8391)
646
            else if(algo == "XMSS") {
25✔
647
               bench_xmss(provider, msec);
1✔
648
            }
649
#endif
650
#if defined(BOTAN_HAS_HSS_LMS)
651
            else if(algo == "HSS-LMS") {
24✔
652
               bench_hss_lms(provider, msec);
×
653
            }
654
#endif
655
#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
656
            else if(algo == "SPHINCS+") {
24✔
657
               bench_sphincs_plus(provider, msec);
1✔
658
            }
659
#endif
660
#if defined(BOTAN_HAS_FRODOKEM)
661
            else if(algo == "FrodoKEM") {
23✔
662
               bench_frodokem(provider, msec);
×
663
            }
664
#endif
665
#if defined(BOTAN_HAS_SCRYPT)
666
            else if(algo == "scrypt") {
23✔
667
               bench_scrypt(provider, msec);
1✔
668
            }
669
#endif
670
#if defined(BOTAN_HAS_ARGON2)
671
            else if(algo == "argon2") {
22✔
672
               bench_argon2(provider, msec);
1✔
673
            }
674
#endif
675
#if defined(BOTAN_HAS_BCRYPT)
676
            else if(algo == "bcrypt") {
21✔
677
               bench_bcrypt();
1✔
678
            }
679
#endif
680
#if defined(BOTAN_HAS_PASSHASH9)
681
            else if(algo == "passhash9") {
20✔
682
               bench_passhash9();
1✔
683
            }
684
#endif
685
#if defined(BOTAN_HAS_ZFEC)
686
            else if(algo == "zfec") {
19✔
687
               bench_zfec(msec);
1✔
688
            }
689
#endif
690
#if defined(BOTAN_HAS_POLY_DBL)
691
            else if(algo == "poly_dbl") {
18✔
692
               bench_poly_dbl(msec);
1✔
693
            }
694
#endif
695

696
#if defined(BOTAN_HAS_DL_GROUP)
697
            else if(algo == "modexp") {
17✔
698
               bench_modexp(msec);
1✔
699
            }
700
#endif
701

702
#if defined(BOTAN_HAS_BIGINT)
703
            else if(algo == "mp_mul") {
16✔
704
               bench_mp_mul(msec);
1✔
705
            } else if(algo == "mp_div") {
15✔
706
               bench_mp_div(msec);
1✔
707
            } else if(algo == "mp_div10") {
14✔
708
               bench_mp_div10(msec);
1✔
709
            }
710
#endif
711

712
#if defined(BOTAN_HAS_NUMBERTHEORY)
713
            else if(algo == "primality_test") {
13✔
714
               bench_primality_tests(msec);
1✔
715
            } else if(algo == "random_prime") {
12✔
716
               bench_random_prime(msec);
1✔
717
            } else if(algo == "inverse_mod") {
11✔
718
               bench_inverse_mod(msec);
1✔
719
            } else if(algo == "bn_redc") {
10✔
720
               bench_bn_redc(msec);
1✔
721
            } else if(algo == "nistp_redc") {
9✔
722
               bench_nistp_redc(msec);
1✔
723
            }
724
#endif
725

726
#if defined(BOTAN_HAS_FPE_FE1)
727
            else if(algo == "fpe_fe1") {
8✔
728
               bench_fpe_fe1(msec);
1✔
729
            }
730
#endif
731

732
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
733
            else if(algo == "rfc3394") {
7✔
734
               bench_rfc3394(msec);
1✔
735
            }
736
#endif
737

738
#if defined(BOTAN_HAS_ECC_GROUP)
739
            else if(algo == "ecc_mult") {
6✔
740
               bench_ecc_mult(ecc_groups, msec);
1✔
741
            } else if(algo == "ecc_init") {
5✔
742
               bench_ecc_init(ecc_groups, msec);
1✔
743
            } else if(algo == "os2ecp") {
4✔
744
               bench_os2ecp(ecc_groups, msec);
1✔
745
            }
746
#endif
747
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
748
            else if(algo == "ec_h2c") {
3✔
749
               bench_ec_h2c(msec);
1✔
750
            }
751
#endif
752
            else if(algo == "RNG") {
2✔
753
#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
754
               Botan::AutoSeeded_RNG auto_rng;
1✔
755
               bench_rng(auto_rng, "AutoSeeded_RNG (with reseed)", msec, buf_sizes);
1✔
756
#endif
757

758
#if defined(BOTAN_HAS_SYSTEM_RNG)
759
               bench_rng(Botan::system_rng(), "System_RNG", msec, buf_sizes);
1✔
760
#endif
761

762
#if defined(BOTAN_HAS_PROCESSOR_RNG)
763
               if(Botan::Processor_RNG::available()) {
1✔
764
                  Botan::Processor_RNG hwrng;
1✔
765
                  bench_rng(hwrng, "Processor_RNG", msec, buf_sizes);
2✔
766
               }
1✔
767
#endif
768

769
#if defined(BOTAN_HAS_HMAC_DRBG)
770
               for(std::string hash : {"SHA-256", "SHA-384", "SHA-512"}) {
4✔
771
                  Botan::HMAC_DRBG hmac_drbg(hash);
3✔
772
                  bench_rng(hmac_drbg, hmac_drbg.name(), msec, buf_sizes);
3✔
773
               }
3✔
774
#endif
775

776
#if defined(BOTAN_HAS_CHACHA_RNG)
777
               // Provide a dummy seed
778
               Botan::ChaCha_RNG chacha_rng(Botan::secure_vector<uint8_t>(32));
1✔
779
               bench_rng(chacha_rng, "ChaCha_RNG", msec, buf_sizes);
1✔
780
#endif
781

782
            } else if(algo == "entropy") {
2✔
783
               bench_entropy_sources(msec);
1✔
784
            } else {
785
               if(verbose() || !using_defaults) {
×
786
                  error_output() << "Unknown algorithm '" << algo << "'\n";
×
787
               }
788
            }
46✔
789
         }
790

791
         if(m_json) {
30✔
792
            output() << m_json->print();
2✔
793
         }
794
         if(m_summary) {
30✔
795
            output() << m_summary->print() << "\n";
3✔
796
         }
797

798
         if(verbose() && m_clock_speed == 0 && m_cycles_consumed > 0 && m_ns_taken > 0) {
30✔
799
            const double seconds = static_cast<double>(m_ns_taken) / 1000000000;
×
800
            const double Hz = static_cast<double>(m_cycles_consumed) / seconds;
×
801
            const double MHz = Hz / 1000000;
×
802
            output() << "\nEstimated clock speed " << MHz << " MHz\n";
×
803
         }
804
      }
105✔
805

806
   private:
807
      size_t m_clock_speed = 0;
808
      double m_clock_cycle_ratio = 0.0;
809
      uint64_t m_cycles_consumed = 0;
810
      uint64_t m_ns_taken = 0;
811
      std::unique_ptr<Summary> m_summary;
812
      std::unique_ptr<JSON_Output> m_json;
813

814
      void record_result(const std::unique_ptr<Timer>& t) {
453✔
815
         m_ns_taken += t->value();
453✔
816
         m_cycles_consumed += t->cycles_consumed();
453✔
817
         if(m_json) {
453✔
818
            m_json->add(*t);
2✔
819
         } else {
820
            output() << t->to_string() << std::flush;
902✔
821
            if(m_summary) {
451✔
822
               m_summary->add(*t);
2✔
823
            }
824
         }
825
      }
453✔
826

827
      template <typename T>
828
      using bench_fn = std::function<void(T&, std::string, std::chrono::milliseconds, const std::vector<size_t>&)>;
829

830
      template <typename T>
831
      void bench_providers_of(const std::string& algo,
7✔
832
                              const std::string& provider, /* user request, if any */
833
                              const std::chrono::milliseconds runtime,
834
                              const std::vector<size_t>& buf_sizes,
835
                              bench_fn<T> bench_one) {
836
         for(const auto& prov : T::providers(algo)) {
14✔
837
            if(provider.empty() || provider == prov) {
7✔
838
               auto p = T::create(algo, prov);
7✔
839

840
               if(p) {
7✔
841
                  bench_one(*p, prov, runtime, buf_sizes);
14✔
842
               }
843
            }
7✔
844
         }
845
      }
7✔
846

847
      std::unique_ptr<Timer> make_timer(const std::string& name,
457✔
848
                                        uint64_t event_mult = 1,
849
                                        const std::string& what = "",
850
                                        const std::string& provider = "",
851
                                        size_t buf_size = 0) {
852
         return std::make_unique<Timer>(name, provider, what, event_mult, buf_size, m_clock_cycle_ratio, m_clock_speed);
154✔
853
      }
854

855
      std::unique_ptr<Timer> make_timer(const std::string& algo, const std::string& provider, const std::string& what) {
216✔
856
         return make_timer(algo, 1, what, provider, 0);
124✔
857
      }
858

859
#if defined(BOTAN_HAS_BLOCK_CIPHER)
860
      void bench_block_cipher(Botan::BlockCipher& cipher,
4✔
861
                              const std::string& provider,
862
                              std::chrono::milliseconds runtime,
863
                              const std::vector<size_t>& buf_sizes) {
864
         auto ks_timer = make_timer(cipher.name(), provider, "key schedule");
8✔
865

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

869
         const size_t bs = cipher.block_size();
4✔
870
         std::set<size_t> buf_sizes_in_blocks;
4✔
871
         for(size_t buf_size : buf_sizes) {
9✔
872
            if(buf_size % bs == 0) {
5✔
873
               buf_sizes_in_blocks.insert(buf_size);
5✔
874
            } else {
875
               buf_sizes_in_blocks.insert(buf_size + bs - (buf_size % bs));
×
876
            }
877
         }
878

879
         for(size_t buf_size : buf_sizes_in_blocks) {
9✔
880
            std::vector<uint8_t> buffer(buf_size);
5✔
881
            const size_t blocks = buf_size / bs;
5✔
882

883
            auto encrypt_timer = make_timer(cipher.name(), buffer.size(), "encrypt", provider, buf_size);
10✔
884
            auto decrypt_timer = make_timer(cipher.name(), buffer.size(), "decrypt", provider, buf_size);
10✔
885

886
            encrypt_timer->run_until_elapsed(runtime, [&]() { cipher.encrypt_n(&buffer[0], &buffer[0], blocks); });
6,157✔
887
            record_result(encrypt_timer);
5✔
888

889
            decrypt_timer->run_until_elapsed(runtime, [&]() { cipher.decrypt_n(&buffer[0], &buffer[0], blocks); });
5,880✔
890
            record_result(decrypt_timer);
5✔
891
         }
5✔
892
      }
8✔
893
#endif
894

895
#if defined(BOTAN_HAS_STREAM_CIPHER)
896
      void bench_stream_cipher(Botan::StreamCipher& cipher,
1✔
897
                               const std::string& provider,
898
                               const std::chrono::milliseconds runtime,
899
                               const std::vector<size_t>& buf_sizes) {
900
         for(auto buf_size : buf_sizes) {
2✔
901
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
902

903
            auto encrypt_timer = make_timer(cipher.name(), buffer.size(), "encrypt", provider, buf_size);
2✔
904

905
            const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
1✔
906
            cipher.set_key(key);
1✔
907

908
            if(cipher.valid_iv_length(12)) {
1✔
909
               const Botan::InitializationVector iv(rng(), 12);
1✔
910
               cipher.set_iv(iv.begin(), iv.size());
1✔
911
            }
1✔
912

913
            while(encrypt_timer->under(runtime)) {
764✔
914
               encrypt_timer->run([&]() { cipher.encipher(buffer); });
1,526✔
915
            }
916

917
            record_result(encrypt_timer);
1✔
918

919
            if(verbose()) {
1✔
920
               auto ks_timer = make_timer(cipher.name(), buffer.size(), "write_keystream", provider, buf_size);
×
921

922
               while(ks_timer->under(runtime)) {
×
923
                  ks_timer->run([&]() { cipher.write_keystream(buffer.data(), buffer.size()); });
×
924
               }
925
               record_result(ks_timer);
×
926
            }
×
927
         }
2✔
928
      }
1✔
929
#endif
930

931
#if defined(BOTAN_HAS_HASH)
932
      void bench_hash(Botan::HashFunction& hash,
1✔
933
                      const std::string& provider,
934
                      const std::chrono::milliseconds runtime,
935
                      const std::vector<size_t>& buf_sizes) {
936
         std::vector<uint8_t> output(hash.output_length());
1✔
937

938
         for(auto buf_size : buf_sizes) {
2✔
939
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
940

941
            auto timer = make_timer(hash.name(), buffer.size(), "hash", provider, buf_size);
2✔
942
            timer->run_until_elapsed(runtime, [&]() {
1✔
943
               hash.update(buffer);
212✔
944
               hash.final(output.data());
212✔
945
            });
212✔
946
            record_result(timer);
1✔
947
         }
2✔
948
      }
1✔
949
#endif
950

951
#if defined(BOTAN_HAS_XOF)
952
      void bench_xof(Botan::XOF& xof,
×
953
                     const std::string& provider,
954
                     const std::chrono::milliseconds runtime,
955
                     const std::vector<size_t>& buf_sizes) {
956
         for(auto buf_size : buf_sizes) {
×
957
            Botan::secure_vector<uint8_t> in = rng().random_vec(buf_size);
×
958
            Botan::secure_vector<uint8_t> out(buf_size);
×
959

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

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

966
            record_result(in_timer);
×
967
            record_result(out_timer);
×
968
         }
×
969
      }
×
970
#endif
971

972
#if defined(BOTAN_HAS_MAC)
973
      void bench_mac(Botan::MessageAuthenticationCode& mac,
1✔
974
                     const std::string& provider,
975
                     const std::chrono::milliseconds runtime,
976
                     const std::vector<size_t>& buf_sizes) {
977
         std::vector<uint8_t> output(mac.output_length());
1✔
978

979
         for(auto buf_size : buf_sizes) {
2✔
980
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
981

982
            const Botan::SymmetricKey key(rng(), mac.maximum_keylength());
1✔
983
            mac.set_key(key);
1✔
984
            mac.start(nullptr, 0);
1✔
985

986
            auto timer = make_timer(mac.name(), buffer.size(), "mac", provider, buf_size);
2✔
987
            timer->run_until_elapsed(runtime, [&]() { mac.update(buffer); });
238✔
988
            timer->run([&]() { mac.final(output.data()); });
2✔
989
            record_result(timer);
1✔
990
         }
3✔
991
      }
1✔
992
#endif
993

994
#if defined(BOTAN_HAS_CIPHER_MODES)
995
      void bench_cipher_mode(Botan::Cipher_Mode& enc,
1✔
996
                             Botan::Cipher_Mode& dec,
997
                             const std::chrono::milliseconds runtime,
998
                             const std::vector<size_t>& buf_sizes) {
999
         auto ks_timer = make_timer(enc.name(), enc.provider(), "key schedule");
2✔
1000

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

1003
         ks_timer->run([&]() { enc.set_key(key); });
2✔
1004
         ks_timer->run([&]() { dec.set_key(key); });
2✔
1005

1006
         record_result(ks_timer);
1✔
1007

1008
         for(auto buf_size : buf_sizes) {
2✔
1009
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
1010

1011
            auto encrypt_timer = make_timer(enc.name(), buffer.size(), "encrypt", enc.provider(), buf_size);
2✔
1012
            auto decrypt_timer = make_timer(dec.name(), buffer.size(), "decrypt", dec.provider(), buf_size);
2✔
1013

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

1016
            if(buf_size >= enc.minimum_final_size()) {
1✔
1017
               while(encrypt_timer->under(runtime) && decrypt_timer->under(runtime)) {
178✔
1018
                  // Must run in this order, or AEADs will reject the ciphertext
1019
                  encrypt_timer->run([&]() {
177✔
1020
                     enc.start(iv);
177✔
1021
                     enc.finish(buffer);
177✔
1022
                  });
177✔
1023

1024
                  decrypt_timer->run([&]() {
177✔
1025
                     dec.start(iv);
177✔
1026
                     dec.finish(buffer);
177✔
1027
                  });
177✔
1028

1029
                  if(!iv.empty()) {
177✔
1030
                     iv[iv.size() - 1] += 1;
177✔
1031
                  }
1032
               }
1033
            }
1034

1035
            record_result(encrypt_timer);
1✔
1036
            record_result(decrypt_timer);
1✔
1037
         }
2✔
1038
      }
1✔
1039
#endif
1040

1041
      void bench_rng(Botan::RandomNumberGenerator& rng,
7✔
1042
                     const std::string& rng_name,
1043
                     const std::chrono::milliseconds runtime,
1044
                     const std::vector<size_t>& buf_sizes) {
1045
         for(auto buf_size : buf_sizes) {
14✔
1046
            Botan::secure_vector<uint8_t> buffer(buf_size);
7✔
1047

1048
#if defined(BOTAN_HAS_SYSTEM_RNG)
1049
            rng.reseed_from_rng(Botan::system_rng(), 256);
7✔
1050
#endif
1051

1052
            auto timer = make_timer(rng_name, buffer.size(), "generate", "", buf_size);
7✔
1053
            timer->run_until_elapsed(runtime, [&]() { rng.randomize(buffer.data(), buffer.size()); });
1,486✔
1054
            record_result(timer);
7✔
1055
         }
14✔
1056
      }
7✔
1057

1058
      void bench_entropy_sources(const std::chrono::milliseconds /*unused*/) {
1✔
1059
         Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources();
1✔
1060

1061
         for(auto src : srcs.enabled_sources()) {
5✔
1062
            size_t entropy_bits = 0;
4✔
1063
            Botan_Tests::SeedCapturing_RNG rng;
4✔
1064

1065
            auto timer = make_timer(src, "", "bytes");
8✔
1066
            timer->run([&]() { entropy_bits = srcs.poll_just(rng, src); });
8✔
1067

1068
            size_t compressed_size = 0;
4✔
1069

1070
#if defined(BOTAN_HAS_ZLIB)
1071
            auto comp = Botan::Compression_Algorithm::create("zlib");
4✔
1072

1073
            if(comp) {
4✔
1074
               Botan::secure_vector<uint8_t> compressed;
4✔
1075
               compressed.assign(rng.seed_material().begin(), rng.seed_material().end());
4✔
1076
               comp->start(9);
4✔
1077
               comp->finish(compressed);
4✔
1078

1079
               compressed_size = compressed.size();
4✔
1080
            }
4✔
1081
#endif
1082

1083
            std::ostringstream msg;
4✔
1084

1085
            msg << "Entropy source " << src << " output " << rng.seed_material().size() << " bytes"
4✔
1086
                << " estimated entropy " << entropy_bits << " in " << timer->milliseconds() << " ms";
4✔
1087

1088
            if(compressed_size > 0) {
4✔
1089
               msg << " output compressed to " << compressed_size << " bytes";
4✔
1090
            }
1091

1092
            msg << " total samples " << rng.samples() << "\n";
4✔
1093

1094
            timer->set_custom_msg(msg.str());
8✔
1095

1096
            record_result(timer);
4✔
1097
         }
9✔
1098
      }
1✔
1099

1100
#if defined(BOTAN_HAS_ECC_GROUP)
1101
      void bench_ecc_init(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1102
         for(std::string group_name : groups) {
7✔
1103
            auto timer = make_timer(group_name + " initialization");
12✔
1104

1105
            while(timer->under(runtime)) {
12✔
1106
               Botan::EC_Group::clear_registered_curve_data();
6✔
1107
               timer->run([&]() { Botan::EC_Group::from_name(group_name); });
12✔
1108
            }
1109

1110
            record_result(timer);
6✔
1111
         }
6✔
1112
      }
1✔
1113

1114
      void bench_ecc_mult(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1115
         for(const std::string& group_name : groups) {
7✔
1116
            const auto ec_group = Botan::EC_Group::from_name(group_name);
6✔
1117

1118
            auto mult_timer = make_timer(group_name + " Montgomery ladder");
12✔
1119
            auto blinded_mult_timer = make_timer(group_name + " blinded comb");
12✔
1120
            auto blinded_var_mult_timer = make_timer(group_name + " blinded window");
12✔
1121

1122
            const Botan::EC_Point& base_point = ec_group.get_base_point();
6✔
1123

1124
            std::vector<Botan::BigInt> ws;
6✔
1125

1126
            while(mult_timer->under(runtime) && blinded_mult_timer->under(runtime) &&
12✔
1127
                  blinded_var_mult_timer->under(runtime)) {
7✔
1128
               const Botan::BigInt scalar = ec_group.random_scalar(rng());
6✔
1129

1130
               const Botan::EC_Point r1 = mult_timer->run([&]() { return base_point * scalar; });
18✔
1131

1132
               const Botan::EC_Point r2 = blinded_mult_timer->run([&]() {
6✔
1133
                  auto pt = ec_group.blinded_base_point_multiply(scalar, rng(), ws);
6✔
1134
                  pt.force_affine();
6✔
1135
                  return pt;
6✔
1136
               });
6✔
1137

1138
               const Botan::EC_Point r3 = blinded_var_mult_timer->run([&]() {
6✔
1139
                  auto pt = ec_group.blinded_var_point_multiply(base_point, scalar, rng(), ws);
6✔
1140
                  pt.force_affine();
6✔
1141
                  return pt;
6✔
1142
               });
6✔
1143

1144
               BOTAN_ASSERT_EQUAL(r1, r2, "Same point computed by Montgomery and comb");
6✔
1145
               BOTAN_ASSERT_EQUAL(r1, r3, "Same point computed by Montgomery and window");
6✔
1146
            }
12✔
1147

1148
            record_result(mult_timer);
6✔
1149
            record_result(blinded_mult_timer);
6✔
1150
            record_result(blinded_var_mult_timer);
6✔
1151
         }
6✔
1152
      }
1✔
1153

1154
      void bench_os2ecp(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1155
         for(const std::string& group_name : groups) {
7✔
1156
            auto uncmp_timer = make_timer("OS2ECP uncompressed " + group_name);
12✔
1157
            auto cmp_timer = make_timer("OS2ECP compressed " + group_name);
12✔
1158

1159
            const auto ec_group = Botan::EC_Group::from_name(group_name);
6✔
1160

1161
            while(uncmp_timer->under(runtime) && cmp_timer->under(runtime)) {
14✔
1162
               const Botan::BigInt k(rng(), 256);
8✔
1163
               const Botan::EC_Point p = ec_group.get_base_point() * k;
8✔
1164
               const std::vector<uint8_t> os_cmp = p.encode(Botan::EC_Point_Format::Compressed);
8✔
1165
               const std::vector<uint8_t> os_uncmp = p.encode(Botan::EC_Point_Format::Uncompressed);
8✔
1166

1167
               uncmp_timer->run([&]() { ec_group.OS2ECP(os_uncmp); });
16✔
1168
               cmp_timer->run([&]() { ec_group.OS2ECP(os_cmp); });
16✔
1169
            }
16✔
1170

1171
            record_result(uncmp_timer);
6✔
1172
            record_result(cmp_timer);
6✔
1173
         }
6✔
1174
      }
1✔
1175

1176
#endif
1177

1178
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
1179
      void bench_ec_h2c(const std::chrono::milliseconds runtime) {
1✔
1180
         for(std::string group_name : {"secp256r1", "secp384r1", "secp521r1"}) {
4✔
1181
            auto h2c_ro_timer = make_timer(group_name + "-RO", "", "hash to curve");
6✔
1182
            auto h2c_nu_timer = make_timer(group_name + "-NU", "", "hash to curve");
6✔
1183

1184
            const auto group = Botan::EC_Group::from_name(group_name);
3✔
1185

1186
            const std::string hash_fn = "SHA-256";
3✔
1187

1188
            while(h2c_ro_timer->under(runtime)) {
14✔
1189
               const auto input = rng().random_array<32>();
11✔
1190
               const auto domain_sep = rng().random_array<32>();
11✔
1191

1192
               h2c_ro_timer->run(
11✔
1193
                  [&]() { return Botan::EC_AffinePoint::hash_to_curve_ro(group, hash_fn, input, domain_sep); });
11✔
1194

1195
               h2c_nu_timer->run(
11✔
1196
                  [&]() { return Botan::EC_AffinePoint::hash_to_curve_nu(group, hash_fn, input, domain_sep); });
11✔
1197
            }
1198

1199
            record_result(h2c_ro_timer);
3✔
1200
            record_result(h2c_nu_timer);
3✔
1201
         }
3✔
1202
      }
1✔
1203
#endif
1204

1205
#if defined(BOTAN_HAS_PCURVES)
1206

1207
      void bench_pcurves(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
×
1208
         for(const auto& group_name : groups) {
×
1209
            if(auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name)) {
×
1210
               auto base_timer = make_timer(group_name + " pcurve base mul");
×
1211
               auto var_timer = make_timer(group_name + " pcurve var mul");
×
1212
               auto mul2_setup_timer = make_timer(group_name + " pcurve mul2 setup");
×
1213
               auto mul2_timer = make_timer(group_name + " pcurve mul2");
×
1214

1215
               auto scalar_invert = make_timer(group_name + " pcurve scalar invert");
×
1216
               auto to_affine = make_timer(group_name + " pcurve proj->affine");
×
1217

1218
               auto g = curve->generator();
×
1219
               auto h = curve->mul(g, curve->random_scalar(rng()), rng()).to_affine();
×
1220
               auto gh_tab = curve->mul2_setup(g, h);
×
1221

1222
               while(base_timer->under(runtime)) {
×
1223
                  const auto scalar = curve->random_scalar(rng());
×
1224
                  base_timer->run([&]() { return curve->mul_by_g(scalar, rng()).to_affine(); });
×
1225
               }
×
1226

1227
               while(var_timer->under(runtime)) {
×
1228
                  const auto scalar = curve->random_scalar(rng());
×
1229
                  var_timer->run([&]() { return curve->mul(h, scalar, rng()).to_affine(); });
×
1230
               }
×
1231

1232
               while(mul2_setup_timer->under(runtime)) {
×
1233
                  mul2_setup_timer->run([&]() { return curve->mul2_setup(g, h); });
×
1234
               }
1235

1236
               while(mul2_timer->under(runtime)) {
×
1237
                  const auto scalar = curve->random_scalar(rng());
×
1238
                  const auto scalar2 = curve->random_scalar(rng());
×
1239
                  mul2_timer->run([&]() -> std::optional<Botan::PCurve::PrimeOrderCurve::AffinePoint> {
×
1240
                     if(auto pt = curve->mul2_vartime(*gh_tab, scalar, scalar2)) {
×
1241
                        return pt->to_affine();
×
1242
                     } else {
1243
                        return {};
×
1244
                     }
×
1245
                  });
1246
               }
×
1247

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

1251
               while(scalar_invert->under(runtime)) {
×
1252
                  const auto scalar = curve->random_scalar(rng());
×
1253
                  scalar_invert->run([&]() { scalar.invert(); });
×
1254
               }
×
1255

1256
               record_result(base_timer);
×
1257
               record_result(var_timer);
×
1258
               record_result(mul2_setup_timer);
×
1259
               record_result(mul2_timer);
×
1260
               record_result(to_affine);
×
1261
               record_result(scalar_invert);
×
1262
            }
×
1263
         }
1264
      }
×
1265

1266
      void bench_pcurve_ecdsa(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
×
1267
         for(const auto& group_name : groups) {
×
1268
            auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name);
×
1269
            if(!curve) {
×
1270
               continue;
×
1271
            }
1272

1273
            // Setup (not timed)
1274
            const auto g = curve->generator();
×
1275
            const auto x = curve->random_scalar(rng());
×
1276
            const auto y = curve->mul_by_g(x, rng()).to_affine();
×
1277
            const auto e = curve->random_scalar(rng());
×
1278

1279
            const auto gy_tab = curve->mul2_setup(g, y);
×
1280

1281
            auto b = curve->random_scalar(rng());
×
1282
            auto b_inv = b.invert();
×
1283

1284
            auto sign_timer = make_timer("ECDSA sign pcurves " + group_name);
×
1285
            auto verify_timer = make_timer("ECDSA verify pcurves " + group_name);
×
1286

1287
            while(sign_timer->under(runtime)) {
×
1288
               sign_timer->start();
×
1289

1290
               const auto signature = [&]() {
×
1291
                  const auto k = curve->random_scalar(rng());
×
1292
                  const auto r = curve->base_point_mul_x_mod_order(k, rng());
×
1293
                  const auto k_inv = (b * k).invert() * b;
×
1294
                  b = b.square();
×
1295
                  b_inv = b_inv.square();
×
1296
                  const auto be = b * e;
×
1297
                  const auto bx = b * x;
×
1298
                  const auto bxr_e = (bx * r) + be;
×
1299
                  const auto s = (k_inv * bxr_e) * b_inv;
×
1300

1301
                  return Botan::concat(r.serialize(), s.serialize());
×
1302
               }();
×
1303

1304
               sign_timer->stop();
×
1305

1306
               verify_timer->start();
×
1307

1308
               auto result = [&](std::span<const uint8_t> sig) {
×
1309
                  const size_t scalar_bytes = curve->scalar_bytes();
×
1310
                  if(sig.size() != 2 * scalar_bytes) {
×
1311
                     return false;
1312
                  }
1313

1314
                  const auto r = curve->deserialize_scalar(sig.first(scalar_bytes));
×
1315
                  const auto s = curve->deserialize_scalar(sig.last(scalar_bytes));
×
1316

1317
                  if(r && s) {
×
1318
                     if(r->is_zero() || s->is_zero()) {
×
1319
                        return false;
×
1320
                     }
1321

1322
                     auto w = s->invert();
×
1323

1324
                     auto u1 = e * w;
×
1325
                     auto u2 = *r * w;
×
1326

1327
                     return curve->mul2_vartime_x_mod_order_eq(*gy_tab, *r, u1, u2);
×
1328
                  }
×
1329

1330
                  return false;
1331
               }(signature);
×
1332

1333
               BOTAN_ASSERT(result, "ECDSA-pcurves signature ok");
×
1334

1335
               verify_timer->stop();
×
1336
            }
×
1337

1338
            record_result(sign_timer);
×
1339
            record_result(verify_timer);
×
1340
         }
×
1341
      }
×
1342

1343
      void bench_pcurve_ecdh(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
×
1344
         for(const auto& group_name : groups) {
×
1345
            auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name);
×
1346
            if(!curve) {
×
1347
               continue;
×
1348
            }
1349

1350
            auto ka_timer = make_timer("ECDH agree pcurves " + group_name);
×
1351

1352
            auto agree = [&](const Botan::PCurve::PrimeOrderCurve::Scalar& sk, std::span<const uint8_t> pt_bytes) {
×
1353
               const auto pt = curve->deserialize_point(pt_bytes);
×
1354
               if(pt) {
×
1355
                  return curve->mul(*pt, sk, rng()).to_affine().serialize();
×
1356
               } else {
1357
                  return std::vector<uint8_t>();
×
1358
               }
1359
            };
×
1360

1361
            while(ka_timer->under(runtime)) {
×
1362
               const auto g = curve->generator();
×
1363
               const auto x1 = curve->random_scalar(rng());
×
1364
               const auto x2 = curve->random_scalar(rng());
×
1365

1366
               const auto y1 = curve->mul_by_g(x1, rng()).to_affine().serialize();
×
1367
               const auto y2 = curve->mul_by_g(x2, rng()).to_affine().serialize();
×
1368

1369
               ka_timer->start();
×
1370
               const auto ss1 = agree(x1, y2);
×
1371
               ka_timer->stop();
×
1372

1373
               ka_timer->start();
×
1374
               const auto ss2 = agree(x1, y2);
×
1375
               ka_timer->stop();
×
1376

1377
               BOTAN_ASSERT(ss1 == ss2, "Key agreement worked");
×
1378
            }
×
1379

1380
            record_result(ka_timer);
×
1381
         }
×
1382
      }
×
1383

1384
#endif
1385

1386
#if defined(BOTAN_HAS_FPE_FE1)
1387

1388
      void bench_fpe_fe1(const std::chrono::milliseconds runtime) {
1✔
1389
         const auto n = Botan::BigInt::from_u64(1000000000000000);
1✔
1390

1391
         auto enc_timer = make_timer("FPE_FE1 encrypt");
2✔
1392
         auto dec_timer = make_timer("FPE_FE1 decrypt");
2✔
1393

1394
         const Botan::SymmetricKey key(rng(), 32);
1✔
1395
         const std::vector<uint8_t> tweak(8);  // 8 zeros
1✔
1396

1397
         auto x = Botan::BigInt::one();
1✔
1398

1399
         Botan::FPE_FE1 fpe_fe1(n);
1✔
1400
         fpe_fe1.set_key(key);
1✔
1401

1402
         while(enc_timer->under(runtime)) {
6✔
1403
            enc_timer->start();
5✔
1404
            x = fpe_fe1.encrypt(x, tweak.data(), tweak.size());
5✔
1405
            enc_timer->stop();
5✔
1406
         }
1407

1408
         for(size_t i = 0; i != enc_timer->events(); ++i) {
6✔
1409
            dec_timer->start();
5✔
1410
            x = fpe_fe1.decrypt(x, tweak.data(), tweak.size());
5✔
1411
            dec_timer->stop();
5✔
1412
         }
1413

1414
         BOTAN_ASSERT(x == 1, "FPE works");
1✔
1415

1416
         record_result(enc_timer);
1✔
1417
         record_result(dec_timer);
1✔
1418
      }
4✔
1419
#endif
1420

1421
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
1422

1423
      void bench_rfc3394(const std::chrono::milliseconds runtime) {
1✔
1424
         auto wrap_timer = make_timer("RFC3394 AES-256 key wrap");
2✔
1425
         auto unwrap_timer = make_timer("RFC3394 AES-256 key unwrap");
2✔
1426

1427
         const Botan::SymmetricKey kek(rng(), 32);
1✔
1428
         Botan::secure_vector<uint8_t> key(64, 0);
1✔
1429

1430
         while(wrap_timer->under(runtime)) {
94✔
1431
            wrap_timer->start();
93✔
1432
            key = Botan::rfc3394_keywrap(key, kek);
186✔
1433
            wrap_timer->stop();
93✔
1434

1435
            unwrap_timer->start();
93✔
1436
            key = Botan::rfc3394_keyunwrap(key, kek);
186✔
1437
            unwrap_timer->stop();
93✔
1438

1439
            key[0] += 1;
93✔
1440
         }
1441

1442
         record_result(wrap_timer);
1✔
1443
         record_result(unwrap_timer);
1✔
1444
      }
2✔
1445
#endif
1446

1447
#if defined(BOTAN_HAS_BIGINT)
1448

1449
      void bench_mp_mul(const std::chrono::milliseconds runtime) {
1✔
1450
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1451
         for(size_t bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) {
10✔
1452
            auto mul_timer = make_timer("BigInt mul " + std::to_string(bits));
18✔
1453
            auto sqr_timer = make_timer("BigInt sqr " + std::to_string(bits));
18✔
1454

1455
            const Botan::BigInt y(rng(), bits);
9✔
1456
            Botan::secure_vector<Botan::word> ws;
9✔
1457

1458
            while(mul_timer->under(runtime_per_size)) {
8,470✔
1459
               Botan::BigInt x(rng(), bits);
8,461✔
1460

1461
               sqr_timer->start();
8,461✔
1462
               x.square(ws);
8,461✔
1463
               sqr_timer->stop();
8,461✔
1464

1465
               x.mask_bits(bits);
8,461✔
1466

1467
               mul_timer->start();
8,461✔
1468
               x.mul(y, ws);
8,461✔
1469
               mul_timer->stop();
8,461✔
1470
            }
8,461✔
1471

1472
            record_result(mul_timer);
9✔
1473
            record_result(sqr_timer);
9✔
1474
         }
18✔
1475
      }
1✔
1476

1477
      void bench_mp_div(const std::chrono::milliseconds runtime) {
1✔
1478
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1479

1480
         for(size_t n_bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) {
10✔
1481
            const size_t q_bits = n_bits / 2;
9✔
1482
            const std::string bit_descr = std::to_string(n_bits) + "/" + std::to_string(q_bits);
27✔
1483

1484
            auto div_timer = make_timer("BigInt div " + bit_descr);
18✔
1485
            auto ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
18✔
1486

1487
            Botan::BigInt y;
9✔
1488
            Botan::BigInt x;
9✔
1489
            Botan::secure_vector<Botan::word> ws;
9✔
1490

1491
            Botan::BigInt q1, r1, q2, r2;
9✔
1492

1493
            while(ct_div_timer->under(runtime_per_size)) {
9✔
1494
               x.randomize(rng(), n_bits);
90✔
1495
               y.randomize(rng(), q_bits);
90✔
1496

1497
               div_timer->start();
90✔
1498
               Botan::vartime_divide(x, y, q1, r1);
90✔
1499
               div_timer->stop();
90✔
1500

1501
               ct_div_timer->start();
90✔
1502
               Botan::ct_divide(x, y, q2, r2);
90✔
1503
               ct_div_timer->stop();
90✔
1504

1505
               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
90✔
1506
               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
189✔
1507
            }
1508

1509
            record_result(div_timer);
9✔
1510
            record_result(ct_div_timer);
9✔
1511
         }
54✔
1512
      }
1✔
1513

1514
      void bench_mp_div10(const std::chrono::milliseconds runtime) {
1✔
1515
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1516

1517
         for(size_t n_bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) {
10✔
1518
            const std::string bit_descr = std::to_string(n_bits) + "/10";
18✔
1519

1520
            auto div_timer = make_timer("BigInt div " + bit_descr);
18✔
1521
            auto ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
18✔
1522

1523
            Botan::BigInt x;
9✔
1524
            Botan::secure_vector<Botan::word> ws;
9✔
1525

1526
            const auto ten = Botan::BigInt::from_word(10);
9✔
1527
            Botan::BigInt q1, r1, q2;
9✔
1528
            Botan::word r2;
9✔
1529

1530
            while(ct_div_timer->under(runtime_per_size)) {
9✔
1531
               x.randomize(rng(), n_bits);
698✔
1532

1533
               div_timer->start();
698✔
1534
               Botan::vartime_divide(x, ten, q1, r1);
698✔
1535
               div_timer->stop();
698✔
1536

1537
               ct_div_timer->start();
698✔
1538
               Botan::ct_divide_word(x, 10, q2, r2);
698✔
1539
               ct_div_timer->stop();
698✔
1540

1541
               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
698✔
1542
               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
1,405✔
1543
            }
1544

1545
            record_result(div_timer);
9✔
1546
            record_result(ct_div_timer);
9✔
1547
         }
45✔
1548
      }
1✔
1549

1550
#endif
1551

1552
#if defined(BOTAN_HAS_DL_GROUP)
1553

1554
      void bench_modexp(const std::chrono::milliseconds runtime) {
1✔
1555
         for(size_t group_bits : {1024, 1536, 2048, 3072, 4096}) {
6✔
1556
            const std::string group_bits_str = std::to_string(group_bits);
5✔
1557
            const Botan::DL_Group group("modp/srp/" + group_bits_str);
5✔
1558

1559
            const size_t e_bits = Botan::dl_exponent_size(group_bits);
5✔
1560
            const size_t f_bits = group_bits - 1;
5✔
1561

1562
            const Botan::BigInt random_e(rng(), e_bits);
5✔
1563
            const Botan::BigInt random_f(rng(), f_bits);
5✔
1564

1565
            auto e_timer = make_timer(group_bits_str + " short exponent");
10✔
1566
            auto f_timer = make_timer(group_bits_str + "  full exponent");
10✔
1567

1568
            while(f_timer->under(runtime)) {
11✔
1569
               e_timer->run([&]() { group.power_g_p(random_e); });
12✔
1570
               f_timer->run([&]() { group.power_g_p(random_f); });
12✔
1571
            }
1572

1573
            record_result(e_timer);
5✔
1574
            record_result(f_timer);
5✔
1575
         }
20✔
1576
      }
6✔
1577
#endif
1578

1579
#if defined(BOTAN_HAS_NUMBERTHEORY)
1580
      void bench_nistp_redc(const std::chrono::milliseconds runtime) {
1✔
1581
         Botan::secure_vector<Botan::word> ws;
1✔
1582

1583
         auto p192_timer = make_timer("P-192 redc");
2✔
1584
         Botan::BigInt r192(rng(), 192 * 2 - 1);
1✔
1585
         while(p192_timer->under(runtime)) {
5,958✔
1586
            Botan::BigInt r = r192;
5,957✔
1587
            p192_timer->run([&]() { Botan::redc_p192(r, ws); });
11,914✔
1588
            r192 += 1;
5,957✔
1589
         }
5,957✔
1590
         record_result(p192_timer);
1✔
1591

1592
         auto p224_timer = make_timer("P-224 redc");
2✔
1593
         Botan::BigInt r224(rng(), 224 * 2 - 1);
1✔
1594
         while(p224_timer->under(runtime)) {
5,211✔
1595
            Botan::BigInt r = r224;
5,210✔
1596
            p224_timer->run([&]() { Botan::redc_p224(r, ws); });
10,420✔
1597
            r224 += 1;
5,210✔
1598
         }
5,210✔
1599
         record_result(p224_timer);
1✔
1600

1601
         auto p256_timer = make_timer("P-256 redc");
2✔
1602
         Botan::BigInt r256(rng(), 256 * 2 - 1);
1✔
1603
         while(p256_timer->under(runtime)) {
5,610✔
1604
            Botan::BigInt r = r256;
5,609✔
1605
            p256_timer->run([&]() { Botan::redc_p256(r, ws); });
11,218✔
1606
            r256 += 1;
5,609✔
1607
         }
5,609✔
1608
         record_result(p256_timer);
1✔
1609

1610
         auto p384_timer = make_timer("P-384 redc");
2✔
1611
         Botan::BigInt r384(rng(), 384 * 2 - 1);
1✔
1612
         while(p384_timer->under(runtime)) {
5,007✔
1613
            Botan::BigInt r = r384;
5,006✔
1614
            p384_timer->run([&]() { Botan::redc_p384(r384, ws); });
10,012✔
1615
            r384 += 1;
5,006✔
1616
         }
5,006✔
1617
         record_result(p384_timer);
1✔
1618

1619
         auto p521_timer = make_timer("P-521 redc");
2✔
1620
         Botan::BigInt r521(rng(), 521 * 2 - 1);
1✔
1621
         while(p521_timer->under(runtime)) {
3,588✔
1622
            Botan::BigInt r = r521;
3,587✔
1623
            p521_timer->run([&]() { Botan::redc_p521(r521, ws); });
7,174✔
1624
            r521 += 1;
3,587✔
1625
         }
3,587✔
1626
         record_result(p521_timer);
1✔
1627
      }
6✔
1628

1629
      void bench_bn_redc(const std::chrono::milliseconds runtime) {
1✔
1630
         for(size_t bitsize : {512, 1024, 2048, 4096}) {
5✔
1631
            Botan::BigInt p(rng(), bitsize);
4✔
1632

1633
            std::string bit_str = std::to_string(bitsize);
4✔
1634
            auto barrett_timer = make_timer("Barrett-" + bit_str);
8✔
1635
            auto schoolbook_timer = make_timer("Schoolbook-" + bit_str);
8✔
1636

1637
            Botan::Modular_Reducer mod_p(p);
4✔
1638

1639
            while(schoolbook_timer->under(runtime)) {
113✔
1640
               const Botan::BigInt x(rng(), p.bits() * 2 - 2);
109✔
1641

1642
               const Botan::BigInt r1 = barrett_timer->run([&] { return mod_p.reduce(x); });
218✔
1643
               const Botan::BigInt r2 = schoolbook_timer->run([&] { return x % p; });
218✔
1644

1645
               BOTAN_ASSERT(r1 == r2, "Computed different results");
109✔
1646
            }
327✔
1647

1648
            record_result(barrett_timer);
4✔
1649
            record_result(schoolbook_timer);
4✔
1650
         }
8✔
1651
      }
1✔
1652

1653
      void bench_inverse_mod(const std::chrono::milliseconds runtime) {
1✔
1654
         for(size_t bits : {256, 384, 512, 1024, 2048}) {
6✔
1655
            const std::string bit_str = std::to_string(bits);
5✔
1656

1657
            auto timer = make_timer("inverse_mod-" + bit_str);
10✔
1658
            auto gcd_timer = make_timer("gcd-" + bit_str);
10✔
1659

1660
            while(timer->under(runtime) && gcd_timer->under(runtime)) {
14✔
1661
               const Botan::BigInt x(rng(), bits - 1);
9✔
1662
               Botan::BigInt mod(rng(), bits);
9✔
1663

1664
               const Botan::BigInt x_inv = timer->run([&] { return Botan::inverse_mod(x, mod); });
18✔
1665

1666
               const Botan::BigInt g = gcd_timer->run([&] { return gcd(x, mod); });
18✔
1667

1668
               if(x_inv == 0) {
9✔
1669
                  BOTAN_ASSERT(g != 1, "Inversion only fails if gcd(x, mod) > 1");
3✔
1670
               } else {
1671
                  BOTAN_ASSERT(g == 1, "Inversion succeeds only if gcd != 1");
6✔
1672
                  const Botan::BigInt check = (x_inv * x) % mod;
6✔
1673
                  BOTAN_ASSERT_EQUAL(check, 1, "Const time inversion correct");
6✔
1674
               }
6✔
1675
            }
34✔
1676

1677
            record_result(timer);
5✔
1678
            record_result(gcd_timer);
5✔
1679
         }
5✔
1680
      }
1✔
1681

1682
      void bench_primality_tests(const std::chrono::milliseconds runtime) {
1✔
1683
         for(size_t bits : {256, 512, 1024}) {
4✔
1684
            auto mr_timer = make_timer("Miller-Rabin-" + std::to_string(bits));
6✔
1685
            auto bpsw_timer = make_timer("Bailie-PSW-" + std::to_string(bits));
6✔
1686
            auto lucas_timer = make_timer("Lucas-" + std::to_string(bits));
6✔
1687

1688
            Botan::BigInt n = Botan::random_prime(rng(), bits);
3✔
1689

1690
            while(lucas_timer->under(runtime)) {
6✔
1691
               Botan::Modular_Reducer mod_n(n);
3✔
1692

1693
               mr_timer->run([&]() { return Botan::is_miller_rabin_probable_prime(n, mod_n, rng(), 2); });
6✔
1694

1695
               bpsw_timer->run([&]() { return Botan::is_bailie_psw_probable_prime(n, mod_n); });
6✔
1696

1697
               lucas_timer->run([&]() { return Botan::is_lucas_probable_prime(n, mod_n); });
6✔
1698

1699
               n += 2;
3✔
1700
            }
3✔
1701

1702
            record_result(mr_timer);
3✔
1703
            record_result(bpsw_timer);
3✔
1704
            record_result(lucas_timer);
3✔
1705
         }
3✔
1706
      }
1✔
1707

1708
      void bench_random_prime(const std::chrono::milliseconds runtime) {
1✔
1709
         const auto coprime = Botan::BigInt::from_word(0x10001);
1✔
1710

1711
         for(size_t bits : {256, 384, 512, 768, 1024, 1536}) {
7✔
1712
            auto genprime_timer = make_timer("random_prime " + std::to_string(bits));
12✔
1713
            auto gensafe_timer = make_timer("random_safe_prime " + std::to_string(bits));
12✔
1714
            auto is_prime_timer = make_timer("is_prime " + std::to_string(bits));
12✔
1715

1716
            while(gensafe_timer->under(runtime)) {
12✔
1717
               const Botan::BigInt p = genprime_timer->run([&] { return Botan::random_prime(rng(), bits, coprime); });
12✔
1718

1719
               if(!is_prime_timer->run([&] { return Botan::is_prime(p, rng(), 64, true); })) {
12✔
1720
                  error_output() << "Generated prime " << p << " which failed a primality test";
×
1721
               }
1722

1723
               const Botan::BigInt sg = gensafe_timer->run([&] { return Botan::random_safe_prime(rng(), bits); });
12✔
1724

1725
               if(!is_prime_timer->run([&] { return Botan::is_prime(sg, rng(), 64, true); })) {
12✔
1726
                  error_output() << "Generated safe prime " << sg << " which failed a primality test";
×
1727
               }
1728

1729
               if(!is_prime_timer->run([&] { return Botan::is_prime(sg / 2, rng(), 64, true); })) {
18✔
1730
                  error_output() << "Generated prime " << sg / 2 << " which failed a primality test";
×
1731
               }
1732

1733
               // Now test p+2, p+4, ... which may or may not be prime
1734
               for(size_t i = 2; i <= 64; i += 2) {
198✔
1735
                  is_prime_timer->run([&]() { Botan::is_prime(p + i, rng(), 64, true); });
576✔
1736
               }
1737
            }
12✔
1738

1739
            record_result(genprime_timer);
6✔
1740
            record_result(gensafe_timer);
6✔
1741
            record_result(is_prime_timer);
6✔
1742
         }
6✔
1743
      }
1✔
1744
#endif
1745

1746
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
1747
      void bench_pk_enc(const Botan::Private_Key& key,
4✔
1748
                        const std::string& nm,
1749
                        const std::string& provider,
1750
                        const std::string& padding,
1751
                        std::chrono::milliseconds msec) {
1752
         std::vector<uint8_t> plaintext, ciphertext;
4✔
1753

1754
         Botan::PK_Encryptor_EME enc(key, rng(), padding, provider);
4✔
1755
         Botan::PK_Decryptor_EME dec(key, rng(), padding, provider);
4✔
1756

1757
         auto enc_timer = make_timer(nm + " " + padding, provider, "encrypt");
8✔
1758
         auto dec_timer = make_timer(nm + " " + padding, provider, "decrypt");
8✔
1759

1760
         while(enc_timer->under(msec) || dec_timer->under(msec)) {
12✔
1761
            // Generate a new random ciphertext to decrypt
1762
            if(ciphertext.empty() || enc_timer->under(msec)) {
4✔
1763
               rng().random_vec(plaintext, enc.maximum_input_size());
4✔
1764
               ciphertext = enc_timer->run([&]() { return enc.encrypt(plaintext, rng()); });
8✔
1765
            }
1766

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

1770
               if(!(Botan::unlock(dec_pt) == plaintext))  // sanity check
4✔
1771
               {
1772
                  error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n";
×
1773
               }
1774
            }
4✔
1775
         }
1776

1777
         record_result(enc_timer);
4✔
1778
         record_result(dec_timer);
4✔
1779
      }
4✔
1780

1781
      void bench_pk_ka(const std::string& algo,
13✔
1782
                       const std::string& nm,
1783
                       const std::string& params,
1784
                       const std::string& provider,
1785
                       std::chrono::milliseconds msec) {
1786
         const std::string kdf = "KDF2(SHA-256)";  // arbitrary choice
13✔
1787

1788
         auto keygen_timer = make_timer(nm, provider, "keygen");
26✔
1789

1790
         std::unique_ptr<Botan::Private_Key> key1(
13✔
1791
            keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); }));
26✔
1792
         std::unique_ptr<Botan::Private_Key> key2(
13✔
1793
            keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); }));
26✔
1794

1795
         record_result(keygen_timer);
13✔
1796

1797
         const Botan::PK_Key_Agreement_Key& ka_key1 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key1);
13✔
1798
         const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key2);
13✔
1799

1800
         Botan::PK_Key_Agreement ka1(ka_key1, rng(), kdf, provider);
13✔
1801
         Botan::PK_Key_Agreement ka2(ka_key2, rng(), kdf, provider);
13✔
1802

1803
         const std::vector<uint8_t> ka1_pub = ka_key1.public_value();
13✔
1804
         const std::vector<uint8_t> ka2_pub = ka_key2.public_value();
13✔
1805

1806
         auto ka_timer = make_timer(nm, provider, "key agreements");
26✔
1807

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

1812
            if(symkey1 != symkey2) {
18✔
1813
               error_output() << "Key agreement mismatch in PK bench\n";
×
1814
            }
1815
         }
36✔
1816

1817
         record_result(ka_timer);
13✔
1818
      }
39✔
1819

1820
      void bench_pk_kem(const Botan::Private_Key& key,
11✔
1821
                        const std::string& nm,
1822
                        const std::string& provider,
1823
                        const std::string& kdf,
1824
                        std::chrono::milliseconds msec) {
1825
         Botan::PK_KEM_Decryptor dec(key, rng(), kdf, provider);
11✔
1826
         Botan::PK_KEM_Encryptor enc(key, kdf, provider);
11✔
1827

1828
         auto kem_enc_timer = make_timer(nm, provider, "KEM encrypt");
22✔
1829
         auto kem_dec_timer = make_timer(nm, provider, "KEM decrypt");
22✔
1830

1831
         while(kem_enc_timer->under(msec) && kem_dec_timer->under(msec)) {
56✔
1832
            Botan::secure_vector<uint8_t> salt = rng().random_vec(16);
45✔
1833

1834
            kem_enc_timer->start();
45✔
1835
            const auto kem_result = enc.encrypt(rng(), 64, salt);
45✔
1836
            kem_enc_timer->stop();
45✔
1837

1838
            kem_dec_timer->start();
45✔
1839
            Botan::secure_vector<uint8_t> dec_shared_key = dec.decrypt(kem_result.encapsulated_shared_key(), 64, salt);
45✔
1840
            kem_dec_timer->stop();
45✔
1841

1842
            if(kem_result.shared_key() != dec_shared_key) {
45✔
1843
               error_output() << "KEM mismatch in PK bench\n";
×
1844
            }
1845
         }
90✔
1846

1847
         record_result(kem_enc_timer);
11✔
1848
         record_result(kem_dec_timer);
11✔
1849
      }
11✔
1850

1851
      void bench_pk_sig_ecc(const std::string& algo,
7✔
1852
                            const std::string& emsa,
1853
                            const std::string& provider,
1854
                            const std::vector<std::string>& params,
1855
                            std::chrono::milliseconds msec) {
1856
         for(std::string grp : params) {
34✔
1857
            const std::string nm = grp.empty() ? algo : Botan::fmt("{}-{}", algo, grp);
27✔
1858

1859
            auto keygen_timer = make_timer(nm, provider, "keygen");
54✔
1860

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

1864
            record_result(keygen_timer);
27✔
1865
            bench_pk_sig(*key, nm, provider, emsa, msec);
27✔
1866
         }
27✔
1867
      }
7✔
1868

1869
      size_t bench_pk_sig(const Botan::Private_Key& key,
42✔
1870
                          const std::string& nm,
1871
                          const std::string& provider,
1872
                          const std::string& padding,
1873
                          std::chrono::milliseconds msec) {
1874
         std::vector<uint8_t> message, signature, bad_signature;
42✔
1875

1876
         Botan::PK_Signer sig(key, rng(), padding, Botan::Signature_Format::Standard, provider);
42✔
1877
         Botan::PK_Verifier ver(key, padding, Botan::Signature_Format::Standard, provider);
42✔
1878

1879
         auto sig_timer = make_timer(nm + " " + padding, provider, "sign");
84✔
1880
         auto ver_timer = make_timer(nm + " " + padding, provider, "verify");
84✔
1881

1882
         size_t invalid_sigs = 0;
42✔
1883

1884
         while(ver_timer->under(msec) || sig_timer->under(msec)) {
223✔
1885
            if(signature.empty() || sig_timer->under(msec)) {
139✔
1886
               /*
1887
               Length here is kind of arbitrary, but 48 bytes fits into a single
1888
               hash block so minimizes hashing overhead versus the PK op itself.
1889
               */
1890
               rng().random_vec(message, 48);
86✔
1891

1892
               signature = sig_timer->run([&]() { return sig.sign_message(message, rng()); });
172✔
1893

1894
               bad_signature = signature;
86✔
1895
               bad_signature[rng().next_byte() % bad_signature.size()] ^= rng().next_nonzero_byte();
86✔
1896
            }
1897

1898
            if(ver_timer->under(msec)) {
139✔
1899
               const bool verified = ver_timer->run([&] { return ver.verify_message(message, signature); });
204✔
1900

1901
               if(!verified) {
102✔
1902
                  invalid_sigs += 1;
×
1903
               }
1904

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

1907
               if(verified_bad) {
102✔
1908
                  error_output() << "Bad signature accepted in " << nm << " signature bench\n";
×
1909
               }
1910
            }
1911
         }
1912

1913
         if(invalid_sigs > 0) {
42✔
1914
            error_output() << invalid_sigs << " generated signatures rejected in " << nm << " signature bench\n";
×
1915
         }
1916

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

1919
         record_result(sig_timer);
42✔
1920
         record_result(ver_timer);
42✔
1921

1922
         return events;
42✔
1923
      }
42✔
1924
#endif
1925

1926
#if defined(BOTAN_HAS_RSA)
1927
      void bench_rsa_keygen(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1928
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
1929
            const std::string nm = "RSA-" + std::to_string(keylen);
8✔
1930
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1931

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

1936
               BOTAN_ASSERT(key->check_key(rng(), true), "Key is ok");
4✔
1937
            }
4✔
1938

1939
            record_result(keygen_timer);
4✔
1940
         }
4✔
1941
      }
1✔
1942

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

1947
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1948

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

1952
            record_result(keygen_timer);
4✔
1953

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

1957
            //bench_pk_sig(*key, nm, provider, "PSSR(SHA-256)", msec);
1958
            //bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
1959
            //bench_pk_enc(*key, nm, provider, "OAEP(SHA-1)", msec);
1960
         }
4✔
1961
      }
1✔
1962
#endif
1963

1964
#if defined(BOTAN_HAS_ECDSA)
1965
      void bench_ecdsa(const std::vector<std::string>& groups,
1✔
1966
                       const std::string& provider,
1967
                       std::chrono::milliseconds msec) {
1968
         return bench_pk_sig_ecc("ECDSA", "SHA-256", provider, groups, msec);
2✔
1969
      }
1970

1971
      void bench_ecdsa_recovery(const std::vector<std::string>& groups,
1✔
1972
                                const std::string& /*unused*/,
1973
                                std::chrono::milliseconds msec) {
1974
         for(const std::string& group_name : groups) {
7✔
1975
            const auto group = Botan::EC_Group::from_name(group_name);
6✔
1976
            auto recovery_timer = make_timer("ECDSA recovery " + group_name);
12✔
1977

1978
            while(recovery_timer->under(msec)) {
12✔
1979
               Botan::ECDSA_PrivateKey key(rng(), group);
6✔
1980

1981
               std::vector<uint8_t> message(group.get_order_bits() / 8);
6✔
1982
               rng().randomize(message.data(), message.size());
6✔
1983

1984
               Botan::PK_Signer signer(key, rng(), "Raw");
6✔
1985
               signer.update(message);
6✔
1986
               std::vector<uint8_t> signature = signer.signature(rng());
6✔
1987

1988
               Botan::PK_Verifier verifier(key, "Raw", Botan::Signature_Format::Standard, "base");
6✔
1989
               verifier.update(message);
6✔
1990
               BOTAN_ASSERT(verifier.check_signature(signature), "Valid signature");
6✔
1991

1992
               Botan::BigInt r(signature.data(), signature.size() / 2);
6✔
1993
               Botan::BigInt s(signature.data() + signature.size() / 2, signature.size() / 2);
6✔
1994

1995
               const uint8_t v = key.recovery_param(message, r, s);
6✔
1996

1997
               recovery_timer->run([&]() {
6✔
1998
                  Botan::ECDSA_PublicKey pubkey(group, message, r, s, v);
6✔
1999
                  BOTAN_ASSERT(pubkey.public_point() == key.public_point(), "Recovered public key");
6✔
2000
               });
6✔
2001
            }
12✔
2002

2003
            record_result(recovery_timer);
6✔
2004
         }
6✔
2005
      }
1✔
2006

2007
#endif
2008

2009
#if defined(BOTAN_HAS_ECKCDSA)
2010
      void bench_eckcdsa(const std::vector<std::string>& groups,
1✔
2011
                         const std::string& provider,
2012
                         std::chrono::milliseconds msec) {
2013
         return bench_pk_sig_ecc("ECKCDSA", "SHA-256", provider, groups, msec);
2✔
2014
      }
2015
#endif
2016

2017
#if defined(BOTAN_HAS_GOST_34_10_2001)
2018
      void bench_gost_3410(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2019
         return bench_pk_sig_ecc("GOST-34.10", "GOST-34.11", provider, {"gost_256A"}, msec);
2✔
2020
      }
2021
#endif
2022

2023
#if defined(BOTAN_HAS_SM2)
2024
      void bench_sm2(const std::vector<std::string>& groups,
1✔
2025
                     const std::string& provider,
2026
                     std::chrono::milliseconds msec) {
2027
         return bench_pk_sig_ecc("SM2_Sig", "SM3", provider, groups, msec);
2✔
2028
      }
2029
#endif
2030

2031
#if defined(BOTAN_HAS_ECGDSA)
2032
      void bench_ecgdsa(const std::vector<std::string>& groups,
1✔
2033
                        const std::string& provider,
2034
                        std::chrono::milliseconds msec) {
2035
         return bench_pk_sig_ecc("ECGDSA", "SHA-256", provider, groups, msec);
2✔
2036
      }
2037
#endif
2038

2039
#if defined(BOTAN_HAS_ED25519)
2040
      void bench_ed25519(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2041
         return bench_pk_sig_ecc("Ed25519", "Pure", provider, std::vector<std::string>{""}, msec);
3✔
2042
      }
2✔
2043
#endif
2044

2045
#if defined(BOTAN_HAS_ED448)
2046
      void bench_ed448(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2047
         return bench_pk_sig_ecc("Ed448", "Pure", provider, std::vector<std::string>{""}, msec);
3✔
2048
      }
2✔
2049
#endif
2050

2051
#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
2052
      void bench_dh(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2053
         for(size_t bits : {2048, 3072, 4096, 6144, 8192}) {
6✔
2054
            bench_pk_ka("DH", "DH-" + std::to_string(bits), "ffdhe/ietf/" + std::to_string(bits), provider, msec);
20✔
2055
         }
2056
      }
1✔
2057
#endif
2058

2059
#if defined(BOTAN_HAS_DSA)
2060
      void bench_dsa(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2061
         for(size_t bits : {1024, 2048, 3072}) {
4✔
2062
            const std::string nm = "DSA-" + std::to_string(bits);
6✔
2063

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

2066
            auto keygen_timer = make_timer(nm, provider, "keygen");
6✔
2067

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

2071
            record_result(keygen_timer);
3✔
2072

2073
            bench_pk_sig(*key, nm, provider, "SHA-256", msec);
6✔
2074
         }
3✔
2075
      }
1✔
2076
#endif
2077

2078
#if defined(BOTAN_HAS_ELGAMAL)
2079
      void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2080
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
2081
            const std::string nm = "ElGamal-" + std::to_string(keylen);
8✔
2082

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

2085
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
2086

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

2090
            record_result(keygen_timer);
4✔
2091

2092
            bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
8✔
2093
         }
4✔
2094
      }
1✔
2095
#endif
2096

2097
#if defined(BOTAN_HAS_ECDH)
2098
      void bench_ecdh(const std::vector<std::string>& groups,
1✔
2099
                      const std::string& provider,
2100
                      std::chrono::milliseconds msec) {
2101
         for(const std::string& grp : groups) {
7✔
2102
            bench_pk_ka("ECDH", "ECDH-" + grp, grp, provider, msec);
12✔
2103
         }
2104
      }
1✔
2105
#endif
2106

2107
#if defined(BOTAN_HAS_X25519)
2108
      void bench_x25519(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2109
         bench_pk_ka("X25519", "X25519", "", provider, msec);
2✔
2110
      }
1✔
2111
#endif
2112

2113
#if defined(BOTAN_HAS_X448)
2114
      void bench_x448(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2115
         bench_pk_ka("X448", "X448", "", provider, msec);
2✔
2116
      }
1✔
2117
#endif
2118

2119
#if defined(BOTAN_HAS_MCELIECE)
2120
      void bench_mceliece(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2121
         /*
2122
         SL=80 n=1632 t=33 - 59 KB pubkey 140 KB privkey
2123
         SL=107 n=2480 t=45 - 128 KB pubkey 300 KB privkey
2124
         SL=128 n=2960 t=57 - 195 KB pubkey 459 KB privkey
2125
         SL=147 n=3408 t=67 - 265 KB pubkey 622 KB privkey
2126
         SL=191 n=4624 t=95 - 516 KB pubkey 1234 KB privkey
2127
         SL=256 n=6624 t=115 - 942 KB pubkey 2184 KB privkey
2128
         */
2129

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

2133
         for(auto params : mce_params) {
6✔
2134
            size_t n = params.first;
5✔
2135
            size_t t = params.second;
5✔
2136

2137
            const std::string nm = "McEliece-" + std::to_string(n) + "," + std::to_string(t) +
20✔
2138
                                   " (WF=" + std::to_string(Botan::mceliece_work_factor(n, t)) + ")";
15✔
2139

2140
            auto keygen_timer = make_timer(nm, provider, "keygen");
10✔
2141

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

2145
            record_result(keygen_timer);
5✔
2146
            bench_pk_kem(*key, nm, provider, "KDF2(SHA-256)", msec);
10✔
2147
         }
5✔
2148
      }
1✔
2149
#endif
2150

2151
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
2152
      void bench_kyber(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2153
         const Botan::KyberMode::Mode all_modes[] = {
1✔
2154
            Botan::KyberMode::Kyber512_R3,
2155
            Botan::KyberMode::Kyber512_90s,
2156
            Botan::KyberMode::Kyber768_R3,
2157
            Botan::KyberMode::Kyber768_90s,
2158
            Botan::KyberMode::Kyber1024_R3,
2159
            Botan::KyberMode::Kyber1024_90s,
2160
         };
2161

2162
         for(auto modet : all_modes) {
7✔
2163
            Botan::KyberMode mode(modet);
6✔
2164

2165
   #if !defined(BOTAN_HAS_KYBER)
2166
            if(mode.is_modern())
2167
               continue;
2168
   #endif
2169

2170
   #if !defined(BOTAN_HAS_KYBER_90S)
2171
            if(mode.is_90s())
2172
               continue;
2173
   #endif
2174

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

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

2179
            record_result(keygen_timer);
6✔
2180

2181
            bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec);
12✔
2182
         }
6✔
2183
      }
1✔
2184
#endif
2185

2186
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
2187
      void bench_dilithium(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2188
         const Botan::DilithiumMode::Mode all_modes[] = {Botan::DilithiumMode::Dilithium4x4,
1✔
2189
                                                         Botan::DilithiumMode::Dilithium4x4_AES,
2190
                                                         Botan::DilithiumMode::Dilithium6x5,
2191
                                                         Botan::DilithiumMode::Dilithium6x5_AES,
2192
                                                         Botan::DilithiumMode::Dilithium8x7,
2193
                                                         Botan::DilithiumMode::Dilithium8x7_AES};
2194

2195
         for(auto modet : all_modes) {
7✔
2196
            Botan::DilithiumMode mode(modet);
6✔
2197

2198
   #if !defined(BOTAN_HAS_DILITHIUM)
2199
            if(mode.is_modern())
2200
               continue;
2201
   #endif
2202

2203
   #if !defined(BOTAN_HAS_DILITHIUM_AES)
2204
            if(mode.is_aes())
2205
               continue;
2206
   #endif
2207

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

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

2212
            record_result(keygen_timer);
6✔
2213

2214
            bench_pk_sig(key, mode.to_string(), provider, "", msec);
12✔
2215
         }
6✔
2216
      }
1✔
2217
#endif
2218

2219
#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
2220
      void bench_sphincs_plus(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2221
         // Sphincs_Parameter_Set set, Sphincs_Hash_Type hash
2222
         std::vector<std::string> sphincs_params{"SphincsPlus-sha2-128s-r3.1",
1✔
2223
                                                 "SphincsPlus-sha2-128f-r3.1",
2224
                                                 "SphincsPlus-sha2-192s-r3.1",
2225
                                                 "SphincsPlus-sha2-192f-r3.1",
2226
                                                 "SphincsPlus-sha2-256s-r3.1",
2227
                                                 "SphincsPlus-sha2-256f-r3.1",
2228
                                                 "SphincsPlus-shake-128s-r3.1",
2229
                                                 "SphincsPlus-shake-128f-r3.1",
2230
                                                 "SphincsPlus-shake-192s-r3.1",
2231
                                                 "SphincsPlus-shake-192f-r3.1",
2232
                                                 "SphincsPlus-shake-256s-r3.1",
2233
                                                 "SphincsPlus-shake-256f-r3.1"};
14✔
2234

2235
         for(auto params : sphincs_params) {
1✔
2236
            try {
1✔
2237
               auto keygen_timer = make_timer(params, provider, "keygen");
2✔
2238

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

2242
               record_result(keygen_timer);
1✔
2243
               if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
1✔
2244
                  break;
2245
               }
2246
            } catch(Botan::Not_Implemented&) {
1✔
2247
               continue;
×
2248
            }
×
2249
         }
1✔
2250
      }
3✔
2251
#endif
2252

2253
#if defined(BOTAN_HAS_FRODOKEM)
2254
      void bench_frodokem(const std::string& provider, std::chrono::milliseconds msec) {
×
2255
         std::vector<Botan::FrodoKEMMode> frodo_modes{
×
2256
            Botan::FrodoKEMMode::FrodoKEM640_SHAKE,
2257
            Botan::FrodoKEMMode::FrodoKEM976_SHAKE,
2258
            Botan::FrodoKEMMode::FrodoKEM1344_SHAKE,
2259
            Botan::FrodoKEMMode::eFrodoKEM640_SHAKE,
2260
            Botan::FrodoKEMMode::eFrodoKEM976_SHAKE,
2261
            Botan::FrodoKEMMode::eFrodoKEM1344_SHAKE,
2262
            Botan::FrodoKEMMode::FrodoKEM640_AES,
2263
            Botan::FrodoKEMMode::FrodoKEM976_AES,
2264
            Botan::FrodoKEMMode::FrodoKEM1344_AES,
2265
            Botan::FrodoKEMMode::eFrodoKEM640_AES,
2266
            Botan::FrodoKEMMode::eFrodoKEM976_AES,
2267
            Botan::FrodoKEMMode::eFrodoKEM1344_AES,
2268
         };
×
2269

2270
         for(auto modet : frodo_modes) {
×
2271
            if(!modet.is_available()) {
×
2272
               continue;
×
2273
            }
2274

2275
            Botan::FrodoKEMMode mode(modet);
×
2276

2277
            auto keygen_timer = make_timer(mode.to_string(), provider, "keygen");
×
2278

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

2281
            record_result(keygen_timer);
×
2282

2283
            bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec);
×
2284
         }
×
2285
      }
×
2286
#endif
2287

2288
#if defined(BOTAN_HAS_XMSS_RFC8391)
2289
      void bench_xmss(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2290
         /*
2291
         We only test H10 signatures here since already they are quite slow (a
2292
         few seconds per signature). On a fast machine, H16 signatures take 1-2
2293
         minutes to generate and H20 signatures take 5-10 minutes to generate
2294
         */
2295
         std::vector<std::string> xmss_params{
1✔
2296
            "XMSS-SHA2_10_256",
2297
            "XMSS-SHAKE_10_256",
2298
            "XMSS-SHA2_10_512",
2299
            "XMSS-SHAKE_10_512",
2300
         };
6✔
2301

2302
         for(std::string params : xmss_params) {
1✔
2303
            auto keygen_timer = make_timer(params, provider, "keygen");
2✔
2304

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

2308
            record_result(keygen_timer);
1✔
2309
            if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
1✔
2310
               break;
2311
            }
2312
         }
1✔
2313
      }
3✔
2314
#endif
2315

2316
#if defined(BOTAN_HAS_HSS_LMS)
2317
      void bench_hss_lms(const std::string& provider, std::chrono::milliseconds msec) {
×
2318
         // At first we compare instances with multiple hash functions. LMS trees with
2319
         // height 10 are suitable, since they can be used for enough signatures and are
2320
         // fast enough for speed testing.
2321
         // Afterward, setups with multiple HSS layers are tested
2322
         std::vector<std::string> hss_lms_instances{"SHA-256,HW(10,1)",
×
2323
                                                    "SHAKE-256(256),HW(10,1)",
2324
                                                    "SHAKE-256(192),HW(10,1)",
2325
                                                    "Truncated(SHA-256,192),HW(10,1)",
2326
                                                    "SHA-256,HW(10,1),HW(10,1)",
2327
                                                    "SHA-256,HW(10,1),HW(10,1),HW(10,1)"};
×
2328

2329
         for(const auto& params : hss_lms_instances) {
×
2330
            auto keygen_timer = make_timer(params, provider, "keygen");
×
2331

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

2335
            record_result(keygen_timer);
×
2336
            if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
×
2337
               break;
2338
            }
2339
         }
×
2340
      }
×
2341
#endif
2342

2343
#if defined(BOTAN_HAS_ZFEC)
2344
      void bench_zfec(std::chrono::milliseconds msec) {
1✔
2345
         const size_t k = 4;
1✔
2346
         const size_t n = 16;
1✔
2347

2348
         Botan::ZFEC zfec(k, n);
1✔
2349

2350
         const size_t share_size = 256 * 1024;
1✔
2351

2352
         std::vector<uint8_t> input(share_size * k);
1✔
2353
         rng().randomize(input.data(), input.size());
1✔
2354

2355
         std::vector<uint8_t> output(share_size * n);
1✔
2356

2357
         auto enc_fn = [&](size_t share, const uint8_t buf[], size_t len) {
17✔
2358
            std::memcpy(&output[share * share_size], buf, len);
16✔
2359
         };
17✔
2360

2361
         auto enc_timer =
1✔
2362
            make_timer("zfec " + std::to_string(k) + "/" + std::to_string(n), input.size(), "encode", "", input.size());
4✔
2363

2364
         enc_timer->run_until_elapsed(msec, [&]() { zfec.encode(input.data(), input.size(), enc_fn); });
3✔
2365

2366
         record_result(enc_timer);
1✔
2367

2368
         auto dec_timer =
1✔
2369
            make_timer("zfec " + std::to_string(k) + "/" + std::to_string(n), input.size(), "decode", "", input.size());
4✔
2370

2371
         std::map<size_t, const uint8_t*> shares;
1✔
2372
         for(size_t i = 0; i != n; ++i) {
17✔
2373
            shares[i] = &output[share_size * i];
16✔
2374
         }
2375

2376
         // remove data shares to make decoding maximally expensive:
2377
         while(shares.size() != k) {
13✔
2378
            shares.erase(shares.begin());
12✔
2379
         }
2380

2381
         std::vector<uint8_t> recovered(share_size * k);
1✔
2382

2383
         auto dec_fn = [&](size_t share, const uint8_t buf[], size_t len) {
5✔
2384
            std::memcpy(&recovered[share * share_size], buf, len);
4✔
2385
         };
5✔
2386

2387
         dec_timer->run_until_elapsed(msec, [&]() { zfec.decode_shares(shares, share_size, dec_fn); });
3✔
2388

2389
         record_result(dec_timer);
1✔
2390

2391
         if(recovered != input) {
1✔
2392
            error_output() << "ZFEC recovery failed\n";
×
2393
         }
2394
      }
1✔
2395

2396
#endif
2397

2398
#if defined(BOTAN_HAS_POLY_DBL)
2399
      void bench_poly_dbl(std::chrono::milliseconds msec) {
1✔
2400
         for(size_t sz : {8, 16, 24, 32, 64, 128}) {
7✔
2401
            auto be_timer = make_timer("poly_dbl_be_" + std::to_string(sz));
12✔
2402
            auto le_timer = make_timer("poly_dbl_le_" + std::to_string(sz));
12✔
2403

2404
            std::vector<uint8_t> buf(sz);
6✔
2405
            rng().randomize(buf.data(), sz);
6✔
2406

2407
            be_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n(buf.data(), buf.data(), sz); });
27,866✔
2408
            le_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n_le(buf.data(), buf.data(), sz); });
38,273✔
2409

2410
            record_result(be_timer);
6✔
2411
            record_result(le_timer);
6✔
2412
         }
6✔
2413
      }
1✔
2414
#endif
2415

2416
#if defined(BOTAN_HAS_BCRYPT)
2417

2418
      void bench_bcrypt() {
1✔
2419
         const std::string password = "not a very good password";
1✔
2420

2421
         for(uint8_t work_factor = 4; work_factor <= 14; ++work_factor) {
12✔
2422
            auto timer = make_timer("bcrypt wf=" + std::to_string(work_factor));
22✔
2423

2424
            timer->run([&] { Botan::generate_bcrypt(password, rng(), work_factor); });
22✔
2425

2426
            record_result(timer);
11✔
2427
         }
11✔
2428
      }
1✔
2429
#endif
2430

2431
#if defined(BOTAN_HAS_PASSHASH9)
2432

2433
      void bench_passhash9() {
1✔
2434
         const std::string password = "not a very good password";
1✔
2435

2436
         for(uint8_t alg = 0; alg <= 4; ++alg) {
6✔
2437
            if(Botan::is_passhash9_alg_supported(alg) == false) {
5✔
2438
               continue;
×
2439
            }
2440

2441
            for(auto work_factor : {10, 15}) {
15✔
2442
               auto timer = make_timer("passhash9 alg=" + std::to_string(alg) + " wf=" + std::to_string(work_factor));
40✔
2443

2444
               timer->run([&] { Botan::generate_passhash9(password, rng(), static_cast<uint8_t>(work_factor), alg); });
20✔
2445

2446
               record_result(timer);
10✔
2447
            }
10✔
2448
         }
2449
      }
1✔
2450
#endif
2451

2452
#if defined(BOTAN_HAS_SCRYPT)
2453

2454
      void bench_scrypt(const std::string& /*provider*/, std::chrono::milliseconds msec) {
1✔
2455
         auto pwdhash_fam = Botan::PasswordHashFamily::create_or_throw("Scrypt");
1✔
2456

2457
         for(size_t N : {8192, 16384, 32768, 65536}) {
5✔
2458
            for(size_t r : {1, 8, 16}) {
16✔
2459
               for(size_t p : {1}) {
12✔
2460
                  auto pwdhash = pwdhash_fam->from_params(N, r, p);
12✔
2461

2462
                  auto scrypt_timer =
12✔
2463
                     make_timer("scrypt-" + std::to_string(N) + "-" + std::to_string(r) + "-" + std::to_string(p) +
72✔
2464
                                " (" + std::to_string(pwdhash->total_memory_usage() / (1024 * 1024)) + " MiB)");
48✔
2465

2466
                  uint8_t out[64];
12✔
2467
                  uint8_t salt[8];
12✔
2468
                  rng().randomize(salt, sizeof(salt));
12✔
2469

2470
                  while(scrypt_timer->under(msec)) {
24✔
2471
                     scrypt_timer->run([&] {
12✔
2472
                        pwdhash->derive_key(out, sizeof(out), "password", 8, salt, sizeof(salt));
12✔
2473

2474
                        Botan::copy_mem(salt, out, 8);
12✔
2475
                     });
12✔
2476
                  }
2477

2478
                  record_result(scrypt_timer);
12✔
2479

2480
                  if(scrypt_timer->events() == 1) {
12✔
2481
                     break;
2482
                  }
2483
               }
24✔
2484
            }
2485
         }
2486
      }
1✔
2487

2488
#endif
2489

2490
#if defined(BOTAN_HAS_ARGON2)
2491

2492
      void bench_argon2(const std::string& /*provider*/, std::chrono::milliseconds msec) {
1✔
2493
         auto pwhash_fam = Botan::PasswordHashFamily::create_or_throw("Argon2id");
1✔
2494

2495
         for(size_t M : {8 * 1024, 64 * 1024, 256 * 1024}) {
4✔
2496
            for(size_t t : {1, 4}) {
9✔
2497
               for(size_t p : {1, 4}) {
18✔
2498
                  auto pwhash = pwhash_fam->from_params(M, t, p);
12✔
2499
                  auto timer = make_timer(pwhash->to_string());
24✔
2500

2501
                  uint8_t out[64];
12✔
2502
                  uint8_t salt[16];
12✔
2503
                  rng().randomize(salt, sizeof(salt));
12✔
2504

2505
                  while(timer->under(msec)) {
24✔
2506
                     timer->run([&] { pwhash->derive_key(out, sizeof(out), "password", 8, salt, sizeof(salt)); });
24✔
2507
                  }
2508

2509
                  record_result(timer);
12✔
2510
               }
24✔
2511
            }
2512
         }
2513
      }
1✔
2514

2515
#endif
2516
};
2517

2518
BOTAN_REGISTER_COMMAND("speed", Speed);
34✔
2519

2520
}  // 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

© 2026 Coveralls, Inc