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

randombit / botan / 10959048213

20 Sep 2024 12:15PM UTC coverage: 91.248% (-0.03%) from 91.279%
10959048213

Pull #4291

github

web-flow
Merge 6f2127671 into 0639e8212
Pull Request #4291: PQC: SLH-DSA

88055 of 96501 relevant lines covered (91.25%)

9430509.52 hits per line

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

80.51
/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_BASE64_CODEC)
58
   #include <botan/base64.h>
59
#endif
60

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

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

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

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

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

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

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

89
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
90
   #include <botan/rfc3394.h>
91
#endif
92

93
#if defined(BOTAN_HAS_COMPRESSION)
94
   #include <botan/compression.h>
95
#endif
96

97
#if defined(BOTAN_HAS_POLY_DBL)
98
   #include <botan/internal/poly_dbl.h>
99
#endif
100

101
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
102
   #include <botan/pk_algs.h>
103
   #include <botan/pkcs8.h>
104
   #include <botan/pubkey.h>
105
   #include <botan/x509_key.h>
106
   #include <botan/internal/workfactor.h>
107
#endif
108

109
#if defined(BOTAN_HAS_NUMBERTHEORY)
110
   #include <botan/numthry.h>
111
   #include <botan/reducer.h>
112
   #include <botan/internal/curve_nistp.h>
113
   #include <botan/internal/primality.h>
114
#endif
115

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

120
#if defined(BOTAN_HAS_PCURVES)
121
   #include <botan/internal/pcurves.h>
122
#endif
123

124
#if defined(BOTAN_HAS_DL_GROUP)
125
   #include <botan/dl_group.h>
126
#endif
127

128
#if defined(BOTAN_HAS_MCELIECE)
129
   #include <botan/mceliece.h>
130
#endif
131

132
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
133
   #include <botan/kyber.h>
134
#endif
135

136
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
137
   #include <botan/dilithium.h>
138
#endif
139

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

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

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

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

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

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

164
#if defined(BOTAN_HAS_PASSWORD_HASHING)
165
   #include <botan/pwdhash.h>
166
#endif
167

168
#if defined(BOTAN_HAS_ZFEC)
169
   #include <botan/zfec.h>
170
#endif
171

172
namespace Botan_CLI {
173

174
using Botan::Timer;
175

176
namespace {
177

178
class JSON_Output final {
2✔
179
   public:
180
      void add(const Timer& timer) { m_results.push_back(timer); }
2✔
181

182
      std::string print() const {
1✔
183
         std::ostringstream out;
1✔
184

185
         out << "[\n";
1✔
186

187
         for(size_t i = 0; i != m_results.size(); ++i) {
3✔
188
            const Timer& t = m_results[i];
2✔
189

190
            out << "{"
2✔
191
                << "\"algo\": \"" << t.get_name() << "\", "
2✔
192
                << "\"op\": \"" << t.doing() << "\", "
2✔
193
                << "\"events\": " << t.events() << ", ";
2✔
194

195
            if(t.cycles_consumed() > 0) {
4✔
196
               out << "\"cycles\": " << t.cycles_consumed() << ", ";
4✔
197
            }
198

199
            if(t.buf_size() > 0) {
2✔
200
               out << "\"bps\": " << static_cast<uint64_t>(t.events() / (t.value() / 1000000000.0)) << ", ";
2✔
201
               out << "\"buf_size\": " << t.buf_size() << ", ";
2✔
202
            }
203

204
            out << "\"nanos\": " << t.value() << "}";
2✔
205

206
            if(i != m_results.size() - 1) {
2✔
207
               out << ",";
1✔
208
            }
209

210
            out << "\n";
2✔
211
         }
212
         out << "]\n";
1✔
213

214
         return out.str();
2✔
215
      }
1✔
216

217
   private:
218
      std::vector<Timer> m_results;
219
};
220

221
class Summary final {
1✔
222
   public:
223
      Summary() = default;
1✔
224

225
      void add(const Timer& t) {
2✔
226
         if(t.buf_size() == 0) {
2✔
227
            m_ops_entries.push_back(t);
×
228
         } else {
229
            m_bps_entries[std::make_pair(t.doing(), t.get_name())].push_back(t);
4✔
230
         }
231
      }
2✔
232

233
      std::string print() {
1✔
234
         const size_t name_padding = 35;
1✔
235
         const size_t op_name_padding = 16;
1✔
236
         const size_t op_padding = 16;
1✔
237

238
         std::ostringstream result_ss;
1✔
239
         result_ss << std::fixed;
1✔
240

241
         if(!m_bps_entries.empty()) {
1✔
242
            result_ss << "\n";
1✔
243

244
            // add table header
245
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
1✔
246
                      << "operation";
1✔
247

248
            for(const Timer& t : m_bps_entries.begin()->second) {
2✔
249
               result_ss << std::setw(op_padding) << std::right << (std::to_string(t.buf_size()) + " bytes");
2✔
250
            }
251
            result_ss << "\n";
1✔
252

253
            // add table entries
254
            for(const auto& entry : m_bps_entries) {
3✔
255
               if(entry.second.empty()) {
2✔
256
                  continue;
×
257
               }
258

259
               result_ss << std::setw(name_padding) << std::left << (entry.first.second) << std::setw(op_name_padding)
2✔
260
                         << std::left << (entry.first.first);
2✔
261

262
               for(const Timer& t : entry.second) {
4✔
263
                  if(t.events() == 0) {
2✔
264
                     result_ss << std::setw(op_padding) << std::right << "N/A";
×
265
                  } else {
266
                     result_ss << std::setw(op_padding) << std::right << std::setprecision(2)
2✔
267
                               << (t.bytes_per_second() / 1000.0);
2✔
268
                  }
269
               }
270

271
               result_ss << "\n";
2✔
272
            }
273

274
            result_ss << "\n[results are the number of 1000s bytes processed per second]\n";
1✔
275
         }
276

277
         if(!m_ops_entries.empty()) {
1✔
278
            result_ss << std::setprecision(6) << "\n";
×
279

280
            // sort entries
281
            std::sort(m_ops_entries.begin(), m_ops_entries.end());
×
282

283
            // add table header
284
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
×
285
                      << "operation" << std::setw(op_padding) << std::right << "sec/op" << std::setw(op_padding)
×
286
                      << std::right << "op/sec"
×
287
                      << "\n";
×
288

289
            // add table entries
290
            for(const Timer& entry : m_ops_entries) {
×
291
               result_ss << std::setw(name_padding) << std::left << entry.get_name() << std::setw(op_name_padding)
×
292
                         << std::left << entry.doing() << std::setw(op_padding) << std::right
×
293
                         << entry.seconds_per_event() << std::setw(op_padding) << std::right
×
294
                         << entry.events_per_second() << "\n";
×
295
            }
296
         }
297

298
         return result_ss.str();
2✔
299
      }
1✔
300

301
   private:
302
      std::map<std::pair<std::string, std::string>, std::vector<Timer>> m_bps_entries;
303
      std::vector<Timer> m_ops_entries;
304
};
305

306
std::vector<size_t> unique_buffer_sizes(const std::string& cmdline_arg) {
33✔
307
   const size_t MAX_BUF_SIZE = 64 * 1024 * 1024;
33✔
308

309
   std::set<size_t> buf;
33✔
310
   for(const std::string& size_str : Command::split_on(cmdline_arg, ',')) {
64✔
311
      size_t x = 0;
34✔
312
      try {
34✔
313
         size_t converted = 0;
34✔
314
         x = static_cast<size_t>(std::stoul(size_str, &converted, 0));
34✔
315

316
         if(converted != size_str.size()) {
33✔
317
            throw CLI_Usage_Error("Invalid integer");
×
318
         }
319
      } catch(std::exception&) {
1✔
320
         throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size");
2✔
321
      }
1✔
322

323
      if(x == 0) {
33✔
324
         throw CLI_Usage_Error("Cannot have a zero-sized buffer");
2✔
325
      }
326

327
      if(x > MAX_BUF_SIZE) {
32✔
328
         throw CLI_Usage_Error("Specified buffer size is too large");
2✔
329
      }
330

331
      buf.insert(x);
31✔
332
   }
33✔
333

334
   return std::vector<size_t>(buf.begin(), buf.end());
33✔
335
}
30✔
336

337
}  // namespace
338

339
class Speed final : public Command {
×
340
   public:
341
      Speed() :
34✔
342
            Command(
343
               "speed --msec=500 --format=default --ecc-groups= --provider= --buf-size=1024 --clear-cpuid= --cpu-clock-speed=0 --cpu-clock-ratio=1.0 *algos") {
68✔
344
      }
34✔
345

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

376
            /* Cipher modes */
377
            "AES-128/CBC",
378
            "AES-128/CTR-BE",
379
            "AES-128/EAX",
380
            "AES-128/OCB",
381
            "AES-128/GCM",
382
            "AES-128/XTS",
383
            "AES-128/SIV",
384

385
            "Serpent/CBC",
386
            "Serpent/CTR-BE",
387
            "Serpent/EAX",
388
            "Serpent/OCB",
389
            "Serpent/GCM",
390
            "Serpent/XTS",
391
            "Serpent/SIV",
392

393
            "ChaCha20Poly1305",
394

395
            /* Stream ciphers */
396
            "RC4",
397
            "Salsa20",
398
            "ChaCha20",
399

400
            /* Hashes */
401
            "SHA-1",
402
            "SHA-256",
403
            "SHA-512",
404
            "SHA-3(256)",
405
            "SHA-3(512)",
406
            "RIPEMD-160",
407
            "Skein-512",
408
            "Blake2b",
409
            "Whirlpool",
410

411
            /* XOFs */
412
            "SHAKE-128",
413
            "SHAKE-256",
414

415
            /* MACs */
416
            "CMAC(AES-128)",
417
            "HMAC(SHA-256)",
418

419
            /* pubkey */
420
            "RSA",
421
            "DH",
422
            "ECDH",
423
            "ECDSA",
424
            "Ed25519",
425
            "Ed448",
426
            "X25519",
427
            "X448",
428
            "McEliece",
429
            "Kyber",
430
            "SLH-DSA",
431
            "SPHINCS+",
432
            "FrodoKEM",
433
            "HSS-LMS",
434
         };
×
435
         // clang-format on
436
      }
437

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

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

