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

randombit / botan / 5080663405

25 May 2023 02:18PM UTC coverage: 91.675% (-0.01%) from 91.688%
5080663405

Pull #3549

github

Pull Request #3549: SPHINCS+

78519 of 85649 relevant lines covered (91.68%)

12135443.66 hits per line

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

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

8
#include "cli.h"
9
#include "../tests/test_rng.h" // FIXME
10

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

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

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

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

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

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

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

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

51
#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
52
   #include <botan/auto_rng.h>
53
#endif
54

55
#if defined(BOTAN_HAS_SYSTEM_RNG)
56
   #include <botan/system_rng.h>
57
#endif
58

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

63
#if defined(BOTAN_HAS_PROCESSOR_RNG)
64
   #include <botan/processor_rng.h>
65
#endif
66

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

71
#if defined(BOTAN_HAS_FPE_FE1)
72
   #include <botan/fpe_fe1.h>
73
#endif
74

75
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
76
   #include <botan/rfc3394.h>
77
#endif
78

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

83
#if defined(BOTAN_HAS_POLY_DBL)
84
   #include <botan/internal/poly_dbl.h>
85
#endif
86

87
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
88
   #include <botan/pkcs8.h>
89
   #include <botan/pubkey.h>
90
   #include <botan/pk_algs.h>
91
   #include <botan/x509_key.h>
92
   #include <botan/internal/workfactor.h>
93
#endif
94

95
#if defined(BOTAN_HAS_NUMBERTHEORY)
96
   #include <botan/numthry.h>
97
   #include <botan/reducer.h>
98
   #include <botan/internal/curve_nistp.h>
99
   #include <botan/internal/primality.h>
100
#endif
101

102
#if defined(BOTAN_HAS_ECC_GROUP)
103
   #include <botan/ec_group.h>
104
#endif
105

106
#if defined(BOTAN_HAS_DL_GROUP)
107
   #include <botan/dl_group.h>
108
#endif
109

110
#if defined(BOTAN_HAS_MCELIECE)
111
   #include <botan/mceliece.h>
112
#endif
113

114
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
115
   #include <botan/kyber.h>
116
#endif
117

118
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
119
   #include <botan/dilithium.h>
120
#endif
121

122
#if defined(BOTAN_HAS_SPHINCS_PLUS)
123
   #include <botan/sphincsplus.h>
124
#endif
125

126
#if defined(BOTAN_HAS_ECDSA)
127
   #include <botan/ecdsa.h>
128
#endif
129

130
#if defined(BOTAN_HAS_BCRYPT)
131
   #include <botan/bcrypt.h>
132
#endif
133

134
#if defined(BOTAN_HAS_PASSHASH9)
135
   #include <botan/passhash9.h>
136
#endif
137

138
#if defined(BOTAN_HAS_PASSWORD_HASHING)
139
  #include <botan/pwdhash.h>
140
#endif
141

142
#if defined(BOTAN_HAS_ZFEC)
143
  #include <botan/zfec.h>
144
#endif
145

146
namespace Botan_CLI {
147

148
using Botan::Timer;
149

150
namespace {
151

152
class JSON_Output final
2✔
153
   {
154
   public:
155
      void add(const Timer& timer) { m_results.push_back(timer); }
2✔
156

157
      std::string print() const
1✔
158
         {
159
         std::ostringstream out;
1✔
160

161
         out << "[\n";
1✔
162

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

167
            out << "{"
2✔
168
                << "\"algo\": \"" << t.get_name() << "\", "
2✔
169
                << "\"op\": \"" << t.doing() << "\", "
4✔
170
                << "\"events\": " << t.events() << ", ";
6✔
171

172
            if(t.cycles_consumed() > 0)
4✔
173
               out << "\"cycles\": " << t.cycles_consumed() << ", ";
4✔
174

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

181
            out << "\"nanos\": " << t.value() << "}";
2✔
182

183
            if(i != m_results.size() - 1)
2✔
184
               out << ",";
1✔
185

186
            out << "\n";
2✔
187
            }
188
         out << "]\n";
1✔
189

190
         return out.str();
2✔
191
         }
1✔
192
   private:
193
      std::vector<Timer> m_results;
194
   };
195

196
class Summary final
1✔
197
   {
198
   public:
199
      Summary() = default;
1✔
200

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

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

219
         std::ostringstream result_ss;
1✔
220
         result_ss << std::fixed;
1✔
221

222
         if(!m_bps_entries.empty())
1✔
223
            {
224
            result_ss << "\n";
1✔
225

226
            // add table header
227
            result_ss << std::setw(name_padding) << std::left << "algo"
1✔
228
                      << std::setw(op_name_padding) << std::left << "operation";
1✔
229

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

236
            // add table entries
237
            for(const auto& entry : m_bps_entries)
3✔
238
               {
239
               if(entry.second.empty())
2✔
240
                  continue;
×
241

242
               result_ss << std::setw(name_padding) << std::left << (entry.first.second)
2✔
243
                         << std::setw(op_name_padding) << std::left << (entry.first.first);
2✔
244

245
               for(const Timer& t : entry.second)
4✔
246
                  {
247

248
                  if(t.events() == 0)
2✔
249
                     {
250
                     result_ss << std::setw(op_padding) << std::right << "N/A";
×
251
                     }
252
                  else
253
                     {
254
                     result_ss << std::setw(op_padding) << std::right
2✔
255
                               << std::setprecision(2) << (t.bytes_per_second() / 1000.0);
4✔
256
                     }
257
                  }
258

259
               result_ss << "\n";
2✔
260
               }
261

262
            result_ss << "\n[results are the number of 1000s bytes processed per second]\n";
1✔
263
            }
264

265
         if(!m_ops_entries.empty())
1✔
266
            {
267
            result_ss << std::setprecision(6) << "\n";
×
268

269
            // sort entries
270
            std::sort(m_ops_entries.begin(), m_ops_entries.end());
×
271

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

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

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

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

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

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

311
         if(converted != size_str.size())
34✔
312
            throw CLI_Usage_Error("Invalid integer");
×
313
         }
314
      catch(std::exception&)
1✔
315
         {
316
         throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size");
3✔
317
         }
1✔
318

319
      if(x == 0)
34✔
320
         throw CLI_Usage_Error("Cannot have a zero-sized buffer");
3✔
321

322
      if(x > MAX_BUF_SIZE)
33✔
323
         throw CLI_Usage_Error("Specified buffer size is too large");
3✔
324

325
      buf.insert(x);
32✔
326
      }
34✔
327

328
   return std::vector<size_t>(buf.begin(), buf.end());
34✔
329
   }
31✔
330

331
}
332

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

339
      static std::vector<std::string> default_benchmark_list()
×
340
         {
341
         /*
342
         This is not intended to be exhaustive: it just hits the high
343
         points of the most interesting or widely used algorithms.
344
         */
345

346
         return {
×
347
            /* Block ciphers */
348
            "AES-128",
349
            "AES-192",
350
            "AES-256",
351
            "ARIA-128",
352
            "ARIA-192",
353
            "ARIA-256",
354
            "Blowfish",
355
            "CAST-128",
356
            "Camellia-128",
357
            "Camellia-192",
358
            "Camellia-256",
359
            "DES",
360
            "TripleDES",
361
            "GOST-28147-89",
362
            "IDEA",
363
            "Noekeon",
364
            "SHACAL2",
365
            "SM4",
366
            "Serpent",
367
            "Threefish-512",
368
            "Twofish",
369

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

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

387
            "ChaCha20Poly1305",
388

389
            /* Stream ciphers */
390
            "RC4",
391
            "Salsa20",
392
            "ChaCha20",
393

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

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

409
            /* pubkey */
410
            "RSA",
411
            "DH",
412
            "ECDH",
413
            "ECDSA",
414
            "Ed25519",
415
            "Curve25519",
416
            "McEliece",
417
            "Kyber",
418
            "SPHINCS+"
419
            };
×
420
         }
421

422
      std::string group() const override
1✔
423
         {
424
         return "misc";
1✔
425
         }
426

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

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

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

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

452
         m_clock_cycle_ratio = 1.0 / m_clock_cycle_ratio;
34✔
453

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

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

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

482
         std::vector<std::string> algos = get_arg_list("algos");
37✔
483

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

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

494
            for(auto bit : bits)
1✔
495
               {
496
               Botan::CPUID::clear_cpuid_bit(bit);
×
497
               }
498
            }
32✔
499

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

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

512
         for(const auto& algo : algos)
