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

randombit / botan / 16818395385

07 Aug 2025 11:51PM UTC coverage: 90.663% (-0.01%) from 90.675%
16818395385

push

github

web-flow
Merge pull request #5051 from randombit/jack/base58-improvements

Base58 optimizations

100010 of 110309 relevant lines covered (90.66%)

12254698.18 hits per line

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

48.7
/src/cli/perf_misc.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
#include <cstring>
9

10
// Always available:
11
#include <botan/assert.h>
12
#include <botan/hex.h>
13

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

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

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

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

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

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

38
namespace Botan_CLI {
39

40
class PerfTest_Hex final : public PerfTest {
×
41
   public:
42
      void go(const PerfConfig& config) override {
×
43
         for(size_t buf_size : config.buffer_sizes()) {
×
44
            std::vector<uint8_t> ibuf(buf_size);
×
45
            std::vector<uint8_t> rbuf(buf_size);
×
46
            const size_t olen = 2 * buf_size;
×
47

48
            auto enc_timer = config.make_timer("hex", ibuf.size(), "encode", "", ibuf.size());
×
49

50
            auto dec_timer = config.make_timer("hex", olen, "decode", "", olen);
×
51

52
            const auto msec = config.runtime();
×
53

54
            while(enc_timer->under(msec) && dec_timer->under(msec)) {
×
55
               config.rng().randomize(ibuf);
×
56

57
               std::string hex = enc_timer->run([&]() { return Botan::hex_encode(ibuf); });
×
58

59
               dec_timer->run([&]() { Botan::hex_decode(rbuf.data(), hex); });
×
60
               BOTAN_ASSERT(rbuf == ibuf, "Encode/decode round trip ok");
×
61
            }
×
62

63
            config.record_result(*enc_timer);
×
64
            config.record_result(*dec_timer);
×
65
         }
×
66
      }
×
67
};
68

69
BOTAN_REGISTER_PERF_TEST("hex", PerfTest_Hex);
×
70

71
#if defined(BOTAN_HAS_BASE32_CODEC)
72
class PerfTest_Base32 final : public PerfTest {
×
73
   public:
74
      void go(const PerfConfig& config) override {
×
75
         for(size_t buf_size : config.buffer_sizes()) {
×
76
            std::vector<uint8_t> ibuf(buf_size);
×
77
            std::vector<uint8_t> rbuf(buf_size);
×
78
            const size_t olen = Botan::base32_encode_max_output(ibuf.size());
×
79

80
            auto enc_timer = config.make_timer("base32", ibuf.size(), "encode", "", ibuf.size());
×
81

82
            auto dec_timer = config.make_timer("base32", olen, "decode", "", olen);
×
83

84
            const auto msec = config.runtime();
×
85

86
            while(enc_timer->under(msec) && dec_timer->under(msec)) {
×
87
               config.rng().randomize(ibuf);
×
88

89
               std::string b32 = enc_timer->run([&]() { return Botan::base32_encode(ibuf); });
×
90

91
               dec_timer->run([&]() { Botan::base32_decode(rbuf.data(), b32); });
×
92
               BOTAN_ASSERT(rbuf == ibuf, "Encode/decode round trip ok");
×
93
            }
×
94

95
            config.record_result(*enc_timer);
×
96
            config.record_result(*dec_timer);
×
97
         }
×
98
      }
×
99
};
100

101
BOTAN_REGISTER_PERF_TEST("base32", PerfTest_Base32);
×
102

103
#endif
104

105
#if defined(BOTAN_HAS_BASE64_CODEC)
106
class PerfTest_Base64 final : public PerfTest {
×
107
   public:
108
      void go(const PerfConfig& config) override {
×
109
         for(size_t buf_size : config.buffer_sizes()) {
×
110
            std::vector<uint8_t> ibuf(buf_size);
×
111
            std::vector<uint8_t> rbuf(buf_size);
×
112
            const size_t olen = Botan::base64_encode_max_output(ibuf.size());
×
113

114
            auto enc_timer = config.make_timer("base64", ibuf.size(), "encode", "", ibuf.size());
×
115

116
            auto dec_timer = config.make_timer("base64", olen, "decode", "", olen);
×
117

118
            const auto msec = config.runtime();
×
119

120
            while(enc_timer->under(msec) && dec_timer->under(msec)) {
×
121
               config.rng().randomize(ibuf);
×
122

123
               std::string b64 = enc_timer->run([&]() { return Botan::base64_encode(ibuf); });
×
124

125
               dec_timer->run([&]() { Botan::base64_decode(rbuf.data(), b64); });
×
126
               BOTAN_ASSERT(rbuf == ibuf, "Encode/decode round trip ok");
×
127
            }
×
128

129
            config.record_result(*enc_timer);
×
130
            config.record_result(*dec_timer);
×
131
         }
×
132
      }
×
133
};
134

135
BOTAN_REGISTER_PERF_TEST("base64", PerfTest_Base64);
×
136

137
#endif
138

139
#if defined(BOTAN_HAS_BASE58_CODEC)
140
class PerfTest_Base58 final : public PerfTest {
×
141
   public:
142
      void go(const PerfConfig& config) override {
×
143
         for(size_t buf_size : config.buffer_sizes()) {
×
144
            std::vector<uint8_t> ibuf(buf_size);
×
145

146
            auto enc_timer = config.make_timer("base58", ibuf.size(), "encode", "", ibuf.size());
×
147
            auto dec_timer = config.make_timer("base58", ibuf.size(), "decode", "", ibuf.size());
×
148

149
            const auto msec = config.runtime();
×
150

151
            while(enc_timer->under(msec) && dec_timer->under(msec)) {
×
152
               config.rng().randomize(ibuf);
×
153

154
               const std::string b58 = enc_timer->run([&]() { return Botan::base58_encode(ibuf); });
×
155
               const auto rbuf = dec_timer->run([&] { return Botan::base58_decode(b58); });
×
156
               BOTAN_ASSERT(rbuf == ibuf, "Encode/decode round trip ok");
×
157
            }
×
158

159
            config.record_result(*enc_timer);
×
160
            config.record_result(*dec_timer);
×
161
         }
×
162
      }
×
163
};
164

165
BOTAN_REGISTER_PERF_TEST("base58", PerfTest_Base58);
×
166

167
#endif
168

169
#if defined(BOTAN_HAS_FPE_FE1)
170

171
class PerfTest_FpeFe1 final : public PerfTest {
1✔
172
   public:
173
      void go(const PerfConfig& config) override {
1✔
174
         const auto n = Botan::BigInt::from_u64(1000000000000000);
1✔
175

176
         auto enc_timer = config.make_timer("FPE_FE1 encrypt");
2✔
177
         auto dec_timer = config.make_timer("FPE_FE1 decrypt");
2✔
178

179
         const Botan::SymmetricKey key(config.rng(), 32);
1✔
180
         const std::vector<uint8_t> tweak(8);  // 8 zeros
1✔
181

182
         auto x = Botan::BigInt::one();
1✔
183

184
         Botan::FPE_FE1 fpe_fe1(n);
1✔
185
         fpe_fe1.set_key(key);
1✔
186

187
         auto runtime = config.runtime();
1✔
188

189
         while(enc_timer->under(runtime)) {
4✔
190
            enc_timer->start();
3✔
191
            x = fpe_fe1.encrypt(x, tweak.data(), tweak.size());
3✔
192
            enc_timer->stop();
3✔
193
         }
194
         config.record_result(*enc_timer);
1✔
195

196
         for(size_t i = 0; i != enc_timer->events(); ++i) {
4✔
197
            dec_timer->start();
3✔
198
            x = fpe_fe1.decrypt(x, tweak.data(), tweak.size());
3✔
199
            dec_timer->stop();
3✔
200
         }
201
         config.record_result(*dec_timer);
1✔
202

203
         BOTAN_ASSERT(x == 1, "FPE works");
1✔
204
      }
4✔
205
};
206

207
BOTAN_REGISTER_PERF_TEST("fpe_fe1", PerfTest_FpeFe1);
1✔
208

209
#endif
210

211
#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
212
class PerfTest_Rfc3394 final : public PerfTest {
1✔
213
      void go(const PerfConfig& config) override {
1✔
214
         auto wrap_timer = config.make_timer("RFC3394 AES-256 key wrap");
2✔
215
         auto unwrap_timer = config.make_timer("RFC3394 AES-256 key unwrap");
2✔
216

217
         const Botan::SymmetricKey kek(config.rng(), 32);
1✔
218
         Botan::secure_vector<uint8_t> key(64, 0);
1✔
219

220
         const auto runtime = config.runtime();
1✔
221

222
         while(wrap_timer->under(runtime)) {
96✔
223
            wrap_timer->start();
95✔
224
            key = Botan::rfc3394_keywrap(key, kek);
190✔
225
            wrap_timer->stop();
95✔
226

227
            unwrap_timer->start();
95✔
228
            key = Botan::rfc3394_keyunwrap(key, kek);
190✔
229
            unwrap_timer->stop();
95✔
230

231
            key[0] += 1;
95✔
232
         }
233

234
         config.record_result(*wrap_timer);
1✔
235
         config.record_result(*unwrap_timer);
2✔
236
      }
3✔
237
};
238

239
BOTAN_REGISTER_PERF_TEST("rfc3394", PerfTest_Rfc3394);
1✔
240

241
#endif
242

243
#if defined(BOTAN_HAS_ZFEC)
244

245
class PerfTest_Zfec final : public PerfTest {
1✔
246
   public:
247
      void go(const PerfConfig& config) override {
1✔
248
         const size_t k = 4;
1✔
249
         const size_t n = 16;
1✔
250

251
         Botan::ZFEC zfec(k, n);
1✔
252

253
         const size_t share_size = 256 * 1024;
1✔
254

255
         std::vector<uint8_t> input(share_size * k);
1✔
256
         config.rng().randomize(input.data(), input.size());
1✔
257

258
         std::vector<uint8_t> output(share_size * n);
1✔
259

260
         auto enc_fn = [&](size_t share, const uint8_t buf[], size_t len) {
17✔
261
            std::memcpy(&output[share * share_size], buf, len);
16✔
262
         };
17✔
263

264
         const auto msec = config.runtime();
1✔
265

266
         const std::string alg = Botan::fmt("zfec {}/{}", k, n);
1✔
267

268
         auto enc_timer = config.make_timer(alg, input.size(), "encode", "", input.size());
1✔
269

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

272
         config.record_result(*enc_timer);
1✔
273

274
         auto dec_timer = config.make_timer(alg, input.size(), "decode", "", input.size());
1✔
275

276
         std::map<size_t, const uint8_t*> shares;
1✔
277
         for(size_t i = 0; i != n; ++i) {
17✔
278
            shares[i] = &output[share_size * i];
16✔
279
         }
280

281
         // remove data shares to make decoding maximally expensive:
282
         while(shares.size() != k) {
13✔
283
            shares.erase(shares.begin());
12✔
284
         }
285

286
         std::vector<uint8_t> recovered(share_size * k);
1✔
287

288
         auto dec_fn = [&](size_t share, const uint8_t buf[], size_t len) {
5✔
289
            std::memcpy(&recovered[share * share_size], buf, len);
4✔
290
         };
5✔
291

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

294
         config.record_result(*dec_timer);
1✔
295

296
         if(recovered != input) {
1✔
297
            config.error_output() << "ZFEC recovery failed\n";
×
298
         }
299
      }
4✔
300
};
301

302
BOTAN_REGISTER_PERF_TEST("zfec", PerfTest_Zfec);
1✔
303

304
#endif
305

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