442
      void go() override {
33✔
443
         std::chrono::milliseconds msec(get_arg_sz("msec"));
33✔
444
         const std::string provider = get_arg("provider");
33✔
445
         std::vector<std::string> ecc_groups = Command::split_on(get_arg("ecc-groups"), ',');
69✔
446
         const std::string format = get_arg("format");
33✔
447
         const std::string clock_ratio = get_arg("cpu-clock-ratio");
36✔
448
         m_clock_speed = get_arg_sz("cpu-clock-speed");
33✔
449

450
         m_clock_cycle_ratio = std::strtod(clock_ratio.c_str(), nullptr);
33✔
451

452
         /*
453
         * This argument is intended to be the ratio between the cycle counter
454
         * and the actual machine cycles. It is extremely unlikely that there is
455
         * any machine where the cycle counter increments faster than the actual
456
         * clock.
457
         */
458
         if(m_clock_cycle_ratio < 0.0 || m_clock_cycle_ratio > 1.0) {
33✔
459
            throw CLI_Usage_Error("Unlikely CPU clock ratio of " + clock_ratio);
×
460
         }
461

462
         m_clock_cycle_ratio = 1.0 / m_clock_cycle_ratio;
33✔
463

464
         if(m_clock_speed != 0 && Botan::OS::get_cpu_cycle_counter() != 0) {
33✔
465
            error_output() << "The --cpu-clock-speed option is only intended to be used on "
×
466
                              "platforms without access to a cycle counter.\n"
467
                              "Expected incorrect results\n\n";
×
468
         }
469

470
         if(format == "table") {
33✔
471
            m_summary = std::make_unique<Summary>();
1✔
472
         } else if(format == "json") {
32✔
473
            m_json = std::make_unique<JSON_Output>();
1✔
474
         } else if(format != "default") {
31✔
475
            throw CLI_Usage_Error("Unknown --format type '" + format + "'");
×
476
         }
477

478
#if defined(BOTAN_HAS_ECC_GROUP)
479
         if(ecc_groups.empty()) {
33✔
480
            ecc_groups = {"secp256r1", "secp384r1", "secp521r1", "brainpool256r1", "brainpool384r1", "brainpool512r1"};
264✔
481
         } else if(ecc_groups.size() == 1 && ecc_groups[0] == "all") {
×
482
            auto all = Botan::EC_Group::known_named_groups();
×
483
            ecc_groups.assign(all.begin(), all.end());
×
484
         }
×
485
#endif
486

487
         std::vector<std::string> algos = get_arg_list("algos");
36✔
488

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

491
         for(const std::string& cpuid_to_clear : Command::split_on(get_arg("clear-cpuid"), ',')) {
31✔
492
            auto bits = Botan::CPUID::bit_from_string(cpuid_to_clear);
1✔
493
            if(bits.empty()) {
1✔
494
               error_output() << "Warning don't know CPUID flag '" << cpuid_to_clear << "'\n";
1✔
495
            }
496

497
            for(auto bit : bits) {
1✔
498
               Botan::CPUID::clear_cpuid_bit(bit);
×
499
            }
500
         }
31✔
501

502
         if(verbose() || m_summary) {
30✔
503
            output() << Botan::version_string() << "\n"
2✔
504
                     << "CPUID: " << Botan::CPUID::to_string() << "\n\n";
3✔
505
         }
506

507
         const bool using_defaults = (algos.empty());
30✔
508
         if(using_defaults) {
30✔
509
            algos = default_benchmark_list();
×
510
         }
511

512
         for(const auto& algo : algos) {
82✔
513
            using namespace std::placeholders;
52✔
514

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

562
#if defined(BOTAN_HAS_PCURVES)
563
            else if(algo == "ECDSA-pcurves") {
42✔
564
               bench_pcurve_ecdsa(ecc_groups, msec);
×
565
            } else if(algo == "ECDH-pcurves") {
42✔
566
               bench_pcurve_ecdh(ecc_groups, msec);
×
567
            } else if(algo == "pcurves") {
42✔
568
               bench_pcurves(ecc_groups, msec);
×
569
            }
570
#endif
571

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

707
#if defined(BOTAN_HAS_DL_GROUP)
708
            else if(algo == "modexp") {
17✔
709
               bench_modexp(msec);
1✔
710
            }
711
#endif
712

713
#if defined(BOTAN_HAS_BIGINT)
714
            else if(algo == "mp_mul") {
16✔
715
               bench_mp_mul(msec);
1✔
716
            } else if(algo == "mp_div") {
15✔
717
               bench_mp_div(msec);
1✔
718
            } else if(algo == "mp_div10") {
14✔
719
               bench_mp_div10(msec);
1✔
720
            }
721
#endif
722

723
#if defined(BOTAN_HAS_NUMBERTHEORY)
724
            else if(algo == "primality_test") {
13✔
725
               bench_primality_tests(msec);
1✔
726
            } else if(algo == "random_prime") {
12✔
727
               bench_random_prime(msec);
1✔
728
            } else if(algo == "inverse_mod") {
11✔
729
               bench_inverse_mod(msec);
1✔
730
            } else if(algo == "bn_redc") {
10✔
731
               bench_bn_redc(msec);
1✔
732
            } else if(algo == "nistp_redc") {
9✔
733
               bench_nistp_redc(msec);
1✔
734
            }
735
#endif
736

737
#if defined(BOTAN_HAS_FPE_FE1)
738
            else if(algo == "fpe_fe1") {
8✔
739
               bench_fpe_fe1(msec);
1✔
740
            }
741
#endif
742

743
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
744
            else if(algo == "rfc3394") {
7✔
745
               bench_rfc3394(msec);
1✔
746
            }
747
#endif
748

749
#if defined(BOTAN_HAS_BASE64_CODEC)
750
            else if(algo == "base64") {
6✔
751
               bench_base64(msec, buf_sizes);
×
752
            }
753
#endif
754
#if defined(BOTAN_HAS_HEX_CODEC)
755
            else if(algo == "hex") {
6✔
756
               bench_hex(msec, buf_sizes);
×
757
            }
758
#endif
759

760
#if defined(BOTAN_HAS_ECC_GROUP)
761
            else if(algo == "ecc_mult") {
6✔
762
               bench_ecc_mult(ecc_groups, msec);
1✔
763
            } else if(algo == "ecc_init") {
5✔
764
               bench_ecc_init(ecc_groups, msec);
1✔
765
            } else if(algo == "os2ecp") {
4✔
766
               bench_os2ecp(ecc_groups, msec);
1✔
767
            }
768
#endif
769
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
770
            else if(algo == "ec_h2c") {
3✔
771
               bench_ec_h2c(msec);
1✔
772
            }
773
#endif
774
            else if(algo == "RNG") {
2✔
775
#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
776
               Botan::AutoSeeded_RNG auto_rng;
1✔
777
               bench_rng(auto_rng, "AutoSeeded_RNG (with reseed)", msec, buf_sizes);
1✔
778
#endif
779

780
#if defined(BOTAN_HAS_SYSTEM_RNG)
781
               bench_rng(Botan::system_rng(), "System_RNG", msec, buf_sizes);
1✔
782
#endif
783

784
#if defined(BOTAN_HAS_PROCESSOR_RNG)
785
               if(Botan::Processor_RNG::available()) {
1✔
786
                  Botan::Processor_RNG hwrng;
1✔
787
                  bench_rng(hwrng, "Processor_RNG", msec, buf_sizes);
2✔
788
               }
1✔
789
#endif
790

791
#if defined(BOTAN_HAS_HMAC_DRBG)
792
               for(std::string hash : {"SHA-256", "SHA-384", "SHA-512"}) {
4✔
793
                  Botan::HMAC_DRBG hmac_drbg(hash);
3✔
794
                  bench_rng(hmac_drbg, hmac_drbg.name(), msec, buf_sizes);
3✔
795
               }
3✔
796
#endif
797

798
#if defined(BOTAN_HAS_CHACHA_RNG)
799
               // Provide a dummy seed
800
               Botan::ChaCha_RNG chacha_rng(Botan::secure_vector<uint8_t>(32));
1✔
801
               bench_rng(chacha_rng, "ChaCha_RNG", msec, buf_sizes);
1✔
802
#endif
803

804
            } else if(algo == "entropy") {
2✔
805
               bench_entropy_sources(msec);
1✔
806
            } else {
807
               if(verbose() || !using_defaults) {
×
808
                  error_output() << "Unknown algorithm '" << algo << "'\n";
×
809
               }
810
            }
46✔
811
         }
812

813
         if(m_json) {
30✔
814
            output() << m_json->print();
2✔
815
         }
816
         if(m_summary) {
30✔
817
            output() << m_summary->print() << "\n";
3✔
818
         }
819

820
         if(verbose() && m_clock_speed == 0 && m_cycles_consumed > 0 && m_ns_taken > 0) {
30✔
821
            const double seconds = static_cast<double>(m_ns_taken) / 1000000000;
×
822
            const double Hz = static_cast<double>(m_cycles_consumed) / seconds;
×
823
            const double MHz = Hz / 1000000;
×
824
            output() << "\nEstimated clock speed " << MHz << " MHz\n";
×
825
         }
826
      }
105✔
827

828
   private:
829
      size_t m_clock_speed = 0;
830
      double m_clock_cycle_ratio = 0.0;
831
      uint64_t m_cycles_consumed = 0;
832
      uint64_t m_ns_taken = 0;
833
      std::unique_ptr<Summary> m_summary;
834
      std::unique_ptr<JSON_Output> m_json;
835

836
      void record_result(const std::unique_ptr<Timer>& t) {
447✔
837
         m_ns_taken += t->value();
447✔
838
         m_cycles_consumed += t->cycles_consumed();
447✔
839
         if(m_json) {
447✔
840
            m_json->add(*t);
2✔
841
         } else {
842
            output() << t->to_string() << std::flush;
890✔
843
            if(m_summary) {
445✔
844
               m_summary->add(*t);
2✔
845
            }
846
         }
847
      }
447✔
848

849
      template <typename T>
850
      using bench_fn = std::function<void(T&, std::string, std::chrono::milliseconds, const std::vector<size_t>&)>;
851

852
      template <typename T>
853
      void bench_providers_of(const std::string& algo,
7✔
854
                              const std::string& provider, /* user request, if any */
855
                              const std::chrono::milliseconds runtime,
856
                              const std::vector<size_t>& buf_sizes,
857
                              bench_fn<T> bench_one) {
858
         for(const auto& prov : T::providers(algo)) {
14✔
859
            if(provider.empty() || provider == prov) {
7✔
860
               auto p = T::create(algo, prov);
7✔
861

862
               if(p) {
7✔
863
                  bench_one(*p, prov, runtime, buf_sizes);
14✔
864
               }
865
            }
7✔
866
         }
867
      }
7✔
868

869
      std::unique_ptr<Timer> make_timer(const std::string& name,
451✔
870
                                        uint64_t event_mult = 1,
871
                                        const std::string& what = "",
872
                                        const std::string& provider = "",
873
                                        size_t buf_size = 0) {
874
         return std::make_unique<Timer>(name, provider, what, event_mult, buf_size, m_clock_cycle_ratio, m_clock_speed);
148✔
875
      }
876

877
      std::unique_ptr<Timer> make_timer(const std::string& algo, const std::string& provider, const std::string& what) {
216✔
878
         return make_timer(algo, 1, what, provider, 0);
124✔
879
      }
880

881
#if defined(BOTAN_HAS_BLOCK_CIPHER)
882
      void bench_block_cipher(Botan::BlockCipher& cipher,
4✔
883
                              const std::string& provider,
884
                              std::chrono::milliseconds runtime,
885
                              const std::vector<size_t>& buf_sizes) {
886
         auto ks_timer = make_timer(cipher.name(), provider, "key schedule");
8✔
887

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

891
         const size_t bs = cipher.block_size();
4✔
892
         std::set<size_t> buf_sizes_in_blocks;
4✔
893
         for(size_t buf_size : buf_sizes) {
9✔
894
            if(buf_size % bs == 0) {
5✔
895
               buf_sizes_in_blocks.insert(buf_size);
5✔
896
            } else {
897
               buf_sizes_in_blocks.insert(buf_size + bs - (buf_size % bs));
×
898
            }
899
         }
900

901
         for(size_t buf_size : buf_sizes_in_blocks) {
9✔
902
            std::vector<uint8_t> buffer(buf_size);
5✔
903
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
5✔
904
            const size_t blocks = buf_size / bs;
5✔
905

906
            auto encrypt_timer = make_timer(cipher.name(), mult * buffer.size(), "encrypt", provider, buf_size);
10✔
907
            auto decrypt_timer = make_timer(cipher.name(), mult * buffer.size(), "decrypt", provider, buf_size);
10✔
908

909
            encrypt_timer->run_until_elapsed(runtime, [&]() {
5✔
910
               for(size_t i = 0; i != mult; ++i) {
16,331✔
911
                  cipher.encrypt_n(&buffer[0], &buffer[0], blocks);
16,256✔
912
               }
913
            });
75✔
914
            record_result(encrypt_timer);
5✔
915

916
            decrypt_timer->run_until_elapsed(runtime, [&]() {
5✔
917
               for(size_t i = 0; i != mult; ++i) {
15,693✔
918
                  cipher.decrypt_n(&buffer[0], &buffer[0], blocks);
15,616✔
919
               }
920
            });
77✔
921
            record_result(decrypt_timer);
5✔
922
         }
5✔
923
      }
