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

randombit / botan / 11331525401

14 Oct 2024 04:29PM UTC coverage: 91.093% (-0.03%) from 91.12%
11331525401

Pull #4291

github

web-flow
Merge f5ffe99f5 into ed74c9542
Pull Request #4291: PQC: SLH-DSA

90346 of 99180 relevant lines covered (91.09%)

9678761.99 hits per line

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

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

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

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

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

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

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

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

39
#if defined(BOTAN_HAS_XOF)
40
   #include <botan/xof.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_ECC_GROUP)
52
   #include <botan/ec_group.h>
53
#endif
54

55
namespace Botan_CLI {
56

57
using Botan::Timer;
58

59
namespace {
60

61
class JSON_Output final {
2✔
62
   public:
63
      void add(const Timer& timer) { m_results.push_back(timer); }
2✔
64

65
      std::string print() const {
1✔
66
         std::ostringstream out;
1✔
67

68
         out << "[\n";
1✔
69

70
         for(size_t i = 0; i != m_results.size(); ++i) {
3✔
71
            const Timer& t = m_results[i];
2✔
72

73
            out << "{"
2✔
74
                << "\"algo\": \"" << t.get_name() << "\", "
2✔
75
                << "\"op\": \"" << t.doing() << "\", "
2✔
76
                << "\"events\": " << t.events() << ", ";
2✔
77

78
            if(t.cycles_consumed() > 0) {
4✔
79
               out << "\"cycles\": " << t.cycles_consumed() << ", ";
4✔
80
            }
81

82
            if(t.buf_size() > 0) {
2✔
83
               out << "\"bps\": " << static_cast<uint64_t>(t.events() / (t.value() / 1000000000.0)) << ", ";
2✔
84
               out << "\"buf_size\": " << t.buf_size() << ", ";
2✔
85
            }
86

87
            out << "\"nanos\": " << t.value() << "}";
2✔
88

89
            if(i != m_results.size() - 1) {
2✔
90
               out << ",";
1✔
91
            }
92

93
            out << "\n";
2✔
94
         }
95
         out << "]\n";
1✔
96

97
         return out.str();
2✔
98
      }
1✔
99

100
   private:
101
      std::vector<Timer> m_results;
102
};
103

104
class Summary final {
1✔
105
   public:
106
      Summary() = default;
1✔
107

108
      void add(const Timer& t) {
2✔
109
         if(t.buf_size() == 0) {
2✔
110
            m_ops_entries.push_back(t);
×
111
         } else {
112
            m_bps_entries[std::make_pair(t.doing(), t.get_name())].push_back(t);
4✔
113
         }
114
      }
2✔
115

116
      std::string print() {
1✔
117
         const size_t name_padding = 35;
1✔
118
         const size_t op_name_padding = 16;
1✔
119
         const size_t op_padding = 16;
1✔
120

121
         std::ostringstream result_ss;
1✔
122
         result_ss << std::fixed;
1✔
123

124
         if(!m_bps_entries.empty()) {
1✔
125
            result_ss << "\n";
1✔
126

127
            // add table header
128
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
1✔
129
                      << "operation";
1✔
130

131
            for(const Timer& t : m_bps_entries.begin()->second) {
2✔
132
               result_ss << std::setw(op_padding) << std::right << (std::to_string(t.buf_size()) + " bytes");
2✔
133
            }
134
            result_ss << "\n";
1✔
135

136
            // add table entries
137
            for(const auto& entry : m_bps_entries) {
3✔
138
               if(entry.second.empty()) {
2✔
139
                  continue;
×
140
               }
141

142
               result_ss << std::setw(name_padding) << std::left << (entry.first.second) << std::setw(op_name_padding)
2✔
143
                         << std::left << (entry.first.first);
2✔
144

145
               for(const Timer& t : entry.second) {
4✔
146
                  if(t.events() == 0) {
2✔
147
                     result_ss << std::setw(op_padding) << std::right << "N/A";
×
148
                  } else {
149
                     result_ss << std::setw(op_padding) << std::right << std::setprecision(2)
2✔
150
                               << (t.bytes_per_second() / 1000.0);
2✔
151
                  }
152
               }
153

154
               result_ss << "\n";
2✔
155
            }
156

157
            result_ss << "\n[results are the number of 1000s bytes processed per second]\n";
1✔
158
         }
159

160
         if(!m_ops_entries.empty()) {
1✔
161
            result_ss << std::setprecision(6) << "\n";
×
162

163
            // sort entries
164
            std::sort(m_ops_entries.begin(), m_ops_entries.end());
×
165

166
            // add table header
167
            result_ss << std::setw(name_padding) << std::left << "algo" << std::setw(op_name_padding) << std::left
×
168
                      << "operation" << std::setw(op_padding) << std::right << "sec/op" << std::setw(op_padding)
×
169
                      << std::right << "op/sec"
×
170
                      << "\n";
×
171

172
            // add table entries
173
            for(const Timer& entry : m_ops_entries) {
×
174
               result_ss << std::setw(name_padding) << std::left << entry.get_name() << std::setw(op_name_padding)
×
175
                         << std::left << entry.doing() << std::setw(op_padding) << std::right
×
176
                         << entry.seconds_per_event() << std::setw(op_padding) << std::right
×
177
                         << entry.events_per_second() << "\n";
×
178
            }
179
         }
180

181
         return result_ss.str();
2✔
182
      }
1✔
183

184
   private:
185
      std::map<std::pair<std::string, std::string>, std::vector<Timer>> m_bps_entries;
186
      std::vector<Timer> m_ops_entries;
187
};
188

189
std::vector<size_t> unique_buffer_sizes(const std::string& cmdline_arg) {
30✔
190
   const size_t MAX_BUF_SIZE = 64 * 1024 * 1024;
30✔
191

192
   std::set<size_t> buf;
30✔
193
   for(const std::string& size_str : Command::split_on(cmdline_arg, ',')) {
58✔
194
      size_t x = 0;
31✔
195
      try {
31✔
196
         size_t converted = 0;
31✔
197
         x = static_cast<size_t>(std::stoul(size_str, &converted, 0));
31✔
198

199
         if(converted != size_str.size()) {
30✔
200
            throw CLI_Usage_Error("Invalid integer");
×
201
         }
202
      } catch(std::exception&) {
1✔
203
         throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size");
2✔
204
      }
1✔
205

206
      if(x == 0) {
30✔
207
         throw CLI_Usage_Error("Cannot have a zero-sized buffer");
2✔
208
      }
209

210
      if(x > MAX_BUF_SIZE) {
29✔
211
         throw CLI_Usage_Error("Specified buffer size is too large");
2✔
212
      }
213

214
      buf.insert(x);
28✔
215
   }
30✔
216

217
   return std::vector<size_t>(buf.begin(), buf.end());
30✔
218
}
27✔
219

220
}  // namespace
221

222
class Speed final : public Command {
×
223
   public:
224
      Speed() :
31✔
225
            Command(
226
               "speed --msec=500 --format=default --ecc-groups= --provider= --buf-size=1024 --clear-cpuid= --cpu-clock-speed=0 --cpu-clock-ratio=1.0 *algos") {
62✔
227
      }
31✔
228

229
      static std::vector<std::string> default_benchmark_list() {
×
230
         /*
231
         This is not intended to be exhaustive: it just hits the high
232
         points of the most interesting or widely used algorithms.
233
         */
234
         // clang-format off
235
         return {
×
236
            /* Block ciphers */
237
            "AES-128",
238
            "AES-192",
239
            "AES-256",
240
            "ARIA-128",
241
            "ARIA-192",
242
            "ARIA-256",
243
            "Blowfish",
244
            "CAST-128",
245
            "Camellia-128",
246
            "Camellia-192",
247
            "Camellia-256",
248
            "DES",
249
            "TripleDES",
250
            "GOST-28147-89",
251
            "IDEA",
252
            "Noekeon",
253
            "SHACAL2",
254
            "SM4",
255
            "Serpent",
256
            "Threefish-512",
257
            "Twofish",
258

259
            /* Cipher modes */
260
            "AES-128/CBC",
261
            "AES-128/CTR-BE",
262
            "AES-128/EAX",
263
            "AES-128/OCB",
264
            "AES-128/GCM",
265
            "AES-128/XTS",
266
            "AES-128/SIV",
267

268
            "Serpent/CBC",
269
            "Serpent/CTR-BE",
270
            "Serpent/EAX",
271
            "Serpent/OCB",
272
            "Serpent/GCM",
273
            "Serpent/XTS",
274
            "Serpent/SIV",
275

276
            "ChaCha20Poly1305",
277

278
            /* Stream ciphers */
279
            "RC4",
280
            "Salsa20",
281
            "ChaCha20",
282

283
            /* Hashes */
284
            "SHA-1",
285
            "SHA-256",
286
            "SHA-512",
287
            "SHA-3(256)",
288
            "SHA-3(512)",
289
            "RIPEMD-160",
290
            "Skein-512",
291
            "Blake2b",
292
            "Whirlpool",
293

294
            /* XOFs */
295
            "SHAKE-128",
296
            "SHAKE-256",
297

298
            /* MACs */
299
            "CMAC(AES-128)",
300
            "HMAC(SHA-256)",
301

302
            /* pubkey */
303
            "RSA",
304
            "DH",
305
            "ECDH",
306
            "ECDSA",
307
            "Ed25519",
308
            "Ed448",
309
            "X25519",
310
            "X448",
311
            "Kyber",
312
            "SLH-DSA",
313
            "SPHINCS+",
314
            "FrodoKEM",
315
            "HSS-LMS",
316
         };
×
317
         // clang-format on
318
      }
319

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

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

324
      void go() override {
30✔
325
         std::chrono::milliseconds msec(get_arg_sz("msec"));
30✔
326
         const std::string provider = get_arg("provider");
30✔
327
         std::vector<std::string> ecc_groups = Command::split_on(get_arg("ecc-groups"), ',');
63✔
328
         const std::string format = get_arg("format");
30✔
329
         const std::string clock_ratio = get_arg("cpu-clock-ratio");
33✔
330
         m_clock_speed = get_arg_sz("cpu-clock-speed");
30✔
331

332
         m_clock_cycle_ratio = std::strtod(clock_ratio.c_str(), nullptr);
30✔
333

334
         /*
335
         * This argument is intended to be the ratio between the cycle counter
336
         * and the actual machine cycles. It is extremely unlikely that there is
337
         * any machine where the cycle counter increments faster than the actual
338
         * clock.
339
         */
340
         if(m_clock_cycle_ratio < 0.0 || m_clock_cycle_ratio > 1.0) {
30✔
341
            throw CLI_Usage_Error("Unlikely CPU clock ratio of " + clock_ratio);
×
342
         }
343

344
         m_clock_cycle_ratio = 1.0 / m_clock_cycle_ratio;
30✔
345

346
         if(m_clock_speed != 0 && Botan::OS::get_cpu_cycle_counter() != 0) {
30✔
347
            error_output() << "The --cpu-clock-speed option is only intended to be used on "
×
348
                              "platforms without access to a cycle counter.\n"
349
                              "Expected incorrect results\n\n";
×
350
         }
351

352
         if(format == "table") {
30✔
353
            m_summary = std::make_unique<Summary>();
1✔
354
         } else if(format == "json") {
29✔
355
            m_json = std::make_unique<JSON_Output>();
1✔
356
         } else if(format != "default") {
28✔
357
            throw CLI_Usage_Error("Unknown --format type '" + format + "'");
×
358
         }
359

360
#if defined(BOTAN_HAS_ECC_GROUP)
361
         if(ecc_groups.empty()) {
30✔
362
            ecc_groups = {"secp256r1", "secp384r1", "secp521r1", "brainpool256r1", "brainpool384r1", "brainpool512r1"};
240✔
363
         } else if(ecc_groups.size() == 1 && ecc_groups[0] == "all") {
×
364
            auto all = Botan::EC_Group::known_named_groups();
×
365
            ecc_groups.assign(all.begin(), all.end());
×
366
         }
×
367
#endif
368

369
         std::vector<std::string> algos = get_arg_list("algos");
33✔
370

371
         const std::vector<size_t> buf_sizes = unique_buffer_sizes(get_arg("buf-size"));
63✔
372

373
         for(const std::string& cpuid_to_clear : Command::split_on(get_arg("clear-cpuid"), ',')) {
28✔
374
            auto bits = Botan::CPUID::bit_from_string(cpuid_to_clear);
1✔
375
            if(bits.empty()) {
1✔
376
               error_output() << "Warning don't know CPUID flag '" << cpuid_to_clear << "'\n";
1✔
377
            }
378

379
            for(auto bit : bits) {
1✔
380
               Botan::CPUID::clear_cpuid_bit(bit);
×
381
            }
382
         }
28✔
383

384
         if(verbose() || m_summary) {
27✔
385
            output() << Botan::version_string() << "\n"
2✔
386
                     << "CPUID: " << Botan::CPUID::to_string() << "\n\n";
3✔
387
         }
388

389
         const bool using_defaults = (algos.empty());
27✔
390
         if(using_defaults) {
27✔
391
            algos = default_benchmark_list();
×
392
         }
393

394
         class PerfConfig_Cli final : public PerfConfig {
27✔
395
            public:
396
               PerfConfig_Cli(std::chrono::milliseconds runtime,
27✔
397
                              const std::vector<std::string>& ecc_groups,
398
                              const std::vector<size_t>& buffer_sizes,
399
                              Speed* speed) :
27✔
400
                     m_runtime(runtime), m_ecc_groups(ecc_groups), m_buffer_sizes(buffer_sizes), m_speed(speed) {}
27✔
401

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

404
               const std::vector<std::string>& ecc_groups() const override { return m_ecc_groups; }
6✔
405

406
               std::chrono::milliseconds runtime() const override { return m_runtime; }
119✔
407

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

410
               Botan::RandomNumberGenerator& rng() const override { return m_speed->rng(); }
10,185✔
411

412
               void record_result(const Botan::Timer& timer) const override { m_speed->record_result(timer); }
439✔
413

414
               std::unique_ptr<Botan::Timer> make_timer(const std::string& alg,
455✔
415
                                                        uint64_t event_mult,
416
                                                        const std::string& what,
417
                                                        const std::string& provider,
418
                                                        size_t buf_size) const override {
419
                  return m_speed->make_timer(alg, event_mult, what, provider, buf_size);
910✔
420
               }
421

422
            private:
423
               std::chrono::milliseconds m_runtime;
424
               std::vector<std::string> m_ecc_groups;
425
               std::vector<size_t> m_buffer_sizes;
426
               Speed* m_speed;
427
         };
428

429
         PerfConfig_Cli perf_config(msec, ecc_groups, buf_sizes, this);
27✔
430

431
         for(const auto& algo : algos) {
74✔
432
            using namespace std::placeholders;
47✔
433

434
            if(auto perf = PerfTest::get(algo)) {
47✔
435
               perf->go(perf_config);
39✔
436
            }
437
#if defined(BOTAN_HAS_HASH)
438
            else if(!Botan::HashFunction::providers(algo).empty()) {
8✔
439
               bench_providers_of<Botan::HashFunction>(
1✔
440
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_hash, this, _1, _2, _3, _4));
2✔
441
            }
442
#endif
443
#if defined(BOTAN_HAS_XOF)
444
            else if(!Botan::XOF::providers(algo).empty()) {
7✔
445
               bench_providers_of<Botan::XOF>(
×
446
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_xof, this, _1, _2, _3, _4));
×
447
            }
448
#endif
449
#if defined(BOTAN_HAS_BLOCK_CIPHER)
450
            else if(!Botan::BlockCipher::providers(algo).empty()) {
7✔
451
               bench_providers_of<Botan::BlockCipher>(
4✔
452
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_block_cipher, this, _1, _2, _3, _4));
8✔
453
            }
454
#endif
455
#if defined(BOTAN_HAS_STREAM_CIPHER)
456
            else if(!Botan::StreamCipher::providers(algo).empty()) {
3✔
457
               bench_providers_of<Botan::StreamCipher>(
1✔
458
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_stream_cipher, this, _1, _2, _3, _4));
2✔
459
            }