81✔
513
            {
514
            using namespace std::placeholders;
50✔
515

516
            if(false)
50✔
517
               {
518
               // Since everything might be disabled, need a block to else if from
519
               }
520
#if defined(BOTAN_HAS_HASH)
521
            else if(!Botan::HashFunction::providers(algo).empty())
50✔
522
               {
523
               bench_providers_of<Botan::HashFunction>(
1✔
524
                  algo, provider, msec, buf_sizes,
525
                  std::bind(&Speed::bench_hash, this, _1, _2, _3, _4));
2✔
526
               }
527
#endif
528
#if defined(BOTAN_HAS_BLOCK_CIPHER)
529
            else if(!Botan::BlockCipher::providers(algo).empty())
49✔
530
               {
531
               bench_providers_of<Botan::BlockCipher>(
4✔
532
                  algo, provider, msec, buf_sizes,
533
                  std::bind(&Speed::bench_block_cipher, this, _1, _2, _3, _4));
8✔
534
               }
535
#endif
536
#if defined(BOTAN_HAS_STREAM_CIPHER)
537
            else if(!Botan::StreamCipher::providers(algo).empty())
45✔
538
               {
539
               bench_providers_of<Botan::StreamCipher>(
1✔
540
                  algo, provider, msec, buf_sizes,
541
                  std::bind(&Speed::bench_stream_cipher, this, _1, _2, _3, _4));
2✔
542
               }
543
#endif
544
#if defined(BOTAN_HAS_CIPHER_MODES)
545
            else if(auto enc = Botan::Cipher_Mode::create(algo, Botan::Cipher_Dir::Encryption, provider))
44✔
546
               {
547
               auto dec = Botan::Cipher_Mode::create_or_throw(algo, Botan::Cipher_Dir::Decryption, provider);
1✔
548
               bench_cipher_mode(*enc, *dec, msec, buf_sizes);
1✔
549
               }
1✔
550
#endif
551
#if defined(BOTAN_HAS_MAC)
552
            else if(!Botan::MessageAuthenticationCode::providers(algo).empty())
43✔
553
               {
554
               bench_providers_of<Botan::MessageAuthenticationCode>(
1✔
555
                  algo, provider, msec, buf_sizes,
556
                  std::bind(&Speed::bench_mac, this, _1, _2, _3, _4));
2✔
557
               }
558
#endif
559
#if defined(BOTAN_HAS_RSA)
560
            else if(algo == "RSA")
42✔
561
               {
562
               bench_rsa(provider, msec);
1✔
563
               }
564
            else if(algo == "RSA_keygen")
41✔
565
               {
566
               bench_rsa_keygen(provider, msec);
1✔
567
               }
568
#endif
569
#if defined(BOTAN_HAS_ECDSA)
570
            else if(algo == "ECDSA")
40✔
571
               {
572
               bench_ecdsa(ecc_groups, provider, msec);
1✔
573
               }
574
            else if(algo == "ecdsa_recovery")
39✔
575
               {
576
               bench_ecdsa_recovery(ecc_groups, provider, msec);
1✔
577
               }
578
#endif
579
#if defined(BOTAN_HAS_SM2)
580
            else if(algo == "SM2")
38✔
581
               {
582
               bench_sm2(ecc_groups, provider, msec);
1✔
583
               }
584
#endif
585
#if defined(BOTAN_HAS_ECKCDSA)
586
            else if(algo == "ECKCDSA")
37✔
587
               {
588
               bench_eckcdsa(ecc_groups, provider, msec);
1✔
589
               }
590
#endif
591
#if defined(BOTAN_HAS_GOST_34_10_2001)
592
            else if(algo == "GOST-34.10")
36✔
593
               {
594
               bench_gost_3410(provider, msec);
1✔
595
               }
596
#endif
597
#if defined(BOTAN_HAS_ECGDSA)
598
            else if(algo == "ECGDSA")
35✔
599
               {
600
               bench_ecgdsa(ecc_groups, provider, msec);
1✔
601
               }
602
#endif
603
#if defined(BOTAN_HAS_ED25519)
604
            else if(algo == "Ed25519")
34✔
605
               {
606
               bench_ed25519(provider, msec);
1✔
607
               }
608
#endif
609
#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
610
            else if(algo == "DH")
33✔
611
               {
612
               bench_dh(provider, msec);
1✔
613
               }
614
#endif
615
#if defined(BOTAN_HAS_DSA)
616
            else if(algo == "DSA")
32✔
617
               {
618
               bench_dsa(provider, msec);
1✔
619
               }
620
#endif
621
#if defined(BOTAN_HAS_ELGAMAL)
622
            else if(algo == "ElGamal")
31✔
623
               {
624
               bench_elgamal(provider, msec);
1✔
625
               }
626
#endif
627
#if defined(BOTAN_HAS_ECDH)
628
            else if(algo == "ECDH")
30✔
629
               {
630
               bench_ecdh(ecc_groups, provider, msec);
1✔
631
               }
632
#endif
633
#if defined(BOTAN_HAS_CURVE_25519)
634
            else if(algo == "Curve25519")
29✔
635
               {
636
               bench_curve25519(provider, msec);
1✔
637
               }
638
#endif
639
#if defined(BOTAN_HAS_MCELIECE)
640
            else if(algo == "McEliece")
28✔
641
               {
642
               bench_mceliece(provider, msec);
1✔
643
               }
644
#endif
645
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
646
            else if(algo == "Kyber")
27✔
647
               {
648
               bench_kyber(provider, msec);
1✔
649
               }
650
#endif
651
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
652
            else if(algo == "Dilithium")
26✔
653
               {
654
               bench_dilithium(provider, msec);
1✔
655
               }
656
#endif
657
#if defined(BOTAN_HAS_XMSS_RFC8391)
658
            else if(algo == "XMSS")
25✔
659
               {
660
               bench_xmss(provider, msec);
1✔
661
               }
662
#endif
663
#if defined(BOTAN_HAS_SPHINCS_PLUS)
664
            else if(algo == "SPHINCS+")
24✔
665
               {
666
               bench_sphincs_plus(provider, msec);
×
667
               }
668
#endif
669
#if defined(BOTAN_HAS_SCRYPT)
670
            else if(algo == "scrypt")
24✔
671
               {
672
               bench_scrypt(provider, msec);
1✔
673
               }
674
#endif
675
#if defined(BOTAN_HAS_ARGON2)
676
            else if(algo == "argon2")
23✔
677
               {
678
               bench_argon2(provider, msec);
1✔
679
               }
680
#endif
681
#if defined(BOTAN_HAS_BCRYPT)
682
            else if(algo == "bcrypt")
22✔
683
               {
684
               bench_bcrypt();
1✔
685
               }
686
#endif
687
#if defined(BOTAN_HAS_PASSHASH9)
688
            else if(algo == "passhash9")
21✔
689
               {
690
               bench_passhash9();
1✔
691
               }
692
#endif
693
#if defined(BOTAN_HAS_ZFEC)
694
            else if(algo == "zfec")
20✔
695
               {
696
               bench_zfec(msec);
1✔
697
               }
698
#endif
699
#if defined(BOTAN_HAS_POLY_DBL)
700
            else if(algo == "poly_dbl")
19✔
701
               {
702
               bench_poly_dbl(msec);
1✔
703
               }
704
#endif
705

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

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

728
#if defined(BOTAN_HAS_NUMBERTHEORY)
729
            else if(algo == "primality_test")
14✔
730
               {
731
               bench_primality_tests(msec);
1✔
732
               }
733
            else if(algo == "random_prime")
13✔
734
               {
735
               bench_random_prime(msec);
1✔
736
               }
737
            else if(algo == "inverse_mod")
12✔
738
               {
739
               bench_inverse_mod(msec);
1✔
740
               }
741
            else if(algo == "bn_redc")
11✔
742
               {
743
               bench_bn_redc(msec);
1✔
744
               }
745
            else if(algo == "nistp_redc")
10✔
746
               {
747
               bench_nistp_redc(msec);
1✔
748
               }
749
#endif
750

751
#if defined(BOTAN_HAS_FPE_FE1)
752
            else if(algo == "fpe_fe1")
9✔
753
               {
754
               bench_fpe_fe1(msec);
1✔
755
               }
756
#endif
757

758
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
759
            else if(algo == "rfc3394")
8✔
760
               {
761
               bench_rfc3394(msec);
1✔
762
               }
763
#endif
764

765
#if defined(BOTAN_HAS_ECC_GROUP)
766
            else if(algo == "ecc_mult")
7✔
767
               {
768
               bench_ecc_mult(ecc_groups, msec);
1✔
769
               }
770
            else if(algo == "ecc_ops")
6✔
771
               {
772
               bench_ecc_ops(ecc_groups, msec);
1✔
773
               }
774
            else if(algo == "ecc_init")
5✔
775
               {
776
               bench_ecc_init(ecc_groups, msec);
1✔
777
               }
778
            else if(algo == "os2ecp")
4✔
779
               {
780
               bench_os2ecp(ecc_groups, msec);
1✔
781
               }
782
#endif
783
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
784
            else if(algo == "ec_h2c")
3✔
785
               {
786
               bench_ec_h2c(msec);
1✔
787
               }
788
#endif
789
            else if(algo == "RNG")
2✔
790
               {
791
#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
792
               Botan::AutoSeeded_RNG auto_rng;
1✔
793
               bench_rng(auto_rng, "AutoSeeded_RNG (with reseed)", msec, buf_sizes);
1✔
794
#endif
795

796
#if defined(BOTAN_HAS_SYSTEM_RNG)
797
               bench_rng(Botan::system_rng(), "System_RNG", msec, buf_sizes);
1✔
798
#endif
799

800
#if defined(BOTAN_HAS_PROCESSOR_RNG)
801
               if(Botan::Processor_RNG::available())
1✔
802
                  {
803
                  Botan::Processor_RNG hwrng;
1✔
804
                  bench_rng(hwrng, "Processor_RNG", msec, buf_sizes);
2✔
805
                  }
1✔
806
#endif
807

808
#if defined(BOTAN_HAS_HMAC_DRBG)
809
               for(std::string hash : { "SHA-256", "SHA-384", "SHA-512" })
4✔
810
                  {
811
                  Botan::HMAC_DRBG hmac_drbg(hash);
3✔
812
                  bench_rng(hmac_drbg, hmac_drbg.name(), msec, buf_sizes);
3✔
813
                  }
3✔
814
#endif
815

816
#if defined(BOTAN_HAS_CHACHA_RNG)
817
               // Provide a dummy seed
818
               Botan::ChaCha_RNG chacha_rng(Botan::secure_vector<uint8_t>(32));
1✔
819
               bench_rng(chacha_rng, "ChaCha_RNG", msec, buf_sizes);
1✔
820
#endif
821

822
               }
1✔
823
            else if(algo == "entropy")
1✔
824
               {
825
               bench_entropy_sources(msec);
1✔
826
               }
827
            else
828
               {
829
               if(verbose() || !using_defaults)
×
830
                  {
831
                  error_output() << "Unknown algorithm '" << algo << "'\n";
×
832
                  }
833
               }
44✔
834
            }
835

836
         if(m_json)
31✔
837
            {
838
            output() << m_json->print();
3✔
839
            }
840
         if(m_summary)
31✔
841
            {
842
            output() << m_summary->print() << "\n";
3✔
843
            }
844

845
         if(verbose() && m_clock_speed == 0 && m_cycles_consumed > 0 && m_ns_taken > 0)
31✔
846
            {
847
            const double seconds = static_cast<double>(m_ns_taken) / 1000000000;
×
848
            const double Hz = static_cast<double>(m_cycles_consumed) / seconds;
×
849
            const double MHz = Hz / 1000000;
×
850
            output() << "\nEstimated clock speed " << MHz << " MHz\n";
×
851
            }
852
         }
40✔
853

854
   private:
855

856
      size_t m_clock_speed = 0;
857
      double m_clock_cycle_ratio = 0.0;
858
      uint64_t m_cycles_consumed = 0;
859
      uint64_t m_ns_taken = 0;
860
      std::unique_ptr<Summary> m_summary;
861
      std::unique_ptr<JSON_Output> m_json;
862

863
      void record_result(const std::unique_ptr<Timer>& t)
467✔
864
         {
865
         m_ns_taken += t->value();
467✔
866
         m_cycles_consumed += t->cycles_consumed();
467✔
867
         if(m_json)
467✔
868
            {
869
            m_json->add(*t);
2✔
870
            }
871
         else
872
            {
873
            output() << t->to_string() << std::flush;
465✔
874
            if(m_summary)
465✔
875
               m_summary->add(*t);
2✔
876
            }
877
         }
467✔
878

879
      template<typename T>
880
      using bench_fn = std::function<void (T&,
881
                                           std::string,
882
                                           std::chrono::milliseconds,
883
                                           const std::vector<size_t>&)>;
884

885
      template<typename T>
