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

randombit / botan / 20469806466

23 Dec 2025 07:30PM UTC coverage: 90.388% (+0.006%) from 90.382%
20469806466

push

github

web-flow
Merge pull request #5190 from randombit/jack/fix-xof-bench

When benchmarking the XOF clear the state between buffer sizes

101301 of 112074 relevant lines covered (90.39%)

12828497.8 hits per line

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

87.5
/src/cli/perf_sym.cpp
1
/*
2
* (C) 2024 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include "perf.h"
8

9
#include <botan/assert.h>
10
#include <set>
11

12
#include <botan/symkey.h>
13

14
#if defined(BOTAN_HAS_BLOCK_CIPHER)
15
   #include <botan/block_cipher.h>
16
#endif
17

18
#if defined(BOTAN_HAS_CIPHER_MODES)
19
   #include <botan/cipher_mode.h>
20
#endif
21

22
#if defined(BOTAN_HAS_STREAM_CIPHER)
23
   #include <botan/stream_cipher.h>
24
#endif
25

26
#if defined(BOTAN_HAS_HASH)
27
   #include <botan/hash.h>
28
#endif
29

30
#if defined(BOTAN_HAS_MAC)
31
   #include <botan/mac.h>
32
#endif
33

34
#if defined(BOTAN_HAS_XOF)
35
   #include <botan/xof.h>
36
#endif
37

38
namespace Botan_CLI {
39

40
#if defined(BOTAN_HAS_BLOCK_CIPHER)
41
class PerfTest_BlockCipher final : public PerfTest {
42
   public:
43
      explicit PerfTest_BlockCipher(std::string_view alg) : m_alg(alg) {}
4✔
44

45
      void go(const PerfConfig& config) override {
4✔
46
         for(const auto& provider : Botan::BlockCipher::providers(m_alg)) {
8✔
47
            if(auto cipher = Botan::BlockCipher::create(m_alg, provider)) {
4✔
48
               bench_stream_cipher(config, *cipher);
4✔
49
            }
4✔
50
         }
4✔
51
      }
4✔
52

53
      static bool has_impl_for(std::string_view alg) { return !Botan::BlockCipher::providers(alg).empty(); }
7✔
54

55
   private:
56
      static void bench_stream_cipher(const PerfConfig& config, Botan::BlockCipher& cipher) {
4✔
57
         auto& rng = config.rng();
4✔
58
         const auto runtime = config.runtime();
4✔
59
         const auto provider = cipher.provider();
4✔
60

61
         auto ks_timer = config.make_timer(cipher.name(), 1, "key schedule", provider);
8✔
62

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

66
         const size_t bs = cipher.block_size();
4✔
67
         std::set<size_t> buf_sizes_in_blocks;
4✔
68
         for(const size_t buf_size : config.buffer_sizes()) {
9✔
69
            if(buf_size % bs == 0) {
5✔
70
               buf_sizes_in_blocks.insert(buf_size);
5✔
71
            } else {
72
               buf_sizes_in_blocks.insert(buf_size + bs - (buf_size % bs));
×
73
            }
74
         }
75

76
         for(const size_t buf_size : buf_sizes_in_blocks) {
9✔
77
            std::vector<uint8_t> buffer(buf_size);
5✔
78
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
5✔
79
            const size_t blocks = buf_size / bs;
5✔
80

81
            auto encrypt_timer = config.make_timer(cipher.name(), mult * buffer.size(), "encrypt", provider, buf_size);
10✔
82
            auto decrypt_timer = config.make_timer(cipher.name(), mult * buffer.size(), "decrypt", provider, buf_size);
10✔
83

84
            encrypt_timer->run_until_elapsed(runtime, [&]() {
5✔
85
               for(size_t i = 0; i != mult; ++i) {
16,653✔
86
                  cipher.encrypt_n(buffer.data(), buffer.data(), blocks);
16,576✔
87
               }
88
            });
77✔
89
            config.record_result(*encrypt_timer);
5✔
90

91
            decrypt_timer->run_until_elapsed(runtime, [&]() {
5✔
92
               for(size_t i = 0; i != mult; ++i) {
16,198✔
93
                  cipher.decrypt_n(buffer.data(), buffer.data(), blocks);
16,128✔
94
               }
95
            });
70✔
96
            config.record_result(*decrypt_timer);
10✔
97
         }
10✔
98
      }
8✔
99

100
      std::string m_alg;
101
};
102
#endif
103

104
#if defined(BOTAN_HAS_CIPHER_MODES)
105
class PerfTest_CipherMode final : public PerfTest {
106
   public:
107
      explicit PerfTest_CipherMode(std::string_view alg) : m_alg(alg) {}
1✔
108

109
      void go(const PerfConfig& config) override {
1✔
110
         for(const auto& provider : Botan::Cipher_Mode::providers(m_alg)) {
2✔
111
            if(auto enc = Botan::Cipher_Mode::create(m_alg, Botan::Cipher_Dir::Encryption, provider)) {
1✔
112
               auto dec = Botan::Cipher_Mode::create_or_throw(m_alg, Botan::Cipher_Dir::Decryption, provider);
1✔
113
               bench_cipher_mode(config, *enc, *dec);
1✔
114
            }
2✔
115
         }
1✔
116
      }
1✔
117

118
      static bool has_impl_for(std::string_view alg) { return !Botan::Cipher_Mode::providers(alg).empty(); }
3✔
119

120
   private:
121
      static void bench_cipher_mode(const PerfConfig& config, Botan::Cipher_Mode& enc, Botan::Cipher_Mode& dec) {
1✔
122
         auto& rng = config.rng();
1✔
123
         const auto runtime = config.runtime();
1✔
124
         const auto provider = enc.provider();
1✔
125

126
         auto ks_timer = config.make_timer(enc.name(), 1, "key schedule", provider);
2✔
127

128
         const Botan::SymmetricKey key(config.rng(), enc.key_spec().maximum_keylength());
1✔
129

130
         ks_timer->run([&]() { enc.set_key(key); });
2✔
131
         ks_timer->run([&]() { dec.set_key(key); });
2✔
132

133
         config.record_result(*ks_timer);
1✔
134

135
         for(auto buf_size : config.buffer_sizes()) {
2✔
136
            Botan::secure_vector<uint8_t> buffer = rng.random_vec(buf_size);
1✔
137
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
138

139
            auto encrypt_timer = config.make_timer(enc.name(), mult * buffer.size(), "encrypt", provider, buf_size);
2✔
140
            auto decrypt_timer = config.make_timer(dec.name(), mult * buffer.size(), "decrypt", provider, buf_size);
2✔
141

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

144
            if(buf_size >= enc.minimum_final_size()) {
1✔
145
               encrypt_timer->run_until_elapsed(runtime, [&]() {
2✔
146
                  for(size_t i = 0; i != mult; ++i) {
260✔
147
                     enc.start(iv);
256✔
148
                     enc.finish(buffer);
256✔
149
                     buffer.resize(buf_size);  // remove any tag or padding
256✔
150
                  }
151
               });
4✔
152

153
               while(decrypt_timer->under(runtime)) {
5✔
154
                  if(!iv.empty()) {
4✔
155
                     iv[iv.size() - 1] += 1;
4✔
156
                  }
157

158
                  // Create a valid ciphertext/tag for decryption to run on
159
                  buffer.resize(buf_size);
4✔
160
                  enc.start(iv);
4✔
161
                  enc.finish(buffer);
4✔
162

163
                  Botan::secure_vector<uint8_t> dbuffer;
4✔
164

165
                  decrypt_timer->run([&]() {
4✔
166
                     for(size_t i = 0; i != mult; ++i) {
260✔
167
                        dbuffer = buffer;
256✔
168
                        dec.start(iv);
256✔
169
                        dec.finish(dbuffer);
256✔
170
                     }
171
                  });
4✔
172
               }
4✔
173
            }
174

175
            config.record_result(*encrypt_timer);
1✔
176
            config.record_result(*decrypt_timer);
2✔
177
         }
4✔
178
      }
1✔
179

180
      std::string m_alg;
181
};
182
#endif
183

184
#if defined(BOTAN_HAS_STREAM_CIPHER)
185
class PerfTest_StreamCipher final : public PerfTest {
186
   public:
187
      explicit PerfTest_StreamCipher(std::string_view alg) : m_alg(alg) {}
1✔
188

189
      void go(const PerfConfig& config) override {
1✔
190
         for(const auto& provider : Botan::StreamCipher::providers(m_alg)) {
2✔
191
            if(auto cipher = Botan::StreamCipher::create(m_alg, provider)) {
1✔
192
               bench_stream_cipher(config, *cipher);
1✔
193
            }
1✔
194
         }
1✔
195
      }
1✔
196

197
      static bool has_impl_for(std::string_view alg) { return !Botan::StreamCipher::providers(alg).empty(); }
8✔
198

199
   private:
200
      static void bench_stream_cipher(const PerfConfig& config, Botan::StreamCipher& cipher) {
1✔
201
         auto& rng = config.rng();
1✔
202
         const auto runtime = config.runtime();
1✔
203
         const auto provider = cipher.provider();
1✔
204

205
         for(auto buf_size : config.buffer_sizes()) {
2✔
206
            const Botan::SymmetricKey key(rng, cipher.maximum_keylength());
1✔
207
            cipher.set_key(key);
1✔
208

209
            if(cipher.valid_iv_length(12)) {
1✔
210
               const Botan::InitializationVector iv(rng, 12);
1✔
211
               cipher.set_iv(iv.begin(), iv.size());
1✔
212
            }
1✔
213

214
            auto buffer = rng.random_vec(buf_size);
1✔
215

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

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

220
            encrypt_timer->run_until_elapsed(runtime, [&]() {
1✔
221
               for(size_t i = 0; i != mult; ++i) {
845✔
222
                  cipher.encipher(buffer);
832✔
223
               }
224
            });
13✔
225

226
            config.record_result(*encrypt_timer);
1✔
227

228
            auto ks_timer =
1✔
229
               config.make_timer(cipher.name(), mult * buffer.size(), "write_keystream", provider, buf_size);
2✔
230

231
            while(ks_timer->under(runtime)) {
23✔
232
               ks_timer->run([&]() {
22✔
233
                  for(size_t i = 0; i != mult; ++i) {
1,430✔
234
                     cipher.write_keystream(buffer.data(), buffer.size());
1,408✔
235
                  }
236
               });
22✔
237
            }
238

239
            config.record_result(*ks_timer);
2✔
240
         }
3✔
241
      }
1✔
242

243
      std::string m_alg;
244
};
245
#endif
246

247
#if defined(BOTAN_HAS_HASH)
248
class PerfTest_HashFunction final : public PerfTest {
249
   public:
250
      explicit PerfTest_HashFunction(std::string_view alg) : m_alg(alg) {}
1✔
251

252
      void go(const PerfConfig& config) override {
1✔
253
         for(const auto& provider : Botan::HashFunction::providers(m_alg)) {
2✔
254
            if(auto hash = Botan::HashFunction::create(m_alg, provider)) {
1✔
255
               bench_hash_fn(config, *hash);
1✔
256
            }
1✔
257
         }
1✔
258
      }
1✔
259

260
      static bool has_impl_for(std::string_view alg) { return !Botan::HashFunction::providers(alg).empty(); }
2✔
261

262
   private:
263
      static void bench_hash_fn(const PerfConfig& config, Botan::HashFunction& hash) {
1✔
264
         std::vector<uint8_t> output(hash.output_length());
1✔
265
         const auto provider = hash.provider();
1✔
266
         const auto runtime = config.runtime();
1✔
267

268
         for(auto buf_size : config.buffer_sizes()) {
2✔
269
            const auto buffer = config.rng().random_vec(buf_size);
1✔
270

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

273
            auto timer = config.make_timer(hash.name(), mult * buffer.size(), "hash", provider, buf_size);
2✔
274
            timer->run_until_elapsed(runtime, [&]() {
1✔
275
               for(size_t i = 0; i != mult; ++i) {
325✔
276
                  hash.update(buffer);
320✔
277
                  hash.final(output.data());
640✔
278
               }
279
            });
5✔
280
            config.record_result(*timer);
2✔
281
         }
2✔
282
      }
2✔
283

284
      std::string m_alg;
285
};
286
#endif
287

288
#if defined(BOTAN_HAS_MAC)
289
class PerfTest_MessageAuthenticationCode final : public PerfTest {
290
   public:
291
      explicit PerfTest_MessageAuthenticationCode(std::string_view alg) : m_alg(alg) {}
1✔
292

293
      void go(const PerfConfig& config) override {
1✔
294
         for(const auto& provider : Botan::MessageAuthenticationCode::providers(m_alg)) {
2✔
295
            if(auto mac = Botan::MessageAuthenticationCode::create(m_alg, provider)) {
1✔
296
               bench_mac_fn(config, *mac);
1✔
297
            }
1✔
298
         }
1✔
299
      }
1✔
300

301
      static bool has_impl_for(std::string_view alg) {
1✔
302
         return !Botan::MessageAuthenticationCode::providers(alg).empty();
1✔
303
      }
304

305
   private:
306
      static void bench_mac_fn(const PerfConfig& config, Botan::MessageAuthenticationCode& mac) {
1✔
307
         std::vector<uint8_t> output(mac.output_length());
1✔
308
         const auto provider = mac.provider();
1✔
309
         const auto runtime = config.runtime();
1✔
310
         auto& rng = config.rng();
1✔
311

312
         for(auto buf_size : config.buffer_sizes()) {
2✔
313
            Botan::secure_vector<uint8_t> buffer = rng.random_vec(buf_size);
1✔
314
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
315

316
            const Botan::SymmetricKey key(rng, mac.maximum_keylength());
1✔
317
            mac.set_key(key);
1✔
318

319
            auto timer = config.make_timer(mac.name(), mult * buffer.size(), "mac", provider, buf_size);
2✔
320
            timer->run_until_elapsed(runtime, [&]() {
1✔
321
               for(size_t i = 0; i != mult; ++i) {
260✔
322
                  if(mac.fresh_key_required_per_message()) {
256✔
323
                     mac.set_key(key);
×
324
                  }
325
                  mac.start(nullptr, 0);
256✔
326
                  mac.update(buffer);
256✔
327
                  mac.final(output.data());
512✔
328
               }
329
            });
4✔
330

331
            config.record_result(*timer);
2✔
332
         }
3✔
333
      }
2✔
334

335
      std::string m_alg;
336
};
337
#endif
338

339
#if defined(BOTAN_HAS_XOF)
340
class PerfTest_XOF final : public PerfTest {
341
   public:
342
      explicit PerfTest_XOF(std::string_view alg) : m_alg(alg) {}
×
343

344
      void go(const PerfConfig& config) override {
×
345
         for(const auto& provider : Botan::XOF::providers(m_alg)) {
×
346
            if(auto xof = Botan::XOF::create(m_alg, provider)) {
×
347
               bench_xof_fn(config, *xof);
×
348
            }
×
349
         }
×
350
      }
×
351

352
      static bool has_impl_for(std::string_view alg) { return !Botan::XOF::providers(alg).empty(); }
8✔
353

354
   private:
355
      static void bench_xof_fn(const PerfConfig& config, Botan::XOF& xof) {
×
356
         const auto runtime = config.runtime();
×
357
         const auto provider = xof.provider();
×
358

359
         for(const size_t buf_size : config.buffer_sizes()) {
×
360
            auto in = config.rng().random_vec(buf_size);
×
361
            Botan::secure_vector<uint8_t> out(buf_size);
×
362

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

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

369
            config.record_result(*in_timer);
×
370
            config.record_result(*out_timer);
×
371

372
            // Our XOFs don't want to consume inputs after producing output, so reset the state
373
            xof.clear();
×
374
         }
×
375
      }
×
376

377
      std::string m_alg;
378
};
379
#endif
380

381
//static
382
std::unique_ptr<PerfTest> PerfTest::get_sym(const std::string& alg) {
8✔
383
#if defined(BOTAN_HAS_XOF)
384
   if(PerfTest_XOF::has_impl_for(alg)) {
8✔
385
      return std::make_unique<PerfTest_XOF>(alg);
×
386
   }
387
#endif
388

389
#if defined(BOTAN_HAS_STREAM_CIPHER)
390
   if(PerfTest_StreamCipher::has_impl_for(alg)) {
8✔
391
      return std::make_unique<PerfTest_StreamCipher>(alg);
1✔
392
   }
393
#endif
394

395
#if defined(BOTAN_HAS_BLOCK_CIPHER)
396
   if(PerfTest_BlockCipher::has_impl_for(alg)) {
7✔
397
      return std::make_unique<PerfTest_BlockCipher>(alg);
4✔
398
   }
399
#endif
400

401
#if defined(BOTAN_HAS_CIPHER_MODES)
402
   if(PerfTest_CipherMode::has_impl_for(alg)) {
3✔
403
      return std::make_unique<PerfTest_CipherMode>(alg);
1✔
404
   }
405
#endif
406

407
#if defined(BOTAN_HAS_HASH)
408
   if(PerfTest_HashFunction::has_impl_for(alg)) {
2✔
409
      return std::make_unique<PerfTest_HashFunction>(alg);
1✔
410
   }
411
#endif
412

413
#if defined(BOTAN_HAS_MAC)
414
   if(PerfTest_MessageAuthenticationCode::has_impl_for(alg)) {
1✔
415
      return std::make_unique<PerfTest_MessageAuthenticationCode>(alg);
1✔
416
   }
417
#endif
418

419
   BOTAN_UNUSED(alg);
×
420
   return {};
×
421
}
422

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

© 2026 Coveralls, Inc