460
#endif
461
#if defined(BOTAN_HAS_CIPHER_MODES)
462
            else if(auto enc = Botan::Cipher_Mode::create(algo, Botan::Cipher_Dir::Encryption, provider)) {
2✔
463
               auto dec = Botan::Cipher_Mode::create_or_throw(algo, Botan::Cipher_Dir::Decryption, provider);
1✔
464
               bench_cipher_mode(*enc, *dec, msec, buf_sizes);
1✔
465
            }
1✔
466
#endif
467
#if defined(BOTAN_HAS_MAC)
468
            else if(!Botan::MessageAuthenticationCode::providers(algo).empty()) {
1✔
469
               bench_providers_of<Botan::MessageAuthenticationCode>(
1✔
470
                  algo, provider, msec, buf_sizes, std::bind(&Speed::bench_mac, this, _1, _2, _3, _4));
2✔
471
            }
472
#endif
473
            else {
474
               if(verbose() || !using_defaults) {
×
475
                  error_output() << "Unknown algorithm '" << algo << "'\n";
×
476
               }
477
            }
49✔
478
         }
479

480
         if(m_json) {
27✔
481
            output() << m_json->print();
2✔
482
         }
483
         if(m_summary) {
27✔
484
            output() << m_summary->print() << "\n";
3✔
485
         }