886
      void bench_providers_of(const std::string& algo,
7✔
887
                              const std::string& provider, /* user request, if any */
888
                              const std::chrono::milliseconds runtime,
889
                              const std::vector<size_t>& buf_sizes,
890
                              bench_fn<T> bench_one)
891
         {
892
         for(auto const& prov : T::providers(algo))
14✔
893
            {
894
            if(provider.empty() || provider == prov)
7✔
895
               {
896
               auto p = T::create(algo, prov);
7✔
897

898
               if(p)
7✔
899
                  {
900
                  bench_one(*p, prov, runtime, buf_sizes);
14✔
901
                  }
902
               }
7✔
903
            }
904
         }
7✔
905

906
      std::unique_ptr<Timer> make_timer(const std::string& name,
471✔
907
                                        uint64_t event_mult = 1,
908
                                        const std::string& what = "",
909
                                        const std::string& provider = "",
910
                                        size_t buf_size = 0)
911
         {
912
         return std::make_unique<Timer>(
471✔
913
            name, provider, what, event_mult, buf_size,
914
            m_clock_cycle_ratio, m_clock_speed);
252✔
915
         }
916

917
      std::unique_ptr<Timer> make_timer(const std::string& algo,
212✔
918
                                        const std::string& provider,
919
                                        const std::string& what)
920
         {
921
         return make_timer(algo, 1, what, provider, 0);
212✔
922
         }
923

924
#if defined(BOTAN_HAS_BLOCK_CIPHER)
925
      void bench_block_cipher(Botan::BlockCipher& cipher,
4✔
926
                              const std::string& provider,
927
                              std::chrono::milliseconds runtime,
928
                              const std::vector<size_t>& buf_sizes)
929
         {
930
         auto ks_timer = make_timer(cipher.name(), provider, "key schedule");
8✔
931

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

935
         const size_t bs = cipher.block_size();
4✔
936
         std::set<size_t> buf_sizes_in_blocks;
4✔
937
         for(size_t buf_size : buf_sizes)
9✔
938
            {
939
            if(buf_size % bs == 0)
5✔
940
               buf_sizes_in_blocks.insert(buf_size);
10✔
941
            else
942
               buf_sizes_in_blocks.insert(buf_size + bs - (buf_size % bs));
×
943
            }
944

945
         for(size_t buf_size : buf_sizes_in_blocks)
9✔
946
            {
947
            std::vector<uint8_t> buffer(buf_size);
5✔
948
            const size_t blocks = buf_size / bs;
5✔
949

950
            auto encrypt_timer = make_timer(cipher.name(), buffer.size(), "encrypt", provider, buf_size);
10✔
951
            auto decrypt_timer = make_timer(cipher.name(), buffer.size(), "decrypt", provider, buf_size);
10✔
952

953
            encrypt_timer->run_until_elapsed(runtime, [&]() { cipher.encrypt_n(&buffer[0], &buffer[0], blocks); });
2,471✔
954
            record_result(encrypt_timer);
5✔
955

956
            decrypt_timer->run_until_elapsed(runtime, [&]() { cipher.decrypt_n(&buffer[0], &buffer[0], blocks); });
2,478✔
957
            record_result(decrypt_timer);
5✔
958
            }
10✔
959
         }
8✔
960
#endif
961

962
#if defined(BOTAN_HAS_STREAM_CIPHER)
963
      void bench_stream_cipher(
1✔
964
         Botan::StreamCipher& cipher,
965
         const std::string& provider,
966
         const std::chrono::milliseconds runtime,
967
         const std::vector<size_t>& buf_sizes)
968
         {
969
         for(auto buf_size : buf_sizes)
2✔
970
            {
971
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
972

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

975
            const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
1✔
976
            cipher.set_key(key);
1✔
977

978
            if(cipher.valid_iv_length(12))
1✔
979
               {
980
               const Botan::InitializationVector iv(rng(), 12);
1✔
981
               cipher.set_iv(iv.begin(), iv.size());
1✔
982
               }
1✔
983

984
            while(encrypt_timer->under(runtime))
655✔
985
               {
986
               encrypt_timer->run([&]() { cipher.encipher(buffer); });
1,308✔
987
               }
988

989
            record_result(encrypt_timer);
1✔
990

991
            if(verbose())
1✔
992
               {
993
               auto ks_timer = make_timer(cipher.name(), buffer.size(), "write_keystream", provider, buf_size);
×
994

995
               while(ks_timer->under(runtime))
×
996
                  {
997
                  ks_timer->run([&]() { cipher.write_keystream(buffer.data(), buffer.size()); });
×
998
                  }
999
               record_result(ks_timer);
×
1000
               }
×
1001
            }
2✔
1002
         }
1✔
1003
#endif
1004

1005
#if defined(BOTAN_HAS_HASH)
1006
      void bench_hash(
1✔
1007
         Botan::HashFunction& hash,
1008
         const std::string& provider,
1009
         const std::chrono::milliseconds runtime,
1010
         const std::vector<size_t>& buf_sizes)
1011
         {
1012
         std::vector<uint8_t> output(hash.output_length());
1✔
1013

1014
         for(auto buf_size : buf_sizes)
2✔
1015
            {
1016
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
1017

1018
            auto timer = make_timer(hash.name(), buffer.size(), "hash", provider, buf_size);
2✔
1019
            timer->run_until_elapsed(runtime, [&]() { hash.update(buffer); hash.final(output.data()); });
126✔
1020
            record_result(timer);
1✔
1021
            }
2✔
1022
         }
1✔
1023
#endif
1024

1025
#if defined(BOTAN_HAS_MAC)
1026
      void bench_mac(
1✔
1027
         Botan::MessageAuthenticationCode& mac,
1028
         const std::string& provider,
1029
         const std::chrono::milliseconds runtime,
1030
         const std::vector<size_t>& buf_sizes)
1031
         {
1032
         std::vector<uint8_t> output(mac.output_length());
1✔
1033

1034
         for(auto buf_size : buf_sizes)
2✔
1035
            {
1036
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
1037

1038
            const Botan::SymmetricKey key(rng(), mac.maximum_keylength());
1✔
1039
            mac.set_key(key);
1✔
1040
            mac.start(nullptr, 0);
1✔
1041

1042
            auto timer = make_timer(mac.name(), buffer.size(), "mac", provider, buf_size);
2✔
1043
            timer->run_until_elapsed(runtime, [&]() { mac.update(buffer); });
138✔
1044
            timer->run([&]() { mac.final(output.data()); });
2✔
1045
            record_result(timer);
1✔
1046
            }
3✔
1047
         }
1✔
1048
#endif
1049

1050
#if defined(BOTAN_HAS_CIPHER_MODES)
1051
      void bench_cipher_mode(
1✔
1052
         Botan::Cipher_Mode& enc,
1053
         Botan::Cipher_Mode& dec,
1054
         const std::chrono::milliseconds runtime,
1055
         const std::vector<size_t>& buf_sizes)
1056
         {
1057
         auto ks_timer = make_timer(enc.name(), enc.provider(), "key schedule");
2✔
1058

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

1061
         ks_timer->run([&]() { enc.set_key(key); });
2✔
1062
         ks_timer->run([&]() { dec.set_key(key); });
2✔
1063

1064
         record_result(ks_timer);
1✔
1065

1066
         for(auto buf_size : buf_sizes)
2✔
1067
            {
1068
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
1069

1070
            auto encrypt_timer = make_timer(enc.name(), buffer.size(), "encrypt", enc.provider(), buf_size);
2✔
1071
            auto decrypt_timer = make_timer(dec.name(), buffer.size(), "decrypt", dec.provider(), buf_size);
2✔
1072

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

1075
            if(buf_size >= enc.minimum_final_size())
1✔
1076
               {
1077
               while(encrypt_timer->under(runtime) && decrypt_timer->under(runtime))
82✔
1078
                  {
1079
                  // Must run in this order, or AEADs will reject the ciphertext
1080
                  encrypt_timer->run([&]() { enc.start(iv); enc.finish(buffer); });
162✔
1081

1082
                  decrypt_timer->run([&]() { dec.start(iv); dec.finish(buffer); });
162✔
1083

1084
                  if(!iv.empty())
81✔
1085
                     {
1086
                     iv[iv.size()-1] += 1;
81✔
1087
                     }
1088
                  }
1089
               }
1090

1091
            record_result(encrypt_timer);
1✔
1092
            record_result(decrypt_timer);
1✔
1093
            }
2✔
1094
         }
1✔
1095
#endif
1096

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

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

1111
            auto timer = make_timer(rng_name, buffer.size(), "generate", "", buf_size);
7✔
1112
            timer->run_until_elapsed(runtime, [&]() { rng.randomize(buffer.data(), buffer.size()); });
902✔
1113
            record_result(timer);
7✔
1114
            }
14✔
1115
         }
7✔
1116

1117
      void bench_entropy_sources(const std::chrono::milliseconds /*unused*/)