8✔
924
#endif
925

926
#if defined(BOTAN_HAS_STREAM_CIPHER)
927
      void bench_stream_cipher(Botan::StreamCipher& cipher,
1✔
928
                               const std::string& provider,
929
                               const std::chrono::milliseconds runtime,
930
                               const std::vector<size_t>& buf_sizes) {
931
         for(auto buf_size : buf_sizes) {
2✔
932
            const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
1✔
933
            cipher.set_key(key);
1✔
934

935
            if(cipher.valid_iv_length(12)) {
1✔
936
               const Botan::InitializationVector iv(rng(), 12);
1✔
937
               cipher.set_iv(iv.begin(), iv.size());
1✔
938
            }
1✔
939

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

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

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

946
            encrypt_timer->run_until_elapsed(runtime, [&]() {
1✔
947
               for(size_t i = 0; i != mult; ++i) {
910✔
948
                  cipher.encipher(buffer);
896✔
949
               }
950
            });
14✔
951

952
            record_result(encrypt_timer);
1✔
953

954
            if(verbose()) {
1✔
955
               auto ks_timer = make_timer(cipher.name(), buffer.size(), "write_keystream", provider, buf_size);
×
956

957
               while(ks_timer->under(runtime)) {
×
958
                  ks_timer->run([&]() { cipher.write_keystream(buffer.data(), buffer.size()); });
×
959
               }
960
               record_result(ks_timer);
×
961
            }
×
962
         }
3✔
963
      }
1✔
964
#endif
965

966
#if defined(BOTAN_HAS_HASH)
967
      void bench_hash(Botan::HashFunction& hash,
1✔
968
                      const std::string& provider,
969
                      const std::chrono::milliseconds runtime,
970
                      const std::vector<size_t>& buf_sizes) {
971
         std::vector<uint8_t> output(hash.output_length());
1✔
972

973
         for(auto buf_size : buf_sizes) {
2✔
974
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
975

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

978
            auto timer = make_timer(hash.name(), mult * buffer.size(), "hash", provider, buf_size);
2✔
979
            timer->run_until_elapsed(runtime, [&]() {
1✔
980
               for(size_t i = 0; i != mult; ++i) {
260✔
981
                  hash.update(buffer);
256✔
982
                  hash.final(output.data());
512✔
983
               }
984
            });
4✔
985
            record_result(timer);
1✔
986
         }
2✔
987
      }
1✔
988
#endif
989

990
#if defined(BOTAN_HAS_XOF)
991
      void bench_xof(Botan::XOF& xof,
×
992
                     const std::string& provider,
993
                     const std::chrono::milliseconds runtime,
994
                     const std::vector<size_t>& buf_sizes) {
995
         for(auto buf_size : buf_sizes) {
×
996
            Botan::secure_vector<uint8_t> in = rng().random_vec(buf_size);
×
997
            Botan::secure_vector<uint8_t> out(buf_size);
×
998

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

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

1005
            record_result(in_timer);
×
1006
            record_result(out_timer);
×
1007
         }
×
1008
      }
×
1009
#endif
1010

1011
#if defined(BOTAN_HAS_MAC)
1012
      void bench_mac(Botan::MessageAuthenticationCode& mac,
1✔
1013
                     const std::string& provider,
1014
                     const std::chrono::milliseconds runtime,
1015
                     const std::vector<size_t>& buf_sizes) {
1016
         std::vector<uint8_t> output(mac.output_length());
1✔
1017

1018
         for(auto buf_size : buf_sizes) {
2✔
1019
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
1020
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
1021

1022
            const Botan::SymmetricKey key(rng(), mac.maximum_keylength());
1✔
1023
            mac.set_key(key);
1✔
1024
            mac.start(nullptr, 0);
1✔
1025

1026
            auto timer = make_timer(mac.name(), mult * buffer.size(), "mac", provider, buf_size);
2✔
1027
            timer->run_until_elapsed(runtime, [&]() {
1✔
1028
               for(size_t i = 0; i != mult; ++i) {
195✔
1029
                  mac.update(buffer);
192✔
1030
                  mac.final(output.data());
384✔
1031
               }
1032
            });
3✔
1033

1034
            record_result(timer);
1✔
1035
         }
3✔
1036
      }
1✔
1037
#endif
1038

1039
#if defined(BOTAN_HAS_CIPHER_MODES)
1040
      void bench_cipher_mode(Botan::Cipher_Mode& enc,
1✔
1041
                             Botan::Cipher_Mode& dec,
1042
                             const std::chrono::milliseconds runtime,
1043
                             const std::vector<size_t>& buf_sizes) {
1044
         auto ks_timer = make_timer(enc.name(), enc.provider(), "key schedule");
2✔
1045

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

1048
         ks_timer->run([&]() { enc.set_key(key); });
2✔
1049
         ks_timer->run([&]() { dec.set_key(key); });
2✔
1050

1051
         record_result(ks_timer);
1✔
1052

1053
         for(auto buf_size : buf_sizes) {
2✔
1054
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
1055
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
1056

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

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

1062
            if(buf_size >= enc.minimum_final_size()) {
1✔
1063
               encrypt_timer->run_until_elapsed(runtime, [&]() {
2✔
1064
                  for(size_t i = 0; i != mult; ++i) {
260✔
1065
                     enc.start(iv);
256✔
1066
                     enc.finish(buffer);
256✔
1067
                     buffer.resize(buf_size);  // remove any tag or padding
256✔
1068
                  }
1069
               });
4✔
1070

1071
               while(decrypt_timer->under(runtime)) {
5✔
1072
                  if(!iv.empty()) {
4✔
1073
                     iv[iv.size() - 1] += 1;
4✔
1074
                  }
1075

1076
                  // Create a valid ciphertext/tag for decryption to run on
1077
                  buffer.resize(buf_size);
4✔
1078
                  enc.start(iv);
4✔
1079
                  enc.finish(buffer);
4✔
1080

1081
                  Botan::secure_vector<uint8_t> dbuffer;
4✔
1082

1083
                  decrypt_timer->run([&]() {
4✔
1084
                     for(size_t i = 0; i != mult; ++i) {
260✔
1085
                        dbuffer = buffer;
256✔
1086
                        dec.start(iv);
256✔
1087
                        dec.finish(dbuffer);
256✔
1088
                     }
1089
                  });
4✔
1090
               }
4✔
1091
            }
1092
            record_result(encrypt_timer);
1✔
1093
            record_result(decrypt_timer);
1✔
1094
         }
2✔
1095
      }
1✔
1096
#endif
1097

1098
      void bench_rng(Botan::RandomNumberGenerator& rng,
7✔
1099
                     const std::string& rng_name,
1100
                     const std::chrono::milliseconds runtime,
1101
                     const std::vector<size_t>& buf_sizes) {
1102
         for(auto buf_size : buf_sizes) {
14✔
1103
            Botan::secure_vector<uint8_t> buffer(buf_size);
7✔
1104
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
7✔
1105

1106
#if defined(BOTAN_HAS_SYSTEM_RNG)
1107
            rng.reseed_from_rng(Botan::system_rng(), 256);
7✔
1108
#endif
1109

1110
            auto timer = make_timer(rng_name, mult * buffer.size(), "generate", "", buf_size);
7✔
1111
            timer->run_until_elapsed(runtime, [&]() {
7✔
1112
               for(size_t i = 0; i != mult; ++i) {
2,015✔
1113
                  rng.randomize(buffer.data(), buffer.size());
1,984✔
1114
               }
1115
            });
31✔
1116

1117
            record_result(timer);
7✔
1118
         }
14✔
1119
      }
7✔
1120

1121
      void bench_entropy_sources(const std::chrono::milliseconds /*unused*/) {
1✔
1122
         Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources();
1✔
1123

1124
         for(auto src : srcs.enabled_sources()) {
5✔
1125
            size_t entropy_bits = 0;
4✔
1126
            Botan_Tests::SeedCapturing_RNG rng;
4✔
1127

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

1131
            size_t compressed_size = 0;
4✔
1132

1133
#if defined(BOTAN_HAS_ZLIB)
1134
            auto comp = Botan::Compression_Algorithm::create("zlib");
4✔
1135

1136
            if(comp) {
4✔
1137
               Botan::secure_vector<uint8_t> compressed;
4✔
1138
               compressed.assign(rng.seed_material().begin(), rng.seed_material().end());
4✔
1139
               comp->start(9);
4✔
1140
               comp->finish(compressed);
4✔
1141

1142
               compressed_size = compressed.size();
4✔
1143
            }
4✔
1144
#endif
1145

1146
            std::ostringstream msg;
4✔
1147

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

1151
            if(compressed_size > 0) {
4✔
1152
               msg << " output compressed to " << compressed_size << " bytes";
4✔
1153
            }
1154

1155
            msg << " total samples " << rng.samples() << "\n";
4✔
1156

1157
            timer->set_custom_msg(msg.str());
8✔
1158

1159
            record_result(timer);
4✔
1160
         }
9✔
1161
      }
1✔
1162

1163
#if defined(BOTAN_HAS_ECC_GROUP)
1164
      void bench_ecc_init(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1165
         for(std::string group_name : groups) {
7✔
1166
            auto timer = make_timer(group_name + " initialization");
12✔
1167

1168
            while(timer->under(runtime)) {
12✔
1169
               Botan::EC_Group::clear_registered_curve_data();
6✔
1170
               timer->run([&]() { Botan::EC_Group::from_name(group_name); });
12✔
1171
            }
1172

1173
            record_result(timer);
6✔
1174
         }
6✔
1175
      }
1✔
1176

1177
      void bench_ecc_mult(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1178
         for(const std::string& group_name : groups) {
7✔
1179
            const auto group = Botan::EC_Group::from_name(group_name);
6✔
1180

1181
            auto bp_timer = make_timer(group_name + " base point");
12✔
1182
            auto vp_timer = make_timer(group_name + " variable point");
12✔
1183

1184
            std::vector<Botan::BigInt> ws;
6✔
1185

1186
            auto g = Botan::EC_AffinePoint::generator(group);
6✔
1187

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

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

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

1195
               BOTAN_ASSERT_EQUAL(
8✔
1196
                  r1.serialize_uncompressed(), r2.serialize_uncompressed(), "Same result for multiplication");
1197
            }
8✔
1198

1199
            record_result(bp_timer);
6✔
1200
            record_result(vp_timer);
6✔
1201
         }
6✔
1202
      }
1✔
1203

1204
      void bench_os2ecp(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1205
         for(const std::string& group_name : groups) {
7✔
1206
            auto uncmp_timer = make_timer("OS2ECP uncompressed " + group_name);
12✔
1207
            auto cmp_timer = make_timer("OS2ECP compressed " + group_name);
12✔
1208

1209
            const auto ec_group = Botan::EC_Group::from_name(group_name);
6✔
1210

1211
            while(uncmp_timer->under(runtime) && cmp_timer->under(runtime)) {
128✔
1212
               const Botan::BigInt k(rng(), 256);
122✔
1213
               const Botan::EC_Point p = ec_group.get_base_point() * k;
122✔
1214
               const std::vector<uint8_t> os_cmp = p.encode(Botan::EC_Point_Format::Compressed);
122✔
1215
               const std::vector<uint8_t> os_uncmp = p.encode(Botan::EC_Point_Format::Uncompressed);
122✔
1216

1217
               uncmp_timer->run([&]() { ec_group.OS2ECP(os_uncmp); });
244✔
1218
               cmp_timer->run([&]() { ec_group.OS2ECP(os_cmp); });
244✔
1219
            }
244✔
1220

1221
            record_result(uncmp_timer);
6✔
1222
            record_result(cmp_timer);
6✔
1223
         }
6✔
1224
      }