486

487
         if(verbose() && m_clock_speed == 0 && m_cycles_consumed > 0 && m_ns_taken > 0) {
27✔
488
            const double seconds = static_cast<double>(m_ns_taken) / 1000000000;
×
489
            const double Hz = static_cast<double>(m_cycles_consumed) / seconds;
×
490
            const double MHz = Hz / 1000000;
×
491
            output() << "\nEstimated clock speed " << MHz << " MHz\n";
×
492
         }
493
      }
123✔
494

495
   private:
496
      size_t m_clock_speed = 0;
497
      double m_clock_cycle_ratio = 0.0;
498
      uint64_t m_cycles_consumed = 0;
499
      uint64_t m_ns_taken = 0;
500
      std::unique_ptr<Summary> m_summary;
501
      std::unique_ptr<JSON_Output> m_json;
502

503
      void record_result(const Timer& t) {
455✔
504
         m_ns_taken += t.value();
455✔
505
         m_cycles_consumed += t.cycles_consumed();
455✔
506
         if(m_json) {
455✔
507
            m_json->add(t);
2✔
508
         } else {
509
            output() << t.to_string() << std::flush;
906✔
510
            if(m_summary) {
453✔
511
               m_summary->add(t);
2✔
512
            }
513
         }
514
      }
455✔
515