1✔
1118
         {
1119
         Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources();
1✔
1120

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

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

1129
            size_t compressed_size = 0;
4✔
1130

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

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

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

1145
            std::ostringstream msg;
4✔
1146

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

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

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

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

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

1163
#if defined(BOTAN_HAS_ECC_GROUP)
1164
      void bench_ecc_ops(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
1✔
1165
         {
1166
         for(const std::string& group_name : groups)
7✔
1167
            {
1168
            const Botan::EC_Group ec_group(group_name);
6✔
1169

1170
            auto add_timer = make_timer(group_name + " add");
15✔
1171
            auto addf_timer = make_timer(group_name + " addf");
15✔
1172
            auto dbl_timer = make_timer(group_name + " dbl");
15✔
1173

1174
            const Botan::EC_Point& base_point = ec_group.get_base_point();
6✔
1175

1176
            // create a non-affine point
1177
            const auto random_k = Botan::BigInt::from_u64(0x4E6F537465707E);
6✔
1178
            Botan::EC_Point non_affine_pt = ec_group.get_base_point() * random_k;
6✔
1179
            Botan::EC_Point pt = ec_group.get_base_point();
6✔
1180

1181
            std::vector<Botan::BigInt> ws(Botan::EC_Point::WORKSPACE_SIZE);
6✔
1182

1183
            while(add_timer->under(runtime) && addf_timer->under(runtime) && dbl_timer->under(runtime))
818✔
1184
               {
1185
               dbl_timer->run([&]() { pt.mult2(ws); });
1,624✔
1186
               add_timer->run([&]() { pt.add(non_affine_pt, ws); });
1,624✔
1187
               addf_timer->run([&]() { pt.add_affine(base_point, ws); });
1,624✔
1188
               }
1189

1190
            record_result(dbl_timer);
6✔
1191
            record_result(add_timer);
6✔
1192
            record_result(addf_timer);
6✔
1193
            }
12✔
1194
         }
1✔
1195

1196
      void bench_ecc_init(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
1✔
1197
         {
1198
         for(std::string group_name : groups)
7✔
1199
            {
1200
            auto timer = make_timer(group_name + " initialization");
18✔
1201

1202
            while(timer->under(runtime))
12✔
1203
               {
1204
               Botan::EC_Group::clear_registered_curve_data();
6✔
1205
               timer->run([&]() { Botan::EC_Group group(group_name); });
12✔
1206
               }
1207

1208
            record_result(timer);
6✔
1209
            }
6✔
1210
         }
1✔
1211

1212
      void bench_ecc_mult(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
1✔
1213
         {
1214
         for(const std::string& group_name : groups)
7✔
1215
            {
1216
            const Botan::EC_Group ec_group(group_name);
6✔
1217

1218
            auto mult_timer = make_timer(group_name + " Montgomery ladder");
18✔
1219
            auto blinded_mult_timer = make_timer(group_name + " blinded comb");
18✔
1220
            auto blinded_var_mult_timer = make_timer(group_name + " blinded window");
18✔
1221

1222
            const Botan::EC_Point& base_point = ec_group.get_base_point();
6✔
1223

1224
            std::vector<Botan::BigInt> ws;
6✔
1225

1226
            while(mult_timer->under(runtime) &&
12✔
1227
                  blinded_mult_timer->under(runtime) &&
12✔
1228
                  blinded_var_mult_timer->under(runtime))
6✔
1229
               {
1230
               const Botan::BigInt scalar(rng(), ec_group.get_p_bits());
6✔
1231

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

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

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

1240
               BOTAN_ASSERT_EQUAL(r1, r2, "Same point computed by Montgomery and comb");
6✔
1241
               BOTAN_ASSERT_EQUAL(r1, r3, "Same point computed by Montgomery and window");
6✔
1242
               }
12✔
1243

1244
            record_result(mult_timer);
6✔
1245
            record_result(blinded_mult_timer);
6✔
1246
            record_result(blinded_var_mult_timer);
6✔
1247
            }
6✔
1248
         }
1✔
1249

1250
      void bench_os2ecp(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
1✔
1251
         {
1252
         for(const std::string& group_name : groups)
7✔
1253
            {
1254
            auto uncmp_timer = make_timer("OS2ECP uncompressed " + group_name);
18✔
1255
            auto cmp_timer = make_timer("OS2ECP compressed " + group_name);
18✔
1256

1257
            const Botan::EC_Group ec_group(group_name);
6✔
1258

1259
            while(uncmp_timer->under(runtime) && cmp_timer->under(runtime))
12✔
1260
               {
1261
               const Botan::BigInt k(rng(), 256);
6✔
1262
               const Botan::EC_Point p = ec_group.get_base_point() * k;
6✔
1263
               const std::vector<uint8_t> os_cmp = p.encode(Botan::EC_Point_Format::Compressed);
6✔
1264
               const std::vector<uint8_t> os_uncmp = p.encode(Botan::EC_Point_Format::Uncompressed);
6✔
1265

1266
               uncmp_timer->run([&]() { ec_group.OS2ECP(os_uncmp); });
12✔
1267
               cmp_timer->run([&]() { ec_group.OS2ECP(os_cmp); });
12✔
1268
               }
18✔
1269

1270
            record_result(uncmp_timer);
6✔
1271
            record_result(cmp_timer);
6✔
1272
            }
6✔
1273
         }
1✔
1274

1275
#endif
1276

1277
#if defined(BOTAN_HAS_EC_HASH_TO_CURVE)
1278
      void bench_ec_h2c(const std::chrono::milliseconds runtime)
1✔
1279
         {
1280
         for(std::string group_name : { "secp256r1", "secp384r1", "secp521r1" })
4✔
1281
            {
1282
            auto h2c_ro_timer = make_timer(group_name + "-RO", "", "hash to curve");
6✔
1283
            auto h2c_nu_timer = make_timer(group_name + "-NU", "", "hash to curve");
6✔
1284

1285
            const Botan::EC_Group group(group_name);
3✔
1286

1287
            while(h2c_ro_timer->under(runtime))
6✔
1288
               {
1289
               std::vector<uint8_t> input(32);
3✔
1290

1291
               rng().randomize(input.data(), input.size());
3✔
1292

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

1297
               BOTAN_ASSERT_NOMSG(p1.on_the_curve());
3✔
1298

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

1303
               BOTAN_ASSERT_NOMSG(p2.on_the_curve());
3✔
1304
               }
6✔
1305

1306
            record_result(h2c_ro_timer);
3✔
1307
            record_result(h2c_nu_timer);
3✔
1308
            }
3✔
1309
         }
1✔
1310
#endif
1311

1312
#if defined(BOTAN_HAS_FPE_FE1)
1313

1314
      void bench_fpe_fe1(const std::chrono::milliseconds runtime)
1✔
1315
         {
1316
         const auto n = Botan::BigInt::from_u64(1000000000000000);
1✔
1317

1318
         auto enc_timer = make_timer("FPE_FE1 encrypt");
2✔
1319
         auto dec_timer = make_timer("FPE_FE1 decrypt");
2✔
1320

1321
         const Botan::SymmetricKey key(rng(), 32);
1✔
1322
         const std::vector<uint8_t> tweak(8); // 8 zeros
1✔
1323

1324
         auto x = Botan::BigInt::one();
1✔
1325

1326
         Botan::FPE_FE1 fpe_fe1(n);
1✔
1327
         fpe_fe1.set_key(key);
1✔
1328

1329
         while(enc_timer->under(runtime))
3✔
1330
            {
1331
            enc_timer->start();
2✔
1332
            x = fpe_fe1.encrypt(x, tweak.data(), tweak.size());
2✔
1333
            enc_timer->stop();
2✔
1334
            }
1335

1336
         for(size_t i = 0; i != enc_timer->events(); ++i)
3✔
1337
            {
1338
            dec_timer->start();
2✔
1339
            x = fpe_fe1.decrypt(x, tweak.data(), tweak.size());
2✔
1340
            dec_timer->stop();
2✔
1341
            }
1342

1343
         BOTAN_ASSERT(x == 1, "FPE works");
1✔
1344

1345
         record_result(enc_timer);
1✔
1346
         record_result(dec_timer);
1✔
1347
         }
5✔
1348
#endif
1349

1350
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
1351

1352
      void bench_rfc3394(const std::chrono::milliseconds runtime)
1✔
1353
         {
1354
         auto wrap_timer = make_timer("RFC3394 AES-256 key wrap");
3✔
1355
         auto unwrap_timer = make_timer("RFC3394 AES-256 key unwrap");
3✔
1356

1357
         const Botan::SymmetricKey kek(rng(), 32);
1✔
1358
         Botan::secure_vector<uint8_t> key(64, 0);
1✔
1359

1360
         while(wrap_timer->under(runtime))
35✔
1361
            {
1362
            wrap_timer->start();
34✔
1363
            key = Botan::rfc3394_keywrap(key, kek);
68✔
1364
            wrap_timer->stop();
34✔
1365

1366
            unwrap_timer->start();
34✔
1367
            key = Botan::rfc3394_keyunwrap(key, kek);
68✔
1368
            unwrap_timer->stop();
34✔
1369

1370
            key[0] += 1;
34✔
1371
            }
1372

1373
         record_result(wrap_timer);
1✔
1374
         record_result(unwrap_timer);
1✔
1375
         }
2✔
1376
#endif
1377

1378
#if defined(BOTAN_HAS_BIGINT)
1379

1380
      void bench_mp_mul(const std::chrono::milliseconds runtime)
1✔
1381
         {
1382
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1383
         for(size_t bits : { 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096 })
10✔
1384
            {
1385
            auto mul_timer = make_timer("BigInt mul " + std::to_string(bits));
18✔
1386
            auto sqr_timer = make_timer("BigInt sqr " + std::to_string(bits));
18✔
1387

1388
            const Botan::BigInt y(rng(), bits);
9✔
1389
            Botan::secure_vector<Botan::word> ws;
9✔
1390

1391
            while(mul_timer->under(runtime_per_size))
4,109✔
1392
               {
1393
               Botan::BigInt x(rng(), bits);
4,100✔
1394

1395
               sqr_timer->start();
4,100✔
1396
               x.square(ws);
4,100✔
1397
               sqr_timer->stop();
4,100✔
1398

1399
               x.mask_bits(bits);
4,100✔
1400

1401
               mul_timer->start();
4,100✔
1402
               x.mul(y, ws);
4,100✔
1403
               mul_timer->stop();
4,100✔
1404
               }
4,100✔
1405

1406
            record_result(mul_timer);
9✔
1407
            record_result(sqr_timer);
9✔
1408
            }
18✔
1409

1410
         }
1✔
1411

1412
      void bench_mp_div(const std::chrono::milliseconds runtime)
1✔
1413
         {
1414
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1415

1416
         for(size_t n_bits : { 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096 })
10✔
1417
            {
1418
            const size_t q_bits = n_bits / 2;
9✔
1419
            const std::string bit_descr = std::to_string(n_bits) + "/" + std::to_string(q_bits);
18✔
1420

1421
            auto div_timer = make_timer("BigInt div " + bit_descr);
27✔
1422
            auto ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
27✔
1423

1424
            Botan::BigInt y;
9✔
1425
            Botan::BigInt x;
9✔
1426
            Botan::secure_vector<Botan::word> ws;
9✔
1427

1428
            Botan::BigInt q1, r1, q2, r2;
9✔
1429

1430
            while(ct_div_timer->under(runtime_per_size))
43✔
1431
               {
1432
               x.randomize(rng(), n_bits);
34✔
1433
               y.randomize(rng(), q_bits);
34✔
1434

1435
               div_timer->start();
34✔
1436
               Botan::vartime_divide(x, y, q1, r1);
34✔
1437
               div_timer->stop();
34✔
1438

1439
               ct_div_timer->start();
34✔
1440
               Botan::ct_divide(x, y, q2, r2);
34✔
1441
               ct_div_timer->stop();
34✔
1442

1443
               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
34✔
1444
               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
77✔
1445
               }
1446

1447
            record_result(div_timer);
9✔
1448
            record_result(ct_div_timer);
9✔
1449
            }
54✔
1450
         }
1✔
1451

1452
      void bench_mp_div10(const std::chrono::milliseconds runtime)
1✔
1453
         {
1454
         std::chrono::milliseconds runtime_per_size = runtime;
1✔
1455

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

1460
            auto div_timer = make_timer("BigInt div " + bit_descr);
27✔
1461
            auto ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
27✔
1462

1463
            Botan::BigInt x;
9✔
1464
            Botan::secure_vector<Botan::word> ws;
9✔
1465

1466
            const auto ten = Botan::BigInt::from_word(10);
9✔
1467
            Botan::BigInt q1, r1, q2;
9✔
1468
            Botan::word r2;
1469

1470
            while(ct_div_timer->under(runtime_per_size))
253✔
1471
               {
1472
               x.randomize(rng(), n_bits);
244✔
1473

1474
               div_timer->start();
244✔
1475
               Botan::vartime_divide(x, ten, q1, r1);
244✔
1476
               div_timer->stop();
244✔
1477

1478
               ct_div_timer->start();
244✔
1479
               Botan::ct_divide_word(x, 10, q2, r2);
244✔
1480
               ct_div_timer->stop();
244✔
1481

1482
               BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
244✔
1483
               BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
497✔
1484
               }
1485

1486
            record_result(div_timer);
9✔
1487
            record_result(ct_div_timer);
9✔
1488
            }
45✔
1489
         }
1✔
1490

1491
#endif
1492

1493
#if defined(BOTAN_HAS_DL_GROUP)
1494

1495
      void bench_modexp(const std::chrono::milliseconds runtime)
1✔
1496
         {
1497
         for(size_t group_bits : { 1024, 1536, 2048, 3072, 4096 })
6✔
1498
            {
1499
            const std::string group_bits_str = std::to_string(group_bits);
5✔
1500
            const Botan::DL_Group group("modp/srp/" + group_bits_str);
5✔
1501

1502
            const size_t e_bits = Botan::dl_exponent_size(group_bits);
5✔
1503
            const size_t f_bits = group_bits - 1;
5✔
1504

1505
            const Botan::BigInt random_e(rng(), e_bits);
5✔
1506
            const Botan::BigInt random_f(rng(), f_bits);
5✔
1507

1508
            auto e_timer = make_timer(group_bits_str + " short exponent");
15✔
1509
            auto f_timer = make_timer(group_bits_str + "  full exponent");
15✔
1510

1511
            while(f_timer->under(runtime))
10✔
1512
               {
1513
               e_timer->run([&]() { group.power_g_p(random_e); });
10✔
1514
               f_timer->run([&]() { group.power_g_p(random_f); });
10✔
1515
               }
1516

1517
            record_result(e_timer);
5✔
1518
            record_result(f_timer);
5✔
1519
            }
20✔
1520
         }
1✔
1521
#endif
1522

1523
#if defined(BOTAN_HAS_NUMBERTHEORY)
1524
      void bench_nistp_redc(const std::chrono::milliseconds runtime)
1✔
1525
         {
1526
         Botan::secure_vector<Botan::word> ws;
1✔
1527

1528
         auto p192_timer = make_timer("P-192 redc");
2✔
1529
         Botan::BigInt r192(rng(), 192*2 - 1);
1✔
1530
         while(p192_timer->under(runtime))
2,595✔
1531
            {
1532
            Botan::BigInt r = r192;
2,594✔
1533
            p192_timer->run([&]() { Botan::redc_p192(r, ws); });
5,188✔
1534
            r192 += 1;
2,594✔
1535
            }
2,594✔
1536
         record_result(p192_timer);
1✔
1537

1538
         auto p224_timer = make_timer("P-224 redc");
2✔
1539
         Botan::BigInt r224(rng(), 224*2 - 1);
1✔
1540
         while(p224_timer->under(runtime))
2,441✔
1541
            {
1542
            Botan::BigInt r = r224;
2,440✔
1543
            p224_timer->run([&]() { Botan::redc_p224(r, ws); });
4,880✔
1544
            r224 += 1;
2,440✔
1545
            }
2,440✔
1546
         record_result(p224_timer);
1✔
1547

1548
         auto p256_timer = make_timer("P-256 redc");
2✔
1549
         Botan::BigInt r256(rng(), 256*2 - 1);
1✔
1550
         while(p256_timer->under(runtime))
2,464✔
1551
            {
1552
            Botan::BigInt r = r256;
2,463✔
1553
            p256_timer->run([&]() { Botan::redc_p256(r, ws); });
4,926✔
1554
            r256 += 1;
2,463✔
1555
            }
2,463✔
1556
         record_result(p256_timer);
1✔
1557

1558
         auto p384_timer = make_timer("P-384 redc");
2✔
1559
         Botan::BigInt r384(rng(), 384*2 - 1);
1✔
1560
         while(p384_timer->under(runtime))
2,268✔
1561
            {
1562
            Botan::BigInt r = r384;
2,267✔
1563
            p384_timer->run([&]() { Botan::redc_p384(r384, ws); });
4,534✔
1564
            r384 += 1;
2,267✔
1565
            }
2,267✔
1566
         record_result(p384_timer);
1✔
1567

1568
         auto p521_timer = make_timer("P-521 redc");
2✔
1569
         Botan::BigInt r521(rng(), 521*2 - 1);
1✔
1570
         while(p521_timer->under(runtime))
1,501✔
1571
            {
1572
            Botan::BigInt r = r521;
1,500✔
1573
            p521_timer->run([&]() { Botan::redc_p521(r521, ws); });
3,000✔
1574
            r521 += 1;
1,500✔
1575
            }
1,500✔
1576
         record_result(p521_timer);
1✔
1577
         }
6✔
1578

1579
      void bench_bn_redc(const std::chrono::milliseconds runtime)
1✔
1580
         {
1581
         for(size_t bitsize : { 512, 1024, 2048, 4096 })
5✔
1582
            {
1583
            Botan::BigInt p(rng(), bitsize);
4✔
1584

1585
            std::string bit_str = std::to_string(bitsize);
4✔
1586
            auto barrett_timer = make_timer("Barrett-" + bit_str);
8✔
1587
            auto schoolbook_timer = make_timer("Schoolbook-" + bit_str);
8✔
1588

1589
            Botan::Modular_Reducer mod_p(p);
4✔
1590

1591
            while(schoolbook_timer->under(runtime))
49✔
1592
               {
1593
               const Botan::BigInt x(rng(), p.bits() * 2 - 2);
45✔
1594

1595
               const Botan::BigInt r1 = barrett_timer->run(
45✔
1596
                  [&] { return mod_p.reduce(x); });
90✔
1597
               const Botan::BigInt r2 = schoolbook_timer->run(
45✔
1598
                  [&] { return x % p; });
90✔
1599

1600
               BOTAN_ASSERT(r1 == r2, "Computed different results");
45✔
1601
               }
135✔
1602

1603
            record_result(barrett_timer);
4✔
1604
            record_result(schoolbook_timer);
4✔
1605
            }
8✔
1606
         }
1✔
1607

1608
      void bench_inverse_mod(const std::chrono::milliseconds runtime)
1✔
1609
         {
1610
         for(size_t bits : { 256, 384, 512, 1024, 2048 })
6✔
1611
            {
1612
            const std::string bit_str = std::to_string(bits);
5✔
1613

1614
            auto timer = make_timer("inverse_mod-" + bit_str);
12✔
1615
            auto gcd_timer = make_timer("gcd-" + bit_str);
10✔
1616

1617
            while(timer->under(runtime) && gcd_timer->under(runtime))
11✔
1618
               {
1619
               const Botan::BigInt x(rng(), bits - 1);
6✔
1620
               Botan::BigInt mod(rng(), bits);
6✔
1621

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

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

1627
               if(x_inv == 0)
6✔
1628
                  {
1629
                  BOTAN_ASSERT(g != 1, "Inversion only fails if gcd(x, mod) > 1");
2✔
1630
                  }
1631
               else
1632
                  {
1633
                  BOTAN_ASSERT(g == 1, "Inversion succeeds only if gcd != 1");
4✔
1634
                  const Botan::BigInt check = (x_inv*x) % mod;
4✔
1635
                  BOTAN_ASSERT_EQUAL(check, 1, "Const time inversion correct");
4✔
1636
                  }
4✔
1637
               }
23✔
1638

1639
            record_result(timer);
5✔
1640
            record_result(gcd_timer);
5✔
1641
            }
5✔
1642
         }
1✔
1643

1644
      void bench_primality_tests(const std::chrono::milliseconds runtime)
1✔
1645
         {
1646
         for(size_t bits : { 256, 512, 1024 })
4✔
1647
            {
1648
            auto mr_timer = make_timer("Miller-Rabin-" + std::to_string(bits));
9✔
1649
            auto bpsw_timer = make_timer("Bailie-PSW-" + std::to_string(bits));
6✔
1650
            auto lucas_timer = make_timer("Lucas-" + std::to_string(bits));
6✔
1651

1652
            Botan::BigInt n = Botan::random_prime(rng(), bits);
3✔
1653

1654
            while(lucas_timer->under(runtime))
6✔
1655
               {
1656
               Botan::Modular_Reducer mod_n(n);
3✔
1657

1658
               mr_timer->run([&]() {
3✔
1659
                  return Botan::is_miller_rabin_probable_prime(n, mod_n, rng(), 2); });
3✔
1660

1661
               bpsw_timer->run([&]() {
3✔
1662
                  return Botan::is_bailie_psw_probable_prime(n, mod_n); });
3✔
1663

1664
               lucas_timer->run([&]() {
3✔
1665
                  return Botan::is_lucas_probable_prime(n, mod_n); });
3✔
1666

1667
               n += 2;
3✔
1668
               }
3✔
1669

1670
            record_result(mr_timer);
3✔
1671
            record_result(bpsw_timer);
3✔
1672
            record_result(lucas_timer);
3✔
1673
            }
3✔
1674
         }
1✔
1675

1676
      void bench_random_prime(const std::chrono::milliseconds runtime)
1✔
1677
         {
1678
         const auto coprime = Botan::BigInt::from_word(0x10001);
1✔
1679

1680
         for(size_t bits : { 256, 384, 512, 768, 1024, 1536 })
7✔
1681
            {
1682
            auto genprime_timer = make_timer("random_prime " + std::to_string(bits));
18✔
1683
            auto gensafe_timer = make_timer("random_safe_prime " + std::to_string(bits));
18✔
1684
            auto is_prime_timer = make_timer("is_prime " + std::to_string(bits));
12✔
1685

1686
            while(gensafe_timer->under(runtime))
12✔
1687
               {
1688
               const Botan::BigInt p = genprime_timer->run([&]
6✔
1689
                  {
1690
                  return Botan::random_prime(rng(), bits, coprime);
6✔
1691
                  });
6✔
1692

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

1698
               const Botan::BigInt sg = gensafe_timer->run([&]
6✔
1699
                  {
1700
                  return Botan::random_safe_prime(rng(), bits);
6✔
1701
                  });
6✔
1702

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

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

1713
               // Now test p+2, p+4, ... which may or may not be prime
1714
               for(size_t i = 2; i <= 64; i += 2)
198✔
1715
                  {
1716
                  is_prime_timer->run([&]() { Botan::is_prime(p + i, rng(), 64, true); });
576✔
1717
                  }
1718
               }
12✔
1719

1720
            record_result(genprime_timer);
6✔
1721
            record_result(gensafe_timer);
6✔
1722
            record_result(is_prime_timer);
6✔
1723
            }
6✔
1724
         }
1✔
1725
#endif
1726

1727
#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
1728
      void bench_pk_enc(
4✔
1729
         const Botan::Private_Key& key,
1730
         const std::string& nm,
1731
         const std::string& provider,
1732
         const std::string& padding,
1733
         std::chrono::milliseconds msec)
1734
         {
1735
         std::vector<uint8_t> plaintext, ciphertext;
4✔
1736

1737
         Botan::PK_Encryptor_EME enc(key, rng(), padding, provider);
4✔
1738
         Botan::PK_Decryptor_EME dec(key, rng(), padding, provider);
4✔
1739

1740
         auto enc_timer = make_timer(nm + " " + padding, provider, "encrypt");
12✔
1741
         auto dec_timer = make_timer(nm + " " + padding, provider, "decrypt");
12✔
1742

1743
         while(enc_timer->under(msec) || dec_timer->under(msec))
12✔
1744
            {
1745
            // Generate a new random ciphertext to decrypt
1746
            if(ciphertext.empty() || enc_timer->under(msec))
4✔
1747
               {
1748
               rng().random_vec(plaintext, enc.maximum_input_size());
4✔
1749
               ciphertext = enc_timer->run([&]() { return enc.encrypt(plaintext, rng()); });
12✔
1750
               }
1751

1752
            if(dec_timer->under(msec))
4✔
1753
               {
1754
               const auto dec_pt = dec_timer->run([&]() { return dec.decrypt(ciphertext); });
12✔
1755

1756
               if(!(Botan::unlock(dec_pt) == plaintext)) // sanity check
8✔
1757
                  {
1758
                  error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n";
×
1759
                  }
1760
               }
4✔
1761
            }
1762

1763
         record_result(enc_timer);
4✔
1764
         record_result(dec_timer);
4✔
1765
         }
12✔
1766

1767
      void bench_pk_ka(const std::string& algo,
14✔
1768
                       const std::string& nm,
1769
                       const std::string& params,
1770
                       const std::string& provider,
1771
                       std::chrono::milliseconds msec)
1772
         {
1773
         const std::string kdf = "KDF2(SHA-256)"; // arbitrary choice
14✔
1774

1775
         auto keygen_timer = make_timer(nm, provider, "keygen");
28✔
1776

1777
         std::unique_ptr<Botan::Private_Key> key1(keygen_timer->run([&]
14✔
1778
            {
1779
            return Botan::create_private_key(algo, rng(), params);
14✔
1780
            }));
14✔
1781
         std::unique_ptr<Botan::Private_Key> key2(keygen_timer->run([&]
14✔
1782
            {
1783
            return Botan::create_private_key(algo, rng(), params);
14✔
1784
            }));
14✔
1785

1786
         record_result(keygen_timer);
14✔
1787

1788
         const Botan::PK_Key_Agreement_Key& ka_key1 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key1);
14✔
1789
         const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key2);
14✔
1790

1791
         Botan::PK_Key_Agreement ka1(ka_key1, rng(), kdf, provider);
14✔
1792
         Botan::PK_Key_Agreement ka2(ka_key2, rng(), kdf, provider);
14✔
1793

1794
         const std::vector<uint8_t> ka1_pub = ka_key1.public_value();
14✔
1795
         const std::vector<uint8_t> ka2_pub = ka_key2.public_value();
14✔
1796

1797
         auto ka_timer = make_timer(nm, provider, "key agreements");
28✔
1798

1799
         while(ka_timer->under(msec))
32✔
1800
            {
1801
            Botan::SymmetricKey symkey1 = ka_timer->run([&]() { return ka1.derive_key(32, ka2_pub); });
36✔
1802
            Botan::SymmetricKey symkey2 = ka_timer->run([&]() { return ka2.derive_key(32, ka1_pub); });
36✔
1803

1804
            if(symkey1 != symkey2)
18✔
1805
               {
1806
               error_output() << "Key agreement mismatch in PK bench\n";
×
1807
               }
1808
            }
36✔
1809

1810
         record_result(ka_timer);
14✔
1811
         }
70✔
1812

1813
      void bench_pk_kem(const Botan::Private_Key& key,
11✔
1814
                        const std::string& nm,
1815
                        const std::string& provider,
1816
                        const std::string& kdf,
1817
                        std::chrono::milliseconds msec)
1818
         {
1819
         Botan::PK_KEM_Decryptor dec(key, rng(), kdf, provider);
11✔
1820
         Botan::PK_KEM_Encryptor enc(key, kdf, provider);
11✔
1821

1822
         auto kem_enc_timer = make_timer(nm, provider, "KEM encrypt");
22✔
1823
         auto kem_dec_timer = make_timer(nm, provider, "KEM decrypt");
22✔
1824

1825
         while(kem_enc_timer->under(msec) && kem_dec_timer->under(msec))
35✔
1826
            {
1827
            Botan::secure_vector<uint8_t> encap_key, enc_shared_key;
24✔
1828
            Botan::secure_vector<uint8_t> salt = rng().random_vec(16);
24✔
1829

1830
            kem_enc_timer->start();
24✔
1831
            enc.encrypt(encap_key, enc_shared_key, 64, rng(), salt);
24✔
1832
            kem_enc_timer->stop();
24✔
1833

1834
            kem_dec_timer->start();
24✔
1835
            Botan::secure_vector<uint8_t> dec_shared_key = dec.decrypt(encap_key, 64, salt);
24✔
1836
            kem_dec_timer->stop();
24✔
1837

1838
            if(enc_shared_key != dec_shared_key)
24✔
1839
               {
1840
               error_output() << "KEM mismatch in PK bench\n";
×
1841
               }
1842
            }
96✔
1843

1844
         record_result(kem_enc_timer);
11✔
1845
         record_result(kem_dec_timer);
11✔
1846
         }
11✔
1847

1848
      void bench_pk_sig_ecc(const std::string& algo,
6✔
1849
                            const std::string& emsa,
1850
                            const std::string& provider,
1851
                            const std::vector<std::string>& params,
1852
                            std::chrono::milliseconds msec)
1853
         {
1854
         for(std::string grp : params)
32✔
1855
            {
1856
            const std::string nm = grp.empty() ? algo : (algo + "-" + grp);
26✔
1857

1858
            auto keygen_timer = make_timer(nm, provider, "keygen");
52✔
1859

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

1865
            record_result(keygen_timer);
26✔
1866
            bench_pk_sig(*key, nm, provider, emsa, msec);
26✔
1867
            }
48✔
1868
         }
6✔
1869

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

1878
         Botan::PK_Signer   sig(key, rng(), padding, Botan::Signature_Format::Standard, provider);
40✔
1879
         Botan::PK_Verifier ver(key, padding, Botan::Signature_Format::Standard, provider);
40✔
1880

1881
         auto sig_timer = make_timer(nm + " " + padding, provider, "sign");
119✔
1882
         auto ver_timer = make_timer(nm + " " + padding, provider, "verify");
119✔
1883

1884
         size_t invalid_sigs = 0;
40✔
1885

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

1896
               signature = sig_timer->run([&]() { return sig.sign_message(message, rng()); });
162✔
1897

1898
               bad_signature = signature;
54✔
1899
               bad_signature[rng().next_byte() % bad_signature.size()] ^= rng().next_nonzero_byte();
54✔
1900
               }
1901

1902
            if(ver_timer->under(msec))
84✔
1903
               {
1904
               const bool verified = ver_timer->run([&]
72✔
1905
                  {
1906
                  return ver.verify_message(message, signature);
72✔
1907
                  });
1908

1909
               if(!verified)
72✔
1910
                  {
1911
                  invalid_sigs += 1;
×
1912
                  }
1913

1914
               const bool verified_bad = ver_timer->run([&]
72✔
1915
                  {
1916
                  return ver.verify_message(message, bad_signature);
72✔
1917
                  });
1918

1919
               if(verified_bad)
72✔
1920
                  {
1921
                  error_output() << "Bad signature accepted in " << nm << " signature bench\n";
×
1922
                  }
1923
               }
1924
            }
