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

randombit / botan / 6181893980

14 Sep 2023 06:43AM UTC coverage: 91.721% (+0.004%) from 91.717%
6181893980

push

github

web-flow
Merge pull request #3671 from Rohde-Schwarz/feature/xof-interface

eXtendable Output Functions as first-class citizen

79094 of 86233 relevant lines covered (91.72%)

8695276.3 hits per line

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

93.84
/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/timer.h>
26

27
#if defined(BOTAN_HAS_BIGINT)
28
   #include <botan/bigint.h>
29
   #include <botan/internal/divide.h>
30
#endif
31

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

111
#if defined(BOTAN_HAS_DL_GROUP)
112
   #include <botan/dl_group.h>
113
#endif
114

115
#if defined(BOTAN_HAS_MCELIECE)
116
   #include <botan/mceliece.h>
117
#endif
118

119
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
120
   #include <botan/kyber.h>
121
#endif
122

123
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
124
   #include <botan/dilithium.h>
125
#endif
126

127
#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
128
   #include <botan/sphincsplus.h>
129
#endif
130

131
#if defined(BOTAN_HAS_ECDSA)
132
   #include <botan/ecdsa.h>
133
#endif
134

135
#if defined(BOTAN_HAS_BCRYPT)
136
   #include <botan/bcrypt.h>
137
#endif
138

139
#if defined(BOTAN_HAS_PASSHASH9)
140
   #include <botan/passhash9.h>
141
#endif
142

143
#if defined(BOTAN_HAS_PASSWORD_HASHING)
144
   #include <botan/pwdhash.h>
145
#endif
146

147
#if defined(BOTAN_HAS_ZFEC)
148
   #include <botan/zfec.h>
149
#endif
150

151
namespace Botan_CLI {
152

153
using Botan::Timer;
154

155
namespace {
156

157
class JSON_Output final {
2✔
158
   public:
159
      void add(const Timer& timer) { m_results.push_back(timer); }
2✔
160

161
      std::string print() const {
1✔
162
         std::ostringstream out;
1✔
163

164
         out << "[\n";
1✔
165

166
         for(size_t i = 0; i != m_results.size(); ++i) {
3✔
167
            const Timer& t = m_results[i];
2✔
168

169
            out << "{"
2✔
170
                << "\"algo\": \"" << t.get_name() << "\", "
2✔
171
                << "\"op\": \"" << t.doing() << "\", "
2✔
172
                << "\"events\": " << t.events() << ", ";
2✔
173

174
            if(t.cycles_consumed() > 0) {
4✔
175
               out << "\"cycles\": " << t.cycles_consumed() << ", ";
4✔
176
            }
177

178
            if(t.buf_size() > 0) {
2✔
179
               out << "\"bps\": " << static_cast<uint64_t>(t.events() / (t.value() / 1000000000.0)) << ", ";
2✔
180
               out << "\"buf_size\": " << t.buf_size() << ", ";
2✔
181
            }
182

183
            out << "\"nanos\": " << t.value() << "}";
2✔
184

185
            if(i != m_results.size() - 1) {
2✔
186
               out << ",";
1✔
187
            }
188

189
            out << "\n";
2✔
190
         }
191
         out << "]\n";
1✔
192

193
         return out.str();
2✔
194
      }
1✔
195

196
   private:
197
      std::vector<Timer> m_results;
198
};
199

200
class Summary final {
1✔
201
   public:
202
      Summary() = default;
1✔
203

204
      void add(const Timer& t) {
2✔
205
         if(t.buf_size() == 0) {
2✔
206
            m_ops_entries.push_back(t);
×
207
         } else {
208
            m_bps_entries[std::make_pair(t.doing(), t.get_name())].push_back(t);
2✔
209
         }
210
      }
2✔
211

212
      std::string print() {
1✔
213
         const size_t name_padding = 35;
1✔
214
         const size_t op_name_padding = 16;
1✔
215
         const size_t op_padding = 16;
1✔
216

217
         std::ostringstream result_ss;
1✔
218
         result_ss << std::fixed;
1✔
219

220
         if(!m_bps_entries.empty()) {
1✔
221
            result_ss << "\n";
1✔
222

223
            // add table header
224
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
1✔
225
                      << "operation";
1✔
226

227
            for(const Timer& t : m_bps_entries.begin()->second) {
2✔
228
               result_ss << std::setw(op_padding) << std::right << (std::to_string(t.buf_size()) + " bytes");
2✔
229
            }
230
            result_ss << "\n";
1✔
231

232
            // add table entries
233
            for(const auto& entry : m_bps_entries) {
3✔
234
               if(entry.second.empty()) {
2✔
235
                  continue;
×
236
               }
237

238
               result_ss << std::setw(name_padding) << std::left << (entry.first.second) << std::setw(op_name_padding)
2✔
239
                         << std::left << (entry.first.first);
2✔
240

241
               for(const Timer& t : entry.second) {
4✔
242
                  if(t.events() == 0) {
2✔
243
                     result_ss << std::setw(op_padding) << std::right << "N/A";
×
244
                  } else {
245
                     result_ss << std::setw(op_padding) << std::right << std::setprecision(2)
2✔
246
                               << (t.bytes_per_second() / 1000.0);
2✔
247
                  }
248
               }
249

250
               result_ss << "\n";
2✔
251
            }
252

253
            result_ss << "\n[results are the number of 1000s bytes processed per second]\n";
1✔
254
         }
255

256
         if(!m_ops_entries.empty()) {
1✔
257
            result_ss << std::setprecision(6) << "\n";
×
258

259
            // sort entries
260
            std::sort(m_ops_entries.begin(), m_ops_entries.end());
×
261

262
            // add table header
263
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
×
264
                      << "operation" << std::setw(op_padding) << std::right << "sec/op" << std::setw(op_padding)
×
265
                      << std::right << "op/sec"
×
266
                      << "\n";
×
267

268
            // add table entries
269
            for(const Timer& entry : m_ops_entries) {
×
270
               result_ss << std::setw(name_padding) << std::left << entry.get_name() << std::setw(op_name_padding)
×
271
                         << std::left << entry.doing() << std::setw(op_padding) << std::right
×
272
                         << entry.seconds_per_event() << std::setw(op_padding) << std::right
×
273
                         << entry.events_per_second() << "\n";
×
274
            }
275
         }
276

277
         return result_ss.str();
2✔
278
      }
1✔
279

280
   private:
281
      std::map<std::pair<std::string, std::string>, std::vector<Timer>> m_bps_entries;
282
      std::vector<Timer> m_ops_entries;
283
};
284

285
std::vector<size_t> unique_buffer_sizes(const std::string& cmdline_arg) {
34✔
286
   const size_t MAX_BUF_SIZE = 64 * 1024 * 1024;
34✔
287

288
   std::set<size_t> buf;
34✔
289
   for(const std::string& size_str : Command::split_on(cmdline_arg, ',')) {
66✔
290
      size_t x = 0;
35✔
291
      try {
35✔
292
         size_t converted = 0;
35✔
293
         x = static_cast<size_t>(std::stoul(size_str, &converted, 0));
35✔
294

295
         if(converted != size_str.size()) {
34✔
296
            throw CLI_Usage_Error("Invalid integer");
×
297
         }
298
      } catch(std::exception&) {
1✔
299
         throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size");
3✔
300
      }
1✔
301

302
      if(x == 0) {
34✔
303
         throw CLI_Usage_Error("Cannot have a zero-sized buffer");
3✔
304
      }
305

306
      if(x > MAX_BUF_SIZE) {
33✔
307
         throw CLI_Usage_Error("Specified buffer size is too large");
3✔
308
      }
309

310
      buf.insert(x);
32✔
311
   }
34✔
312

313
   return std::vector<size_t>(buf.begin(), buf.end());
34✔
314
}
31✔
315

316
}  // namespace
317

318
class Speed final : public Command {
×
319
   public:
320
      Speed() :
35✔
321
            Command(
322
               "speed --msec=500 --format=default --ecc-groups= --provider= --buf-size=1024 --clear-cpuid= --cpu-clock-speed=0 --cpu-clock-ratio=1.0 *algos") {
70✔
323
      }
35✔
324

325
      static std::vector<std::string> default_benchmark_list() {
×
326
         /*
327
         This is not intended to be exhaustive: it just hits the high
328
         points of the most interesting or widely used algorithms.
329
         */
330
         // clang-format off
331
         return {
×
332
            /* Block ciphers */
333
            "AES-128",
334
            "AES-192",
335
            "AES-256",
336
            "ARIA-128",
337
            "ARIA-192",
338
            "ARIA-256",
339
            "Blowfish",
340
            "CAST-128",
341
            "Camellia-128",
342
            "Camellia-192",
343
            "Camellia-256",
344
            "DES",
345
            "TripleDES",
346
            "GOST-28147-89",
347
            "IDEA",
348
            "Noekeon",
349
            "SHACAL2",
350
            "SM4",
351
            "Serpent",
352
            "Threefish-512",
353
            "Twofish",
354

355
            /* Cipher modes */
356
            "AES-128/CBC",
357
            "AES-128/CTR-BE",
358
            "AES-128/EAX",
359
            "AES-128/OCB",
360
            "AES-128/GCM",
361
            "AES-128/XTS",
362
            "AES-128/SIV",
363

364
            "Serpent/CBC",
365
            "Serpent/CTR-BE",
366
            "Serpent/EAX",
367
            "Serpent/OCB",
368
            "Serpent/GCM",
369
            "Serpent/XTS",
370
            "Serpent/SIV",
371

372
            "ChaCha20Poly1305",
373

374
            /* Stream ciphers */
375
            "RC4",
376
            "Salsa20",
377
            "ChaCha20",
378

379
            /* Hashes */
380
            "SHA-1",
381
            "SHA-256",
382
            "SHA-512",
383
            "SHA-3(256)",
384
            "SHA-3(512)",
385
            "RIPEMD-160",
386
            "Skein-512",
387
            "Blake2b",
388
            "Whirlpool",
389

390
            /* XOFs */
391
            "SHAKE-128",
392
            "SHAKE-256",
393

394
            /* MACs */
395
            "CMAC(AES-128)",
396
            "HMAC(SHA-256)",
397

398
            /* pubkey */
399
            "RSA",
400
            "DH",
401
            "ECDH",
402
            "ECDSA",
403
            "Ed25519",
404
            "Curve25519",
405
            "McEliece",
406
            "Kyber",
407
            "SPHINCS+"
408
         };
×
409
         // clang-format on
410
      }
411

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

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

416
      void go() override {
34✔
417
         std::chrono::milliseconds msec(get_arg_sz("msec"));
34✔
418
         const std::string provider = get_arg("provider");
34✔
419
         std::vector<std::string> ecc_groups = Command::split_on(get_arg("ecc-groups"), ',');
71✔
420
         const std::string format = get_arg("format");
34✔
421
         const std::string clock_ratio = get_arg("cpu-clock-ratio");
37✔
422
         m_clock_speed = get_arg_sz("cpu-clock-speed");
34✔
423

424
         m_clock_cycle_ratio = std::strtod(clock_ratio.c_str(), nullptr);
34✔
425

426
         /*
427
         * This argument is intended to be the ratio between the cycle counter
428
         * and the actual machine cycles. It is extremely unlikely that there is
429
         * any machine where the cycle counter increments faster than the actual
430
         * clock.
431
         */
432
         if(m_clock_cycle_ratio < 0.0 || m_clock_cycle_ratio > 1.0) {
34✔
433
            throw CLI_Usage_Error("Unlikely CPU clock ratio of " + clock_ratio);
×
434
         }
435

436
         m_clock_cycle_ratio = 1.0 / m_clock_cycle_ratio;
34✔
437

438
         if(m_clock_speed != 0 && Botan::OS::get_cpu_cycle_counter() != 0) {
34✔
439
            error_output() << "The --cpu-clock-speed option is only intended to be used on "
×
440
                              "platforms without access to a cycle counter.\n"
441
                              "Expected incorrect results\n\n";
×
442
         }
443

444
         if(format == "table") {
34✔
445
            m_summary = std::make_unique<Summary>();
1✔
446
         } else if(format == "json") {
33✔
447
            m_json = std::make_unique<JSON_Output>();
1✔
448
         } else if(format != "default") {
32✔
449
            throw CLI_Usage_Error("Unknown --format type '" + format + "'");
×
450
         }
451

452
#if defined(BOTAN_HAS_ECC_GROUP)
453
         if(ecc_groups.empty()) {
34✔
454
            ecc_groups = {"secp256r1", "brainpool256r1", "secp384r1", "brainpool384r1", "secp521r1", "brainpool512r1"};
272✔
455
         } else if(ecc_groups.size() == 1 && ecc_groups[0] == "all") {
×
456
            auto all = Botan::EC_Group::known_named_groups();
×
457
            ecc_groups.assign(all.begin(), all.end());
×
458
         }
×
459
#endif
460

461
         std::vector<std::string> algos = get_arg_list("algos");
37✔
462

463
         const std::vector<size_t> buf_sizes = unique_buffer_sizes(get_arg("buf-size"));
68✔
464

465
         for(const std::string& cpuid_to_clear : Command::split_on(get_arg("clear-cpuid"), ',')) {
32✔
466
            auto bits = Botan::CPUID::bit_from_string(cpuid_to_clear);
1✔
467
            if(bits.empty()) {
1✔
468
               error_output() << "Warning don't know CPUID flag '" << cpuid_to_clear << "'\n";
1✔
469
            }
470

471
            for(auto bit : bits) {
1✔
472
               Botan::CPUID::clear_cpuid_bit(bit);
×
473
            }
474
         }
32✔
475

476
         if(verbose() || m_summary) {
31✔
477
            output() << Botan::version_string() << "\n"
2✔
478
                     << "CPUID: " << Botan::CPUID::to_string() << "\n\n";
3✔
479
         }
480

481
         const bool using_defaults = (algos.empty());
31✔
482
         if(using_defaults) {
31✔
483
            algos = default_benchmark_list();
×
484
         }
485

486
         for(const auto& algo : algos) {
82✔
487
            using namespace std::placeholders;
51✔
488

489
            if(false) {
51✔
490
               // Since everything might be disabled, need a block to else if from
491
            }
492
#if defined(BOTAN_HAS_HASH)
493
            else if(!Botan::HashFunction::providers(algo).empty()) {
51✔
494
               bench_providers_of<Botan::HashFunction>(
1✔
495
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_hash, this, _1, _2, _3, _4));
2✔
496
            }
497
#endif
498
#if defined(BOTAN_HAS_XOF)
499
            else if(!Botan::XOF::providers(algo).empty()) {
50✔
500
               bench_providers_of<Botan::XOF>(
×
501
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_xof, this, _1, _2, _3, _4));
×
502
            }
503
#endif
504
#if defined(BOTAN_HAS_BLOCK_CIPHER)
505
            else if(!Botan::BlockCipher::providers(algo).empty()) {
50✔
506
               bench_providers_of<Botan::BlockCipher>(
4✔
507
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_block_cipher, this, _1, _2, _3, _4));
8✔
508
            }
509
#endif
510
#if defined(BOTAN_HAS_STREAM_CIPHER)
511
            else if(!Botan::StreamCipher::providers(algo).empty()) {
46✔
512
               bench_providers_of<Botan::StreamCipher>(
1✔
513
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_stream_cipher, this, _1, _2, _3, _4));
2✔
514
            }