1✔
1225

1226
#endif
1227

1228
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
1229
      void bench_ec_h2c(const std::chrono::milliseconds runtime) {
1✔
1230
         for(std::string group_name : {"secp256r1", "secp384r1", "secp521r1"}) {
4✔
1231
            auto h2c_ro_timer = make_timer(group_name + "-RO", "", "hash to curve");
6✔
1232
            auto h2c_nu_timer = make_timer(group_name + "-NU", "", "hash to curve");
6✔
1233

1234
            const auto group = Botan::EC_Group::from_name(group_name);
3✔
1235

1236
            const std::string hash_fn = "SHA-256";
3✔
1237

1238
            while(h2c_ro_timer->under(runtime)) {
15✔
1239
               const auto input = rng().random_array<32>();
12✔
1240
               const auto domain_sep = rng().random_array<32>();
12✔
1241

1242
               h2c_ro_timer->run(
12✔
1243
                  [&]() { return Botan::EC_AffinePoint::hash_to_curve_ro(group, hash_fn, input, domain_sep); });
12✔
1244

1245
               h2c_nu_timer->run(
12✔
1246
                  [&]() { return Botan::EC_AffinePoint::hash_to_curve_nu(group, hash_fn, input, domain_sep); });
12✔
1247
            }
1248

1249
            record_result(h2c_ro_timer);
3✔
1250
            record_result(h2c_nu_timer);
3✔
1251
         }
3✔
1252
      }
1✔
1253
#endif
1254

1255
#if defined(BOTAN_HAS_PCURVES)
1256

1257
      void bench_pcurves(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
×
1258
         for(const auto& group_name : groups) {
×
1259
            if(auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name)) {
×
1260
               auto base_timer = make_timer(group_name + " pcurve base mul");
×
1261
               auto var_timer = make_timer(group_name + " pcurve var mul");
×
1262
               auto mul2_setup_timer = make_timer(group_name + " pcurve mul2 setup");
×
1263
               auto mul2_timer = make_timer(group_name + " pcurve mul2");
×
1264

1265
               auto scalar_invert = make_timer(group_name + " pcurve scalar invert");
×
1266
               auto to_affine = make_timer(group_name + " pcurve proj->affine");
×
1267

1268
               auto g = curve->generator();
×
1269
               auto h = curve->mul(g, curve->random_scalar(rng()), rng()).to_affine();
×
1270
               auto gh_tab = curve->mul2_setup(g, h);
×
1271

1272
               while(base_timer->under(runtime)) {
×
1273
                  const auto scalar = curve->random_scalar(rng());
×
1274
                  base_timer->run([&]() { return curve->mul_by_g(scalar, rng()).to_affine(); });
×
1275
               }
×
1276

1277
               while(var_timer->under(runtime)) {
×
1278
                  const auto scalar = curve->random_scalar(rng());
×
1279
                  var_timer->run([&]() { return curve->mul(h, scalar, rng()).to_affine(); });
×
1280
               }
×
1281

1282
               while(mul2_setup_timer->under(runtime)) {
×
1283
                  mul2_setup_timer->run([&]() { return curve->mul2_setup(g, h); });
×
1284
               }
1285

1286
               while(mul2_timer->under(runtime)) {
×
1287
                  const auto scalar = curve->random_scalar(rng());
×
1288
                  const auto scalar2 = curve->random_scalar(rng());
×
1289
                  mul2_timer->run([&]() -> std::optional<Botan::PCurve::PrimeOrderCurve::AffinePoint> {
×
1290
                     if(auto pt = curve->mul2_vartime(*gh_tab, scalar, scalar2)) {
×
1291
                        return pt->to_affine();
×
1292
                     } else {
1293
                        return {};
×
1294
                     }
×
1295
                  });
1296
               }
×
1297

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

1301
               while(scalar_invert->under(runtime)) {
×
1302
                  const auto scalar = curve->random_scalar(rng());
×
1303
                  scalar_invert->run([&]() { scalar.invert(); });
×
1304
               }
×
1305

1306
               record_result(base_timer);
×
1307
               record_result(var_timer);
×
1308
               record_result(mul2_setup_timer);
×
1309
               record_result(mul2_timer);
×
1310
               record_result(to_affine);
×
1311
               record_result(scalar_invert);
×
1312
            }
×
1313
         }
1314
      }
×
1315

1316
      void bench_pcurve_ecdsa(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
×
1317
         for(const auto& group_name : groups) {
×
1318
            auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name);
×
1319
            if(!curve) {
×
1320
               continue;
×
1321
            }
1322

1323
            // Setup (not timed)
1324
            const auto g = curve->generator();
×
1325
            const auto x = curve->random_scalar(rng());
×
1326
            const auto y = curve->mul_by_g(x, rng()).to_affine();
×
1327
            const auto e = curve->random_scalar(rng());
×
1328

1329
            const auto gy_tab = curve->mul2_setup(g, y);
×
1330

1331
            auto b = curve->random_scalar(rng());
×
1332
            auto b_inv = b.invert();
×
1333

1334
            auto sign_timer = make_timer("ECDSA sign pcurves " + group_name);
×
1335
            auto verify_timer = make_timer("ECDSA verify pcurves " + group_name);
×
1336

1337
            while(sign_timer->under(runtime)) {
×
1338
               sign_timer->start();
×
1339

1340
               const auto signature = [&]() {
×
1341
                  const auto k = curve->random_scalar(rng());
×
1342
                  const auto r = curve->base_point_mul_x_mod_order(k, rng());
×
1343
                  const auto k_inv = (b * k).invert() * b;
×
1344
                  b = b.square();
×
1345
                  b_inv = b_inv.square();
×
1346
                  const auto be = b * e;
×
1347
                  const auto bx = b * x;
×
1348
                  const auto bxr_e = (bx * r) + be;
×
1349
                  const auto s = (k_inv * bxr_e) * b_inv;
×
1350

1351
                  return Botan::concat(r.serialize(), s.serialize());
×
1352
               }();
×
1353

1354
               sign_timer->stop();
×
1355

1356
               verify_timer->start();
×
1357

1358
               auto result = [&](std::span<const uint8_t> sig) {
×
1359
                  const size_t scalar_bytes = curve->scalar_bytes();
×
1360
                  if(sig.size() != 2 * scalar_bytes) {
×
1361
                     return false;
1362
                  }
1363

1364
                  const auto r = curve->deserialize_scalar(sig.first(scalar_bytes));
×
1365
                  const auto s = curve->deserialize_scalar(sig.last(scalar_bytes));
×
1366

1367
                  if(r && s) {
×
1368
                     if(r->is_zero() || s->is_zero()) {
×
1369
                        return false;
×
1370
                     }
1371

1372
                     auto w = s->invert();
×
1373

1374
                     auto u1 = e * w;
×
1375
                     auto u2 = *r * w;
×
1376

1377
                     return curve->mul2_vartime_x_mod_order_eq(*gy_tab, *r, u1, u2);
×
1378
                  }
×
1379

1380
                  return false;
1381
               }(signature);
×
1382

1383
               BOTAN_ASSERT(result, "ECDSA-pcurves signature ok");
×
1384

1385
               verify_timer->stop();
×
1386
            }
×
1387

1388
            record_result(sign_timer);
×
1389
            record_result(verify_timer);
×
1390
         }
×
1391
      }
×
1392

1393
      void bench_pcurve_ecdh(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
×
1394
         for(const auto& group_name : groups) {
×
1395
            auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name);
×
1396
            if(!curve) {
×
1397
               continue;
×
1398
            }
1399

1400
            auto ka_timer = make_timer("ECDH agree pcurves " + group_name);
×
1401

1402
            auto agree = [&](const Botan::PCurve::PrimeOrderCurve::Scalar& sk, std::span<const uint8_t> pt_bytes) {
×
1403
               const auto pt = curve->deserialize_point(pt_bytes);
×
1404
               if(pt) {
×
1405
                  return curve->mul(*pt, sk, rng()).to_affine().serialize();
×
1406
               } else {
1407
                  return std::vector<uint8_t>();
×
1408
               }
1409
            };
×
1410

1411
            while(ka_timer->under(runtime)) {
×
1412
               const auto g = curve->generator();
×
1413
               const auto x1 = curve->random_scalar(rng());
×
1414
               const auto x2 = curve->random_scalar(rng());
×
1415

1416
               const auto y1 = curve->mul_by_g(x1, rng()).to_affine().serialize();
×
1417
               const auto y2 = curve->mul_by_g(x2, rng()).to_affine().serialize();
×
1418

1419
               ka_timer->start();
×
1420
               const auto ss1 = agree(x1, y2);
×
1421
               ka_timer->stop();
×
1422

1423
               ka_timer->start();
×
1424
               const auto ss2 = agree(x1, y2);
×
1425
               ka_timer->stop();
×
1426

1427
               BOTAN_ASSERT(ss1 == ss2, "Key agreement worked");
×
1428
            }
×
1429

1430
            record_result(ka_timer);
×
1431
         }
×
1432
      }
×
1433

1434
#endif
1435

1436
#if defined(BOTAN_HAS_FPE_FE1)
1437

1438
      void bench_fpe_fe1(const std::chrono::milliseconds runtime) {
1✔
1439
         const auto n = Botan::BigInt::from_u64(1000000000000000);
1✔
1440

1441
         auto enc_timer = make_timer("FPE_FE1 encrypt");
2✔
1442
         auto dec_timer = make_timer("FPE_FE1 decrypt");
2✔
1443

1444
         const Botan::SymmetricKey key(rng(), 32);
1✔
1445
         const std::vector<uint8_t> tweak(8);  // 8 zeros
1✔
1446

1447
         auto x = Botan::BigInt::one();
1✔
1448

1449
         Botan::FPE_FE1 fpe_fe1(n);
1✔
1450
         fpe_fe1.set_key(key);
1✔
1451

1452
         while(enc_timer->under(runtime)) {
6✔
1453
            enc_timer->start();
5✔
1454
            x = fpe_fe1.encrypt(x, tweak.data(), tweak.size());
5✔
1455
            enc_timer->stop();
5✔
1456
         }
1457

1458
         for(size_t i = 0; i != enc_timer->events(); ++i) {
6✔
1459
            dec_timer->start();
5✔
1460
            x = fpe_fe1.decrypt(x, tweak.data(), tweak.size());
5✔
1461
            dec_timer->stop();
5✔
1462
         }
1463

1464
         BOTAN_ASSERT(x == 1, "FPE works");
1✔
1465

1466
         record_result(enc_timer);
1✔
1467
         record_result(dec_timer);
1✔
1468
      }
4✔
1469
#endif
1470

1471
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
1472

1473
      void bench_rfc3394(const std::chrono::milliseconds runtime) {
1✔
1474
         auto wrap_timer = make_timer("RFC3394 AES-256 key wrap");
2✔
1475
         auto unwrap_timer = make_timer("RFC3394 AES-256 key unwrap");
2✔
1476

1477
         const Botan::SymmetricKey kek(rng(), 32);
1✔
1478
         Botan::secure_vector<uint8_t> key(64, 0);
1✔
1479

1480
         while(wrap_timer->under(runtime)) {
101✔
1481
            wrap_timer->start();
100✔
1482
            key = Botan::rfc3394_keywrap(key, kek);
200✔
1483
            wrap_timer->stop();
100✔
1484

1485
            unwrap_timer->start();
100✔
1486
            key = Botan::rfc3394_keyunwrap(key, kek);
200✔
1487
            unwrap_timer->stop();
100✔
1488

1489
            key[0] += 1;
100✔
1490
         }
1491

1492
         record_result(wrap_timer);
1✔
1493
         record_result(unwrap_timer);
1✔
1494
      }