1925

1926
         if(invalid_sigs > 0)
40✔
1927
            error_output() << invalid_sigs << " generated signatures rejected in " << nm << " signature bench\n";
×
1928

1929
         const size_t events = static_cast<size_t>(std::min(sig_timer->events(), ver_timer->events()));
40✔
1930

1931
         record_result(sig_timer);
40✔
1932
         record_result(ver_timer);
40✔
1933

1934
         return events;
80✔
1935
         }
160✔
1936
#endif
1937

1938
#if defined(BOTAN_HAS_RSA)
1939
      void bench_rsa_keygen(const std::string& provider,
1✔
1940
                            std::chrono::milliseconds msec)
1941
         {
1942
         for(size_t keylen : { 1024, 2048, 3072, 4096 })
5✔
1943
            {
1944
            const std::string nm = "RSA-" + std::to_string(keylen);
4✔
1945
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1946

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

1953
               BOTAN_ASSERT(key->check_key(rng(), true), "Key is ok");
4✔
1954
               }
4✔
1955

1956
            record_result(keygen_timer);
4✔
1957
            }
4✔
1958
         }
1✔
1959

1960
      void bench_rsa(const std::string& provider,
1✔
1961
                     std::chrono::milliseconds msec)
1962
         {
1963
         for(size_t keylen : { 1024, 2048, 3072, 4096 })
5✔
1964
            {
1965
            const std::string nm = "RSA-" + std::to_string(keylen);
4✔
1966

1967
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
1968

1969
            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
4✔
1970
               {
1971
               return Botan::create_private_key("RSA", rng(), std::to_string(keylen));
4✔
1972
               }));
