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

randombit / botan / 23025868463

12 Mar 2026 09:57PM UTC coverage: 90.208% (-1.7%) from 91.918%
23025868463

push

github

web-flow
Merge pull request #5433 from randombit/jack/des-bitslice

Implement DES using bitslicing

104163 of 115470 relevant lines covered (90.21%)

11550481.86 hits per line

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

87.67
/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/rng.h>
13
#include <botan/symkey.h>
14

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

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

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

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

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

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

39
namespace Botan_CLI {
40

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

154
               Botan::secure_vector<uint8_t> dbuffer;
1✔
155

156
               size_t iter = 0;
1✔
157

158
               while(decrypt_timer->under(runtime)) {
5✔
159
                  if(iter == 0 || iter % 128 == 0) {
4✔
160
                     if(!iv.empty()) {
1✔
161
                        iv[iv.size() - 1] += 1;
1✔
162
                     }
163

164
                     // Create a valid ciphertext/tag for decryption to run on
165
                     buffer.resize(buf_size);
1✔
166
                     enc.start(iv);
1✔
167
                     enc.finish(buffer);
1✔
168
                  }
169

170
                  ++iter;
4✔
171

172
                  decrypt_timer->run([&]() {
4✔
173
                     for(size_t i = 0; i != mult; ++i) {
260✔
174
                        dbuffer = buffer;
256✔
175
                        dec.start(iv);
256✔
176
                        dec.finish(dbuffer);
256✔
177
                     }
178
                  });
4✔
179
               }
180
            }
1✔
181

182
            config.record_result(*encrypt_timer);
1✔
183
            config.record_result(*decrypt_timer);
2✔
184
         }
4✔
185
      }
1✔
186

187
      std::string m_alg;
188
};
189
#endif
190

191
#if defined(BOTAN_HAS_STREAM_CIPHER)
192
class PerfTest_StreamCipher final : public PerfTest {
193
   public:
194
      explicit PerfTest_StreamCipher(std::string_view alg) : m_alg(alg) {}
1✔
195

196
      void go(const PerfConfig& config) override {
1✔
197
         for(const auto& provider : Botan::StreamCipher::providers(m_alg)) {
2✔
198
            if(auto cipher = Botan::StreamCipher::create(m_alg, provider)) {
1✔
199
               bench_stream_cipher(config, *cipher);
1✔
200
            }
1✔
201
         }
1✔
202
      }
1✔
203

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

206
   private:
207
      static void bench_stream_cipher(const PerfConfig& config, Botan::StreamCipher& cipher) {
1✔
208
         auto& rng = config.rng();
1✔
209
         const auto runtime = config.runtime();
1✔
210
         const auto provider = cipher.provider();
1✔
211

212
         for(auto buf_size : config.buffer_sizes()) {
2✔
213
            const Botan::SymmetricKey key(rng, cipher.maximum_keylength());
1✔
214
            cipher.set_key(key);
1✔
215

216
            if(cipher.valid_iv_length(12)) {
1✔
217
               const Botan::InitializationVector iv(rng, 12);
1✔
218
               cipher.set_iv(iv.begin(), iv.size());
1✔
219
            }
1✔
220

221
            auto buffer = rng.random_vec(buf_size);
1✔
222

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

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

227
            encrypt_timer->run_until_elapsed(runtime, [&]() {
1✔
228
               for(size_t i = 0; i != mult; ++i) {
975✔
229
                  cipher.encipher(buffer);
960✔
230
               }
231
            });
15✔
232

233
            config.record_result(*encrypt_timer);
1✔
234

235
            auto ks_timer =
1✔
236
               config.make_timer(cipher.name(), mult * buffer.size(), "write_keystream", provider, buf_size);
2✔
237

238
            while(ks_timer->under(runtime)) {
29✔
239
               ks_timer->run([&]() {
28✔
240
                  for(size_t i = 0; i != mult; ++i) {
1,820✔
241
                     cipher.write_keystream(buffer.data(), buffer.size());
1,792✔
242
                  }
243
               });
28✔
244
            }
245

246
            config.record_result(*ks_timer);
2✔
247
         }
3✔
248
      }
1✔
249

250
      std::string m_alg;
251
};
252
#endif
253

254
#if defined(BOTAN_HAS_HASH)
255
class PerfTest_HashFunction final : public PerfTest {
256
   public:
257
      explicit PerfTest_HashFunction(std::string_view alg) : m_alg(alg) {}
1✔
258

259
      void go(const PerfConfig& config) override {
1✔
260
         for(const auto& provider : Botan::HashFunction::providers(m_alg)) {
2✔
261
            if(auto hash = Botan::HashFunction::create(m_alg, provider)) {
1✔
262
               bench_hash_fn(config, *hash);
1✔
263
            }
1✔
264
         }
1✔
265
      }
1✔
266

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

269
   private:
270
      static void bench_hash_fn(const PerfConfig& config, Botan::HashFunction& hash) {
1✔
271
         std::vector<uint8_t> output(hash.output_length());
1✔
272
         const auto provider = hash.provider();
1✔
273
         const auto runtime = config.runtime();
1✔
274

275
         for(auto buf_size : config.buffer_sizes()) {
2✔
276
            const auto buffer = config.rng().random_vec(buf_size);
1✔
277

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

280
            auto timer = config.make_timer(hash.name(), mult * buffer.size(), "hash", provider, buf_size);
2✔
281
            timer->run_until_elapsed(runtime, [&]() {
1✔
282
               for(size_t i = 0; i != mult; ++i) {
325✔
283
                  hash.update(buffer);
320✔
284
                  hash.final(output.data());
640✔
285
               }
286
            });
5✔
287
            config.record_result(*timer);
2✔
288
         }
2✔
289
      }
2✔
290

291
      std::string m_alg;
292
};
293
#endif
294