2✔
1495
#endif
1496

1497
#if defined(BOTAN_HAS_BIGINT)
1498

1499
      void bench_mp_mul(const std::chrono::milliseconds runtime) {
1✔
1500
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1501
         for(size_t bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) {
10✔
1502
            auto mul_timer = make_timer("BigInt mul " + std::to_string(bits));
18✔
1503
            auto sqr_timer = make_timer("BigInt sqr " + std::to_string(bits));
18✔
1504

1505
            const Botan::BigInt y(rng(), bits);
9✔
1506
            Botan::secure_vector<Botan::word> ws;
9✔
1507

1508
            while(mul_timer->under(runtime_per_size)) {
9,064✔
1509
               Botan::BigInt x(rng(), bits);
9,055✔
1510

1511
               sqr_timer->start();
9,055✔
1512
               x.square(ws);
9,055✔
1513
               sqr_timer->stop();
9,055✔
1514

1515
               x.mask_bits(bits);
9,055✔
1516

1517
               mul_timer->start();
9,055✔
1518
               x.mul(y, ws);
9,055✔
1519
               mul_timer->stop();
9,055✔
1520
            }
9,055✔
1521

1522
            record_result(mul_timer);
9✔
1523
            record_result(sqr_timer);
9✔
1524
         }
18✔
1525
      }
1✔
1526

1527
      void bench_mp_div(const std::chrono::milliseconds runtime) {
1✔
1528
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1529

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

1534
            auto div_timer = make_timer("BigInt div " + bit_descr);
18✔
1535
            auto ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
18✔
1536

1537
            Botan::BigInt y;
9✔
1538
            Botan::BigInt x;
9✔
1539
            Botan::secure_vector<Botan::word> ws;
9✔
1540

1541
            Botan::BigInt q1, r1, q2, r2;
9✔
1542

1543
            while(ct_div_timer->under(runtime_per_size)) {
9✔
1544
               x.randomize(rng(), n_bits);
90✔
1545
               y.randomize(rng(), q_bits);
90✔
1546

1547
               div_timer->start();
90✔
1548
               Botan::vartime_divide(x, y, q1, r1);
90✔
1549
               div_timer->stop();
90✔
1550

1551
               ct_div_timer->start();
90✔
1552
               Botan::ct_divide(x, y, q2, r2);
90✔
1553
               ct_div_timer->stop();
90✔
1554

1555
               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
90✔
1556
               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
189✔
1557
            }
1558

1559
            record_result(div_timer);
9✔
1560
            record_result(ct_div_timer);
9✔
1561
         }
54✔
1562
      }
1✔
1563

1564
      void bench_mp_div10(const std::chrono::milliseconds runtime) {
1✔
1565
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1566

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

1570
            auto div_timer = make_timer("BigInt div " + bit_descr);
18✔
1571
            auto ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
18✔
1572

1573
            Botan::BigInt x;
9✔
1574
            Botan::secure_vector<Botan::word> ws;
9✔
1575

1576
            const auto ten = Botan::BigInt::from_word(10);
9✔
1577
            Botan::BigInt q1, r1, q2;
9✔
1578
            Botan::word r2;
9✔
1579

1580
            while(ct_div_timer->under(runtime_per_size)) {
9✔
1581
               x.randomize(rng(), n_bits);
657✔
1582

1583
               div_timer->start();
657✔
1584
               Botan::vartime_divide(x, ten, q1, r1);
657✔
1585
               div_timer->stop();
657✔
1586

1587
               ct_div_timer->start();
657✔
1588
               Botan::ct_divide_word(x, 10, q2, r2);
657✔
1589
               ct_div_timer->stop();
657✔
1590

1591
               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
657✔
1592
               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
1,323✔
1593
            }
1594

1595
            record_result(div_timer);
9✔
1596
            record_result(ct_div_timer);
9✔
1597
         }
45✔
1598
      }
1✔
1599

1600
#endif
1601

1602
#if defined(BOTAN_HAS_DL_GROUP)
1603

1604
      void bench_modexp(const std::chrono::milliseconds runtime) {
1✔
1605
         for(size_t group_bits : {1024, 1536, 2048, 3072, 4096}) {
6✔
1606
            const std::string group_bits_str = std::to_string(group_bits);
5✔
1607
            const Botan::DL_Group group("modp/srp/" + group_bits_str);
5✔
1608

1609
            const size_t e_bits = Botan::dl_exponent_size(group_bits);
5✔
1610
            const size_t f_bits = group_bits - 1;
5✔
1611

1612
            const Botan::BigInt random_e(rng(), e_bits);
5✔
1613
            const Botan::BigInt random_f(rng(), f_bits);
5✔
1614

1615
            auto e_timer = make_timer(group_bits_str + " short exponent");
10✔
1616
            auto f_timer = make_timer(group_bits_str + "  full exponent");
10✔
1617

1618
            while(f_timer->under(runtime)) {
11✔
1619
               e_timer->run([&]() { group.power_g_p(random_e); });
12✔
1620
               f_timer->run([&]() { group.power_g_p(random_f); });
12✔
1621
            }
1622

1623
            record_result(e_timer);
5✔
1624
            record_result(f_timer);
5✔
1625
         }
20✔
1626
      }
6✔
1627
#endif
1628

1629
#if defined(BOTAN_HAS_NUMBERTHEORY)
1630
      void bench_nistp_redc(const std::chrono::milliseconds runtime) {
1✔
1631
         Botan::secure_vector<Botan::word> ws;
1✔
1632

1633
         auto p192_timer = make_timer("P-192 redc");
2✔
1634
         Botan::BigInt r192(rng(), 192 * 2 - 1);
1✔
1635
         while(p192_timer->under(runtime)) {
6,926✔
1636
            Botan::BigInt r = r192;
6,925✔
1637
            p192_timer->run([&]() { Botan::redc_p192(r, ws); });
13,850✔
1638
            r192 += 1;
6,925✔
1639
         }
6,925✔
1640
         record_result(p192_timer);
1✔
1641

1642
         auto p224_timer = make_timer("P-224 redc");
2✔
1643
         Botan::BigInt r224(rng(), 224 * 2 - 1);
1✔
1644
         while(p224_timer->under(runtime)) {
6,870✔
1645
            Botan::BigInt r = r224;
6,869✔
1646
            p224_timer->run([&]() { Botan::redc_p224(r, ws); });
13,738✔
1647
            r224 += 1;
6,869✔
1648
         }
6,869✔
1649
         record_result(p224_timer);
1✔
1650

1651
         auto p256_timer = make_timer("P-256 redc");
2✔
1652
         Botan::BigInt r256(rng(), 256 * 2 - 1);
1✔
1653
         while(p256_timer->under(runtime)) {
6,463✔
1654
            Botan::BigInt r = r256;
6,462✔
1655
            p256_timer->run([&]() { Botan::redc_p256(r, ws); });
12,924✔
1656
            r256 += 1;
6,462✔
1657
         }
6,462✔
1658
         record_result(p256_timer);
1✔
1659

1660
         auto p384_timer = make_timer("P-384 redc");
2✔
1661
         Botan::BigInt r384(rng(), 384 * 2 - 1);
1✔
1662
         while(p384_timer->under(runtime)) {
3,482✔
1663
            Botan::BigInt r = r384;
3,481✔
1664
            p384_timer->run([&]() { Botan::redc_p384(r384, ws); });
6,962✔
1665
            r384 += 1;
3,481✔
1666
         }
3,481✔
1667
         record_result(p384_timer);
1✔
1668

1669
         auto p521_timer = make_timer("P-521 redc");
2✔
1670
         Botan::BigInt r521(rng(), 521 * 2 - 1);
1✔
1671
         while(p521_timer->under(runtime)) {
3,932✔
1672
            Botan::BigInt r = r521;
3,931✔
1673
            p521_timer->run([&]() { Botan::redc_p521(r521, ws); });
7,862✔
1674
            r521 += 1;
3,931✔
1675
         }
3,931✔
1676
         record_result(p521_timer);
1✔
1677
      }
6✔
1678

1679
      void bench_bn_redc(const std::chrono::milliseconds runtime) {
1✔
1680
         for(size_t bitsize : {512, 1024, 2048, 4096}) {
5✔
1681
            Botan::BigInt p(rng(), bitsize);
4✔
1682

1683
            std::string bit_str = std::to_string(bitsize);
4✔
1684
            auto barrett_timer = make_timer("Barrett-" + bit_str);
8✔
1685
            auto schoolbook_timer = make_timer("Schoolbook-" + bit_str);
8✔
1686

1687
            Botan::Modular_Reducer mod_p(p);
4✔
1688

1689
            while(schoolbook_timer->under(runtime)) {
112✔
1690
               const Botan::BigInt x(rng(), p.bits() * 2 - 2);
108✔
1691

1692
               const Botan::BigInt r1 = barrett_timer->run([&] { return mod_p.reduce(x); });
216✔
1693
               const Botan::BigInt r2 = schoolbook_timer->run([&] { return x % p; });
216✔
1694

1695
               BOTAN_ASSERT(r1 == r2, "Computed different results");
108✔
1696
            }
324✔
1697

1698
            record_result(barrett_timer);
4✔
1699
            record_result(schoolbook_timer);
4✔
1700
         }
8✔
1701
      }
1✔
1702

1703
      void bench_inverse_mod(const std::chrono::milliseconds runtime) {
1✔
1704
         for(size_t bits : {256, 384, 512, 1024, 2048}) {
6✔
1705
            const std::string bit_str = std::to_string(bits);
5✔
1706

1707
            auto timer = make_timer("inverse_mod-" + bit_str);
10✔
1708
            auto gcd_timer = make_timer("gcd-" + bit_str);
10✔
1709

1710
            while(timer->under(runtime) && gcd_timer->under(runtime)) {
15✔
1711
               const Botan::BigInt x(rng(), bits - 1);
10✔
1712
               Botan::BigInt mod(rng(), bits);
10✔
1713

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

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

1718
               if(x_inv == 0) {
10✔
1719
                  BOTAN_ASSERT(g != 1, "Inversion only fails if gcd(x, mod) > 1");
4✔
1720
               } else {
1721
                  BOTAN_ASSERT(g == 1, "Inversion succeeds only if gcd != 1");
6✔
1722
                  const Botan::BigInt check = (x_inv * x) % mod;
6✔
1723
                  BOTAN_ASSERT_EQUAL(check, 1, "Const time inversion correct");
6✔
1724
               }
6✔
1725
            }
38✔
1726

1727
            record_result(timer);
5✔
1728
            record_result(gcd_timer);
5✔
1729
         }
5✔
1730
      }
1✔
1731

1732
      void bench_primality_tests(const std::chrono::milliseconds runtime) {
1✔
1733
         for(size_t bits : {256, 512, 1024}) {
4✔
1734
            auto mr_timer = make_timer("Miller-Rabin-" + std::to_string(bits));
6✔
1735
            auto bpsw_timer = make_timer("Bailie-PSW-" + std::to_string(bits));
6✔
1736
            auto lucas_timer = make_timer("Lucas-" + std::to_string(bits));
6✔
1737

1738
            Botan::BigInt n = Botan::random_prime(rng(), bits);
3✔
1739

1740
            while(lucas_timer->under(runtime)) {
6✔
1741
               Botan::Modular_Reducer mod_n(n);
3✔
1742

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

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

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

1749
               n += 2;
3✔
1750
            }
3✔
1751

1752
            record_result(mr_timer);
3✔
1753
            record_result(bpsw_timer);
3✔
1754
            record_result(lucas_timer);
3✔
1755
         }
3✔
1756
      }
1✔
1757