515
#endif
516
#if defined(BOTAN_HAS_CIPHER_MODES)
517
            else if(auto enc = Botan::Cipher_Mode::create(algo, Botan::Cipher_Dir::Encryption, provider)) {
45✔
518
               auto dec = Botan::Cipher_Mode::create_or_throw(algo, Botan::Cipher_Dir::Decryption, provider);
1✔
519
               bench_cipher_mode(*enc, *dec, msec, buf_sizes);
1✔
520
            }
1✔
521
#endif
522
#if defined(BOTAN_HAS_MAC)
523
            else if(!Botan::MessageAuthenticationCode::providers(algo).empty()) {
44✔
524
               bench_providers_of<Botan::MessageAuthenticationCode>(
1✔
525
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_mac, this, _1, _2, _3, _4));
2✔
526
            }
527
#endif
528
#if defined(BOTAN_HAS_RSA)
529
            else if(algo == "RSA") {
43✔
530
               bench_rsa(provider, msec);
1✔
531
            } else if(algo == "RSA_keygen") {
42✔
532
               bench_rsa_keygen(provider, msec);
1✔
533
            }
534
#endif
535
#if defined(BOTAN_HAS_ECDSA)
536
            else if(algo == "ECDSA") {
41✔
537
               bench_ecdsa(ecc_groups, provider, msec);
1✔
538
            } else if(algo == "ecdsa_recovery") {
40✔
539
               bench_ecdsa_recovery(ecc_groups, provider, msec);
1✔
540
            }
541
#endif
542
#if defined(BOTAN_HAS_SM2)
543
            else if(algo == "SM2") {
39✔
544
               bench_sm2(ecc_groups, provider, msec);
1✔
545
            }
546
#endif
547
#if defined(BOTAN_HAS_ECKCDSA)
548
            else if(algo == "ECKCDSA") {
38✔
549
               bench_eckcdsa(ecc_groups, provider, msec);
1✔
550
            }
551
#endif
552
#if defined(BOTAN_HAS_GOST_34_10_2001)
553
            else if(algo == "GOST-34.10") {
37✔
554
               bench_gost_3410(provider, msec);
1✔
555
            }
556
#endif
557
#if defined(BOTAN_HAS_ECGDSA)
558
            else if(algo == "ECGDSA") {
36✔
559
               bench_ecgdsa(ecc_groups, provider, msec);
1✔
560
            }
561
#endif
562
#if defined(BOTAN_HAS_ED25519)
563
            else if(algo == "Ed25519") {
35✔
564
               bench_ed25519(provider, msec);
1✔
565
            }
566
#endif
567
#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
568
            else if(algo == "DH") {
34✔
569
               bench_dh(provider, msec);
1✔
570
            }
571
#endif
572
#if defined(BOTAN_HAS_DSA)
573
            else if(algo == "DSA") {
33✔
574
               bench_dsa(provider, msec);
1✔
575
            }
576
#endif
577
#if defined(BOTAN_HAS_ELGAMAL)
578
            else if(algo == "ElGamal") {
32✔
579
               bench_elgamal(provider, msec);
1✔
580
            }
581
#endif
582
#if defined(BOTAN_HAS_ECDH)
583
            else if(algo == "ECDH") {
31✔
584
               bench_ecdh(ecc_groups, provider, msec);
1✔
585
            }
586
#endif
587
#if defined(BOTAN_HAS_CURVE_25519)
588
            else if(algo == "Curve25519") {
30✔
589
               bench_curve25519(provider, msec);
1✔
590
            }
591
#endif
592
#if defined(BOTAN_HAS_MCELIECE)
593
            else if(algo == "McEliece") {
29✔
594
               bench_mceliece(provider, msec);
1✔
595
            }
596
#endif
597
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
598
            else if(algo == "Kyber") {
28✔
599
               bench_kyber(provider, msec);
1✔
600
            }
601
#endif
602
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
603
            else if(algo == "Dilithium") {
27✔
604
               bench_dilithium(provider, msec);
1✔
605
            }
606
#endif
607
#if defined(BOTAN_HAS_XMSS_RFC8391)
608
            else if(algo == "XMSS") {
26✔
609
               bench_xmss(provider, msec);
1✔
610
            }
611
#endif
612
#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
613
            else if(algo == "SPHINCS+") {
25✔
614
               bench_sphincs_plus(provider, msec);
1✔
615
            }
616
#endif
617
#if defined(BOTAN_HAS_SCRYPT)
618
            else if(algo == "scrypt") {
24✔
619
               bench_scrypt(provider, msec);
1✔
620
            }
621
#endif
622
#if defined(BOTAN_HAS_ARGON2)
623
            else if(algo == "argon2") {
23✔
624
               bench_argon2(provider, msec);
1✔
625
            }
626
#endif
627
#if defined(BOTAN_HAS_BCRYPT)
628
            else if(algo == "bcrypt") {
22✔
629
               bench_bcrypt();
1✔
630
            }
631
#endif
632
#if defined(BOTAN_HAS_PASSHASH9)
633
            else if(algo == "passhash9") {
21✔
634
               bench_passhash9();
1✔
635
            }
636
#endif
637
#if defined(BOTAN_HAS_ZFEC)
638
            else if(algo == "zfec") {
20✔
639
               bench_zfec(msec);
1✔
640
            }
641
#endif
642
#if defined(BOTAN_HAS_POLY_DBL)
643
            else if(algo == "poly_dbl") {
19✔
644
               bench_poly_dbl(msec);
1✔
645
            }
646
#endif
647

648
#if defined(BOTAN_HAS_DL_GROUP)
649
            else if(algo == "modexp") {
18✔
650
               bench_modexp(msec);
1✔
651
            }
652
#endif
653

654
#if defined(BOTAN_HAS_BIGINT)
655
            else if(algo == "mp_mul") {
17✔
656
               bench_mp_mul(msec);
1✔
657
            } else if(algo == "mp_div") {
16✔
658
               bench_mp_div(msec);
1✔
659
            } else if(algo == "mp_div10") {
15✔
660
               bench_mp_div10(msec);
1✔
661
            }
662
#endif
663

664
#if defined(BOTAN_HAS_NUMBERTHEORY)
665
            else if(algo == "primality_test") {
14✔
666
               bench_primality_tests(msec);
1✔
667
            } else if(algo == "random_prime") {
13✔
668
               bench_random_prime(msec);
1✔
669
            } else if(algo == "inverse_mod") {
12✔
670
               bench_inverse_mod(msec);
1✔
671
            } else if(algo == "bn_redc") {
11✔
672
               bench_bn_redc(msec);
1✔
673
            } else if(algo == "nistp_redc") {
10✔
674
               bench_nistp_redc(msec);
1✔
675
            }
676
#endif
677

678
#if defined(BOTAN_HAS_FPE_FE1)
679
            else if(algo == "fpe_fe1") {
9✔
680
               bench_fpe_fe1(msec);
1✔
681
            }