516
      void record_result(const std::unique_ptr<Timer>& t) { record_result(*t); }
16✔
517

518
      template <typename T>
519
      using bench_fn = std::function<void(T&, std::string, std::chrono::milliseconds, const std::vector<size_t>&)>;
520

521
      template <typename T>
522
      void bench_providers_of(const std::string& algo,
7✔
523
                              const std::string& provider, /* user request, if any */
524
                              const std::chrono::milliseconds runtime,
525
                              const std::vector<size_t>& buf_sizes,
526
                              bench_fn<T> bench_one) {
527
         for(const auto& prov : T::providers(algo)) {
14✔
528
            if(provider.empty() || provider == prov) {
7✔
529
               auto p = T::create(algo, prov);
7✔
530

531
               if(p) {
7✔
532
                  bench_one(*p, prov, runtime, buf_sizes);
14✔
533
               }
534
            }
7✔
535
         }
536
      }
7✔
537

538
      std::unique_ptr<Timer> make_timer(const std::string& name,
475✔
539
                                        uint64_t event_mult = 1,
540
                                        const std::string& what = "",
541
                                        const std::string& provider = "",
542
                                        size_t buf_size = 0) {
543
         return std::make_unique<Timer>(name, provider, what, event_mult, buf_size, m_clock_cycle_ratio, m_clock_speed);
470✔
544
      }