1758
      void bench_random_prime(const std::chrono::milliseconds runtime) {
1✔
1759
         const auto coprime = Botan::BigInt::from_word(0x10001);
1✔
1760

1761
         for(size_t bits : {256, 384, 512, 768, 1024, 1536}) {
7✔
1762
            auto genprime_timer = make_timer("random_prime " + std::to_string(bits));
12✔
1763
            auto gensafe_timer = make_timer("random_safe_prime " + std::to_string(bits));
12✔
1764
            auto is_prime_timer = make_timer("is_prime " + std::to_string(bits));
12✔
1765

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

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

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

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

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

1783
               // Now test p+2, p+4, ... which may or may not be prime
1784
               for(size_t i = 2; i <= 64; i += 2) {
198✔
1785
                  is_prime_timer->run([&]() { Botan::is_prime(p + i, rng(), 64, true); });
576✔
1786
               }
1787
            }
12✔
1788

1789
            record_result(genprime_timer);
6✔
1790
            record_result(gensafe_timer);
6✔
1791
            record_result(is_prime_timer);
6✔
1792
         }
6✔
1793
      }
1✔
1794
#endif
1795

1796
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
1797
      void bench_pk_enc(const Botan::Private_Key& key,
4✔
1798
                        const std::string& nm,
1799
                        const std::string& provider,
1800
                        const std::string& padding,
1801
                        std::chrono::milliseconds msec) {
1802
         std::vector<uint8_t> plaintext, ciphertext;
4✔
1803

1804
         Botan::PK_Encryptor_EME enc(key, rng(), padding, provider);
4✔
1805
         Botan::PK_Decryptor_EME dec(key, rng(), padding, provider);
4✔
1806

1807
         auto enc_timer = make_timer(nm + " " + padding, provider, "encrypt");
8✔
1808
         auto dec_timer = make_timer(nm + " " + padding, provider, "decrypt");
8✔
1809

1810
         while(enc_timer->under(msec) || dec_timer->under(msec)) {
12✔
1811
            // Generate a new random ciphertext to decrypt
1812
            if(ciphertext.empty() || enc_timer->under(msec)) {
4✔
1813
               rng().random_vec(plaintext, enc.maximum_input_size());
4✔
1814
               ciphertext = enc_timer->run([&]() { return enc.encrypt(plaintext, rng()); });
8✔
1815
            }
1816

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

1820
               if(!(Botan::unlock(dec_pt) == plaintext))  // sanity check
4✔
1821
               {
1822
                  error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n";
×
1823
               }
1824
            }
4✔
1825
         }
1826

1827
         record_result(enc_timer);
4✔
1828
         record_result(dec_timer);
4✔
1829
      }
4✔
1830

1831
      void bench_pk_ka(const std::string& algo,
13✔
1832
                       const std::string& nm,
1833
                       const std::string& params,
1834
                       const std::string& provider,
1835
                       std::chrono::milliseconds msec) {
1836
         const std::string kdf = "KDF2(SHA-256)";  // arbitrary choice
13✔
1837

1838
         auto keygen_timer = make_timer(nm, provider, "keygen");
26✔
1839

1840
         std::unique_ptr<Botan::Private_Key> key1(
13✔
1841
            keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); }));
26✔
1842
         std::unique_ptr<Botan::Private_Key> key2(
13✔
1843
            keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); }));
26✔
1844

1845
         record_result(keygen_timer);
13✔
1846

1847
         const Botan::PK_Key_Agreement_Key& ka_key1 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key1);
13✔
1848
         const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key2);
13✔
1849

1850
         Botan::PK_Key_Agreement ka1(ka_key1, rng(), kdf, provider);
13✔
1851
         Botan::PK_Key_Agreement ka2(ka_key2, rng(), kdf, provider);
13✔
1852

1853
         const std::vector<uint8_t> ka1_pub = ka_key1.public_value();
13✔
1854
         const std::vector<uint8_t> ka2_pub = ka_key2.public_value();
13✔
1855

1856
         auto ka_timer = make_timer(nm, provider, "key agreements");
26✔
1857

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

1862
            if(symkey1 != symkey2) {
18✔
1863
               error_output() << "Key agreement mismatch in PK bench\n";
×
1864
            }
1865
         }
36✔
1866

1867
         record_result(ka_timer);
13✔
1868
      }
39✔
1869

1870
      void bench_pk_kem(const Botan::Private_Key& key,
11✔
1871
                        const std::string& nm,
1872
                        const std::string& provider,
1873
                        const std::string& kdf,
1874
                        std::chrono::milliseconds msec) {
1875
         Botan::PK_KEM_Decryptor dec(key, rng(), kdf, provider);
11✔
1876
         Botan::PK_KEM_Encryptor enc(key, kdf, provider);
11✔
1877

1878
         auto kem_enc_timer = make_timer(nm, provider, "KEM encrypt");
22✔
1879
         auto kem_dec_timer = make_timer(nm, provider, "KEM decrypt");
22✔
1880

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

1884
            kem_enc_timer->start();
45✔
1885
            const auto kem_result = enc.encrypt(rng(), 64, salt);
45✔
1886
            kem_enc_timer->stop();
45✔
1887

1888
            kem_dec_timer->start();
45✔
1889
            Botan::secure_vector<uint8_t> dec_shared_key = dec.decrypt(kem_result.encapsulated_shared_key(), 64, salt);
45✔
1890
            kem_dec_timer->stop();
45✔
1891

1892
            if(kem_result.shared_key() != dec_shared_key) {
45✔
1893
               error_output() << "KEM mismatch in PK bench\n";
×
1894
            }
1895
         }
90✔
1896

1897
         record_result(kem_enc_timer);
11✔
1898
         record_result(kem_dec_timer);
11✔
1899
      }
11✔
1900

1901
      void bench_pk_sig_ecc(const std::string& algo,
7✔
1902
                            const std::string& emsa,
1903
                            const std::string& provider,
1904
                            const std::vector<std::string>& params,
1905
                            std::chrono::milliseconds msec) {
1906
         for(std::string grp : params) {
34✔
1907
            const std::string nm = grp.empty() ? algo : Botan::fmt("{}-{}", algo, grp);
27✔
1908

1909
            auto keygen_timer = make_timer(nm, provider, "keygen");
54✔
1910

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

1914
            record_result(keygen_timer);
27✔
1915
            bench_pk_sig(*key, nm, provider, emsa, msec);
27✔
1916
         }
27✔
1917
      }
7✔
1918

1919
      size_t bench_pk_sig(const Botan::Private_Key& key,
42✔
1920
                          const std::string& nm,
1921
                          const std::string& provider,
1922
                          const std::string& padding,
1923
                          std::chrono::milliseconds msec) {
1924
         std::vector<uint8_t> message, signature, bad_signature;
42✔
1925

1926
         Botan::PK_Signer sig(key, rng(), padding, Botan::Signature_Format::Standard, provider);
42✔
1927
         Botan::PK_Verifier ver(key, padding, Botan::Signature_Format::Standard, provider);
42✔
1928

1929
         auto sig_timer = make_timer(nm + " " + padding, provider, "sign");
84✔
1930
         auto ver_timer = make_timer(nm + " " + padding, provider, "verify");
84✔
1931

1932
         size_t invalid_sigs = 0;
42✔
1933

1934
         while(ver_timer->under(msec) || sig_timer->under(msec)) {
249✔
1935
            if(signature.empty() || sig_timer->under(msec)) {
165✔
1936
               /*
1937
               Length here is kind of arbitrary, but 48 bytes fits into a single
1938
               hash block so minimizes hashing overhead versus the PK op itself.
1939
               */
1940
               rng().random_vec(message, 48);
113✔
1941

1942
               signature = sig_timer->run([&]() { return sig.sign_message(message, rng()); });
226✔
1943

1944
               bad_signature = signature;
113✔
1945
               bad_signature[rng().next_byte() % bad_signature.size()] ^= rng().next_nonzero_byte();
113✔
1946
            }
1947

1948
            if(ver_timer->under(msec)) {
165✔
1949
               const bool verified = ver_timer->run([&] { return ver.verify_message(message, signature); });
218✔
1950

1951
               if(!verified) {
109✔
1952
                  invalid_sigs += 1;
×
1953
               }
1954

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

1957
               if(verified_bad) {
109✔
1958
                  error_output() << "Bad signature accepted in " << nm << " signature bench\n";
×
1959
               }
1960
            }
1961
         }
1962

1963
         if(invalid_sigs > 0) {
42✔
1964
            error_output() << invalid_sigs << " generated signatures rejected in " << nm << " signature bench\n";
×
1965
         }
1966

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

1969
         record_result(sig_timer);
42✔
1970
         record_result(ver_timer);
42✔
1971

1972
         return events;
42✔
1973
      }
42✔
1974
#endif
1975

1976
#if defined(BOTAN_HAS_RSA)
1977
      void bench_rsa_keygen(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1978
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
1979
            const std::string nm = "RSA-" + std::to_string(keylen);
8✔
1980
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1981

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

1986
               BOTAN_ASSERT(key->check_key(rng(), true), "Key is ok");
4✔
1987
            }
4✔
1988

1989
            record_result(keygen_timer);
4✔
1990
         }
4✔
1991
      }
1✔
1992

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

1997
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1998

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

2002
            record_result(keygen_timer);
4✔
2003

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

2007
            //bench_pk_sig(*key, nm, provider, "PSSR(SHA-256)", msec);
2008
            //bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
2009
            //bench_pk_enc(*key, nm, provider, "OAEP(SHA-1)", msec);
2010
         }
4✔
2011
      }
1✔
2012
#endif
2013

2014
#if defined(BOTAN_HAS_ECDSA)
2015
      void bench_ecdsa(const std::vector<std::string>& groups,
1✔
2016
                       const std::string& provider,
2017
                       std::chrono::milliseconds msec) {
2018
         return bench_pk_sig_ecc("ECDSA", "SHA-256", provider, groups, msec);
2✔
2019
      }
2020

2021
      void bench_ecdsa_recovery(const std::vector<std::string>& groups,
1✔
2022
                                const std::string& /*unused*/,
2023
                                std::chrono::milliseconds msec) {
2024
         for(const std::string& group_name : groups) {
7✔
2025
            const auto group = Botan::EC_Group::from_name(group_name);
6✔
2026
            auto recovery_timer = make_timer("ECDSA recovery " + group_name);
12✔
2027

2028
            while(recovery_timer->under(msec)) {
14✔
2029
               Botan::ECDSA_PrivateKey key(rng(), group);
8✔
2030

2031
               std::vector<uint8_t> message(group.get_order_bits() / 8);
8✔
2032
               rng().randomize(message.data(), message.size());
8✔
2033

2034
               Botan::PK_Signer signer(key, rng(), "Raw");
8✔
2035
               signer.update(message);
8✔
2036
               std::vector<uint8_t> signature = signer.signature(rng());
8✔
2037

2038
               Botan::PK_Verifier verifier(key, "Raw", Botan::Signature_Format::Standard, "base");
8✔
2039
               verifier.update(message);
8✔
2040
               BOTAN_ASSERT(verifier.check_signature(signature), "Valid signature");
8✔
2041

2042
               Botan::BigInt r(signature.data(), signature.size() / 2);
8✔
2043
               Botan::BigInt s(signature.data() + signature.size() / 2, signature.size() / 2);
8✔
2044

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

2047
               recovery_timer->run([&]() {
8✔
2048
                  Botan::ECDSA_PublicKey pubkey(group, message, r, s, v);
8✔
2049
                  BOTAN_ASSERT(pubkey.public_point() == key.public_point(), "Recovered public key");
8✔
2050
               });
8✔
2051
            }
16✔
2052

2053
            record_result(recovery_timer);
6✔
2054
         }
6✔
2055
      }
1✔
2056

2057
#endif
2058