4✔
1973

1974
            record_result(keygen_timer);
4✔
1975

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

1979
            //bench_pk_sig(*key, nm, provider, "PSSR(SHA-256)", msec);
1980
            //bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
1981
            //bench_pk_enc(*key, nm, provider, "OAEP(SHA-1)", msec);
1982
            }
4✔
1983
         }
1✔
1984
#endif
1985

1986
#if defined(BOTAN_HAS_ECDSA)
1987
      void bench_ecdsa(const std::vector<std::string>& groups,
1✔
1988
                       const std::string& provider,
1989
                       std::chrono::milliseconds msec)
1990
         {
1991
         return bench_pk_sig_ecc("ECDSA", "SHA-256", provider, groups, msec);
2✔
1992
         }
1993

1994
      void bench_ecdsa_recovery(const std::vector<std::string>& groups,
1✔
1995
                                const std::string& /*unused*/,
1996
                                std::chrono::milliseconds msec)
1997
         {
1998
         for(const std::string& group_name : groups)
7✔
1999
            {
2000
            Botan::EC_Group group(group_name);
6✔
2001
            auto recovery_timer = make_timer("ECDSA recovery " + group_name);
18✔
2002

2003
            while(recovery_timer->under(msec))
12✔
2004
               {
2005
               Botan::ECDSA_PrivateKey key(rng(), group);
6✔
2006

2007
               std::vector<uint8_t> message(group.get_order_bits() / 8);
6✔
2008
               rng().randomize(message.data(), message.size());
6✔
2009

2010
               Botan::PK_Signer signer(key, rng(), "Raw");
6✔
2011
               signer.update(message);
6✔
2012
               std::vector<uint8_t> signature = signer.signature(rng());
6✔
2013

2014
               Botan::PK_Verifier verifier(key, "Raw", Botan::Signature_Format::Standard, "base");
6✔
2015
               verifier.update(message);
6✔
2016
               BOTAN_ASSERT(verifier.check_signature(signature), "Valid signature");
6✔
2017

2018
               Botan::BigInt r(signature.data(), signature.size()/2);
6✔
2019
               Botan::BigInt s(signature.data() + signature.size()/2, signature.size()/2);
6✔
2020

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

2023
               recovery_timer->run([&]() {
6✔
2024
                  Botan::ECDSA_PublicKey pubkey(group, message, r, s, v);
6✔
2025
                  BOTAN_ASSERT(pubkey.public_point() == key.public_point(), "Recovered public key");
6✔
2026
                  });
6✔
2027
               }
24✔
2028

2029
            record_result(recovery_timer);
6✔
2030
            }
6✔
2031

2032
         }