545

546
      std::unique_ptr<Timer> make_timer(const std::string& algo, const std::string& provider, const std::string& what) {
5✔
547
         return make_timer(algo, 1, what, provider, 0);
5✔
548
      }
549

550
#if defined(BOTAN_HAS_BLOCK_CIPHER)
551
      void bench_block_cipher(Botan::BlockCipher& cipher,
4✔
552
                              const std::string& provider,
553
                              std::chrono::milliseconds runtime,
554
                              const std::vector<size_t>& buf_sizes) {
555
         auto ks_timer = make_timer(cipher.name(), provider, "key schedule");
8✔
556

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

560
         const size_t bs = cipher.block_size();
4✔
561
         std::set<size_t> buf_sizes_in_blocks;
4✔
562
         for(size_t buf_size : buf_sizes) {
9✔
563
            if(buf_size % bs == 0) {
5✔
564
               buf_sizes_in_blocks.insert(buf_size);
5✔
565
            } else {
566
               buf_sizes_in_blocks.insert(buf_size + bs - (buf_size % bs));
×
567
            }
568
         }
569

570
         for(size_t buf_size : buf_sizes_in_blocks) {
9✔
571
            std::vector<uint8_t> buffer(buf_size);
5✔
572
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
5✔
573
            const size_t blocks = buf_size / bs;
5✔
574

575
            auto encrypt_timer = make_timer(cipher.name(), mult * buffer.size(), "encrypt", provider, buf_size);
10✔
576
            auto decrypt_timer = make_timer(cipher.name(), mult * buffer.size(), "decrypt", provider, buf_size);
10✔
577

578
            encrypt_timer->run_until_elapsed(runtime, [&]() {
5✔
579
               for(size_t i = 0; i != mult; ++i) {
16,784✔
580
                  cipher.encrypt_n(&buffer[0], &buffer[0], blocks);
16,704✔
581
               }
582
            });
80✔
583
            record_result(encrypt_timer);
5✔
584

585
            decrypt_timer->run_until_elapsed(runtime, [&]() {
5✔
586
               for(size_t i = 0; i != mult; ++i) {
16,848✔
587
                  cipher.decrypt_n(&buffer[0], &buffer[0], blocks);
16,768✔
588
               }
589
            });
80✔
590
            record_result(decrypt_timer);
5✔
591
         }
10✔
592
      }