682
#endif
683

684
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
685
            else if(algo == "rfc3394") {
8✔
686
               bench_rfc3394(msec);
1✔
687
            }
688
#endif
689

690
#if defined(BOTAN_HAS_ECC_GROUP)
691
            else if(algo == "ecc_mult") {
7✔
692
               bench_ecc_mult(ecc_groups, msec);
1✔
693
            } else if(algo == "ecc_ops") {
6✔
694
               bench_ecc_ops(ecc_groups, msec);
1✔
695
            } else if(algo == "ecc_init") {
5✔
696
               bench_ecc_init(ecc_groups, msec);
1✔
697
            } else if(algo == "os2ecp") {
4✔
698
               bench_os2ecp(ecc_groups, msec);
1✔
699
            }
700
#endif
701
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
702
            else if(algo == "ec_h2c") {
3✔
703
               bench_ec_h2c(msec);
1✔
704
            }
705
#endif
706
            else if(algo == "RNG") {
2✔
707
#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
708
               Botan::AutoSeeded_RNG auto_rng;
1✔
709
               bench_rng(auto_rng, "AutoSeeded_RNG (with reseed)", msec, buf_sizes);
1✔
710
#endif
711

712
#if defined(BOTAN_HAS_SYSTEM_RNG)
713
               bench_rng(Botan::system_rng(), "System_RNG", msec, buf_sizes);
1✔
714
#endif
715

716
#if defined(BOTAN_HAS_PROCESSOR_RNG)
717
               if(Botan::Processor_RNG::available()) {
1✔
718
                  Botan::Processor_RNG hwrng;
1✔
719
                  bench_rng(hwrng, "Processor_RNG", msec, buf_sizes);
2✔
720
               }
1✔
721
#endif
722

723
#if defined(BOTAN_HAS_HMAC_DRBG)
724
               for(std::string hash : {"SHA-256", "SHA-384", "SHA-512"}) {
4✔
725
                  Botan::HMAC_DRBG hmac_drbg(hash);
3✔
726
                  bench_rng(hmac_drbg, hmac_drbg.name(), msec, buf_sizes);
3✔
727
               }
3✔
728
#endif
729

730
#if defined(BOTAN_HAS_CHACHA_RNG)
731
               // Provide a dummy seed
732
               Botan::ChaCha_RNG chacha_rng(Botan::secure_vector<uint8_t>(32));
1✔
733
               bench_rng(chacha_rng, "ChaCha_RNG", msec, buf_sizes);
1✔
734
#endif
735

736
            } else if(algo == "entropy") {
2✔
737
               bench_entropy_sources(msec);
1✔
738
            } else {
739
               if(verbose() || !using_defaults) {
×
740
                  error_output() << "Unknown algorithm '" << algo << "'\n";
×
741
               }
742
            }
45✔
743
         }
744

745
         if(m_json) {
31✔
746
            output() << m_json->print();
3✔
747
         }
748
         if(m_summary) {
31✔
749
            output() << m_summary->print() << "\n";
3✔
750
         }
751

752
         if(verbose() && m_clock_speed == 0 && m_cycles_consumed > 0 && m_ns_taken > 0) {
31✔
753
            const double seconds = static_cast<double>(m_ns_taken) / 1000000000;
×
754
            const double Hz = static_cast<double>(m_cycles_consumed) / seconds;
×
755
            const double MHz = Hz / 1000000;
×
756
            output() << "\nEstimated clock speed " << MHz << " MHz\n";
×
757
         }
758
      }
40✔
759

760
   private:
761
      size_t m_clock_speed = 0;
762
      double m_clock_cycle_ratio = 0.0;
763
      uint64_t m_cycles_consumed = 0;
764
      uint64_t m_ns_taken = 0;
765
      std::unique_ptr<Summary> m_summary;
766
      std::unique_ptr<JSON_Output> m_json;
767

768
      void record_result(const std::unique_ptr<Timer>& t) {
470✔
769
         m_ns_taken += t->value();
470✔
770
         m_cycles_consumed += t->cycles_consumed();
470✔
771
         if(m_json) {
470✔
772
            m_json->add(*t);
2✔
773
         } else {
774
            output() << t->to_string() << std::flush;
468✔
775
            if(m_summary) {
468✔
776
               m_summary->add(*t);
2✔
777
            }
778
         }
779
      }
470✔
780

781
      template <typename T>
782
      using bench_fn = std::function<void(T&, std::string, std::chrono::milliseconds, const std::vector<size_t>&)>;
783

784
      template <typename T>
785
      void bench_providers_of(const std::string& algo,
7✔
786
                              const std::string& provider, /* user request, if any */
787
                              const std::chrono::milliseconds runtime,
788
                              const std::vector<size_t>& buf_sizes,
789
                              bench_fn<T> bench_one) {
790
         for(const auto& prov : T::providers(algo)) {
14✔
791
            if(provider.empty() || provider == prov) {
7✔
792
               auto p = T::create(algo, prov);
7✔
793

794
               if(p) {
7✔
795
                  bench_one(*p, prov, runtime, buf_sizes);
14✔
796
               }
797
            }
7✔
798
         }
799
      }
7✔
800

801
      std::unique_ptr<Timer> make_timer(const std::string& name,
474✔
802
                                        uint64_t event_mult = 1,
803
                                        const std::string& what = "",
804
                                        const std::string& provider = "",
805
                                        size_t buf_size = 0) {
806
         return std::make_unique<Timer>(name, provider, what, event_mult, buf_size, m_clock_cycle_ratio, m_clock_speed);
252✔
807
      }
808

809
      std::unique_ptr<Timer> make_timer(const std::string& algo, const std::string& provider, const std::string& what) {
215✔
810
         return make_timer(algo, 1, what, provider, 0);
215✔
811
      }
812

813
#if defined(BOTAN_HAS_BLOCK_CIPHER)
814
      void bench_block_cipher(Botan::BlockCipher& cipher,
4✔
815
                              const std::string& provider,
816
                              std::chrono::milliseconds runtime,
817
                              const std::vector<size_t>& buf_sizes) {
818
         auto ks_timer = make_timer(cipher.name(), provider, "key schedule");
8✔
819

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

823
         const size_t bs = cipher.block_size();
4✔
824
         std::set<size_t> buf_sizes_in_blocks;
4✔
825
         for(size_t buf_size : buf_sizes) {
9✔
826
            if(buf_size % bs == 0) {
5✔
827
               buf_sizes_in_blocks.insert(buf_size);
10✔
828
            } else {
829
               buf_sizes_in_blocks.insert(buf_size + bs - (buf_size % bs));
×
830
            }
831
         }
832

833
         for(size_t buf_size : buf_sizes_in_blocks) {
9✔
834
            std::vector<uint8_t> buffer(buf_size);
5✔
835
            const size_t blocks = buf_size / bs;
5✔
836

837
            auto encrypt_timer = make_timer(cipher.name(), buffer.size(), "encrypt", provider, buf_size);
10✔
838
            auto decrypt_timer = make_timer(cipher.name(), buffer.size(), "decrypt", provider, buf_size);
10✔
839

840
            encrypt_timer->run_until_elapsed(runtime, [&]() { cipher.encrypt_n(&buffer[0], &buffer[0], blocks); });
2,092✔
841
            record_result(encrypt_timer);
5✔
842

843
            decrypt_timer->run_until_elapsed(runtime, [&]() { cipher.decrypt_n(&buffer[0], &buffer[0], blocks); });
2,096✔
844
            record_result(decrypt_timer);
5✔
845
         }
10✔
846
      }
8✔
847
#endif
848

849
#if defined(BOTAN_HAS_STREAM_CIPHER)
850
      void bench_stream_cipher(Botan::StreamCipher& cipher,
1✔
851
                               const std::string& provider,
852
                               const std::chrono::milliseconds runtime,
853
                               const std::vector<size_t>& buf_sizes) {
854
         for(auto buf_size : buf_sizes) {
2✔
855
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
856

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

859
            const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
1✔
860
            cipher.set_key(key);
1✔
861

862
            if(cipher.valid_iv_length(12)) {
1✔
863
               const Botan::InitializationVector iv(rng(), 12);
1✔
864
               cipher.set_iv(iv.begin(), iv.size());
1✔
865
            }
1✔
866

867
            while(encrypt_timer->under(runtime)) {
612✔
868
               encrypt_timer->run([&]() { cipher.encipher(buffer); });
1,222✔
869
            }
870

871
            record_result(encrypt_timer);
1✔
872

873
            if(verbose()) {
1✔
874
               auto ks_timer = make_timer(cipher.name(), buffer.size(), "write_keystream", provider, buf_size);
×
875

876
               while(ks_timer->under(runtime)) {
×
877
                  ks_timer->run([&]() { cipher.write_keystream(buffer.data(), buffer.size()); });
×
878
               }
879
               record_result(ks_timer);
×
880
            }
×
881
         }
2✔
882
      }
1✔
883
#endif
884

885
#if defined(BOTAN_HAS_HASH)
886
      void bench_hash(Botan::HashFunction& hash,
1✔
887
                      const std::string& provider,
888
                      const std::chrono::milliseconds runtime,
889
                      const std::vector<size_t>& buf_sizes) {
890
         std::vector<uint8_t> output(hash.output_length());
1✔
891

892
         for(auto buf_size : buf_sizes) {
2✔
893
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
894

895
            auto timer = make_timer(hash.name(), buffer.size(), "hash", provider, buf_size);
2✔
896
            timer->run_until_elapsed(runtime, [&]() {
1✔
897
               hash.update(buffer);
107✔
898
               hash.final(output.data());
107✔
899
            });
107✔
900
            record_result(timer);
1✔
901
         }
2✔
902
      }
1✔
903
#endif
904

905
#if defined(BOTAN_HAS_XOF)
906
      void bench_xof(Botan::XOF& xof,
×
907
                     const std::string& provider,
908
                     const std::chrono::milliseconds runtime,
909
                     const std::vector<size_t>& buf_sizes) {
910
         for(auto buf_size : buf_sizes) {
×
911
            Botan::secure_vector<uint8_t> in = rng().random_vec(buf_size);
×
912
            Botan::secure_vector<uint8_t> out(buf_size);
×
913

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

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

920
            record_result(in_timer);
×
921
            record_result(out_timer);
×
922
         }
×
923
      }
×
924
#endif
925