295
#if defined(BOTAN_HAS_MAC)
296
class PerfTest_MessageAuthenticationCode final : public PerfTest {
297
   public:
298
      explicit PerfTest_MessageAuthenticationCode(std::string_view alg) : m_alg(alg) {}
1✔
299

300
      void go(const PerfConfig& config) override {
1✔
301
         for(const auto& provider : Botan::MessageAuthenticationCode::providers(m_alg)) {
2✔
302
            if(auto mac = Botan::MessageAuthenticationCode::create(m_alg, provider)) {
1✔
303
               bench_mac_fn(config, *mac);
1✔
304
            }
1✔
305
         }
1✔
306
      }
1✔
307

308
      static bool has_impl_for(std::string_view alg) {
1✔
309
         return !Botan::MessageAuthenticationCode::providers(alg).empty();
1✔
310
      }
311

312
   private:
313
      static void bench_mac_fn(const PerfConfig& config, Botan::MessageAuthenticationCode& mac) {
1✔
314
         std::vector<uint8_t> output(mac.output_length());
1✔
315
         const auto provider = mac.provider();
1✔
316
         const auto runtime = config.runtime();
1✔
317
         auto& rng = config.rng();
1✔
318

319
         for(auto buf_size : config.buffer_sizes()) {
2✔
320
            Botan::secure_vector<uint8_t> buffer = rng.random_vec(buf_size);
1✔
321
            const size_t mult = std::max<size_t>(1, 65536 / buf_size);
1✔
322

323
            const Botan::SymmetricKey key(rng, mac.maximum_keylength());
1✔
324
            mac.set_key(key);
1✔
325

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

338
            config.record_result(*timer);
2✔
339
         }
3✔
340
      }
2✔
341

342
      std::string m_alg;
343
};
344
#endif
345

346
#if defined(BOTAN_HAS_XOF)
347
class PerfTest_XOF final : public PerfTest {
348
   public:
349
      explicit PerfTest_XOF(std::string_view alg) : m_alg(alg) {}
×
350

351
      void go(const PerfConfig& config) override {
×
352
         for(const auto& provider : Botan::XOF::providers(m_alg)) {
×
353
            if(auto xof = Botan::XOF::create(m_alg, provider)) {
×
354
               bench_xof_fn(config, *xof);
×
355
            }
×
356
         }
×
357
      }
×
358

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

361
   private:
362
      static void bench_xof_fn(const PerfConfig& config, Botan::XOF& xof) {
×
363
         const auto runtime = config.runtime();
×
364
         const auto provider = xof.provider();
×
365

366
         for(const size_t buf_size : config.buffer_sizes()) {
×
367
            auto in = config.rng().random_vec(buf_size);
×
368
            Botan::secure_vector<uint8_t> out(buf_size);
×
369

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

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

376
            config.record_result(*in_timer);
×
377
            config.record_result(*out_timer);
×
378

379
            // Our XOFs don't want to consume inputs after producing output, so reset the state
380
            xof.clear();
×
381
         }
×
382
      }
×
383

384
      std::string m_alg;
385
};
386
#endif
387

388
//static
389
std::unique_ptr<PerfTest> PerfTest::get_sym(const std::string& alg) {
8✔
390
#if defined(BOTAN_HAS_XOF)
391
   if(PerfTest_XOF::has_impl_for(alg)) {
8✔
392
      return std::make_unique<PerfTest_XOF>(alg);
×
393
   }
394
#endif
395

396
#if defined(BOTAN_HAS_STREAM_CIPHER)
397
   if(PerfTest_StreamCipher::has_impl_for(alg)) {
8✔
398
      return std::make_unique<PerfTest_StreamCipher>(alg);
1✔
399
   }
400
#endif
401

402
#if defined(BOTAN_HAS_BLOCK_CIPHER)
403
   if(PerfTest_BlockCipher::has_impl_for(alg)) {
7✔
404
      return std::make_unique<PerfTest_BlockCipher>(alg);
4✔
405
   }
406
#endif
407

408
#if defined(BOTAN_HAS_CIPHER_MODES)
409
   if(PerfTest_CipherMode::has_impl_for(alg)) {
3✔
410
      return std::make_unique<PerfTest_CipherMode>(alg);
1✔
411
   }
412
#endif
413

414
#if defined(BOTAN_HAS_HASH)
415
   if(PerfTest_HashFunction::has_impl_for(alg)) {
2✔
416
      return std::make_unique<PerfTest_HashFunction>(alg);
1✔
417
   }
418
#endif
419

420
#if defined(BOTAN_HAS_MAC)
421
   if(PerfTest_MessageAuthenticationCode::has_impl_for(alg)) {
1✔
422
      return std::make_unique<PerfTest_MessageAuthenticationCode>(alg);
1✔
423
   }
424
#endif
425

426
   BOTAN_UNUSED(alg);
×
427
   return {};
×
428
}
429

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