2059
#if defined(BOTAN_HAS_ECKCDSA)
2060
      void bench_eckcdsa(const std::vector<std::string>& groups,
1✔
2061
                         const std::string& provider,
2062
                         std::chrono::milliseconds msec) {
2063
         return bench_pk_sig_ecc("ECKCDSA", "SHA-256", provider, groups, msec);
2✔
2064
      }
2065
#endif
2066

2067
#if defined(BOTAN_HAS_GOST_34_10_2001)
2068
      void bench_gost_3410(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2069
         return bench_pk_sig_ecc("GOST-34.10", "GOST-34.11", provider, {"gost_256A"}, msec);
2✔
2070
      }
2071
#endif
2072

2073
#if defined(BOTAN_HAS_SM2)
2074
      void bench_sm2(const std::vector<std::string>& groups,
1✔
2075
                     const std::string& provider,
2076
                     std::chrono::milliseconds msec) {
2077
         return bench_pk_sig_ecc("SM2_Sig", "SM3", provider, groups, msec);
2✔
2078
      }
2079
#endif
2080

2081
#if defined(BOTAN_HAS_ECGDSA)
2082
      void bench_ecgdsa(const std::vector<std::string>& groups,
1✔
2083
                        const std::string& provider,
2084
                        std::chrono::milliseconds msec) {
2085
         return bench_pk_sig_ecc("ECGDSA", "SHA-256", provider, groups, msec);
2✔
2086
      }
2087
#endif
2088

2089
#if defined(BOTAN_HAS_ED25519)
2090
      void bench_ed25519(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2091
         return bench_pk_sig_ecc("Ed25519", "Pure", provider, std::vector<std::string>{""}, msec);
3✔
2092
      }
2✔
2093
#endif
2094

2095
#if defined(BOTAN_HAS_ED448)
2096
      void bench_ed448(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2097
         return bench_pk_sig_ecc("Ed448", "Pure", provider, std::vector<std::string>{""}, msec);
3✔
2098
      }
2✔
2099
#endif
2100

2101
#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
2102
      void bench_dh(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2103
         for(size_t bits : {2048, 3072, 4096, 6144, 8192}) {
6✔
2104
            bench_pk_ka("DH", "DH-" + std::to_string(bits), "ffdhe/ietf/" + std::to_string(bits), provider, msec);
20✔
2105
         }
2106
      }
1✔
2107
#endif
2108

2109
#if defined(BOTAN_HAS_DSA)
2110
      void bench_dsa(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2111
         for(size_t bits : {1024, 2048, 3072}) {
4✔
2112
            const std::string nm = "DSA-" + std::to_string(bits);
6✔
2113

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

2116
            auto keygen_timer = make_timer(nm, provider, "keygen");
6✔
2117

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

2121
            record_result(keygen_timer);
3✔
2122

2123
            bench_pk_sig(*key, nm, provider, "SHA-256", msec);
6✔
2124
         }
3✔
2125
      }
1✔
2126
#endif
2127

2128
#if defined(BOTAN_HAS_ELGAMAL)
2129
      void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2130
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
2131
            const std::string nm = "ElGamal-" + std::to_string(keylen);
8✔
2132

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

2135
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
2136

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

2140
            record_result(keygen_timer);
4✔
2141

2142
            bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
8✔
2143
         }
4✔
2144
      }
1✔
2145
#endif
2146

2147
#if defined(BOTAN_HAS_ECDH)
2148
      void bench_ecdh(const std::vector<std::string>& groups,
1✔
2149
                      const std::string& provider,
2150
                      std::chrono::milliseconds msec) {
2151
         for(const std::string& grp : groups) {
7✔
2152
            bench_pk_ka("ECDH", "ECDH-" + grp, grp, provider, msec);
12✔
2153
         }
2154
      }
1✔
2155
#endif
2156

2157
#if defined(BOTAN_HAS_X25519)
2158
      void bench_x25519(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2159
         bench_pk_ka("X25519", "X25519", "", provider, msec);
2✔
2160
      }
1✔
2161
#endif
2162

2163
#if defined(BOTAN_HAS_X448)
2164
      void bench_x448(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2165
         bench_pk_ka("X448", "X448", "", provider, msec);
2✔
2166
      }
1✔
2167
#endif
2168

2169
#if defined(BOTAN_HAS_MCELIECE)
2170
      void bench_mceliece(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2171
         /*
2172
         SL=80 n=1632 t=33 - 59 KB pubkey 140 KB privkey
2173
         SL=107 n=2480 t=45 - 128 KB pubkey 300 KB privkey
2174
         SL=128 n=2960 t=57 - 195 KB pubkey 459 KB privkey
2175
         SL=147 n=3408 t=67 - 265 KB pubkey 622 KB privkey
2176
         SL=191 n=4624 t=95 - 516 KB pubkey 1234 KB privkey
2177
         SL=256 n=6624 t=115 - 942 KB pubkey 2184 KB privkey
2178
         */
2179

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

2183
         for(auto params : mce_params) {
6✔
2184
            size_t n = params.first;
5✔
2185
            size_t t = params.second;
5✔
2186

2187
            const std::string nm = "McEliece-" + std::to_string(n) + "," + std::to_string(t) +
20✔
2188
                                   " (WF=" + std::to_string(Botan::mceliece_work_factor(n, t)) + ")";
15✔
2189

2190
            auto keygen_timer = make_timer(nm, provider, "keygen");
10✔
2191

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

2195
            record_result(keygen_timer);
5✔
2196
            bench_pk_kem(*key, nm, provider, "KDF2(SHA-256)", msec);
10✔
2197
         }
5✔
2198
      }
1✔
2199
#endif
2200

2201
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
2202
      void bench_kyber(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2203
         const Botan::KyberMode::Mode all_modes[] = {
1✔
2204
            Botan::KyberMode::Kyber512_R3,
2205
            Botan::KyberMode::Kyber512_90s,
2206
            Botan::KyberMode::Kyber768_R3,
2207
            Botan::KyberMode::Kyber768_90s,
2208
            Botan::KyberMode::Kyber1024_R3,
2209
            Botan::KyberMode::Kyber1024_90s,
2210
         };
2211

2212
         for(auto modet : all_modes) {
7✔
2213
            Botan::KyberMode mode(modet);
6✔
2214

2215
   #if !defined(BOTAN_HAS_KYBER)
2216
            if(mode.is_modern())
2217
               continue;
2218
   #endif
2219

2220
   #if !defined(BOTAN_HAS_KYBER_90S)
2221
            if(mode.is_90s())
2222
               continue;
2223
   #endif
2224

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

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

2229
            record_result(keygen_timer);
6✔
2230

2231
            bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec);
12✔
2232
         }
6✔
2233
      }
1✔
2234
#endif
2235

2236
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
2237
      void bench_dilithium(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2238
         const Botan::DilithiumMode::Mode all_modes[] = {Botan::DilithiumMode::Dilithium4x4,
1✔
2239
                                                         Botan::DilithiumMode::Dilithium4x4_AES,
2240
                                                         Botan::DilithiumMode::Dilithium6x5,
2241
                                                         Botan::DilithiumMode::Dilithium6x5_AES,
2242
                                                         Botan::DilithiumMode::Dilithium8x7,
2243
                                                         Botan::DilithiumMode::Dilithium8x7_AES};
2244

2245
         for(auto modet : all_modes) {
7✔
2246
            Botan::DilithiumMode mode(modet);
6✔
2247

2248
   #if !defined(BOTAN_HAS_DILITHIUM)
2249
            if(mode.is_modern())
2250
               continue;
2251
   #endif
2252

2253
   #if !defined(BOTAN_HAS_DILITHIUM_AES)
2254
            if(mode.is_aes())
2255
               continue;
2256
   #endif
2257

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

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

2262
            record_result(keygen_timer);
6✔
2263

2264
            bench_pk_sig(key, mode.to_string(), provider, "", msec);
12✔
2265
         }
6✔
2266
      }
1✔
2267
#endif
2268

2269
#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON)
2270
      void bench_slh_dsa(const std::string& provider, std::chrono::milliseconds msec, bool is_slh_dsa) {
1✔
2271
         // Sphincs_Parameter_Set set, Sphincs_Hash_Type hash
2272
         std::vector<std::string> instances_to_bench = [&]() -> std::vector<std::string> {
3✔
2273
            if(is_slh_dsa) {
1✔
2274
               // Bench the SLH-DSA instances
2275
               return {
1✔
2276
                  "SLH-DSA-SHA2-128s",
2277
                  "SLH-DSA-SHA2-128f",
2278
                  "SLH-DSA-SHA2-192s",
2279
                  "SLH-DSA-SHA2-192f",
2280
                  "SLH-DSA-SHA2-256s",
2281
                  "SLH-DSA-SHA2-256f",
2282
                  "SLH-DSA-SHAKE-128s",
2283
                  "SLH-DSA-SHAKE-128f",
2284
                  "SLH-DSA-SHAKE-192s",
2285
                  "SLH-DSA-SHAKE-192f",
2286
                  "SLH-DSA-SHAKE-256s",
2287
                  "SLH-DSA-SHAKE-256f",
2288
               };
1✔
2289
            } else {
2290
               // Bench the SPHINCS+ Round 3.1 instances
2291
               return {
×
2292
                  "SphincsPlus-sha2-128s-r3.1",
2293
                  "SphincsPlus-sha2-128f-r3.1",
2294
                  "SphincsPlus-sha2-192s-r3.1",
2295
                  "SphincsPlus-sha2-192f-r3.1",
2296
                  "SphincsPlus-sha2-256s-r3.1",
2297
                  "SphincsPlus-sha2-256f-r3.1",
2298
                  "SphincsPlus-shake-128s-r3.1",
2299
                  "SphincsPlus-shake-128f-r3.1",
2300
                  "SphincsPlus-shake-192s-r3.1",
2301
                  "SphincsPlus-shake-192f-r3.1",
2302
                  "SphincsPlus-shake-256s-r3.1",
2303
                  "SphincsPlus-shake-256f-r3.1",
2304
               };
×
2305
            }
2306
         }();
1✔
2307

2308
         for(auto params_str : instances_to_bench) {
1✔
2309
            auto sp_params = Botan::Sphincs_Parameters::create(params_str);
1✔
2310
            if(!sp_params.is_available()) {
1✔
2311
               continue;
×
2312
            }
2313
            auto keygen_timer = make_timer(params_str, provider, "keygen");
2✔
2314

2315
            std::unique_ptr<Botan::Private_Key> key(
1✔
2316
               keygen_timer->run([&] { return Botan::create_private_key("SLH-DSA", rng(), params_str); }));
2✔
2317

2318
            record_result(keygen_timer);
1✔
2319
            if(bench_pk_sig(*key, params_str, provider, "", msec) == 1) {
1✔
2320
               break;
2321
            }
2322
         }
1✔
2323
      }
1✔
2324
#endif
2325

2326
#if defined(BOTAN_HAS_FRODOKEM)
2327
      void bench_frodokem(const std::string& provider, std::chrono::milliseconds msec) {
×
2328
         std::vector<Botan::FrodoKEMMode> frodo_modes{
×
2329
            Botan::FrodoKEMMode::FrodoKEM640_SHAKE,
2330
            Botan::FrodoKEMMode::FrodoKEM976_SHAKE,
2331
            Botan::FrodoKEMMode::FrodoKEM1344_SHAKE,
2332
            Botan::FrodoKEMMode::eFrodoKEM640_SHAKE,
2333
            Botan::FrodoKEMMode::eFrodoKEM976_SHAKE,
2334
            Botan::FrodoKEMMode::eFrodoKEM1344_SHAKE,
2335
            Botan::FrodoKEMMode::FrodoKEM640_AES,
2336
            Botan::FrodoKEMMode::FrodoKEM976_AES,
2337
            Botan::FrodoKEMMode::FrodoKEM1344_AES,
2338
            Botan::FrodoKEMMode::eFrodoKEM640_AES,
2339
            Botan::FrodoKEMMode::eFrodoKEM976_AES,
2340
            Botan::FrodoKEMMode::eFrodoKEM1344_AES,
2341
         };
×
2342

2343
         for(auto modet : frodo_modes) {
×
2344
            if(!modet.is_available()) {
×
2345
               continue;
×
2346
            }
2347

2348
            Botan::FrodoKEMMode mode(modet);
×
2349

2350
            auto keygen_timer = make_timer(mode.to_string(), provider, "keygen");
×
2351

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

2354
            record_result(keygen_timer);
×
2355

2356
            bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec);
×
2357
         }