926
#if defined(BOTAN_HAS_MAC)
927
      void bench_mac(Botan::MessageAuthenticationCode& mac,
1✔
928
                     const std::string& provider,
929
                     const std::chrono::milliseconds runtime,
930
                     const std::vector<size_t>& buf_sizes) {
931
         std::vector<uint8_t> output(mac.output_length());
1✔
932

933
         for(auto buf_size : buf_sizes) {
2✔
934
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
935

936
            const Botan::SymmetricKey key(rng(), mac.maximum_keylength());
1✔
937
            mac.set_key(key);
1✔
938
            mac.start(nullptr, 0);
1✔
939

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

948
#if defined(BOTAN_HAS_CIPHER_MODES)
949
      void bench_cipher_mode(Botan::Cipher_Mode& enc,
1✔
950
                             Botan::Cipher_Mode& dec,
951
                             const std::chrono::milliseconds runtime,
952
                             const std::vector<size_t>& buf_sizes) {
953
         auto ks_timer = make_timer(enc.name(), enc.provider(), "key schedule");
2✔
954

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

957
         ks_timer->run([&]() { enc.set_key(key); });
2✔
958
         ks_timer->run([&]() { dec.set_key(key); });
2✔
959

960
         record_result(ks_timer);
1✔
961

962
         for(auto buf_size : buf_sizes) {
2✔
963
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
964

965
            auto encrypt_timer = make_timer(enc.name(), buffer.size(), "encrypt", enc.provider(), buf_size);
2✔
966
            auto decrypt_timer = make_timer(dec.name(), buffer.size(), "decrypt", dec.provider(), buf_size);
2✔
967

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

970
            if(buf_size >= enc.minimum_final_size()) {
1✔
971
               while(encrypt_timer->under(runtime) && decrypt_timer->under(runtime)) {
65✔
972
                  // Must run in this order, or AEADs will reject the ciphertext
973
                  encrypt_timer->run([&]() {
64✔
974
                     enc.start(iv);
64✔
975
                     enc.finish(buffer);
64✔
976
                  });
64✔
977

978
                  decrypt_timer->run([&]() {
64✔
979
                     dec.start(iv);
64✔
980
                     dec.finish(buffer);
64✔
981
                  });
64✔
982

983
                  if(!iv.empty()) {
64✔
984
                     iv[iv.size() - 1] += 1;
64✔
985
                  }
986
               }
987
            }
988

989
            record_result(encrypt_timer);
1✔
990
            record_result(decrypt_timer);
1✔
991
         }
2✔
992
      }
1✔
993
#endif
994

995
      void bench_rng(Botan::RandomNumberGenerator& rng,
7✔
996
                     const std::string& rng_name,
997
                     const std::chrono::milliseconds runtime,
998
                     const std::vector<size_t>& buf_sizes) {
999
         for(auto buf_size : buf_sizes) {
14✔
1000
            Botan::secure_vector<uint8_t> buffer(buf_size);
7✔
1001

1002
#if defined(BOTAN_HAS_SYSTEM_RNG)
1003
            rng.reseed_from_rng(Botan::system_rng(), 256);
7✔
1004
#endif
1005

1006
            auto timer = make_timer(rng_name, buffer.size(), "generate", "", buf_size);
7✔
1007
            timer->run_until_elapsed(runtime, [&]() { rng.randomize(buffer.data(), buffer.size()); });
762✔
1008
            record_result(timer);
7✔
1009
         }
14✔
1010
      }
7✔
1011

1012
      void bench_entropy_sources(const std::chrono::milliseconds /*unused*/) {
1✔
1013
         Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources();
1✔
1014

1015
         for(auto src : srcs.enabled_sources()) {
5✔
1016
            size_t entropy_bits = 0;
4✔
1017
            Botan_Tests::SeedCapturing_RNG rng;
4✔
1018

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

1022
            size_t compressed_size = 0;
4✔
1023

1024
#if defined(BOTAN_HAS_ZLIB)
1025
            auto comp = Botan::Compression_Algorithm::create("zlib");
4✔
1026

1027
            if(comp) {
4✔
1028
               Botan::secure_vector<uint8_t> compressed;
4✔
1029
               compressed.assign(rng.seed_material().begin(), rng.seed_material().end());
4✔
1030
               comp->start(9);
4✔
1031
               comp->finish(compressed);
4✔
1032

1033
               compressed_size = compressed.size();
4✔
1034
            }
4✔
1035
#endif
1036

1037
            std::ostringstream msg;
4✔
1038

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

1042
            if(compressed_size > 0) {
4✔
1043
               msg << " output compressed to " << compressed_size << " bytes";
4✔
1044
            }
1045

1046
            msg << " total samples " << rng.samples() << "\n";
4✔
1047

1048
            timer->set_custom_msg(msg.str());
8✔
1049

1050
            record_result(timer);
4✔
1051
         }
13✔
1052
      }
1✔
1053

1054
#if defined(BOTAN_HAS_ECC_GROUP)
1055
      void bench_ecc_ops(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1056
         for(const std::string& group_name : groups) {
7✔
1057
            const Botan::EC_Group ec_group(group_name);
6✔
1058

1059
            auto add_timer = make_timer(group_name + " add");
15✔
1060
            auto addf_timer = make_timer(group_name + " addf");
15✔
1061
            auto dbl_timer = make_timer(group_name + " dbl");
15✔
1062

1063
            const Botan::EC_Point& base_point = ec_group.get_base_point();
6✔
1064

1065
            // create a non-affine point
1066
            const auto random_k = Botan::BigInt::from_u64(0x4E6F537465707E);
6✔
1067
            Botan::EC_Point non_affine_pt = ec_group.get_base_point() * random_k;
6✔
1068
            Botan::EC_Point pt = ec_group.get_base_point();
6✔
1069

1070
            std::vector<Botan::BigInt> ws(Botan::EC_Point::WORKSPACE_SIZE);
6✔
1071

1072
            while(add_timer->under(runtime) && addf_timer->under(runtime) && dbl_timer->under(runtime)) {
580✔
1073
               dbl_timer->run([&]() { pt.mult2(ws); });
1,148✔
1074
               add_timer->run([&]() { pt.add(non_affine_pt, ws); });
1,148✔
1075
               addf_timer->run([&]() { pt.add_affine(base_point, ws); });
1,148✔
1076
            }
1077

1078
            record_result(dbl_timer);
6✔
1079
            record_result(add_timer);
6✔
1080
            record_result(addf_timer);
6✔
1081
         }
12✔
1082
      }
1✔
1083

1084
      void bench_ecc_init(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1085
         for(std::string group_name : groups) {
7✔
1086
            auto timer = make_timer(group_name + " initialization");
18✔
1087

1088
            while(timer->under(runtime)) {
12✔
1089
               Botan::EC_Group::clear_registered_curve_data();
6✔
1090
               timer->run([&]() { Botan::EC_Group group(group_name); });
12✔
1091
            }
1092

1093
            record_result(timer);
6✔
1094
         }
6✔
1095
      }
1✔
1096

1097
      void bench_ecc_mult(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1098
         for(const std::string& group_name : groups) {
7✔
1099
            const Botan::EC_Group ec_group(group_name);
6✔
1100

1101
            auto mult_timer = make_timer(group_name + " Montgomery ladder");
18✔
1102
            auto blinded_mult_timer = make_timer(group_name + " blinded comb");
18✔
1103
            auto blinded_var_mult_timer = make_timer(group_name + " blinded window");
18✔
1104

1105
            const Botan::EC_Point& base_point = ec_group.get_base_point();
6✔
1106

1107
            std::vector<Botan::BigInt> ws;
6✔
1108

1109
            while(mult_timer->under(runtime) && blinded_mult_timer->under(runtime) &&
12✔
1110
                  blinded_var_mult_timer->under(runtime)) {
6✔
1111
               const Botan::BigInt scalar(rng(), ec_group.get_p_bits());
6✔
1112

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

1115
               const Botan::EC_Point r2 =
6✔
1116
                  blinded_mult_timer->run([&]() { return ec_group.blinded_base_point_multiply(scalar, rng(), ws); });
12✔
1117

1118
               const Botan::EC_Point r3 = blinded_var_mult_timer->run(
6✔
1119
                  [&]() { return ec_group.blinded_var_point_multiply(base_point, scalar, rng(), ws); });
12✔
1120

1121
               BOTAN_ASSERT_EQUAL(r1, r2, "Same point computed by Montgomery and comb");
6✔
1122
               BOTAN_ASSERT_EQUAL(r1, r3, "Same point computed by Montgomery and window");
6✔
1123
            }
12✔
1124

1125
            record_result(mult_timer);
6✔
1126
            record_result(blinded_mult_timer);
6✔
1127
            record_result(blinded_var_mult_timer);
6✔
1128
         }
6✔
1129
      }
1✔
1130

1131
      void bench_os2ecp(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime) {
1✔
1132
         for(const std::string& group_name : groups) {
7✔
1133
            auto uncmp_timer = make_timer("OS2ECP uncompressed " + group_name);
18✔
1134
            auto cmp_timer = make_timer("OS2ECP compressed " + group_name);
18✔
1135

1136
            const Botan::EC_Group ec_group(group_name);
6✔
1137

1138
            while(uncmp_timer->under(runtime) && cmp_timer->under(runtime)) {
12✔
1139
               const Botan::BigInt k(rng(), 256);
6✔
1140
               const Botan::EC_Point p = ec_group.get_base_point() * k;
6✔
1141
               const std::vector<uint8_t> os_cmp = p.encode(Botan::EC_Point_Format::Compressed);
6✔
1142
               const std::vector<uint8_t> os_uncmp = p.encode(Botan::EC_Point_Format::Uncompressed);
6✔
1143

1144
               uncmp_timer->run([&]() { ec_group.OS2ECP(os_uncmp); });
12✔
1145
               cmp_timer->run([&]() { ec_group.OS2ECP(os_cmp); });
12✔
1146
            }
18✔
1147

1148
            record_result(uncmp_timer);
6✔
1149
            record_result(cmp_timer);
6✔
1150
         }
6✔
1151
      }
1✔
1152

1153
#endif
1154

1155
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
1156
      void bench_ec_h2c(const std::chrono::milliseconds runtime) {
1✔
1157
         for(std::string group_name : {"secp256r1", "secp384r1", "secp521r1"}) {
4✔
1158
            auto h2c_ro_timer = make_timer(group_name + "-RO", "", "hash to curve");
6✔
1159
            auto h2c_nu_timer = make_timer(group_name + "-NU", "", "hash to curve");
6✔
1160

1161
            const Botan::EC_Group group(group_name);
3✔
1162

1163
            while(h2c_ro_timer->under(runtime)) {
6✔
1164
               std::vector<uint8_t> input(32);
3✔
1165

1166
               rng().randomize(input.data(), input.size());
3✔
1167

1168
               const Botan::EC_Point p1 = h2c_ro_timer->run(
3✔
1169
                  [&]() { return group.hash_to_curve("SHA-256", input.data(), input.size(), nullptr, 0, true); });
6✔
1170

1171
               BOTAN_ASSERT_NOMSG(p1.on_the_curve());
3✔
1172

1173
               const Botan::EC_Point p2 = h2c_nu_timer->run(
3✔
1174
                  [&]() { return group.hash_to_curve("SHA-256", input.data(), input.size(), nullptr, 0, false); });
6✔
1175

1176
               BOTAN_ASSERT_NOMSG(p2.on_the_curve());
3✔
1177
            }
6✔
1178

1179
            record_result(h2c_ro_timer);
3✔
1180
            record_result(h2c_nu_timer);
3✔
1181
         }
3✔
1182
      }
1✔
1183
#endif
1184

1185
#if defined(BOTAN_HAS_FPE_FE1)
1186

1187
      void bench_fpe_fe1(const std::chrono::milliseconds runtime) {
1✔
1188
         const auto n = Botan::BigInt::from_u64(1000000000000000);
1✔
1189

1190
         auto enc_timer = make_timer("FPE_FE1 encrypt");
2✔
1191
         auto dec_timer = make_timer("FPE_FE1 decrypt");
2✔
1192

1193
         const Botan::SymmetricKey key(rng(), 32);
1✔
1194
         const std::vector<uint8_t> tweak(8);  // 8 zeros
1✔
1195

1196
         auto x = Botan::BigInt::one();
1✔
1197

1198
         Botan::FPE_FE1 fpe_fe1(n);
1✔
1199
         fpe_fe1.set_key(key);
1✔
1200

1201
         while(enc_timer->under(runtime)) {
3✔
1202
            enc_timer->start();
2✔
1203
            x = fpe_fe1.encrypt(x, tweak.data(), tweak.size());
2✔
1204
            enc_timer->stop();
2✔
1205
         }
1206

1207
         for(size_t i = 0; i != enc_timer->events(); ++i) {
3✔
1208
            dec_timer->start();
2✔
1209
            x = fpe_fe1.decrypt(x, tweak.data(), tweak.size());
2✔
1210
            dec_timer->stop();
2✔
1211
         }
1212

1213
         BOTAN_ASSERT(x == 1, "FPE works");
1✔
1214

1215
         record_result(enc_timer);
1✔
1216
         record_result(dec_timer);
1✔
1217
      }
5✔
1218
#endif
1219

1220
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
1221

1222
      void bench_rfc3394(const std::chrono::milliseconds runtime) {
1✔
1223
         auto wrap_timer = make_timer("RFC3394 AES-256 key wrap");
3✔
1224
         auto unwrap_timer = make_timer("RFC3394 AES-256 key unwrap");
3✔
1225

1226
         const Botan::SymmetricKey kek(rng(), 32);
1✔
1227
         Botan::secure_vector<uint8_t> key(64, 0);
1✔
1228

1229
         while(wrap_timer->under(runtime)) {
35✔
1230
            wrap_timer->start();
34✔
1231
            key = Botan::rfc3394_keywrap(key, kek);
68✔
1232
            wrap_timer->stop();
34✔
1233

1234
            unwrap_timer->start();
34✔
1235
            key = Botan::rfc3394_keyunwrap(key, kek);
68✔
1236
            unwrap_timer->stop();
34✔
1237

1238
            key[0] += 1;
34✔
1239
         }
1240

1241
         record_result(wrap_timer);
1✔
1242
         record_result(unwrap_timer);
1✔
1243
      }
2✔
1244
#endif
1245

1246
#if defined(BOTAN_HAS_BIGINT)
1247

1248
      void bench_mp_mul(const std::chrono::milliseconds runtime) {
1✔
1249
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1250
         for(size_t bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) {
10✔
1251
            auto mul_timer = make_timer("BigInt mul " + std::to_string(bits));
18✔
1252
            auto sqr_timer = make_timer("BigInt sqr " + std::to_string(bits));
18✔
1253

1254
            const Botan::BigInt y(rng(), bits);
9✔
1255
            Botan::secure_vector<Botan::word> ws;
9✔
1256

1257
            while(mul_timer->under(runtime_per_size)) {
3,746✔
1258
               Botan::BigInt x(rng(), bits);
3,737✔
1259

1260
               sqr_timer->start();
3,737✔
1261
               x.square(ws);
3,737✔
1262
               sqr_timer->stop();
3,737✔
1263

1264
               x.mask_bits(bits);
3,737✔
1265

1266
               mul_timer->start();
3,737✔
1267
               x.mul(y, ws);
3,737✔
1268
               mul_timer->stop();
3,737✔
1269
            }
3,737✔
1270

1271
            record_result(mul_timer);
9✔
1272
            record_result(sqr_timer);
9✔
1273
         }
18✔
1274
      }
1✔
1275

1276
      void bench_mp_div(const std::chrono::milliseconds runtime) {
1✔
1277
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1278

1279
         for(size_t n_bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) {
10✔
1280
            const size_t q_bits = n_bits / 2;
9✔
1281
            const std::string bit_descr = std::to_string(n_bits) + "/" + std::to_string(q_bits);
18✔
1282

1283
            auto div_timer = make_timer("BigInt div " + bit_descr);
27✔
1284
            auto ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
27✔
1285

1286
            Botan::BigInt y;
9✔
1287
            Botan::BigInt x;
9✔
1288
            Botan::secure_vector<Botan::word> ws;
9✔
1289

1290
            Botan::BigInt q1, r1, q2, r2;
9✔
1291

1292
            while(ct_div_timer->under(runtime_per_size)) {
42✔
1293
               x.randomize(rng(), n_bits);
33✔
1294
               y.randomize(rng(), q_bits);
33✔
1295

1296
               div_timer->start();
33✔
1297
               Botan::vartime_divide(x, y, q1, r1);
33✔
1298
               div_timer->stop();
33✔
1299

1300
               ct_div_timer->start();
33✔
1301
               Botan::ct_divide(x, y, q2, r2);
33✔
1302
               ct_div_timer->stop();
33✔
1303

1304
               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
33✔
1305
               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
75✔
1306
            }
1307

1308
            record_result(div_timer);
9✔
1309
            record_result(ct_div_timer);
9✔
1310
         }
54✔
1311
      }
1✔
1312

1313
      void bench_mp_div10(const std::chrono::milliseconds runtime) {
1✔
1314
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1315

1316
         for(size_t n_bits : {256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}) {
10✔
1317
            const std::string bit_descr = std::to_string(n_bits) + "/10";
9✔
1318

1319
            auto div_timer = make_timer("BigInt div " + bit_descr);
27✔
1320
            auto ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
27✔
1321

1322
            Botan::BigInt x;
9✔
1323
            Botan::secure_vector<Botan::word> ws;
9✔
1324

1325
            const auto ten = Botan::BigInt::from_word(10);
9✔
1326
            Botan::BigInt q1, r1, q2;
9✔
1327
            Botan::word r2;
1328

1329
            while(ct_div_timer->under(runtime_per_size)) {
219✔
1330
               x.randomize(rng(), n_bits);
210✔
1331

1332
               div_timer->start();
210✔
1333
               Botan::vartime_divide(x, ten, q1, r1);
210✔
1334
               div_timer->stop();
210✔
1335

1336
               ct_div_timer->start();
210✔
1337
               Botan::ct_divide_word(x, 10, q2, r2);
210✔
1338
               ct_div_timer->stop();
210✔
1339

1340
               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
210✔
1341
               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
429✔
1342
            }
1343

1344
            record_result(div_timer);
9✔
1345
            record_result(ct_div_timer);
9✔
1346
         }
45✔
1347
      }
1✔
1348

1349
#endif
1350

1351
#if defined(BOTAN_HAS_DL_GROUP)
1352

1353
      void bench_modexp(const std::chrono::milliseconds runtime) {
1✔
1354
         for(size_t group_bits : {1024, 1536, 2048, 3072, 4096}) {
6✔
1355
            const std::string group_bits_str = std::to_string(group_bits);
5✔
1356
            const Botan::DL_Group group("modp/srp/" + group_bits_str);
5✔
1357

1358
            const size_t e_bits = Botan::dl_exponent_size(group_bits);
5✔
1359
            const size_t f_bits = group_bits - 1;
5✔
1360

1361
            const Botan::BigInt random_e(rng(), e_bits);
5✔
1362
            const Botan::BigInt random_f(rng(), f_bits);
5✔
1363

1364
            auto e_timer = make_timer(group_bits_str + " short exponent");
15✔
1365
            auto f_timer = make_timer(group_bits_str + "  full exponent");
15✔
1366

1367
            while(f_timer->under(runtime)) {
10✔
1368
               e_timer->run([&]() { group.power_g_p(random_e); });
10✔
1369
               f_timer->run([&]() { group.power_g_p(random_f); });
10✔
1370
            }
1371

1372
            record_result(e_timer);
5✔
1373
            record_result(f_timer);
5✔
1374
         }
20✔
1375
      }
1✔
1376
#endif
1377

1378
#if defined(BOTAN_HAS_NUMBERTHEORY)
1379
      void bench_nistp_redc(const std::chrono::milliseconds runtime) {
1✔
1380
         Botan::secure_vector<Botan::word> ws;
1✔
1381

1382
         auto p192_timer = make_timer("P-192 redc");
2✔
1383
         Botan::BigInt r192(rng(), 192 * 2 - 1);
1✔
1384
         while(p192_timer->under(runtime)) {
2,312✔
1385
            Botan::BigInt r = r192;
2,311✔
1386
            p192_timer->run([&]() { Botan::redc_p192(r, ws); });
4,622✔
1387
            r192 += 1;
2,311✔
1388
         }
2,311✔
1389
         record_result(p192_timer);
1✔
1390

1391
         auto p224_timer = make_timer("P-224 redc");
2✔
1392
         Botan::BigInt r224(rng(), 224 * 2 - 1);
1✔
1393
         while(p224_timer->under(runtime)) {
2,215✔
1394
            Botan::BigInt r = r224;
2,214✔
1395
            p224_timer->run([&]() { Botan::redc_p224(r, ws); });
4,428✔
1396
            r224 += 1;
2,214✔
1397
         }
2,214✔
1398
         record_result(p224_timer);
1✔
1399

1400
         auto p256_timer = make_timer("P-256 redc");
2✔
1401
         Botan::BigInt r256(rng(), 256 * 2 - 1);
1✔
1402
         while(p256_timer->under(runtime)) {
2,185✔
1403
            Botan::BigInt r = r256;
2,184✔
1404
            p256_timer->run([&]() { Botan::redc_p256(r, ws); });
4,368✔
1405
            r256 += 1;
2,184✔
1406
         }
2,184✔
1407
         record_result(p256_timer);
1✔
1408

1409
         auto p384_timer = make_timer("P-384 redc");
2✔
1410
         Botan::BigInt r384(rng(), 384 * 2 - 1);
1✔
1411
         while(p384_timer->under(runtime)) {
1,999✔
1412
            Botan::BigInt r = r384;
1,998✔
1413
            p384_timer->run([&]() { Botan::redc_p384(r384, ws); });
3,996✔
1414
            r384 += 1;
1,998✔
1415
         }
1,998✔
1416
         record_result(p384_timer);
1✔
1417

1418
         auto p521_timer = make_timer("P-521 redc");
2✔
1419
         Botan::BigInt r521(rng(), 521 * 2 - 1);
1✔
1420
         while(p521_timer->under(runtime)) {
1,353✔
1421
            Botan::BigInt r = r521;
1,352✔
1422
            p521_timer->run([&]() { Botan::redc_p521(r521, ws); });
2,704✔
1423
            r521 += 1;
1,352✔
1424
         }
1,352✔
1425
         record_result(p521_timer);
1✔
1426
      }
6✔
1427

1428
      void bench_bn_redc(const std::chrono::milliseconds runtime) {
1✔
1429
         for(size_t bitsize : {512, 1024, 2048, 4096}) {
5✔
1430
            Botan::BigInt p(rng(), bitsize);
4✔
1431

1432
            std::string bit_str = std::to_string(bitsize);
4✔
1433
            auto barrett_timer = make_timer("Barrett-" + bit_str);
8✔
1434
            auto schoolbook_timer = make_timer("Schoolbook-" + bit_str);
8✔
1435

1436
            Botan::Modular_Reducer mod_p(p);
4✔
1437

1438
            while(schoolbook_timer->under(runtime)) {
45✔
1439
               const Botan::BigInt x(rng(), p.bits() * 2 - 2);
41✔
1440

1441
               const Botan::BigInt r1 = barrett_timer->run([&] { return mod_p.reduce(x); });
82✔
1442
               const Botan::BigInt r2 = schoolbook_timer->run([&] { return x % p; });
82✔
1443

1444
               BOTAN_ASSERT(r1 == r2, "Computed different results");
41✔
1445
            }
123✔
1446

1447
            record_result(barrett_timer);
4✔
1448
            record_result(schoolbook_timer);
4✔
1449
         }
8✔
1450
      }
1✔
1451

1452
      void bench_inverse_mod(const std::chrono::milliseconds runtime) {
1✔
1453
         for(size_t bits : {256, 384, 512, 1024, 2048}) {
6✔
1454
            const std::string bit_str = std::to_string(bits);
5✔
1455

1456
            auto timer = make_timer("inverse_mod-" + bit_str);
12✔
1457
            auto gcd_timer = make_timer("gcd-" + bit_str);
10✔
1458

1459
            while(timer->under(runtime) && gcd_timer->under(runtime)) {
11✔
1460
               const Botan::BigInt x(rng(), bits - 1);
6✔
1461
               Botan::BigInt mod(rng(), bits);
6✔
1462

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

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

1467
               if(x_inv == 0) {
6✔
1468
                  BOTAN_ASSERT(g != 1, "Inversion only fails if gcd(x, mod) > 1");
2✔
1469
               } else {
1470
                  BOTAN_ASSERT(g == 1, "Inversion succeeds only if gcd != 1");
4✔
1471
                  const Botan::BigInt check = (x_inv * x) % mod;
4✔
1472
                  BOTAN_ASSERT_EQUAL(check, 1, "Const time inversion correct");
4✔
1473
               }
4✔
1474
            }
23✔
1475

1476
            record_result(timer);
5✔
1477
            record_result(gcd_timer);
5✔
1478
         }
5✔
1479
      }
1✔
1480

1481
      void bench_primality_tests(const std::chrono::milliseconds runtime) {
1✔
1482
         for(size_t bits : {256, 512, 1024}) {
4✔
1483
            auto mr_timer = make_timer("Miller-Rabin-" + std::to_string(bits));
9✔
1484
            auto bpsw_timer = make_timer("Bailie-PSW-" + std::to_string(bits));
6✔
1485
            auto lucas_timer = make_timer("Lucas-" + std::to_string(bits));
6✔
1486

1487
            Botan::BigInt n = Botan::random_prime(rng(), bits);
3✔
1488

1489
            while(lucas_timer->under(runtime)) {
6✔
1490
               Botan::Modular_Reducer mod_n(n);
3✔
1491

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

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

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

1498
               n += 2;
3✔
1499
            }
3✔
1500

1501
            record_result(mr_timer);
3✔
1502
            record_result(bpsw_timer);
3✔
1503
            record_result(lucas_timer);
3✔
1504
         }
3✔
1505
      }
1✔
1506

1507
      void bench_random_prime(const std::chrono::milliseconds runtime) {
1✔
1508
         const auto coprime = Botan::BigInt::from_word(0x10001);
1✔
1509

1510
         for(size_t bits : {256, 384, 512, 768, 1024, 1536}) {
7✔
1511
            auto genprime_timer = make_timer("random_prime " + std::to_string(bits));
18✔
1512
            auto gensafe_timer = make_timer("random_safe_prime " + std::to_string(bits));
18✔
1513
            auto is_prime_timer = make_timer("is_prime " + std::to_string(bits));
12✔
1514

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

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

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

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

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

1532
               // Now test p+2, p+4, ... which may or may not be prime
1533
               for(size_t i = 2; i <= 64; i += 2) {
198✔
1534
                  is_prime_timer->run([&]() { Botan::is_prime(p + i, rng(), 64, true); });
576✔
1535
               }
1536
            }
12✔
1537

1538
            record_result(genprime_timer);
6✔
1539
            record_result(gensafe_timer);
6✔
1540
            record_result(is_prime_timer);
6✔
1541
         }
6✔
1542
      }
1✔
1543
#endif
1544

1545
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
1546
      void bench_pk_enc(const Botan::Private_Key& key,
4✔
1547
                        const std::string& nm,
1548
                        const std::string& provider,
1549
                        const std::string& padding,
1550
                        std::chrono::milliseconds msec) {
1551
         std::vector<uint8_t> plaintext, ciphertext;
4✔
1552

1553
         Botan::PK_Encryptor_EME enc(key, rng(), padding, provider);
4✔
1554
         Botan::PK_Decryptor_EME dec(key, rng(), padding, provider);
4✔
1555

1556
         auto enc_timer = make_timer(nm + " " + padding, provider, "encrypt");
12✔
1557
         auto dec_timer = make_timer(nm + " " + padding, provider, "decrypt");
12✔
1558

1559
         while(enc_timer->under(msec) || dec_timer->under(msec)) {
12✔
1560
            // Generate a new random ciphertext to decrypt
1561
            if(ciphertext.empty() || enc_timer->under(msec)) {
4✔
1562
               rng().random_vec(plaintext, enc.maximum_input_size());
4✔
1563
               ciphertext = enc_timer->run([&]() { return enc.encrypt(plaintext, rng()); });
12✔
1564
            }
1565

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

1569
               if(!(Botan::unlock(dec_pt) == plaintext))  // sanity check
8✔
1570
               {
1571
                  error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n";
×
1572
               }
1573
            }
4✔
1574
         }
1575

1576
         record_result(enc_timer);
4✔
1577
         record_result(dec_timer);
4✔
1578
      }