1✔
2033

2034
#endif
2035

2036
#if defined(BOTAN_HAS_ECKCDSA)
2037
      void bench_eckcdsa(const std::vector<std::string>& groups,
1✔
2038
                         const std::string& provider,
2039
                         std::chrono::milliseconds msec)
2040
         {
2041
         return bench_pk_sig_ecc("ECKCDSA", "SHA-256", provider, groups, msec);
2✔
2042
         }
2043
#endif
2044

2045
#if defined(BOTAN_HAS_GOST_34_10_2001)
2046
      void bench_gost_3410(const std::string& provider,
1✔
2047
                           std::chrono::milliseconds msec)
2048
         {
2049
         return bench_pk_sig_ecc("GOST-34.10", "GOST-34.11", provider, {"gost_256A"}, msec);
3✔
2050
         }
2051
#endif
2052

2053
#if defined(BOTAN_HAS_SM2)
2054
      void bench_sm2(const std::vector<std::string>& groups,
1✔
2055
                     const std::string& provider,
2056
                     std::chrono::milliseconds msec)
2057
         {
2058
         return bench_pk_sig_ecc("SM2_Sig", "SM3", provider, groups, msec);
2✔
2059
         }
2060
#endif
2061

2062
#if defined(BOTAN_HAS_ECGDSA)
2063
      void bench_ecgdsa(const std::vector<std::string>& groups,
1✔
2064
                        const std::string& provider,
2065
                        std::chrono::milliseconds msec)
2066
         {
2067
         return bench_pk_sig_ecc("ECGDSA", "SHA-256", provider, groups, msec);
2✔
2068
         }
2069
#endif
2070

2071
#if defined(BOTAN_HAS_ED25519)
2072
      void bench_ed25519(const std::string& provider,
1✔
2073
                         std::chrono::milliseconds msec)
2074
         {
2075
         return bench_pk_sig_ecc("Ed25519", "Pure", provider, std::vector<std::string>{""}, msec);
3✔
2076
         }
2077
#endif
2078

2079
#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
2080
      void bench_dh(const std::string& provider,
1✔
2081
                    std::chrono::milliseconds msec)
2082
         {
2083
         for(size_t bits : { 1024, 1536, 2048, 3072, 4096, 6144, 8192 })
8✔
2084
            {
2085
            bench_pk_ka("DH",
14✔
2086
                        "DH-" + std::to_string(bits),
14✔
2087
                        "modp/ietf/" + std::to_string(bits),
14✔
2088
                        provider, msec);
2089
            }
2090
         }
1✔
2091
#endif
2092

2093
#if defined(BOTAN_HAS_DSA)
2094
      void bench_dsa(const std::string& provider, std::chrono::milliseconds msec)
1✔
2095
         {
2096
         for(size_t bits : { 1024, 2048, 3072 })
4✔
2097
            {
2098
            const std::string nm = "DSA-" + std::to_string(bits);
3✔
2099

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

2103
            auto keygen_timer = make_timer(nm, provider, "keygen");
6✔
2104

2105
            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
3✔
2106
               {
2107
               return Botan::create_private_key("DSA", rng(), params);
3✔
2108
               }));
3✔
2109

2110
            record_result(keygen_timer);
3✔
2111

2112
            bench_pk_sig(*key, nm, provider, "SHA-256", msec);
6✔
2113
            }
3✔
2114
         }
1✔
2115
#endif
2116

2117
#if defined(BOTAN_HAS_ELGAMAL)
2118
      void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec)
1✔
2119
         {
2120
         for(size_t keylen : { 1024, 2048, 3072, 4096 })
5✔
2121
            {
2122
            const std::string nm = "ElGamal-" + std::to_string(keylen);
4✔
2123

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

2126
            auto keygen_timer = make_timer(nm, provider, "keygen");
8✔
2127

2128
            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
4✔
2129
               {
2130
               return Botan::create_private_key("ElGamal", rng(), params);
4✔
2131
               }));
4✔
2132

2133
            record_result(keygen_timer);
4✔
2134

2135
            bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
8✔
2136
            }
4✔
2137
         }
1✔
2138
#endif
2139

2140
#if defined(BOTAN_HAS_ECDH)
2141
      void bench_ecdh(const std::vector<std::string>& groups,
1✔
2142
                      const std::string& provider,
2143
                      std::chrono::milliseconds msec)
2144
         {
2145
         for(const std::string& grp : groups)
7✔
2146
            {
2147
            bench_pk_ka("ECDH", "ECDH-" + grp, grp, provider, msec);
15✔
2148
            }
2149
         }
1✔
2150
#endif
2151

2152
#if defined(BOTAN_HAS_CURVE_25519)
2153
      void bench_curve25519(const std::string& provider,
1✔
2154
                            std::chrono::milliseconds msec)
2155
         {
2156
         bench_pk_ka("Curve25519", "Curve25519", "", provider, msec);
2✔
2157
         }
1✔
2158
#endif
2159

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

2173
         const std::vector<std::pair<size_t, size_t>> mce_params =
1✔
2174
            {
2175
               { 2480, 45 },
2176
               { 2960, 57 },
2177
               { 3408, 67 },
2178
               { 4624, 95 },
2179
               { 6624, 115 }
2180
            };
1✔
2181

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

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

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

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

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

2203
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
2204
      void bench_kyber(const std::string& provider,
1✔
2205
                       std::chrono::milliseconds msec)
2206
         {
2207
         const Botan::KyberMode::Mode all_modes[] = {
1✔
2208
            Botan::KyberMode::Kyber512,
2209
            Botan::KyberMode::Kyber512_90s,
2210
            Botan::KyberMode::Kyber768,
2211
            Botan::KyberMode::Kyber768_90s,
2212
            Botan::KyberMode::Kyber1024,
2213
            Botan::KyberMode::Kyber1024_90s,
2214
         };
2215

2216
         for(auto modet: all_modes)
7✔
2217
            {
2218
            Botan::KyberMode mode(modet);
6✔
2219

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

2225
#if !defined(BOTAN_HAS_KYBER_90S)
2226
            if(mode.is_90s())
2227
               continue;
2228
#endif
2229

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

2232
            auto key = keygen_timer->run([&]
6✔
2233
               {
2234
               return Botan::Kyber_PrivateKey(rng(), mode);
6✔
2235
               });
6✔
2236

2237
            record_result(keygen_timer);
6✔
2238

2239
            bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec);
12✔
2240
            }
6✔
2241
         }
1✔
2242
#endif
2243

2244
#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
2245
      void bench_dilithium(const std::string& provider,
1✔
2246
                       std::chrono::milliseconds msec)
2247
         {
2248
         const Botan::DilithiumMode::Mode all_modes[] = {
1✔
2249
            Botan::DilithiumMode::Dilithium4x4,
2250
            Botan::DilithiumMode::Dilithium4x4_AES,
2251
            Botan::DilithiumMode::Dilithium6x5,
2252
            Botan::DilithiumMode::Dilithium6x5_AES,
2253
            Botan::DilithiumMode::Dilithium8x7,
2254
            Botan::DilithiumMode::Dilithium8x7_AES
2255
         };
2256

2257
         for(auto modet: all_modes)
7✔
2258
            {
2259
            Botan::DilithiumMode mode(modet);
6✔
2260

2261
#if !defined(BOTAN_HAS_DILITHIUM)
2262
            if(mode.is_modern())
2263
               continue;
2264
#endif
2265

2266
#if !defined(BOTAN_HAS_DILITHIUM_AES)
2267
            if(mode.is_aes())
2268
               continue;
2269
#endif
2270

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

2273
            auto key = keygen_timer->run([&]
6✔
2274
               {
2275
               return Botan::Dilithium_PrivateKey(rng(), mode);
6✔
2276
               });
6✔
2277

2278
            record_result(keygen_timer);
6✔
2279

2280
            bench_pk_sig(key, mode.to_string(), provider, "", msec);
12✔
2281
            }
6✔
2282
         }
1✔
2283
#endif
2284

2285
#if defined(BOTAN_HAS_SPHINCS_PLUS)
2286
      void bench_sphincs_plus(const std::string& provider,
×
2287
                       std::chrono::milliseconds msec)
2288
         {
2289

2290
            // Sphincs_Parameter_Set set, Sphincs_Hash_Type hash
2291
            std::vector<std::string> sphincs_params
×
2292
               {
2293
               "SphincsPlus-sha2-128s-r3.1",
2294
               "SphincsPlus-sha2-128f-r3.1",
2295
               "SphincsPlus-sha2-192s-r3.1",
2296
               "SphincsPlus-sha2-192f-r3.1",
2297
               "SphincsPlus-sha2-256s-r3.1",
2298
               "SphincsPlus-sha2-256f-r3.1",
2299
               "SphincsPlus-shake-128s-r3.1",
2300
               "SphincsPlus-shake-128f-r3.1",
2301
               "SphincsPlus-shake-192s-r3.1",
2302
               "SphincsPlus-shake-192f-r3.1",
2303
               "SphincsPlus-shake-256s-r3.1",
2304
               "SphincsPlus-shake-256f-r3.1"
2305
               };
×
2306

2307
         for(auto params : sphincs_params)
×
2308
            {
2309
            auto keygen_timer = make_timer(params, provider, "keygen");
×
2310

2311
            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
×
2312
               {
2313
               return Botan::create_private_key("SPHINCS+", rng(), params);
×
2314
               }));