8✔
593
#endif
594

595
#if defined(BOTAN_HAS_STREAM_CIPHER)
596
      void bench_stream_cipher(Botan::StreamCipher& cipher,
1✔
597
                               const std::string& provider,
598
                               const std::chrono::milliseconds runtime,
599
                               const std::vector<size_t>& buf_sizes) {
600
         for(auto buf_size : buf_sizes) {
2✔
601
            const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
1✔
602
            cipher.set_key(key);
1✔
603

604
            if(cipher.valid_iv_length(12)) {
1✔
605
               const Botan::InitializationVector iv(rng(), 12);
1✔
606
               cipher.set_iv(iv.begin(), iv.size());
1✔
607
            }
1✔
608

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

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

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

615
            encrypt_timer->run_until_elapsed(runtime, [&]() {
1✔
616
               for(size_t i = 0; i != mult; ++i) {
910✔
617
                  cipher.encipher(buffer);
896✔
618
               }
619
            });
14✔
620

621
            record_result(encrypt_timer);
1✔
622

623
            if(verbose()) {
1✔
624
               auto ks_timer = make_timer(cipher.name(), buffer.size(), "write_keystream", provider, buf_size);
×
625

626
               while(ks_timer->under(runtime)) {
×
627
                  ks_timer->run([&]() { cipher.write_keystream(buffer.data(), buffer.size()); });
×
628
               }
629
               record_result(ks_timer);
×
630
            }
×
631
         }
3✔
632
      }
1✔
633
#endif
634

635
#if defined(BOTAN_HAS_HASH)
636
      void bench_hash(Botan::HashFunction& hash,
1✔
637
                      const std::string& provider,
638
                      const std::chrono::milliseconds runtime,
639
                      const std::vector<size_t>& buf_sizes) {
640
         std::vector<uint8_t> output(hash.output_length());
1✔
641

642
         for(auto buf_size : buf_sizes) {
2✔
643
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
644

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

647
            auto timer = make_timer(hash.name(), mult * buffer.size(), "hash", provider, buf_size);
2✔
648
            timer->run_until_elapsed(runtime, [&]() {
1✔
649
               for(size_t i = 0; i != mult; ++i) {
260✔
650
                  hash.update(buffer);
256✔
651
                  hash.final(output.data());
512✔
652
               }
653
            });
4✔
654
            record_result(timer);
1✔
655
         }
2✔
656
      }
1✔
657
#endif
658