12✔
1579

1580
      void bench_pk_ka(const std::string& algo,
14✔
1581
                       const std::string& nm,
1582
                       const std::string& params,
1583
                       const std::string& provider,
1584
                       std::chrono::milliseconds msec) {
1585
         const std::string kdf = "KDF2(SHA-256)";  // arbitrary choice
14✔
1586

1587
         auto keygen_timer = make_timer(nm, provider, "keygen");
28✔
1588

1589
         std::unique_ptr<Botan::Private_Key> key1(
14✔
1590
            keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); }));
28✔
1591
         std::unique_ptr<Botan::Private_Key> key2(
14✔
1592
            keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); }));
28✔
1593

1594
         record_result(keygen_timer);
14✔
1595

1596
         const Botan::PK_Key_Agreement_Key& ka_key1 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key1);
14✔
1597
         const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key2);
14✔
1598

1599
         Botan::PK_Key_Agreement ka1(ka_key1, rng(), kdf, provider);
14✔
1600
         Botan::PK_Key_Agreement ka2(ka_key2, rng(), kdf, provider);
14✔
1601

1602
         const std::vector<uint8_t> ka1_pub = ka_key1.public_value();
14✔
1603
         const std::vector<uint8_t> ka2_pub = ka_key2.public_value();
14✔
1604