×
2315

2316
            record_result(keygen_timer);
×
2317
            if(bench_pk_sig(*key, params, provider, "", msec) == 1)
×
2318
               break;
2319
            }
×
2320
         }
×
2321
#endif
2322

2323
#if defined(BOTAN_HAS_XMSS_RFC8391)
2324
      void bench_xmss(const std::string& provider,
1✔
2325
                      std::chrono::milliseconds msec)
2326
         {
2327
         /*
2328
         We only test H10 signatures here since already they are quite slow (a
2329
         few seconds per signature). On a fast machine, H16 signatures take 1-2
2330
         minutes to generate and H20 signatures take 5-10 minutes to generate
2331
         */
2332
         std::vector<std::string> xmss_params
1✔
2333
            {
2334
            "XMSS-SHA2_10_256",
2335
            "XMSS-SHAKE_10_256",
2336
            "XMSS-SHA2_10_512",
2337
            "XMSS-SHAKE_10_512",
2338
            };
5✔
2339

2340
         for(std::string params : xmss_params)
1✔
2341
            {
2342
            auto keygen_timer = make_timer(params, provider, "keygen");
2✔
2343

2344
            std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
1✔
2345
               {
2346
               return Botan::create_private_key("XMSS", rng(), params);
1✔
2347
               }));
1✔
2348

2349
            record_result(keygen_timer);
1✔
2350
            if(bench_pk_sig(*key, params, provider, "", msec) == 1)
1✔
2351
               break;
2352
            }
2✔
2353
         }
1✔
2354
#endif
2355

2356
#if defined(BOTAN_HAS_ZFEC)
2357
      void bench_zfec(std::chrono::milliseconds msec)
1✔
2358
         {
2359
         const size_t k = 4;
1✔
2360
         const size_t n = 16;
1✔
2361

2362
         Botan::ZFEC zfec(k, n);
1✔
2363

2364
         const size_t share_size = 256 * 1024;
1✔
2365

2366
         std::vector<uint8_t> input(share_size * k);
1✔
2367
         rng().randomize(input.data(), input.size());
1✔
2368

2369
         std::vector<uint8_t> output(share_size * n);
1✔
2370

2371
         auto enc_fn = [&](size_t share, const uint8_t buf[], size_t len)
17✔
2372
            {
2373
            std::memcpy(&output[share*share_size], buf, len);
16✔
2374
            };
1✔
2375

2376
         auto enc_timer = make_timer("zfec " +
3✔
2377
                                     std::to_string(k) + "/" +
4✔
2378
                                     std::to_string(n),
1✔
2379
                                     input.size(),
2380
                                     "encode", "", input.size());
3✔
2381

2382
         enc_timer->run_until_elapsed(msec, [&]() {
1✔
2383
            zfec.encode(input.data(), input.size(), enc_fn);
1✔
2384
         });
1✔
2385

2386
         record_result(enc_timer);
1✔
2387

2388
         auto dec_timer = make_timer("zfec " +
3✔
2389
                                     std::to_string(k) + "/" +
4✔
2390
                                     std::to_string(n),
1✔
2391
                                     input.size(),
2392
                                     "decode", "", input.size());
3✔
2393

2394
         std::map<size_t, const uint8_t*> shares;
1✔
2395
         for(size_t i = 0; i != n; ++i)
17✔
2396
            {
2397
            shares[i] = &output[share_size * i];
16✔
2398
            }
2399

2400
         // remove data shares to make decoding maximally expensive:
2401
         while(shares.size() != k)
13✔
2402
            {
2403
            shares.erase(shares.begin());
12✔
2404
            }
2405

2406
         std::vector<uint8_t> recovered(share_size * k);
1✔
2407

2408
         auto dec_fn = [&](size_t share, const uint8_t buf[], size_t len)
5✔
2409
            {
2410
            std::memcpy(&recovered[share * share_size], buf, len);
4✔
2411
            };
1✔
2412

2413
         dec_timer->run_until_elapsed(msec, [&]() {
1✔
2414
            zfec.decode_shares(shares, share_size, dec_fn);
1✔
2415
         });
1✔
2416

2417
         record_result(dec_timer);
1✔
2418

2419
         if(recovered != input)
1✔
2420
            {
2421
            error_output() << "ZFEC recovery failed\n";
×
2422
            }
2423
         }
4✔
2424

2425
#endif
2426

2427
#if defined(BOTAN_HAS_POLY_DBL)
2428
      void bench_poly_dbl(std::chrono::milliseconds msec)
1✔
2429
         {
2430
         for(size_t sz : { 8, 16, 24, 32, 64, 128 })
7✔
2431
            {
2432
            auto be_timer = make_timer("poly_dbl_be_" + std::to_string(sz));
12✔
2433
            auto le_timer = make_timer("poly_dbl_le_" + std::to_string(sz));
12✔
2434

2435
            std::vector<uint8_t> buf(sz);
6✔
2436
            rng().randomize(buf.data(), sz);
6✔
2437

2438
            be_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n(buf.data(), buf.data(), sz); });
16,519✔
2439
            le_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n_le(buf.data(), buf.data(), sz); });
18,620✔
2440

2441
            record_result(be_timer);
6✔
2442
            record_result(le_timer);
6✔
2443
            }
6✔
2444
         }
1✔
2445
#endif
2446

2447
#if defined(BOTAN_HAS_BCRYPT)
2448

2449
      void bench_bcrypt()
1✔
2450
         {
2451
         const std::string password = "not a very good password";
1✔
2452

2453
         for(uint8_t work_factor = 4; work_factor <= 14; ++work_factor)
12✔
2454
            {
2455
            auto timer = make_timer("bcrypt wf=" + std::to_string(work_factor));
22✔
2456

2457
            timer->run([&] {
11✔
2458
               Botan::generate_bcrypt(password, rng(), work_factor);
11✔
2459
                  });
11✔
2460

2461
            record_result(timer);
11✔
2462
            }
11✔
2463
         }
1✔
2464
#endif
2465

2466
#if defined(BOTAN_HAS_PASSHASH9)
2467

2468
      void bench_passhash9()
1✔
2469
         {
2470
         const std::string password = "not a very good password";
1✔
2471

2472
         for(uint8_t alg = 0; alg <= 4; ++alg)
6✔
2473
            {
2474
            if(Botan::is_passhash9_alg_supported(alg) == false)
5✔
2475
               continue;
×
2476

2477
            for(auto work_factor : { 10, 15 })
15✔
2478
               {
2479
               auto timer = make_timer("passhash9 alg=" + std::to_string(alg) +
30✔
2480
                                                         " wf=" + std::to_string(work_factor));
50✔
2481

2482
               timer->run([&] {
10✔
2483
                  Botan::generate_passhash9(password, rng(), static_cast<uint8_t>(work_factor), alg);
10✔
2484
                  });
10✔
2485

2486
               record_result(timer);
10✔
2487
               }
10✔
2488
            }
2489
         }
1✔
2490
#endif
2491

2492
#if defined(BOTAN_HAS_SCRYPT)
2493

2494
      void bench_scrypt(const std::string& /*provider*/,
1✔
2495
                        std::chrono::milliseconds msec)
2496
         {
2497
         auto pwdhash_fam = Botan::PasswordHashFamily::create_or_throw("Scrypt");
1✔
2498

2499
         for(size_t N : { 8192, 16384, 32768, 65536 })
5✔
2500
            {
2501
            for(size_t r : { 1, 8, 16 })
16✔
2502
               {
2503
               for(size_t p : { 1 })
12✔
2504
                  {
2505
                  auto pwdhash = pwdhash_fam->from_params(N, r, p);
12✔
2506

2507
                  auto scrypt_timer = make_timer(
12✔
2508
                     "scrypt-" + std::to_string(N) + "-" +
24✔
2509
                     std::to_string(r) + "-" + std::to_string(p) +
48✔
2510
                     " (" + std::to_string(pwdhash->total_memory_usage() / (1024*1024)) + " MiB)");
60✔
2511

2512
                  uint8_t out[64];
12✔
2513
                  uint8_t salt[8];
12✔
2514
                  rng().randomize(salt, sizeof(salt));
12✔
2515

2516
                  while(scrypt_timer->under(msec))
24✔
2517
                     {
2518
                     scrypt_timer->run([&] {
12✔
2519
                        pwdhash->derive_key(out, sizeof(out),
12✔
2520
                                            "password", 8,
2521
                                            salt, sizeof(salt));
12✔
2522

2523
                        Botan::copy_mem(salt, out, 8);
12✔
2524
                        });
12✔
2525
                     }
2526

2527
                  record_result(scrypt_timer);
12✔
2528

2529
                  if(scrypt_timer->events() == 1)
12✔
2530
                     break;
2531
                  }
24✔
2532
               }
2533
            }
2534

2535
         }
1✔
2536

2537
#endif
2538

2539
#if defined(BOTAN_HAS_ARGON2)
2540

2541
      void bench_argon2(const std::string& /*provider*/,
1✔
2542
                        std::chrono::milliseconds msec)
2543
         {
2544
         auto pwhash_fam = Botan::PasswordHashFamily::create_or_throw("Argon2id");
1✔
2545

2546
         for(size_t M : { 8*1024, 64*1024, 256*1024 })
4✔
2547
            {
2548
            for(size_t t : { 1, 4 })
9✔
2549
               {
2550
               for(size_t p : { 1, 4 })
18✔
2551
                  {
2552
                  auto pwhash = pwhash_fam->from_params(M, t, p);
12✔
2553
                  auto timer = make_timer(pwhash->to_string());
36✔
2554

2555
                  uint8_t out[64];
12✔
2556
                  uint8_t salt[16];
12✔
2557
                  rng().randomize(salt, sizeof(salt));
12✔
2558

2559
                  while(timer->under(msec))
24✔
2560
                     {
2561
                     timer->run([&]
12✔
2562
                        {
2563
                        pwhash->derive_key(out, sizeof(out),
12✔
2564
                                           "password", 8,
2565
                                           salt, sizeof(salt));
12✔
2566
                        });
12✔
2567
                     }
2568

2569
                  record_result(timer);
12✔
2570
                  }
24✔
2571
               }
2572
            }
2573
         }
1✔
2574

2575
#endif
2576

2577
   };
2578

2579
BOTAN_REGISTER_COMMAND("speed", Speed);
35✔
2580

2581
}
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