×
2358
      }
×
2359
#endif
2360

2361
#if defined(BOTAN_HAS_XMSS_RFC8391)
2362
      void bench_xmss(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2363
         /*
2364
         We only test H10 signatures here since already they are quite slow (a
2365
         few seconds per signature). On a fast machine, H16 signatures take 1-2
2366
         minutes to generate and H20 signatures take 5-10 minutes to generate
2367
         */
2368
         std::vector<std::string> xmss_params{
1✔
2369
            "XMSS-SHA2_10_256",
2370
            "XMSS-SHAKE_10_256",
2371
            "XMSS-SHA2_10_512",
2372
            "XMSS-SHAKE_10_512",
2373
         };
6✔
2374

2375
         for(std::string params : xmss_params) {
1✔
2376
            auto keygen_timer = make_timer(params, provider, "keygen");
2✔
2377

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

2381
            record_result(keygen_timer);
1✔
2382
            if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
1✔
2383
               break;
2384
            }
2385
         }
1✔
2386
      }
3✔
2387
#endif
2388

2389
#if defined(BOTAN_HAS_HSS_LMS)
2390
      void bench_hss_lms(const std::string& provider, std::chrono::milliseconds msec) {
×
2391
         // At first we compare instances with multiple hash functions. LMS trees with
2392
         // height 10 are suitable, since they can be used for enough signatures and are
2393
         // fast enough for speed testing.
2394
         // Afterward, setups with multiple HSS layers are tested
2395
         std::vector<std::string> hss_lms_instances{"SHA-256,HW(10,1)",
×
2396
                                                    "SHAKE-256(256),HW(10,1)",
2397
                                                    "SHAKE-256(192),HW(10,1)",
2398
                                                    "Truncated(SHA-256,192),HW(10,1)",
2399
                                                    "SHA-256,HW(10,1),HW(10,1)",
2400
                                                    "SHA-256,HW(10,1),HW(10,1),HW(10,1)"};
×
2401

2402
         for(const auto& params : hss_lms_instances) {
×
2403
            auto keygen_timer = make_timer(params, provider, "keygen");
×
2404

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

2408
            record_result(keygen_timer);
×
2409
            if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
×
2410
               break;
2411
            }
2412
         }
×
2413
      }
×
2414
#endif
2415

2416
#if defined(BOTAN_HAS_ZFEC)
2417
      void bench_zfec(std::chrono::milliseconds msec) {
1✔
2418
         const size_t k = 4;
1✔
2419
         const size_t n = 16;
1✔
2420

2421
         Botan::ZFEC zfec(k, n);
1✔
2422

2423
         const size_t share_size = 256 * 1024;
1✔
2424

2425
         std::vector<uint8_t> input(share_size * k);
1✔
2426
         rng().randomize(input.data(), input.size());
1✔
2427

2428
         std::vector<uint8_t> output(share_size * n);
1✔
2429

2430
         auto enc_fn = [&](size_t share, const uint8_t buf[], size_t len) {
17✔
2431
            std::memcpy(&output[share * share_size], buf, len);
16✔
2432
         };
17✔
2433

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

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

2439
         record_result(enc_timer);
1✔
2440

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

2444
         std::map<size_t, const uint8_t*> shares;
1✔
2445
         for(size_t i = 0; i != n; ++i) {
17✔
2446
            shares[i] = &output[share_size * i];
16✔
2447
         }
2448

2449
         // remove data shares to make decoding maximally expensive:
2450
         while(shares.size() != k) {
13✔
2451
            shares.erase(shares.begin());
12✔
2452
         }
2453

2454
         std::vector<uint8_t> recovered(share_size * k);
1✔
2455

2456
         auto dec_fn = [&](size_t share, const uint8_t buf[], size_t len) {
5✔
2457
            std::memcpy(&recovered[share * share_size], buf, len);
4✔
2458
         };
5✔
2459

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

2462
         record_result(dec_timer);
1✔
2463

2464
         if(recovered != input) {
1✔
2465
            error_output() << "ZFEC recovery failed\n";
×
2466
         }
2467
      }
1✔
2468

2469
#endif
2470

2471
#if defined(BOTAN_HAS_POLY_DBL)
2472
      void bench_poly_dbl(std::chrono::milliseconds msec) {
1✔
2473
         for(size_t sz : {8, 16, 24, 32, 64, 128}) {
7✔
2474
            auto be_timer = make_timer("poly_dbl_be_" + std::to_string(sz));
12✔
2475
            auto le_timer = make_timer("poly_dbl_le_" + std::to_string(sz));
12✔
2476

2477
            std::vector<uint8_t> buf(sz);
6✔
2478
            rng().randomize(buf.data(), sz);
6✔
2479

2480
            be_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n(buf.data(), buf.data(), sz); });
33,353✔
2481
            le_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n_le(buf.data(), buf.data(), sz); });
47,418✔
2482

2483
            record_result(be_timer);
6✔
2484
            record_result(le_timer);
6✔
2485
         }
6✔
2486
      }
1✔
2487
#endif
2488

2489
#if defined(BOTAN_HAS_BCRYPT)
2490

2491
      void bench_bcrypt() {
1✔
2492
         const std::string password = "not a very good password";
1✔
2493

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

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

2499
            record_result(timer);
11✔
2500
         }
11✔
2501
      }
1✔
2502
#endif
2503

2504
#if defined(BOTAN_HAS_PASSHASH9)
2505

2506
      void bench_passhash9() {
1✔
2507
         const std::string password = "not a very good password";
1✔
2508

2509
         for(uint8_t alg = 0; alg <= 4; ++alg) {
6✔
2510
            if(Botan::is_passhash9_alg_supported(alg) == false) {
5✔
2511
               continue;
×
2512
            }
2513

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

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

2519
               record_result(timer);
10✔
2520
            }
10✔
2521
         }
2522
      }
1✔
2523
#endif
2524

2525
#if defined(BOTAN_HAS_SCRYPT)
2526

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

2530
         for(size_t N : {8192, 16384, 32768, 65536}) {
5✔
2531
            for(size_t r : {1, 8, 16}) {
16✔
2532
               for(size_t p : {1}) {
12✔
2533
                  auto pwdhash = pwdhash_fam->from_params(N, r, p);
12✔
2534

2535
                  auto scrypt_timer =
12✔
2536
                     make_timer("scrypt-" + std::to_string(N) + "-" + std::to_string(r) + "-" + std::to_string(p) +
72✔
2537
                                " (" + std::to_string(pwdhash->total_memory_usage() / (1024 * 1024)) + " MiB)");
48✔
2538

2539
                  uint8_t out[64];
12✔
2540
                  uint8_t salt[8];
12✔
2541
                  rng().randomize(salt, sizeof(salt));
12✔
2542

2543
                  while(scrypt_timer->under(msec)) {
24✔
2544
                     scrypt_timer->run([&] {
12✔
2545
                        pwdhash->derive_key(out, sizeof(out), "password", 8, salt, sizeof(salt));
12✔
2546

2547
                        Botan::copy_mem(salt, out, 8);
12✔
2548
                     });
12✔
2549
                  }
2550

2551
                  record_result(scrypt_timer);
12✔
2552

2553
                  if(scrypt_timer->events() == 1) {
12✔
2554
                     break;
2555
                  }
2556
               }
24✔
2557
            }
2558
         }
2559
      }
1✔
2560

2561
#endif
2562

2563
#if defined(BOTAN_HAS_ARGON2)
2564

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

2568
         for(size_t M : {8 * 1024, 64 * 1024, 256 * 1024}) {
4✔
2569
            for(size_t t : {1, 4}) {
9✔
2570
               for(size_t p : {1, 4}) {
18✔
2571
                  auto pwhash = pwhash_fam->from_params(M, t, p);
12✔
2572
                  auto timer = make_timer(pwhash->to_string());
24✔
2573

2574
                  uint8_t out[64];
12✔
2575
                  uint8_t salt[16];
12✔
2576
                  rng().randomize(salt, sizeof(salt));
12✔
2577

2578
                  while(timer->under(msec)) {
24✔
2579
                     timer->run([&] { pwhash->derive_key(out, sizeof(out), "password", 8, salt, sizeof(salt)); });
24✔
2580
                  }
2581

2582
                  record_result(timer);
12✔
2583
               }
24✔
2584
            }
2585
         }
2586
      }
1✔
2587

2588
#endif
2589

2590
#if defined(BOTAN_HAS_BASE64_CODEC)
2591
      void bench_base64(std::chrono::milliseconds msec, const std::vector<size_t>& buf_sizes) {
×
2592
         for(size_t buf_size : buf_sizes) {
×
2593
            std::vector<uint8_t> ibuf(buf_size);
×
2594
            std::vector<uint8_t> rbuf(buf_size);
×
2595
            const size_t olen = Botan::base64_encode_max_output(ibuf.size());
×
2596

2597
            auto enc_timer = make_timer("base64", ibuf.size(), "encode", "", ibuf.size());
×
2598

2599
            auto dec_timer = make_timer("base64", olen, "decode", "", olen);
×
2600

2601
            while(enc_timer->under(msec) && dec_timer->under(msec)) {
×
2602
               rng().randomize(ibuf);
×
2603

2604
               std::string b64 = enc_timer->run([&]() { return Botan::base64_encode(ibuf); });
×
2605

2606
               dec_timer->run([&]() { Botan::base64_decode(rbuf.data(), b64); });
×
2607
               BOTAN_ASSERT(rbuf == ibuf, "Encode/decode round trip ok");
×
2608
            }
×
2609

2610
            record_result(enc_timer);
×
2611
            record_result(dec_timer);
×
2612
         }
×
2613
      }
×
2614
#endif
2615

2616
#if defined(BOTAN_HAS_HEX_CODEC)
2617
      void bench_hex(std::chrono::milliseconds msec, const std::vector<size_t>& buf_sizes) {
×
2618
         for(size_t buf_size : buf_sizes) {
×
2619
            std::vector<uint8_t> ibuf(buf_size);
×
2620
            std::vector<uint8_t> rbuf(buf_size);
×
2621
            const size_t olen = 2 * buf_size;
×
2622

2623
            auto enc_timer = make_timer("hex", ibuf.size(), "encode", "", ibuf.size());
×
2624

2625
            auto dec_timer = make_timer("hex", olen, "decode", "", olen);
×
2626

2627
            while(enc_timer->under(msec) && dec_timer->under(msec)) {
×
2628
               rng().randomize(ibuf);
×
2629

2630
               std::string hex = enc_timer->run([&]() { return Botan::hex_encode(ibuf); });
×
2631

2632
               dec_timer->run([&]() { Botan::hex_decode(rbuf.data(), hex); });
×
2633
               BOTAN_ASSERT(rbuf == ibuf, "Encode/decode round trip ok");
×
2634
            }
×
2635

2636
            record_result(enc_timer);
×
2637
            record_result(dec_timer);
×
2638
         }
×
2639
      }
×
2640
#endif
2641
};
2642

2643
BOTAN_REGISTER_COMMAND("speed", Speed);
34✔
2644

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