1605
         auto ka_timer = make_timer(nm, provider, "key agreements");
28✔
1606

1607
         while(ka_timer->under(msec)) {
31✔
1608
            Botan::SymmetricKey symkey1 = ka_timer->run([&]() { return ka1.derive_key(32, ka2_pub); });
34✔
1609
            Botan::SymmetricKey symkey2 = ka_timer->run([&]() { return ka2.derive_key(32, ka1_pub); });
34✔
1610

1611
            if(symkey1 != symkey2) {
17✔
1612
               error_output() << "Key agreement mismatch in PK bench\n";
×
1613
            }
1614
         }
34✔
1615

1616
         record_result(ka_timer);
14✔
1617
      }
70✔
1618

1619
      void bench_pk_kem(const Botan::Private_Key& key,
11✔
1620
                        const std::string& nm,
1621
                        const std::string& provider,
1622
                        const std::string& kdf,
1623
                        std::chrono::milliseconds msec) {
1624
         Botan::PK_KEM_Decryptor dec(key, rng(), kdf, provider);
11✔
1625
         Botan::PK_KEM_Encryptor enc(key, kdf, provider);
11✔
1626

1627
         auto kem_enc_timer = make_timer(nm, provider, "KEM encrypt");
22✔
1628
         auto kem_dec_timer = make_timer(nm, provider, "KEM decrypt");
22✔
1629

1630
         while(kem_enc_timer->under(msec) && kem_dec_timer->under(msec)) {
33✔
1631
            Botan::secure_vector<uint8_t> salt = rng().random_vec(16);
22✔
1632

1633
            kem_enc_timer->start();
22✔
1634
            const auto kem_result = enc.encrypt(rng(), 64, salt);
22✔
1635
            kem_enc_timer->stop();
22✔
1636

1637
            kem_dec_timer->start();
22✔
1638
            Botan::secure_vector<uint8_t> dec_shared_key = dec.decrypt(kem_result.encapsulated_shared_key(), 64, salt);
22✔
1639
            kem_dec_timer->stop();
22✔
1640

1641
            if(kem_result.shared_key() != dec_shared_key) {
22✔
1642
               error_output() << "KEM mismatch in PK bench\n";
×
1643
            }
1644
         }
44✔
1645

1646
         record_result(kem_enc_timer);
11✔
1647
         record_result(kem_dec_timer);
11✔
1648
      }
11✔
1649

1650
      void bench_pk_sig_ecc(const std::string& algo,
6✔
1651
                            const std::string& emsa,
1652
                            const std::string& provider,
1653
                            const std::vector<std::string>& params,
1654
                            std::chrono::milliseconds msec) {
1655
         for(std::string grp : params) {
32✔
1656
            const std::string nm = grp.empty() ? algo : Botan::fmt("{}-{}", algo, grp);
26✔
1657

1658
            auto keygen_timer = make_timer(nm, provider, "keygen");
52✔
1659

1660
            std::unique_ptr<Botan::Private_Key> key(
26✔
1661
               keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), grp); }));
52✔
1662

1663
            record_result(keygen_timer);
26✔
1664
            bench_pk_sig(*key, nm, provider, emsa, msec);
26✔
1665
         }
48✔
1666
      }
6✔
1667