659
#if defined(BOTAN_HAS_XOF)
660
      void bench_xof(Botan::XOF& xof,
×
661
                     const std::string& provider,
662
                     const std::chrono::milliseconds runtime,
663
                     const std::vector<size_t>& buf_sizes) {
664
         for(auto buf_size : buf_sizes) {
×
665
            Botan::secure_vector<uint8_t> in = rng().random_vec(buf_size);
×
666
            Botan::secure_vector<uint8_t> out(buf_size);
×
667

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

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

674
            record_result(in_timer);
×
675
            record_result(out_timer);
×
676
         }
×
677
      }
×
678
#endif
679

680
#if defined(BOTAN_HAS_MAC)
681
      void bench_mac(Botan::MessageAuthenticationCode& mac,
1✔
682
                     const std::string& provider,
683
                     const std::chrono::milliseconds runtime,
684
                     const std::vector<size_t>& buf_sizes) {
685
         std::vector<uint8_t> output(mac.output_length());
1✔
686

687
         for(auto buf_size : buf_sizes) {
2✔
688
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
689
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
690

691
            const Botan::SymmetricKey key(rng(), mac.maximum_keylength());
1✔
692
            mac.set_key(key);
1✔
693

694
            auto timer = make_timer(mac.name(), mult * buffer.size(), "mac", provider, buf_size);
2✔
695
            timer->run_until_elapsed(runtime, [&]() {
1✔
696
               for(size_t i = 0; i != mult; ++i) {
195✔
697
                  if(mac.fresh_key_required_per_message()) {
192✔
698
                     mac.set_key(key);
×
699
                  }
700
                  mac.start(nullptr, 0);
192✔
701
                  mac.update(buffer);
192✔
702
                  mac.final(output.data());
384✔
703
               }
704
            });
3✔
705

706
            record_result(timer);
1✔
707
         }
3✔
708
      }
1✔
709
#endif
710

711
#if defined(BOTAN_HAS_CIPHER_MODES)
712
      void bench_cipher_mode(Botan::Cipher_Mode& enc,
1✔
713
                             Botan::Cipher_Mode& dec,
714
                             const std::chrono::milliseconds runtime,
715
                             const std::vector<size_t>& buf_sizes) {
716
         auto ks_timer = make_timer(enc.name(), enc.provider(), "key schedule");
2✔
717

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

720
         ks_timer->run([&]() { enc.set_key(key); });
2✔
721
         ks_timer->run([&]() { dec.set_key(key); });
2✔
722

723
         record_result(ks_timer);
1✔
724

725
         for(auto buf_size : buf_sizes) {
2✔
726
            Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
1✔
727
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
728

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

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

734
            if(buf_size >= enc.minimum_final_size()) {
1✔
735
               encrypt_timer->run_until_elapsed(runtime, [&]() {
2✔
736
                  for(size_t i = 0; i != mult; ++i) {
260✔
737
                     enc.start(iv);
256✔
738
                     enc.finish(buffer);
256✔
739
                     buffer.resize(buf_size);  // remove any tag or padding
256✔
740
                  }
741
               });
4✔
742

743
               while(decrypt_timer->under(runtime)) {
5✔
744
                  if(!iv.empty()) {
4✔
745
                     iv[iv.size() - 1] += 1;
4✔
746
                  }
747

748
                  // Create a valid ciphertext/tag for decryption to run on
749
                  buffer.resize(buf_size);
4✔
750
                  enc.start(iv);
4✔
751
                  enc.finish(buffer);
4✔
752

753
                  Botan::secure_vector<uint8_t> dbuffer;
4✔
754

755
                  decrypt_timer->run([&]() {
4✔
756
                     for(size_t i = 0; i != mult; ++i) {
260✔
757
                        dbuffer = buffer;
256✔
758
                        dec.start(iv);
256✔
759
                        dec.finish(dbuffer);
256✔
760
                     }
761
                  });
4✔
762
               }
4✔
763
            }
764
            record_result(encrypt_timer);
1✔
765
            record_result(decrypt_timer);
1✔
766
         }
2✔
767
      }
1✔
768
#endif
769
};
770

771
BOTAN_REGISTER_COMMAND("speed", Speed);
31✔
772

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

© 2025 Coveralls, Inc