1668
      size_t bench_pk_sig(const Botan::Private_Key& key,
41✔
1669
                          const std::string& nm,
1670
                          const std::string& provider,
1671
                          const std::string& padding,
1672
                          std::chrono::milliseconds msec) {
1673
         std::vector<uint8_t> message, signature, bad_signature;
41✔
1674

1675
         Botan::PK_Signer sig(key, rng(), padding, Botan::Signature_Format::Standard, provider);
41✔
1676
         Botan::PK_Verifier ver(key, padding, Botan::Signature_Format::Standard, provider);
41✔
1677

1678
         auto sig_timer = make_timer(nm + " " + padding, provider, "sign");
122✔
1679
         auto ver_timer = make_timer(nm + " " + padding, provider, "verify");
122✔
1680

1681
         size_t invalid_sigs = 0;
41✔
1682

1683
         while(ver_timer->under(msec) || sig_timer->under(msec)) {
160✔
1684
            if(signature.empty() || sig_timer->under(msec)) {
78✔
1685
               /*
1686
               Length here is kind of arbitrary, but 48 bytes fits into a single
1687
               hash block so minimizes hashing overhead versus the PK op itself.
1688
               */
1689
               rng().random_vec(message, 48);
52✔
1690

1691
               signature = sig_timer->run([&]() { return sig.sign_message(message, rng()); });
156✔
1692

1693
               bad_signature = signature;
52✔
1694
               bad_signature[rng().next_byte() % bad_signature.size()] ^= rng().next_nonzero_byte();
52✔
1695
            }
1696

1697
            if(ver_timer->under(msec)) {
78✔
1698
               const bool verified = ver_timer->run([&] { return ver.verify_message(message, signature); });
134✔
1699

1700
               if(!verified) {
67✔
1701
                  invalid_sigs += 1;
×
1702
               }
1703

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

1706
               if(verified_bad) {
67✔
1707
                  error_output() << "Bad signature accepted in " << nm << " signature bench\n";
×
1708
               }
1709
            }
1710
         }
1711

1712
         if(invalid_sigs > 0) {
41✔
1713
            error_output() << invalid_sigs << " generated signatures rejected in " << nm << " signature bench\n";
×
1714
         }
1715

1716
         const size_t events = static_cast<size_t>(std::min(sig_timer->events(), ver_timer->events()));
41✔
1717

1718
         record_result(sig_timer);
41✔
1719
         record_result(ver_timer);
41✔
1720

1721
         return events;
82✔
1722
      }
164✔
1723
#endif
1724

1725
#if defined(BOTAN_HAS_RSA)
1726
      void bench_rsa_keygen(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1727
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
1728
            const std::string nm = "RSA-" + std::to_string(keylen);
4✔
1729
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1730

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

1735
               BOTAN_ASSERT(key->check_key(rng(), true), "Key is ok");
4✔
1736
            }
4✔
1737

1738
            record_result(keygen_timer);
4✔
1739
         }
4✔
1740
      }
1✔
1741

1742
      void bench_rsa(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1743
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
1744
            const std::string nm = "RSA-" + std::to_string(keylen);
4✔
1745

1746
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1747

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

1751
            record_result(keygen_timer);
4✔
1752

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

1756
            //bench_pk_sig(*key, nm, provider, "PSSR(SHA-256)", msec);
1757
            //bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
1758
            //bench_pk_enc(*key, nm, provider, "OAEP(SHA-1)", msec);
1759
         }
4✔
1760
      }
1✔
1761
#endif
1762

1763
#if defined(BOTAN_HAS_ECDSA)
1764
      void bench_ecdsa(const std::vector<std::string>& groups,
1✔
1765
                       const std::string& provider,
1766
                       std::chrono::milliseconds msec) {
1767
         return bench_pk_sig_ecc("ECDSA", "SHA-256", provider, groups, msec);
2✔
1768
      }
1769

1770
      void bench_ecdsa_recovery(const std::vector<std::string>& groups,
1✔
1771
                                const std::string& /*unused*/,
1772
                                std::chrono::milliseconds msec) {
1773
         for(const std::string& group_name : groups) {
7✔
1774
            Botan::EC_Group group(group_name);
6✔
1775
            auto recovery_timer = make_timer("ECDSA recovery " + group_name);
18✔
1776

1777
            while(recovery_timer->under(msec)) {
12✔
1778
               Botan::ECDSA_PrivateKey key(rng(), group);
6✔
1779

1780
               std::vector<uint8_t> message(group.get_order_bits() / 8);
6✔
1781
               rng().randomize(message.data(), message.size());
6✔
1782

1783
               Botan::PK_Signer signer(key, rng(), "Raw");
6✔
1784
               signer.update(message);
6✔
1785
               std::vector<uint8_t> signature = signer.signature(rng());
6✔
1786

1787
               Botan::PK_Verifier verifier(key, "Raw", Botan::Signature_Format::Standard, "base");
6✔
1788
               verifier.update(message);
6✔
1789
               BOTAN_ASSERT(verifier.check_signature(signature), "Valid signature");
6✔
1790

1791
               Botan::BigInt r(signature.data(), signature.size() / 2);
6✔
1792
               Botan::BigInt s(signature.data() + signature.size() / 2, signature.size() / 2);
6✔
1793

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

1796
               recovery_timer->run([&]() {
6✔
1797
                  Botan::ECDSA_PublicKey pubkey(group, message, r, s, v);
6✔
1798
                  BOTAN_ASSERT(pubkey.public_point() == key.public_point(), "Recovered public key");
6✔
1799
               });
6✔
1800
            }
24✔
1801

1802
            record_result(recovery_timer);
6✔
1803
         }
6✔
1804
      }
1✔
1805

1806
#endif
1807

1808
#if defined(BOTAN_HAS_ECKCDSA)
1809
      void bench_eckcdsa(const std::vector<std::string>& groups,
1✔
1810
                         const std::string& provider,
1811
                         std::chrono::milliseconds msec) {
1812
         return bench_pk_sig_ecc("ECKCDSA", "SHA-256", provider, groups, msec);
2✔
1813
      }
1814
#endif
1815

1816
#if defined(BOTAN_HAS_GOST_34_10_2001)
1817
      void bench_gost_3410(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1818
         return bench_pk_sig_ecc("GOST-34.10", "GOST-34.11", provider, {"gost_256A"}, msec);
3✔
1819
      }
1820
#endif
1821

1822
#if defined(BOTAN_HAS_SM2)
1823
      void bench_sm2(const std::vector<std::string>& groups,
1✔
1824
                     const std::string& provider,
1825
                     std::chrono::milliseconds msec) {
1826
         return bench_pk_sig_ecc("SM2_Sig", "SM3", provider, groups, msec);
2✔
1827
      }
1828
#endif
1829

1830
#if defined(BOTAN_HAS_ECGDSA)
1831
      void bench_ecgdsa(const std::vector<std::string>& groups,
1✔
1832
                        const std::string& provider,
1833
                        std::chrono::milliseconds msec) {
1834
         return bench_pk_sig_ecc("ECGDSA", "SHA-256", provider, groups, msec);
2✔
1835
      }
1836
#endif
1837

1838
#if defined(BOTAN_HAS_ED25519)
1839
      void bench_ed25519(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1840
         return bench_pk_sig_ecc("Ed25519", "Pure", provider, std::vector<std::string>{""}, msec);
3✔
1841
      }
1842
#endif
1843

1844
#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
1845
      void bench_dh(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1846
         for(size_t bits : {1024, 1536, 2048, 3072, 4096, 6144, 8192}) {
8✔
1847
            bench_pk_ka("DH", "DH-" + std::to_string(bits), "modp/ietf/" + std::to_string(bits), provider, msec);
14✔
1848
         }
1849
      }
1✔
1850
#endif
1851

1852
#if defined(BOTAN_HAS_DSA)
1853
      void bench_dsa(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1854
         for(size_t bits : {1024, 2048, 3072}) {
4✔
1855
            const std::string nm = "DSA-" + std::to_string(bits);
3✔
1856

1857
            const std::string params = (bits == 1024) ? "dsa/jce/1024" : ("dsa/botan/" + std::to_string(bits));
3✔
1858

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

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

1864
            record_result(keygen_timer);
3✔
1865

1866
            bench_pk_sig(*key, nm, provider, "SHA-256", msec);
6✔
1867
         }
3✔
1868
      }
1✔
1869
#endif
1870

1871
#if defined(BOTAN_HAS_ELGAMAL)
1872
      void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1873
         for(size_t keylen : {1024, 2048, 3072, 4096}) {
5✔
1874
            const std::string nm = "ElGamal-" + std::to_string(keylen);
4✔
1875

1876
            const std::string params = "modp/ietf/" + std::to_string(keylen);
4✔
1877

1878
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1879

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

1883
            record_result(keygen_timer);
4✔
1884

1885
            bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
8✔
1886
         }
4✔
1887
      }
1✔
1888
#endif
1889

1890
#if defined(BOTAN_HAS_ECDH)
1891
      void bench_ecdh(const std::vector<std::string>& groups,
1✔
1892
                      const std::string& provider,
1893
                      std::chrono::milliseconds msec) {
1894
         for(const std::string& grp : groups) {
7✔
1895
            bench_pk_ka("ECDH", "ECDH-" + grp, grp, provider, msec);
15✔
1896
         }
1897
      }
1✔
1898
#endif
1899

1900
#if defined(BOTAN_HAS_CURVE_25519)
1901
      void bench_curve25519(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1902
         bench_pk_ka("Curve25519", "Curve25519", "", provider, msec);
2✔
1903
      }
1✔
1904
#endif
1905

1906
#if defined(BOTAN_HAS_MCELIECE)
1907
      void bench_mceliece(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1908
         /*
1909
         SL=80 n=1632 t=33 - 59 KB pubkey 140 KB privkey
1910
         SL=107 n=2480 t=45 - 128 KB pubkey 300 KB privkey
1911
         SL=128 n=2960 t=57 - 195 KB pubkey 459 KB privkey
1912
         SL=147 n=3408 t=67 - 265 KB pubkey 622 KB privkey
1913
         SL=191 n=4624 t=95 - 516 KB pubkey 1234 KB privkey
1914
         SL=256 n=6624 t=115 - 942 KB pubkey 2184 KB privkey
1915
         */
1916

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

1920
         for(auto params : mce_params) {
6✔
1921
            size_t n = params.first;
5✔
1922
            size_t t = params.second;
5✔
1923

1924
            const std::string nm = "McEliece-" + std::to_string(n) + "," + std::to_string(t) +
10✔
1925
                                   " (WF=" + std::to_string(Botan::mceliece_work_factor(n, t)) + ")";
15✔
1926

1927
            auto keygen_timer = make_timer(nm, provider, "keygen");
10✔
1928

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

1932
            record_result(keygen_timer);
5✔
1933
            bench_pk_kem(*key, nm, provider, "KDF2(SHA-256)", msec);
10✔
1934
         }
10✔
1935
      }
1✔
1936
#endif
1937

1938
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
1939
      void bench_kyber(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1940
         const Botan::KyberMode::Mode all_modes[] = {
1✔
1941
            Botan::KyberMode::Kyber512,
1942
            Botan::KyberMode::Kyber512_90s,
1943
            Botan::KyberMode::Kyber768,
1944
            Botan::KyberMode::Kyber768_90s,
1945
            Botan::KyberMode::Kyber1024,
1946
            Botan::KyberMode::Kyber1024_90s,
1947
         };
1948

1949
         for(auto modet : all_modes) {
7✔
1950
            Botan::KyberMode mode(modet);
6✔
1951

1952
   #if !defined(BOTAN_HAS_KYBER)
1953
            if(mode.is_modern())
1954
               continue;
1955
   #endif
1956

1957
   #if !defined(BOTAN_HAS_KYBER_90S)
1958
            if(mode.is_90s())
1959
               continue;
1960
   #endif
1961

1962
            auto keygen_timer = make_timer(mode.to_string(), provider, "keygen");
15✔
1963

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

1966
            record_result(keygen_timer);
6✔
1967

1968
            bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec);
12✔
1969
         }
6✔
1970
      }
1✔
1971
#endif
1972

1973
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
1974
      void bench_dilithium(const std::string& provider, std::chrono::milliseconds msec) {
1✔
1975
         const Botan::DilithiumMode::Mode all_modes[] = {Botan::DilithiumMode::Dilithium4x4,
1✔
1976
                                                         Botan::DilithiumMode::Dilithium4x4_AES,
1977
                                                         Botan::DilithiumMode::Dilithium6x5,
1978
                                                         Botan::DilithiumMode::Dilithium6x5_AES,
1979
                                                         Botan::DilithiumMode::Dilithium8x7,
1980
                                                         Botan::DilithiumMode::Dilithium8x7_AES};
1981

1982
         for(auto modet : all_modes) {
7✔
1983
            Botan::DilithiumMode mode(modet);
6✔
1984

1985
   #if !defined(BOTAN_HAS_DILITHIUM)
1986
            if(mode.is_modern())
1987
               continue;
1988
   #endif
1989

1990
   #if !defined(BOTAN_HAS_DILITHIUM_AES)
1991
            if(mode.is_aes())
1992
               continue;
1993
   #endif
1994

1995
            auto keygen_timer = make_timer(mode.to_string(), provider, "keygen");
18✔
1996

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

1999
            record_result(keygen_timer);
6✔
2000

2001
            bench_pk_sig(key, mode.to_string(), provider, "", msec);
12✔
2002
         }
6✔
2003
      }
1✔
2004
#endif
2005

2006
#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
2007
      void bench_sphincs_plus(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2008
         // Sphincs_Parameter_Set set, Sphincs_Hash_Type hash
2009
         std::vector<std::string> sphincs_params{"SphincsPlus-sha2-128s-r3.1",
1✔
2010
                                                 "SphincsPlus-sha2-128f-r3.1",
2011
                                                 "SphincsPlus-sha2-192s-r3.1",
2012
                                                 "SphincsPlus-sha2-192f-r3.1",
2013
                                                 "SphincsPlus-sha2-256s-r3.1",
2014
                                                 "SphincsPlus-sha2-256f-r3.1",
2015
                                                 "SphincsPlus-shake-128s-r3.1",
2016
                                                 "SphincsPlus-shake-128f-r3.1",
2017
                                                 "SphincsPlus-shake-192s-r3.1",
2018
                                                 "SphincsPlus-shake-192f-r3.1",
2019
                                                 "SphincsPlus-shake-256s-r3.1",
2020
                                                 "SphincsPlus-shake-256f-r3.1"};
13✔
2021

2022
         for(auto params : sphincs_params) {
1✔
2023
            try {
1✔
2024
               auto keygen_timer = make_timer(params, provider, "keygen");
2✔
2025

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

2029
               record_result(keygen_timer);
1✔
2030
               if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
1✔
2031
                  break;
2032
               }
2033
            } catch(Botan::Not_Implemented&) {
1✔
2034
               continue;
×
2035
            }
×
2036
         }
×
2037
      }
1✔
2038
#endif
2039

2040
#if defined(BOTAN_HAS_XMSS_RFC8391)
2041
      void bench_xmss(const std::string& provider, std::chrono::milliseconds msec) {
1✔
2042
         /*
2043
         We only test H10 signatures here since already they are quite slow (a
2044
         few seconds per signature). On a fast machine, H16 signatures take 1-2
2045
         minutes to generate and H20 signatures take 5-10 minutes to generate
2046
         */
2047
         std::vector<std::string> xmss_params{
1✔
2048
            "XMSS-SHA2_10_256",
2049
            "XMSS-SHAKE_10_256",
2050
            "XMSS-SHA2_10_512",
2051
            "XMSS-SHAKE_10_512",
2052
         };
5✔
2053

2054
         for(std::string params : xmss_params) {
1✔
2055
            auto keygen_timer = make_timer(params, provider, "keygen");
2✔
2056

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

2060
            record_result(keygen_timer);
1✔
2061
            if(bench_pk_sig(*key, params, provider, "", msec) == 1) {
1✔
2062
               break;
2063
            }
2064
         }
2✔
2065
      }
1✔
2066
#endif
2067

2068
#if defined(BOTAN_HAS_ZFEC)
2069
      void bench_zfec(std::chrono::milliseconds msec) {
1✔
2070
         const size_t k = 4;
1✔
2071
         const size_t n = 16;
1✔
2072

2073
         Botan::ZFEC zfec(k, n);
1✔
2074

2075
         const size_t share_size = 256 * 1024;
1✔
2076

2077
         std::vector<uint8_t> input(share_size * k);
1✔
2078
         rng().randomize(input.data(), input.size());
1✔
2079

2080
         std::vector<uint8_t> output(share_size * n);
1✔
2081

2082
         auto enc_fn = [&](size_t share, const uint8_t buf[], size_t len) {
17✔
2083
            std::memcpy(&output[share * share_size], buf, len);
16✔
2084
         };
1✔
2085

2086
         auto enc_timer =
1✔
2087
            make_timer("zfec " + std::to_string(k) + "/" + std::to_string(n), input.size(), "encode", "", input.size());
2✔
2088

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

2091
         record_result(enc_timer);
1✔
2092

2093
         auto dec_timer =
1✔
2094
            make_timer("zfec " + std::to_string(k) + "/" + std::to_string(n), input.size(), "decode", "", input.size());
2✔
2095

2096
         std::map<size_t, const uint8_t*> shares;
1✔
2097
         for(size_t i = 0; i != n; ++i) {
17✔
2098
            shares[i] = &output[share_size * i];
16✔
2099
         }
2100

2101
         // remove data shares to make decoding maximally expensive:
2102
         while(shares.size() != k) {
13✔
2103
            shares.erase(shares.begin());
12✔
2104
         }
2105

2106
         std::vector<uint8_t> recovered(share_size * k);
1✔
2107

2108
         auto dec_fn = [&](size_t share, const uint8_t buf[], size_t len) {
5✔
2109
            std::memcpy(&recovered[share * share_size], buf, len);
4✔
2110
         };
1✔
2111

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

2114
         record_result(dec_timer);
1✔
2115

2116
         if(recovered != input) {
1✔
2117
            error_output() << "ZFEC recovery failed\n";
×
2118
         }
2119
      }
4✔
2120

2121
#endif
2122

2123
#if defined(BOTAN_HAS_POLY_DBL)
2124
      void bench_poly_dbl(std::chrono::milliseconds msec) {
1✔
2125
         for(size_t sz : {8, 16, 24, 32, 64, 128}) {
7✔
2126
            auto be_timer = make_timer("poly_dbl_be_" + std::to_string(sz));
12✔
2127
            auto le_timer = make_timer("poly_dbl_le_" + std::to_string(sz));
12✔
2128

2129
            std::vector<uint8_t> buf(sz);
6✔
2130
            rng().randomize(buf.data(), sz);
6✔
2131

2132
            be_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n(buf.data(), buf.data(), sz); });
14,203✔
2133
            le_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n_le(buf.data(), buf.data(), sz); });
17,525✔
2134

2135
            record_result(be_timer);
6✔
2136
            record_result(le_timer);
6✔
2137
         }
6✔
2138
      }
1✔
2139
#endif
2140

2141
#if defined(BOTAN_HAS_BCRYPT)
2142

2143
      void bench_bcrypt() {
1✔
2144
         const std::string password = "not a very good password";
1✔
2145

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

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

2151
            record_result(timer);
11✔
2152
         }
11✔
2153
      }
1✔
2154
#endif
2155

2156
#if defined(BOTAN_HAS_PASSHASH9)
2157

2158
      void bench_passhash9() {
1✔
2159
         const std::string password = "not a very good password";
1✔
2160

2161
         for(uint8_t alg = 0; alg <= 4; ++alg) {
6✔
2162
            if(Botan::is_passhash9_alg_supported(alg) == false) {
5✔
2163
               continue;
×
2164
            }
2165

2166
            for(auto work_factor : {10, 15}) {
15✔
2167
               auto timer = make_timer("passhash9 alg=" + std::to_string(alg) + " wf=" + std::to_string(work_factor));
30✔
2168

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

2171
               record_result(timer);
10✔
2172
            }
10✔
2173
         }
2174
      }
1✔
2175
#endif
2176

2177
#if defined(BOTAN_HAS_SCRYPT)
2178

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

2182
         for(size_t N : {8192, 16384, 32768, 65536}) {
5✔
2183
            for(size_t r : {1, 8, 16}) {
16✔
2184
               for(size_t p : {1}) {
12✔
2185
                  auto pwdhash = pwdhash_fam->from_params(N, r, p);
12✔
2186

2187
                  auto scrypt_timer =
12✔
2188
                     make_timer("scrypt-" + std::to_string(N) + "-" + std::to_string(r) + "-" + std::to_string(p) +
24✔
2189
                                " (" + std::to_string(pwdhash->total_memory_usage() / (1024 * 1024)) + " MiB)");
60✔
2190

2191
                  uint8_t out[64];
12✔
2192
                  uint8_t salt[8];
12✔
2193
                  rng().randomize(salt, sizeof(salt));
12✔
2194

2195
                  while(scrypt_timer->under(msec)) {
24✔
2196
                     scrypt_timer->run([&] {
12✔
2197
                        pwdhash->derive_key(out, sizeof(out), "password", 8, salt, sizeof(salt));
12✔
2198

2199
                        Botan::copy_mem(salt, out, 8);
12✔
2200
                     });
12✔
2201
                  }
2202

2203
                  record_result(scrypt_timer);
12✔
2204

2205
                  if(scrypt_timer->events() == 1) {
12✔
2206
                     break;
2207
                  }
2208
               }
24✔
2209
            }
2210
         }
2211
      }
1✔
2212

2213
#endif
2214

2215
#if defined(BOTAN_HAS_ARGON2)
2216

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

2220
         for(size_t M : {8 * 1024, 64 * 1024, 256 * 1024}) {
4✔
2221
            for(size_t t : {1, 4}) {
9✔
2222
               for(size_t p : {1, 4}) {
18✔
2223
                  auto pwhash = pwhash_fam->from_params(M, t, p);
12✔
2224
                  auto timer = make_timer(pwhash->to_string());
36✔
2225

2226
                  uint8_t out[64];
12✔
2227
                  uint8_t salt[16];
12✔
2228
                  rng().randomize(salt, sizeof(salt));
12✔
2229

2230
                  while(timer->under(msec)) {
24✔
2231
                     timer->run([&] { pwhash->derive_key(out, sizeof(out), "password", 8, salt, sizeof(salt)); });
24✔
2232
                  }
2233

2234
                  record_result(timer);
12✔
2235
               }
24✔
2236
            }
2237
         }
2238
      }
1✔
2239

2240
#endif
2241
};
2242

2243
BOTAN_REGISTER_COMMAND("speed", Speed);
35✔
2244

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

© 2025 Coveralls, Inc