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

randombit / botan / 37409379121

06 Oct 2026 03:02AM UTC coverage: 89.915% (-2.3%) from 92.214%
37409379121

push

github

web-flow
Merge pull request #5997 from randombit/jack/dl-key-validation

Tighten DL key range checks and make weak check_key cheaper

130419 of 145047 relevant lines covered (89.91%)

9048497.74 hits per line

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

94.75
/src/tests/test_utils.cpp
1
/*
2
* (C) 2015,2018,2024 Jack Lloyd
3
* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
4
* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include "tests.h"
10

11
#include "test_arb_eq.h"
12
#include <botan/exceptn.h>
13
#include <botan/hex.h>
14
#include <botan/rng.h>
15
#include <botan/strong_type.h>
16
#include <botan/version.h>
17
#include <botan/internal/algorithm_spec.h>
18
#include <botan/internal/bit_ops.h>
19
#include <botan/internal/calendar.h>
20
#include <botan/internal/charset.h>
21
#include <botan/internal/concat_util.h>
22
#include <botan/internal/ct_utils.h>
23
#include <botan/internal/fmt.h>
24
#include <botan/internal/int_utils.h>
25
#include <botan/internal/loadstor.h>
26
#include <botan/internal/parsing.h>
27
#include <botan/internal/rounding.h>
28
#include <botan/internal/target_info.h>
29

30
#include <bit>
31

32
#if defined(BOTAN_HAS_CPUID)
33
   #include <botan/internal/cpuid.h>
34
#endif
35

36
#if defined(BOTAN_HAS_IPV4_ADDRESS)
37
   #include <botan/ipv4_address.h>
38
#endif
39

40
#if defined(BOTAN_HAS_IPV6_ADDRESS)
41
   #include <botan/ipv6_address.h>
42
#endif
43

44
#if defined(BOTAN_HAS_POLY_DBL)
45
   #include <botan/internal/poly_dbl.h>
46
#endif
47

48
#if defined(BOTAN_HAS_UUID)
49
   #include <botan/uuid.h>
50
#endif
51

52
namespace Botan_Tests {
53

54
namespace {
55

56
class Utility_Function_Tests final : public Test {
1 ✔
57
   public:
58
      std::vector<Test::Result> run() override {
1 ✔
59
         std::vector<Test::Result> results;
1 ✔
60

61
         results.push_back(test_checked_add());
2 ✔
62
         results.push_back(test_checked_mul());
2 ✔
63
         results.push_back(test_checked_cast());
2 ✔
64
         results.push_back(test_round_up());
2 ✔
65
         results.push_back(test_loadstore());
2 ✔
66
         results.push_back(test_loadstore_ambiguity());
2 ✔
67
         results.push_back(test_loadstore_fallback());
2 ✔
68
         results.push_back(test_loadstore_constexpr());
2 ✔
69
         return Botan::concat(results, test_copy_out_be_le());
3 ✔
70
      }
1 ✔
71

72
   private:
73
      Test::Result test_checked_add() {
1 ✔
74
         Test::Result result("checked_add");
1 ✔
75

76
         const size_t large = static_cast<size_t>(-5);
1 ✔
77
         const size_t zero = 0;
1 ✔
78

79
         for(int si = -15; si != 15; ++si) {
31 ✔
80
            const size_t i = static_cast<size_t>(si);
30 ✔
81
            auto sum1 = Botan::checked_add<size_t>(i, zero, zero, zero, large);
30 ✔
82
            const auto sum2 = Botan::checked_add<size_t>(large, zero, zero, zero, i);
30 ✔
83

84
            result.test_is_true("checked_add looks at all args", sum1 == sum2);
60 ✔
85

86
            if(i < 5) {
30 ✔
87
               result.test_sz_eq("checked_add worked", sum1.value(), i + large);
5 ✔
88
            } else {
89
               result.test_is_true("checked_add did not return a result", !sum1.has_value());
25 ✔
90
            }
91
         }
92

93
         auto& rng = Test::rng();
1 ✔
94

95
         for(size_t i = 0; i != 100; ++i) {
101 ✔
96
            const uint16_t x = Botan::make_uint16(rng.next_byte(), rng.next_byte());
100 ✔
97
            const uint16_t y = Botan::make_uint16(rng.next_byte(), rng.next_byte());
100 ✔
98

99
            const uint32_t ref = static_cast<uint32_t>(x) + y;
100 ✔
100

101
            if(auto z = Botan::checked_add(x, y)) {
200 ✔
102
               result.test_u32_eq("checked_add adds", static_cast<uint32_t>(z.value()), ref);
61 ✔
103
            } else {
104
               result.test_is_true("checked_add checks", (ref >> 16) > 0);
39 ✔
105
            }
106
         }
107

108
         return result;
1 ✔
109
      }
×
110

111
      Test::Result test_checked_mul() {
1 ✔
112
         Test::Result result("checked_mul");
1 ✔
113

114
         auto& rng = Test::rng();
1 ✔
115

116
         for(size_t i = 0; i != 100; ++i) {
101 ✔
117
            const uint16_t x = Botan::make_uint16(rng.next_byte(), rng.next_byte());
100 ✔
118
            const uint16_t y = Botan::make_uint16(rng.next_byte(), rng.next_byte());
100 ✔
119

120
            const uint32_t ref = static_cast<uint32_t>(x) * y;
100 ✔
121

122
            if(auto z = Botan::checked_mul(x, y)) {
200 ✔
123
               result.test_u32_eq("checked_mul multiplies", static_cast<uint32_t>(z.value()), ref);
×
124
            } else {
125
               result.test_is_true("checked_mul checks", (ref >> 16) > 0);
100 ✔
126
            }
127
         }
128

129
         return result;
1 ✔
130
      }
×
131

132
      Test::Result test_checked_cast() {
1 ✔
133
         Test::Result result("checked_cast");
1 ✔
134

135
         const uint32_t large = static_cast<uint32_t>(-1);
1 ✔
136
         const uint32_t is_16_bits = 0x8123;
1 ✔
137
         const uint32_t is_8_bits = 0x89;
1 ✔
138

139
         result.test_throws("checked_cast checks", [&] { Botan::checked_cast_to<uint16_t>(large); });
2 ✔
140
         result.test_throws("checked_cast checks", [&] { Botan::checked_cast_to<uint8_t>(large); });
2 ✔
141

142
         result.test_u32_eq("checked_cast converts", Botan::checked_cast_to<uint32_t>(large), large);
1 ✔
143
         result.test_u16_eq("checked_cast converts", Botan::checked_cast_to<uint16_t>(is_16_bits), 0x8123);
1 ✔
144
         result.test_u8_eq("checked_cast converts", Botan::checked_cast_to<uint8_t>(is_8_bits), 0x89);
1 ✔
145

146
         return result;
1 ✔
147
      }
×
148

149
      Test::Result test_round_up() {
1 ✔
150
         Test::Result result("Util round_up");
1 ✔
151

152
         // clang-format off
153
         const std::vector<size_t> inputs = {
1 ✔
154
            0, 1, 2, 3, 4, 9, 10, 32, 99, 100, 101, 255, 256, 1000, 10000,
155
            65535, 65536, 65537,
156
         };
1 ✔
157

158
         const std::vector<size_t> alignments = {
1 ✔
159
            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16, 32, 50, 64, 100, 512, 521,
160
            1000, 1023, 1024, 1025, 10000, 65535, 65536
161
         };
1 ✔
162
         // clang-format on
163

164
         for(const size_t i : inputs) {
19 ✔
165
            for(const size_t m : alignments) {
450 ✔
166
               try {
432 ✔
167
                  const size_t z = Botan::round_up(i, m);
432 ✔
168

169
                  result.test_is_true("z % m == 0", z % m == 0);
432 ✔
170
                  result.test_is_true("z >= i", z >= i);
432 ✔
171
                  result.test_is_true("z <= i + m", z <= i + m);
432 ✔
172
               } catch(Botan::Exception& e) {
×
173
                  result.test_failure(Botan::fmt("round_up({},{})", i, m), e.what());
×
174
               }
×
175
            }
176
         }
177

178
         result.test_throws("Integer overflow is detected", []() { Botan::round_up(static_cast<size_t>(-1), 1024); });
1 ✔
179

180
         return result;
1 ✔
181
      }
1 ✔
182

183
      using TestInt64 = Botan::Strong<uint64_t, struct TestInt64_>;
184
      using TestInt32 = Botan::Strong<uint32_t, struct TestInt64_>;
185
      using TestVectorSink = Botan::Strong<std::vector<uint8_t>, struct TestVectorSink_>;
186

187
      enum class TestEnum64 : uint64_t {
188
         _1 = 0x1234567890ABCDEF,
189
         _2 = 0xEFCDAB9078563412,
190
      };
191

192
      enum class TestEnum32 : uint32_t {
193
         _1 = 0x12345678,
194
         _2 = 0x78563412,
195
      };
196

197
      static Test::Result test_loadstore() {
1 ✔
198
         Test::Result result("Util load/store");
1 ✔
199

200
         const std::vector<uint8_t> membuf = Botan::hex_decode("00112233445566778899AABBCCDDEEFF");
1 ✔
201
         const uint8_t* mem = membuf.data();
1 ✔
202

203
         const uint16_t in16 = 0x1234;
1 ✔
204
         const uint32_t in32 = 0xA0B0C0D0;
1 ✔
205
         const uint64_t in64 = 0xABCDEF0123456789;
1 ✔
206

207
         result.test_u8_eq(Botan::get_byte<0>(in32), 0xA0);
1 ✔
208
         result.test_u8_eq(Botan::get_byte<1>(in32), 0xB0);
1 ✔
209
         result.test_u8_eq(Botan::get_byte<2>(in32), 0xC0);
1 ✔
210
         result.test_u8_eq(Botan::get_byte<3>(in32), 0xD0);
1 ✔
211

212
         result.test_u16_eq(Botan::make_uint16(0xAA, 0xBB), 0xAABB);
1 ✔
213
         result.test_u32_eq(Botan::make_uint32(0x01, 0x02, 0x03, 0x04), 0x01020304);
1 ✔
214

215
         result.test_u16_eq(Botan::load_be<uint16_t>(mem, 0), 0x0011);
1 ✔
216
         result.test_u16_eq(Botan::load_be<uint16_t>(mem, 1), 0x2233);
1 ✔
217
         result.test_u16_eq(Botan::load_be<uint16_t>(mem, 2), 0x4455);
1 ✔
218
         result.test_u16_eq(Botan::load_be<uint16_t>(mem, 3), 0x6677);
1 ✔
219

220
         result.test_u16_eq(Botan::load_le<uint16_t>(mem, 0), 0x1100);
1 ✔
221
         result.test_u16_eq(Botan::load_le<uint16_t>(mem, 1), 0x3322);
1 ✔
222
         result.test_u16_eq(Botan::load_le<uint16_t>(mem, 2), 0x5544);
1 ✔
223
         result.test_u16_eq(Botan::load_le<uint16_t>(mem, 3), 0x7766);
1 ✔
224

225
         result.test_u32_eq(Botan::load_be<uint32_t>(mem, 0), 0x00112233);
1 ✔
226
         result.test_u32_eq(Botan::load_be<uint32_t>(mem, 1), 0x44556677);
1 ✔
227
         result.test_u32_eq(Botan::load_be<uint32_t>(mem, 2), 0x8899AABB);
1 ✔
228
         result.test_u32_eq(Botan::load_be<uint32_t>(mem, 3), 0xCCDDEEFF);
1 ✔
229

230
         result.test_u32_eq(Botan::load_le<uint32_t>(mem, 0), 0x33221100);
1 ✔
231
         result.test_u32_eq(Botan::load_le<uint32_t>(mem, 1), 0x77665544);
1 ✔
232
         result.test_u32_eq(Botan::load_le<uint32_t>(mem, 2), 0xBBAA9988);
1 ✔
233
         result.test_u32_eq(Botan::load_le<uint32_t>(mem, 3), 0xFFEEDDCC);
1 ✔
234

235
         result.test_u64_eq(Botan::load_be<uint64_t>(mem, 0), 0x0011223344556677);
1 ✔
236
         result.test_u64_eq(Botan::load_be<uint64_t>(mem, 1), 0x8899AABBCCDDEEFF);
1 ✔
237

238
         result.test_u64_eq(Botan::load_le<uint64_t>(mem, 0), 0x7766554433221100);
1 ✔
239
         result.test_u64_eq(Botan::load_le<uint64_t>(mem, 1), 0xFFEEDDCCBBAA9988);
1 ✔
240

241
         // Check misaligned loads:
242
         result.test_u16_eq(Botan::load_be<uint16_t>(mem + 1, 0), 0x1122);
1 ✔
243
         result.test_u16_eq(Botan::load_le<uint16_t>(mem + 3, 0), 0x4433);
1 ✔
244

245
         result.test_u32_eq(Botan::load_be<uint32_t>(mem + 1, 1), 0x55667788);
1 ✔
246
         result.test_u32_eq(Botan::load_le<uint32_t>(mem + 3, 1), 0xAA998877);
1 ✔
247

248
         result.test_u64_eq(Botan::load_be<uint64_t>(mem + 1, 0), 0x1122334455667788);
1 ✔
249
         result.test_u64_eq(Botan::load_le<uint64_t>(mem + 7, 0), 0xEEDDCCBBAA998877);
1 ✔
250
         result.test_u64_eq(Botan::load_le<uint64_t>(mem + 5, 0), 0xCCBBAA9988776655);
1 ✔
251

252
         uint8_t outbuf[16] = {0};
1 ✔
253

254
         for(size_t offset = 0; offset != 7; ++offset) {
8 ✔
255
            uint8_t* out = outbuf + offset;  // NOLINT(*-const-correctness) clang-tidy bug
7 ✔
256

257
            Botan::store_be(in16, out);
7 ✔
258
            result.test_u8_eq(out[0], 0x12);
7 ✔
259
            result.test_u8_eq(out[1], 0x34);
7 ✔
260

261
            Botan::store_le(in16, out);
7 ✔
262
            result.test_u8_eq(out[0], 0x34);
7 ✔
263
            result.test_u8_eq(out[1], 0x12);
7 ✔
264

265
            Botan::store_be(in32, out);
7 ✔
266
            result.test_u8_eq(out[0], 0xA0);
7 ✔
267
            result.test_u8_eq(out[1], 0xB0);
7 ✔
268
            result.test_u8_eq(out[2], 0xC0);
7 ✔
269
            result.test_u8_eq(out[3], 0xD0);
7 ✔
270

271
            Botan::store_le(in32, out);
7 ✔
272
            result.test_u8_eq(out[0], 0xD0);
7 ✔
273
            result.test_u8_eq(out[1], 0xC0);
7 ✔
274
            result.test_u8_eq(out[2], 0xB0);
7 ✔
275
            result.test_u8_eq(out[3], 0xA0);
7 ✔
276

277
            Botan::store_be(in64, out);
7 ✔
278
            result.test_u8_eq(out[0], 0xAB);
7 ✔
279
            result.test_u8_eq(out[1], 0xCD);
7 ✔
280
            result.test_u8_eq(out[2], 0xEF);
7 ✔
281
            result.test_u8_eq(out[3], 0x01);
7 ✔
282
            result.test_u8_eq(out[4], 0x23);
7 ✔
283
            result.test_u8_eq(out[5], 0x45);
7 ✔
284
            result.test_u8_eq(out[6], 0x67);
7 ✔
285
            result.test_u8_eq(out[7], 0x89);
7 ✔
286

287
            Botan::store_le(in64, out);
7 ✔
288
            result.test_u8_eq(out[0], 0x89);
7 ✔
289
            result.test_u8_eq(out[1], 0x67);
7 ✔
290
            result.test_u8_eq(out[2], 0x45);
7 ✔
291
            result.test_u8_eq(out[3], 0x23);
7 ✔
292
            result.test_u8_eq(out[4], 0x01);
7 ✔
293
            result.test_u8_eq(out[5], 0xEF);
7 ✔
294
            result.test_u8_eq(out[6], 0xCD);
7 ✔
295
            result.test_u8_eq(out[7], 0xAB);
7 ✔
296
         }
297

298
         std::array<uint8_t, 8> outarr{};
1 ✔
299
         uint16_t i0 = 0;
1 ✔
300
         uint16_t i1 = 0;
1 ✔
301
         uint16_t i2 = 0;
1 ✔
302
         uint16_t i3 = 0;
1 ✔
303
         Botan::store_be(in64, outarr);
1 ✔
304

305
         Botan::load_be(outarr, i0, i1, i2, i3);
1 ✔
306
         result.test_u16_eq(i0, 0xABCD);
1 ✔
307
         result.test_u16_eq(i1, 0xEF01);
1 ✔
308
         result.test_u16_eq(i2, 0x2345);
1 ✔
309
         result.test_u16_eq(i3, 0x6789);
1 ✔
310

311
         Botan::load_le(std::span{outarr}.first<6>(), i0, i1, i2);
1 ✔
312
         result.test_u16_eq(i0, 0xCDAB);
1 ✔
313
         result.test_u16_eq(i1, 0x01EF);
1 ✔
314
         result.test_u16_eq(i2, 0x4523);
1 ✔
315
         result.test_u16_eq(i3, 0x6789);  // remains unchanged
1 ✔
316

317
         Botan::store_le(in64, outarr);
1 ✔
318

319
         Botan::load_le(outarr, i0, i1, i2, i3);
1 ✔
320
         result.test_u16_eq(i0, 0x6789);
1 ✔
321
         result.test_u16_eq(i1, 0x2345);
1 ✔
322
         result.test_u16_eq(i2, 0xEF01);
1 ✔
323
         result.test_u16_eq(i3, 0xABCD);
1 ✔
324

325
         Botan::load_be(std::span{outarr}.first<6>(), i0, i1, i2);
1 ✔
326
         result.test_u16_eq(i0, 0x8967);
1 ✔
327
         result.test_u16_eq(i1, 0x4523);
1 ✔
328
         result.test_u16_eq(i2, 0x01EF);
1 ✔
329
         result.test_u16_eq(i3, 0xABCD);  // remains unchanged
1 ✔
330

331
         i0 = 0xAA11;
1 ✔
332
         i1 = 0xBB22;
1 ✔
333
         i2 = 0xCC33;
1 ✔
334
         i3 = 0xDD44;
1 ✔
335
         Botan::store_be(outarr, i0, i1, i2, i3);
1 ✔
336
         result.test_bin_eq("store_be", outarr, "AA11BB22CC33DD44");
1 ✔
337
         std::vector<uint8_t> outvec(8);
1 ✔
338
         Botan::store_be(outvec, i0, i1, i2, i3);
1 ✔
339
         result.test_bin_eq("store_be", outvec, "AA11BB22CC33DD44");
1 ✔
340

341
         Botan::store_le(outarr, i0, i1, i2, i3);
1 ✔
342
         result.test_bin_eq("store_le(arr)", outarr, "11AA22BB33CC44DD");
1 ✔
343
         Botan::store_le(outvec, i0, i1, i2, i3);
1 ✔
344
         result.test_bin_eq("store_le", outvec, "11AA22BB33CC44DD");
1 ✔
345

346
#if !defined(BOTAN_TERMINATE_ON_ASSERTS)
347
         std::vector<uint8_t> sink56bits(7);
348
         std::vector<uint8_t> sink72bits(9);
349
         result.test_throws("store_le with a buffer that is too small",
350
                            [&] { Botan::store_le(sink56bits, i0, i1, i2, i3); });
351
         result.test_throws("store_le with a buffer that is too big",
352
                            [&] { Botan::store_le(sink72bits, i0, i1, i2, i3); });
353
         result.test_throws("store_be with a buffer that is too small",
354
                            [&] { Botan::store_be(sink56bits, i0, i1, i2, i3); });
355
         result.test_throws("store_be with a buffer that is too big",
356
                            [&] { Botan::store_be(sink72bits, i0, i1, i2, i3); });
357
#endif
358

359
         // can store multiple values straight into a collection
360
         auto out64_array_be = Botan::store_be(i0, i1, i2, i3);
1 ✔
361
         auto out64_vec_be = Botan::store_be<std::vector<uint8_t>>(i0, i1, i2, i3);
1 ✔
362
         auto out64_strong_be = Botan::store_be<TestVectorSink>(i0, i1, i2, i3);
1 ✔
363
         result.test_bin_eq("store_be(arr)", out64_array_be, "AA11BB22CC33DD44");
1 ✔
364
         result.test_bin_eq("store_be(vec)", out64_vec_be, "AA11BB22CC33DD44");
1 ✔
365
         result.test_bin_eq("store_be(strong)", out64_strong_be, "AA11BB22CC33DD44");
1 ✔
366
         auto out64_array_le = Botan::store_le(i0, i1, i2, i3);
1 ✔
367
         auto out64_vec_le = Botan::store_le<std::vector<uint8_t>>(i0, i1, i2, i3);
1 ✔
368
         auto out64_strong_le = Botan::store_le<TestVectorSink>(i0, i1, i2, i3);
1 ✔
369
         result.test_bin_eq("store_le(arr)", out64_array_le, "11AA22BB33CC44DD");
1 ✔
370
         result.test_bin_eq("store_le(vec)", out64_vec_le, "11AA22BB33CC44DD");
1 ✔
371
         result.test_bin_eq("store_le(strong)", out64_strong_le, "11AA22BB33CC44DD");
1 ✔
372

373
         result.test_u16_eq(in16, Botan::load_be(Botan::store_be(in16)));
1 ✔
374
         result.test_u32_eq(in32, Botan::load_be(Botan::store_be(in32)));
1 ✔
375
         result.test_u64_eq(in64, Botan::load_be(Botan::store_be(in64)));
1 ✔
376

377
         result.test_u16_eq(in16, Botan::load_le(Botan::store_le(in16)));
1 ✔
378
         result.test_u32_eq(in32, Botan::load_le(Botan::store_le(in32)));
1 ✔
379
         result.test_u64_eq(in64, Botan::load_le(Botan::store_le(in64)));
1 ✔
380

381
         // Test that the runtime detects incompatible range sizes
382
#if !defined(BOTAN_TERMINATE_ON_ASSERTS)
383
         std::vector<uint16_t> too_big16(4);
384
         std::vector<uint16_t> too_small16(1);
385
         result.test_throws("load_le with incompatible buffers",
386
                            [&] { Botan::load_le(too_big16, Botan::hex_decode("BAADB00B")); });
387
         result.test_throws("load_le with incompatible buffers",
388
                            [&] { Botan::load_le(too_small16, Botan::hex_decode("BAADB00B")); });
389
         result.test_throws("load_be with incompatible buffers",
390
                            [&] { Botan::load_be(too_big16, Botan::hex_decode("BAADB00B")); });
391
         result.test_throws("load_be with incompatible buffers",
392
                            [&] { Botan::load_be(too_small16, Botan::hex_decode("BAADB00B")); });
393

394
         std::vector<uint8_t> too_big8(4);
395
         std::vector<uint8_t> too_small8(1);
396
         result.test_throws("store_le with incompatible buffers",
397
                            [&] { Botan::store_le(too_big8, std::array<uint16_t, 1>{}); });
398
         result.test_throws("store_le with incompatible buffers",
399
                            [&] { Botan::store_le(too_small8, std::array<uint16_t, 1>{}); });
400
         result.test_throws("store_be with incompatible buffers",
401
                            [&] { Botan::store_be(too_big8, std::array<uint16_t, 1>{}); });
402
         result.test_throws("store_be with incompatible buffers",
403
                            [&] { Botan::store_be(too_small8, std::array<uint16_t, 1>{}); });
404
#endif
405

406
         // Test store of entire ranges
407
         const std::array<uint16_t, 2> in16_array = {0x0A0B, 0x0C0D};
1 ✔
408
         result.test_bin_eq("store_be(vec)", Botan::store_be<std::vector<uint8_t>>(in16_array), "0A0B0C0D");
1 ✔
409
         result.test_bin_eq("store_le(vec)", Botan::store_le<std::vector<uint8_t>>(in16_array), "0B0A0D0C");
1 ✔
410

411
         const std::vector<uint16_t> in16_vector = {0x0A0B, 0x0C0D};
1 ✔
412
         result.test_bin_eq("store_be(vec)", Botan::store_be<std::vector<uint8_t>>(in16_vector), "0A0B0C0D");
1 ✔
413
         result.test_bin_eq("store_le(vec)", Botan::store_le<std::vector<uint8_t>>(in16_vector), "0B0A0D0C");
1 ✔
414

415
         std::array<uint8_t, 4> out_array{};
1 ✔
416
         Botan::store_be(out_array, in16_array);
1 ✔
417
         result.test_bin_eq("store_be(arr)", out_array, "0A0B0C0D");
1 ✔
418
         Botan::store_le(out_array, in16_array);
1 ✔
419
         result.test_bin_eq("store_le(arr)", out_array, "0B0A0D0C");
1 ✔
420

421
         const auto be_inferred = Botan::store_be(in16_array);
1 ✔
422
         result.test_bin_eq("store_be(arr)", be_inferred, "0A0B0C0D");
1 ✔
423
         const auto le_inferred = Botan::store_le(in16_array);
1 ✔
424
         result.test_bin_eq("store_le(arr)", le_inferred, "0B0A0D0C");
1 ✔
425

426
         // Test load of entire ranges
427
         const auto in_buffer = Botan::hex_decode("AABBCCDD");
1 ✔
428
         auto out16_array_be = Botan::load_be<std::array<uint16_t, 2>>(in_buffer);
1 ✔
429
         result.test_u16_eq(out16_array_be[0], 0xAABB);
1 ✔
430
         result.test_u16_eq(out16_array_be[1], 0xCCDD);
1 ✔
431
         auto out16_vec_be = Botan::load_be<std::vector<uint16_t>>(in_buffer);
1 ✔
432
         result.test_sz_eq("be-vector has expected size", out16_vec_be.size(), 2);
1 ✔
433
         result.test_u16_eq(out16_vec_be[0], 0xAABB);
1 ✔
434
         result.test_u16_eq(out16_vec_be[1], 0xCCDD);
1 ✔
435

436
         auto out16_array_le = Botan::load_le<std::array<uint16_t, 2>>(in_buffer);
1 ✔
437
         result.test_u16_eq(out16_array_le[0], 0xBBAA);
1 ✔
438
         result.test_u16_eq(out16_array_le[1], 0xDDCC);
1 ✔
439
         auto out16_vec_le = Botan::load_le<Botan::secure_vector<uint16_t>>(in_buffer);
1 ✔
440
         result.test_sz_eq("le-vector has expected size", out16_vec_be.size(), 2);
1 ✔
441
         result.test_u16_eq(out16_vec_le[0], 0xBBAA);
1 ✔
442
         result.test_u16_eq(out16_vec_le[1], 0xDDCC);
1 ✔
443

444
         // Test loading/storing of strong type integers
445
         const TestInt64 in64_strong{0xABCDEF0123456789};
1 ✔
446
         const TestInt32 in32_strong{0xABCDEF01};
1 ✔
447

448
         result.test_bin_eq(
1 ✔
449
            "store_be(u64,strong)", Botan::store_be<std::vector<uint8_t>>(in64_strong), "ABCDEF0123456789");
×
450
         result.test_bin_eq(
1 ✔
451
            "store_le(u64,strong)", Botan::store_le<std::vector<uint8_t>>(in64_strong), "8967452301EFCDAB");
×
452
         result.test_bin_eq("store_be(u32,strong)", Botan::store_be<std::vector<uint8_t>>(in32_strong), "ABCDEF01");
1 ✔
453
         result.test_bin_eq("store_le(u32,strong)", Botan::store_le<std::vector<uint8_t>>(in32_strong), "01EFCDAB");
1 ✔
454

455
         test_arb_eq(
2 ✔
456
            result, "load_be(strong64)", Botan::load_be<TestInt64>(Botan::hex_decode("ABCDEF0123456789")), in64_strong);
2 ✔
457
         test_arb_eq(
2 ✔
458
            result, "load_le(strong64)", Botan::load_le<TestInt64>(Botan::hex_decode("8967452301EFCDAB")), in64_strong);
2 ✔
459
         test_arb_eq(
2 ✔
460
            result, "load_be(strong32)", Botan::load_be<TestInt32>(Botan::hex_decode("ABCDEF01")), in32_strong);
2 ✔
461
         test_arb_eq(
2 ✔
462
            result, "load_le(strong32)", Botan::load_le<TestInt32>(Botan::hex_decode("01EFCDAB")), in32_strong);
2 ✔
463

464
         const std::vector<TestInt64> some_in64_strongs{TestInt64{0xABCDEF0123456789}, TestInt64{0x0123456789ABCDEF}};
1 ✔
465
         result.test_bin_eq("store_be(vector,strong)",
1 ✔
466
                            Botan::store_be<std::vector<uint8_t>>(some_in64_strongs),
×
467
                            "ABCDEF01234567890123456789ABCDEF");
468
         result.test_bin_eq("store_le(vector,strong)",
1 ✔
469
                            Botan::store_le<std::vector<uint8_t>>(some_in64_strongs),
×
470
                            "8967452301EFCDABEFCDAB8967452301");
471

472
         const auto in64_strongs_le =
1 ✔
473
            Botan::load_le<std::array<TestInt64, 2>>(Botan::hex_decode("8967452301EFCDABEFCDAB8967452301"));
2 ✔
474
         test_arb_eq(result, "load_le(strong arr)", in64_strongs_le[0], TestInt64{0xABCDEF0123456789});
1 ✔
475
         test_arb_eq(result, "load_le(strong arr)", in64_strongs_le[1], TestInt64{0x0123456789ABCDEF});
1 ✔
476

477
         const auto in64_strongs_be =
1 ✔
478
            Botan::load_be<std::vector<TestInt64>>(Botan::hex_decode("ABCDEF01234567890123456789ABCDEF"));
2 ✔
479
         test_arb_eq(result, "load_be(strong arr)", in64_strongs_be[0], TestInt64{0xABCDEF0123456789});
1 ✔
480
         test_arb_eq(result, "load_be(strong arr)", in64_strongs_be[1], TestInt64{0x0123456789ABCDEF});
1 ✔
481

482
         // Test loading/storing of enum types with different endianness
483
         const auto in64_enum_le = Botan::load_le<TestEnum64>(Botan::hex_decode("1234567890ABCDEF"));
2 ✔
484
         result.test_enum_eq("load_le(enum64)", in64_enum_le, TestEnum64::_2);
1 ✔
485
         const auto in64_enum_be = Botan::load_be<TestEnum64>(Botan::hex_decode("1234567890ABCDEF"));
2 ✔
486
         result.test_enum_eq("load_be(enum64)", in64_enum_be, TestEnum64::_1);
1 ✔
487
         result.test_bin_eq(
1 ✔
488
            "store_be(enum64)", Botan::store_le<std::vector<uint8_t>>(TestEnum64::_1), "EFCDAB9078563412");
1 ✔
489
         result.test_bin_eq("store_be(enum64)", Botan::store_be(TestEnum64::_2), "EFCDAB9078563412");
1 ✔
490

491
         const auto in32_enum_le = Botan::load_le<TestEnum32>(Botan::hex_decode("78563412"));
2 ✔
492
         result.test_enum_eq("load_le(enum32)", in32_enum_le, TestEnum32::_1);
1 ✔
493
         const auto in32_enum_be = Botan::load_be<TestEnum32>(Botan::hex_decode("78563412"));
2 ✔
494
         result.test_enum_eq("load_be(enum32)", in32_enum_be, TestEnum32::_2);
1 ✔
495
         result.test_bin_eq("store_le(enum32)", Botan::store_le<std::vector<uint8_t>>(TestEnum32::_1), "78563412");
1 ✔
496
         result.test_bin_eq("store_be(enum32)", Botan::store_be(TestEnum32::_2), "78563412");
1 ✔
497

498
         return result;
2 ✔
499
      }
10 ✔
500

501
      template <std::unsigned_integral T>
502
      static T fb_load_be(std::array<const uint8_t, sizeof(T)> in) {
3 ✔
503
         return Botan::detail::fallback_load_any<std::endian::big, T>(in);
1 ✔
504
      }
505

506
      template <std::unsigned_integral T>
507
      static T fb_load_le(std::array<const uint8_t, sizeof(T)> in) {
3 ✔
508
         return Botan::detail::fallback_load_any<std::endian::little, T>(in);
1 ✔
509
      }
510

511
      template <std::unsigned_integral T>
512
      static decltype(auto) fb_store_be(const T in) {
3 ✔
513
         std::array<uint8_t, sizeof(T)> out{};
2 ✔
514
         Botan::detail::fallback_store_any<std::endian::big, T>(in, out);
1 ✔
515
         return out;
2 ✔
516
      }
517

518
      template <std::unsigned_integral T>
519
      static decltype(auto) fb_store_le(const T in) {
3 ✔
520
         std::array<uint8_t, sizeof(T)> out{};
2 ✔
521
         Botan::detail::fallback_store_any<std::endian::little, T>(in, out);
1 ✔
522
         return out;
2 ✔
523
      }
524

525
      template <size_t N>
526
      using a = std::array<uint8_t, N>;
527

528
      static Test::Result test_loadstore_ambiguity() {
1 ✔
529
         // This is a regression test for a (probable) compiler bug in Xcode 15
530
         // where it would fail to compile the load/store functions for size_t
531
         //
532
         // It seems that this platform defines uint64_t as "unsigned long long"
533
         // and size_t as "unsigned long". Both are 64-bits but the compiler
534
         // was unable to disambiguate the two in reverse_bytes in bswap.h
535

536
         const uint32_t in32 = 0x01234567;
1 ✔
537
         const uint64_t in64 = 0x0123456789ABCDEF;
1 ✔
538
         const size_t inszt = 0x87654321;
1 ✔
539

540
         Test::Result result("Util load/store ambiguity");
1 ✔
541
         const auto out_be_32 = Botan::store_be(in32);
1 ✔
542
         const auto out_le_32 = Botan::store_le(in32);
1 ✔
543
         const auto out_be_64 = Botan::store_be(in64);
1 ✔
544
         const auto out_le_64 = Botan::store_le(in64);
1 ✔
545
         const auto out_be_szt = Botan::store_be(inszt);
1 ✔
546
         const auto out_le_szt = Botan::store_le(inszt);
1 ✔
547

548
         result.test_u32_eq("be 32", Botan::load_be<uint32_t>(out_be_32), in32);
1 ✔
549
         result.test_u32_eq("le 32", Botan::load_le<uint32_t>(out_le_32), in32);
1 ✔
550
         result.test_u64_eq("be 64", Botan::load_be<uint64_t>(out_be_64), in64);
1 ✔
551
         result.test_u64_eq("le 64", Botan::load_le<uint64_t>(out_le_64), in64);
1 ✔
552
         result.test_sz_eq("be sz", Botan::load_be<size_t>(out_be_szt), inszt);
1 ✔
553
         result.test_sz_eq("le sz", Botan::load_le<size_t>(out_le_szt), inszt);
1 ✔
554

555
         return result;
1 ✔
556
      }
×
557

558
      static Test::Result test_loadstore_fallback() {
1 ✔
559
         // The fallback implementation is only used if we don't know the
560
         // endianness of the target at compile time. This makes sure that the
561
         // fallback implementation is correct. On all typical platforms it
562
         // won't be called in production.
563
         Test::Result result("Util load/store fallback");
1 ✔
564

565
         result.test_u16_eq("lLE 16", fb_load_le<uint16_t>({1, 2}), 0x0201);
1 ✔
566
         result.test_u32_eq("lLE 32", fb_load_le<uint32_t>({1, 2, 3, 4}), 0x04030201);
1 ✔
567
         result.test_u64_eq("lLE 64", fb_load_le<uint64_t>({1, 2, 3, 4, 5, 6, 7, 8}), 0x0807060504030201);
1 ✔
568

569
         result.test_u16_eq("lBE 16", fb_load_be<uint16_t>({1, 2}), 0x0102);
1 ✔
570
         result.test_u32_eq("lBE 32", fb_load_be<uint32_t>({1, 2, 3, 4}), 0x01020304);
1 ✔
571
         result.test_u64_eq("lBE 64", fb_load_be<uint64_t>({1, 2, 3, 4, 5, 6, 7, 8}), 0x0102030405060708);
1 ✔
572

573
         result.test_bin_eq("sLE 16", fb_store_le<uint16_t>(0x0201), "0102");
1 ✔
574
         result.test_bin_eq("sLE 32", fb_store_le<uint32_t>(0x04030201), "01020304");
1 ✔
575
         result.test_bin_eq("sLE 64", fb_store_le<uint64_t>(0x0807060504030201), "0102030405060708");
1 ✔
576

577
         result.test_bin_eq("sBE 16", fb_store_be<uint16_t>(0x0102), "0102");
1 ✔
578
         result.test_bin_eq("sBE 32", fb_store_be<uint32_t>(0x01020304), "01020304");
1 ✔
579
         result.test_bin_eq("sBE 64", fb_store_be<uint64_t>(0x0102030405060708), "0102030405060708");
1 ✔
580

581
         return result;
1 ✔
582
      }
×
583

584
      static Test::Result test_loadstore_constexpr() {
1 ✔
585
         Test::Result result("Util load/store constexpr");
1 ✔
586

587
         constexpr uint16_t in16 = 0x1234;
1 ✔
588
         constexpr uint32_t in32 = 0xA0B0C0D0;
1 ✔
589
         constexpr uint64_t in64 = 0xABCDEF0123456789;
1 ✔
590

591
         // clang-format off
592
         constexpr std::array<uint8_t, 16> cex_mem = {
1 ✔
593
            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
594
         };
595
         // clang-format on
596

597
         // get_byte<> w/ 16bit
598
         constexpr auto cex_byte_16_0 = Botan::get_byte<0>(in16);
1 ✔
599
         result.test_u8_eq(cex_byte_16_0, 0x12);
1 ✔
600
         constexpr auto cex_byte_16_1 = Botan::get_byte<1>(in16);
1 ✔
601
         result.test_u8_eq(cex_byte_16_1, 0x34);
1 ✔
602

603
         // get_byte<> w/ 32bit
604
         constexpr auto cex_byte_32_0 = Botan::get_byte<0>(in32);
1 ✔
605
         result.test_u8_eq(cex_byte_32_0, 0xA0);
1 ✔
606
         constexpr auto cex_byte_32_1 = Botan::get_byte<1>(in32);
1 ✔
607
         result.test_u8_eq(cex_byte_32_1, 0xB0);
1 ✔
608
         constexpr auto cex_byte_32_2 = Botan::get_byte<2>(in32);
1 ✔
609
         result.test_u8_eq(cex_byte_32_2, 0xC0);
1 ✔
610
         constexpr auto cex_byte_32_3 = Botan::get_byte<3>(in32);
1 ✔
611
         result.test_u8_eq(cex_byte_32_3, 0xD0);
1 ✔
612

613
         // get_byte<> w/ 64bit
614
         constexpr auto cex_byte_64_0 = Botan::get_byte<0>(in64);
1 ✔
615
         result.test_u8_eq(cex_byte_64_0, 0xAB);
1 ✔
616
         constexpr auto cex_byte_64_1 = Botan::get_byte<1>(in64);
1 ✔
617
         result.test_u8_eq(cex_byte_64_1, 0xCD);
1 ✔
618
         constexpr auto cex_byte_64_2 = Botan::get_byte<2>(in64);
1 ✔
619
         result.test_u8_eq(cex_byte_64_2, 0xEF);
1 ✔
620
         constexpr auto cex_byte_64_3 = Botan::get_byte<3>(in64);
1 ✔
621
         result.test_u8_eq(cex_byte_64_3, 0x01);
1 ✔
622
         constexpr auto cex_byte_64_4 = Botan::get_byte<4>(in64);
1 ✔
623
         result.test_u8_eq(cex_byte_64_4, 0x23);
1 ✔
624
         constexpr auto cex_byte_64_5 = Botan::get_byte<5>(in64);
1 ✔
625
         result.test_u8_eq(cex_byte_64_5, 0x45);
1 ✔
626
         constexpr auto cex_byte_64_6 = Botan::get_byte<6>(in64);
1 ✔
627
         result.test_u8_eq(cex_byte_64_6, 0x67);
1 ✔
628
         constexpr auto cex_byte_64_7 = Botan::get_byte<7>(in64);
1 ✔
629
         result.test_u8_eq(cex_byte_64_7, 0x89);
1 ✔
630

631
         // make_uintXX()
632
         constexpr auto cex_uint16_t = Botan::make_uint16(0x12, 0x34);
1 ✔
633
         result.test_u16_eq(cex_uint16_t, in16);
1 ✔
634
         constexpr auto cex_uint32_t = Botan::make_uint32(0xA0, 0xB0, 0xC0, 0xD0);
1 ✔
635
         result.test_u32_eq(cex_uint32_t, in32);
1 ✔
636
         constexpr auto cex_uint64_t = Botan::make_uint64(0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89);
1 ✔
637
         result.test_u64_eq(cex_uint64_t, in64);
1 ✔
638

639
         // store_le/be with a single integer
640
         constexpr std::array<uint8_t, 2> cex_store_le16 = Botan::store_le(in16);
1 ✔
641
         result.test_bin_eq("store_le(u16 arr)", cex_store_le16, "3412");
1 ✔
642
         constexpr std::array<uint8_t, 4> cex_store_le32 = Botan::store_le(in32);
1 ✔
643
         result.test_bin_eq("store_le(u32 arr)", cex_store_le32, "D0C0B0A0");
1 ✔
644
         constexpr std::array<uint8_t, 8> cex_store_le64 = Botan::store_le(in64);
1 ✔
645
         result.test_bin_eq("store_le(u32,arr)", cex_store_le64, "8967452301EFCDAB");
1 ✔
646

647
         constexpr std::array<uint8_t, 2> cex_store_be16 = Botan::store_be(in16);
1 ✔
648
         result.test_bin_eq("store_be(u16 arr)", cex_store_be16, "1234");
1 ✔
649
         constexpr std::array<uint8_t, 4> cex_store_be32 = Botan::store_be(in32);
1 ✔
650
         result.test_bin_eq("store_be(u32 arr)", cex_store_be32, "A0B0C0D0");
1 ✔
651
         constexpr std::array<uint8_t, 8> cex_store_be64 = Botan::store_be(in64);
1 ✔
652
         result.test_bin_eq("store_be(u64 arr)", cex_store_be64, "ABCDEF0123456789");
1 ✔
653

654
         // store_le/be with multiple integers, both as a parameter pack and a range (std::array for constexpr)
655
         constexpr std::array<uint8_t, 16> cex_store_le16s =
1 ✔
656
            Botan::store_le(in16, in16, in16, in16, in16, in16, in16, in16);
657
         constexpr std::array<uint8_t, 16> cex_store_le16s2 =
1 ✔
658
            Botan::store_le(std::array{in16, in16, in16, in16, in16, in16, in16, in16});
659
         result.test_bin_eq("store_le", cex_store_le16s, "34123412341234123412341234123412");
1 ✔
660
         result.test_bin_eq("cex_store_le16s", cex_store_le16s, cex_store_le16s2);
1 ✔
661
         constexpr std::array<uint8_t, 16> cex_store_le32s = Botan::store_le(in32, in32, in32, in32);
1 ✔
662
         constexpr std::array<uint8_t, 16> cex_store_le32s2 = Botan::store_le(std::array{in32, in32, in32, in32});
1 ✔
663
         result.test_bin_eq("cex_store_le32s", cex_store_le32s, "D0C0B0A0D0C0B0A0D0C0B0A0D0C0B0A0");
1 ✔
664
         result.test_bin_eq("cex_store_le32s2", cex_store_le32s, cex_store_le32s2);
1 ✔
665
         constexpr std::array<uint8_t, 16> cex_store_le64s = Botan::store_le(in64, in64);
1 ✔
666
         constexpr std::array<uint8_t, 16> cex_store_le64s2 = Botan::store_le(std::array{in64, in64});
1 ✔
667
         result.test_bin_eq("cex_store_le64s", cex_store_le64s, "8967452301EFCDAB8967452301EFCDAB");
1 ✔
668
         result.test_bin_eq("cex_store_le64s2", cex_store_le64s, cex_store_le64s2);
1 ✔
669

670
         constexpr std::array<uint8_t, 16> cex_store_be16s =
1 ✔
671
            Botan::store_be(in16, in16, in16, in16, in16, in16, in16, in16);
672
         constexpr std::array<uint8_t, 16> cex_store_be16s2 =
1 ✔
673
            Botan::store_be(std::array{in16, in16, in16, in16, in16, in16, in16, in16});
674
         result.test_bin_eq("cex_store_be16s", cex_store_be16s, "12341234123412341234123412341234");
1 ✔
675
         result.test_bin_eq("cex_store_be16s2", cex_store_be16s, cex_store_be16s2);
1 ✔
676
         constexpr std::array<uint8_t, 16> cex_store_be32s = Botan::store_be(in32, in32, in32, in32);
1 ✔
677
         constexpr std::array<uint8_t, 16> cex_store_be32s2 = Botan::store_be(std::array{in32, in32, in32, in32});
1 ✔
678
         result.test_bin_eq("cex_store_be32s", cex_store_be32s, "A0B0C0D0A0B0C0D0A0B0C0D0A0B0C0D0");
1 ✔
679
         result.test_bin_eq("cex_store_be32s2", cex_store_be32s, cex_store_be32s2);
1 ✔
680
         constexpr std::array<uint8_t, 16> cex_store_be64s = Botan::store_be(in64, in64);
1 ✔
681
         constexpr std::array<uint8_t, 16> cex_store_be64s2 = Botan::store_be(std::array{in64, in64});
1 ✔
682
         result.test_bin_eq("cex_store_be64s", cex_store_be64s, "ABCDEF0123456789ABCDEF0123456789");
1 ✔
683
         result.test_bin_eq("cex_store_be64s2", cex_store_be64s, cex_store_be64s2);
1 ✔
684

685
         // load_le/be a single integer
686
         constexpr uint16_t cex_load_le16 = Botan::load_le<uint16_t>(cex_store_le16);
1 ✔
687
         result.test_u16_eq(cex_load_le16, in16);
1 ✔
688
         constexpr uint32_t cex_load_le32 = Botan::load_le<uint32_t>(cex_store_le32);
1 ✔
689
         result.test_u32_eq(cex_load_le32, in32);
1 ✔
690
         constexpr uint64_t cex_load_le64 = Botan::load_le<uint64_t>(cex_store_le64);
1 ✔
691
         result.test_u64_eq(cex_load_le64, in64);
1 ✔
692

693
         constexpr uint16_t cex_load_be16 = Botan::load_be<uint16_t>(cex_store_be16);
1 ✔
694
         result.test_u16_eq(cex_load_be16, in16);
1 ✔
695
         constexpr uint32_t cex_load_be32 = Botan::load_be<uint32_t>(cex_store_be32);
1 ✔
696
         result.test_u32_eq(cex_load_be32, in32);
1 ✔
697
         constexpr uint64_t cex_load_be64 = Botan::load_be<uint64_t>(cex_store_be64);
1 ✔
698
         result.test_u64_eq(cex_load_be64, in64);
1 ✔
699

700
         // load_le/be multiple integers into a std::array for constexpr
701
         constexpr auto cex_load_le16s = Botan::load_le<std::array<uint16_t, cex_mem.size() / 2>>(cex_mem);
1 ✔
702
         test_arb_eq(result,
1 ✔
703
                     "constexpr load_le(u16)",
704
                     cex_load_le16s,
705
                     {0x1100, 0x3322, 0x5544, 0x7766, 0x9988, 0xBBAA, 0xDDCC, 0xFFEE});
706
         constexpr auto cex_load_le32s = Botan::load_le<std::array<uint32_t, cex_mem.size() / 4>>(cex_mem);
1 ✔
707
         test_arb_eq(
1 ✔
708
            result, "constexpr load_le(u32)", cex_load_le32s, {0x33221100, 0x77665544, 0xBBAA9988, 0xFFEEDDCC});
709
         constexpr auto cex_load_le64s = Botan::load_le<std::array<uint64_t, cex_mem.size() / 8>>(cex_mem);
1 ✔
710
         test_arb_eq(result, "constexpr load_le(u64)", cex_load_le64s, {0x7766554433221100, 0xFFEEDDCCBBAA9988});
1 ✔
711

712
         constexpr auto cex_load_be16s = Botan::load_be<std::array<uint16_t, cex_mem.size() / 2>>(cex_mem);
1 ✔
713
         test_arb_eq(result,
1 ✔
714
                     "constexpr load_be(u16)",
715
                     cex_load_be16s,
716
                     {0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xAABB, 0xCCDD, 0xEEFF});
717
         constexpr auto cex_load_be32s = Botan::load_be<std::array<uint32_t, cex_mem.size() / 4>>(cex_mem);
1 ✔
718
         test_arb_eq(
1 ✔
719
            result, "constexpr load_be(u32)", cex_load_be32s, {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF});
720
         constexpr auto cex_load_be64s = Botan::load_be<std::array<uint64_t, cex_mem.size() / 8>>(cex_mem);
1 ✔
721
         test_arb_eq(result, "constexpr load_be(u64)", cex_load_be64s, {0x0011223344556677, 0x8899AABBCCDDEEFF});
1 ✔
722

723
         return result;
1 ✔
724
      }
×
725

726
      static std::vector<Test::Result> test_copy_out_be_le() {
1 ✔
727
         return {
1 ✔
728
            CHECK("copy_out_be with 16bit input (word aligned)",
729
                  [&](auto& result) {
1 ✔
730
                     std::vector<uint8_t> out_vector(4);
1 ✔
731
                     const std::array<uint16_t, 2> in_array = {0x0A0B, 0x0C0D};
1 ✔
732
                     Botan::copy_out_be(out_vector, in_array);
1 ✔
733
                     result.test_bin_eq("copy_out_be", out_vector, "0A0B0C0D");
1 ✔
734
                  }),
1 ✔
735

736
            CHECK("copy_out_be with 16bit input (partial words)",
737
                  [&](auto& result) {
1 ✔
738
                     std::vector<uint8_t> out_vector(3);
1 ✔
739
                     const std::array<uint16_t, 2> in_array = {0x0A0B, 0x0C0D};
1 ✔
740
                     Botan::copy_out_be(out_vector, in_array);
1 ✔
741
                     result.test_bin_eq("copy_out_be(u16)", out_vector, "0A0B0C");
1 ✔
742
                  }),
1 ✔
743

744
            CHECK("copy_out_le with 16bit input (word aligned)",
745
                  [&](auto& result) {
1 ✔
746
                     std::vector<uint8_t> out_vector(4);
1 ✔
747
                     const std::array<uint16_t, 2> in_array = {0x0A0B, 0x0C0D};
1 ✔
748
                     Botan::copy_out_le(out_vector, in_array);
1 ✔
749
                     result.test_bin_eq("copy_out_le(u16)", out_vector, "0B0A0D0C");
1 ✔
750
                  }),
1 ✔
751

752
            CHECK("copy_out_le with 16bit input (partial words)",
753
                  [&](auto& result) {
1 ✔
754
                     std::vector<uint8_t> out_vector(3);
1 ✔
755
                     const std::array<uint16_t, 2> in_array = {0x0A0B, 0x0C0D};
1 ✔
756
                     Botan::copy_out_le(out_vector, in_array);
1 ✔
757
                     result.test_bin_eq("copy_out_le(u16)", out_vector, "0B0A0D");
1 ✔
758
                  }),
1 ✔
759

760
            CHECK("copy_out_be with 64bit input (word aligned)",
761
                  [&](auto& result) {
1 ✔
762
                     std::vector<uint8_t> out_vector(16);
1 ✔
763
                     const std::array<uint64_t, 2> in_array = {0x0A0B0C0D0E0F1011, 0x1213141516171819};
1 ✔
764
                     Botan::copy_out_be(out_vector, in_array);
1 ✔
765
                     result.test_bin_eq("copy_out_be(u64)", out_vector, "0A0B0C0D0E0F10111213141516171819");
1 ✔
766
                  }),
1 ✔
767

768
            CHECK("copy_out_le with 64bit input (word aligned)",
769
                  [&](auto& result) {
1 ✔
770
                     std::vector<uint8_t> out_vector(16);
1 ✔
771
                     const std::array<uint64_t, 2> in_array = {0x0A0B0C0D0E0F1011, 0x1213141516171819};
1 ✔
772
                     Botan::copy_out_le(out_vector, in_array);
1 ✔
773
                     result.test_bin_eq("copy_out_le(u64)", out_vector, "11100F0E0D0C0B0A1918171615141312");
1 ✔
774
                  }),
1 ✔
775

776
            CHECK("copy_out_be with 64bit input (partial words)",
777
                  [&](auto& result) {
1 ✔
778
                     std::vector<uint8_t> out_vector(15);
1 ✔
779
                     const std::array<uint64_t, 2> in_array = {0x0A0B0C0D0E0F1011, 0x1213141516171819};
1 ✔
780
                     Botan::copy_out_be(out_vector, in_array);
1 ✔
781
                     result.test_bin_eq("copy_out_be(u64)", out_vector, "0A0B0C0D0E0F101112131415161718");
1 ✔
782
                  }),
1 ✔
783

784
            CHECK("copy_out_le with 64bit input (partial words)",
785
                  [&](auto& result) {
1 ✔
786
                     std::vector<uint8_t> out_vector(15);
1 ✔
787
                     const std::array<uint64_t, 2> in_array = {0x0A0B0C0D0E0F1011, 0x1213141516171819};
1 ✔
788
                     Botan::copy_out_le(out_vector, in_array);
1 ✔
789
                     result.test_bin_eq("copy_out_le(u64)", out_vector, "11100F0E0D0C0B0A19181716151413");
1 ✔
790
                  }),
1 ✔
791
         };
9 ✔
792
      }
1 ✔
793
};
794

795
BOTAN_REGISTER_SMOKE_TEST("utils", "util", Utility_Function_Tests);
796

797
class AlgorithmSpec_Tests final : public Test {
1 ✔
798
   public:
799
      std::vector<Test::Result> run() override {
1 ✔
800
         std::vector<Test::Result> results;
1 ✔
801

802
         results.push_back(parse_bare_name());
2 ✔
803
         results.push_back(parse_name_with_args());
2 ✔
804
         results.push_back(parse_cipher_mode_syntax());
2 ✔
805
         results.push_back(parse_invalid_names());
2 ✔
806
         results.push_back(parse_nesting_depth());
2 ✔
807
         results.push_back(match_exact());
2 ✔
808
         results.push_back(match_head_alternatives());
2 ✔
809
         results.push_back(match_wildcard_head());
2 ✔
810
         results.push_back(match_int_placeholders());
2 ✔
811
         results.push_back(match_optional_placeholders());
2 ✔
812
         results.push_back(match_str_and_name_placeholders());
2 ✔
813
         results.push_back(match_variadic());
2 ✔
814
         results.push_back(match_literals());
2 ✔
815
         results.push_back(match_optional_literals());
2 ✔
816
         results.push_back(match_accessor_errors());
2 ✔
817

818
         return results;
1 ✔
819
      }
×
820

821
   private:
822
      static Test::Result parse_bare_name() {
1 ✔
823
         Test::Result result("AlgorithmSpec bare name");
1 ✔
824
         const Botan::AlgorithmSpec spec("SHA-256");
1 ✔
825
         result.test_str_eq("head", spec.head(), "SHA-256");
1 ✔
826
         result.test_sz_eq("no args", spec.arg_count(), 0);
1 ✔
827
         result.test_str_eq("text", spec.to_string(), "SHA-256");
1 ✔
828
         result.test_str_eq("canonical", spec.canonical_form(), "SHA-256");
1 ✔
829

830
         // Without the cipher mode syntax a slash is an ordinary character
831
         const Botan::AlgorithmSpec slashed("AES-128/CBC/PKCS7");
1 ✔
832
         result.test_str_eq("slash is not a separator", slashed.head(), "AES-128/CBC/PKCS7");
1 ✔
833
         result.test_sz_eq("no args", slashed.arg_count(), 0);
1 ✔
834
         return result;
1 ✔
835
      }
1 ✔
836

837
      static Test::Result parse_name_with_args() {
1 ✔
838
         Test::Result result("AlgorithmSpec with arguments");
1 ✔
839

840
         const Botan::AlgorithmSpec hmac("HMAC(SHA-256)");
1 ✔
841
         result.test_str_eq("head", hmac.head(), "HMAC");
1 ✔
842
         result.test_sz_eq("one arg", hmac.arg_count(), 1);
1 ✔
843
         result.test_str_eq("arg", hmac.arg(0).to_string(), "SHA-256");
1 ✔
844
         result.test_throws<Botan::Invalid_Argument>("arg out of range", [&] { hmac.arg(1); });
2 ✔
845

846
         const Botan::AlgorithmSpec nested("Foo(A,B(C,D),E)");
1 ✔
847
         result.test_str_eq("head", nested.head(), "Foo");
1 ✔
848
         result.test_sz_eq("three args", nested.arg_count(), 3);
1 ✔
849
         result.test_str_eq("arg0", nested.arg(0).to_string(), "A");
1 ✔
850
         result.test_str_eq("arg1", nested.arg(1).to_string(), "B(C,D)");
1 ✔
851
         result.test_str_eq("arg1 head", nested.arg(1).head(), "B");
1 ✔
852
         result.test_sz_eq("arg1 args", nested.arg(1).arg_count(), 2);
1 ✔
853
         result.test_str_eq("arg1 arg1", nested.arg(1).arg(1).to_string(), "D");
1 ✔
854
         result.test_str_eq("arg2", nested.arg(2).to_string(), "E");
1 ✔
855
         result.test_str_eq("canonical", nested.canonical_form(), "Foo(A,B(C,D),E)");
1 ✔
856
         result.test_str_eq("with_head", nested.with_head("Bar").to_string(), "Bar(A,B(C,D),E)");
1 ✔
857

858
         return result;
1 ✔
859
      }
1 ✔
860

861
      static Test::Result parse_cipher_mode_syntax() {
1 ✔
862
         Test::Result result("AlgorithmSpec cipher mode syntax");
1 ✔
863

864
         using Syntax = Botan::AlgorithmSpec::Syntax;
1 ✔
865

866
         const Botan::AlgorithmSpec cbc("AES-128/CBC/PKCS7", Syntax::CipherMode);
1 ✔
867
         result.test_str_eq("head", cbc.head(), "CBC");
1 ✔
868
         result.test_sz_eq("two args", cbc.arg_count(), 2);
1 ✔
869
         result.test_str_eq("cipher", cbc.arg(0).to_string(), "AES-128");
1 ✔
870
         result.test_str_eq("padding", cbc.arg(1).to_string(), "PKCS7");
1 ✔
871
         result.test_str_eq("text", cbc.to_string(), "AES-128/CBC/PKCS7");
1 ✔
872
         result.test_str_eq("canonical", cbc.canonical_form(), "CBC(AES-128,PKCS7)");
1 ✔
873

874
         const Botan::AlgorithmSpec gcm("AES-128/GCM(12)", Syntax::CipherMode);
1 ✔
875
         result.test_str_eq("head", gcm.head(), "GCM");
1 ✔
876
         result.test_str_eq("canonical", gcm.canonical_form(), "GCM(AES-128,12)");
1 ✔
877

878
         const Botan::AlgorithmSpec gcm_pad("AES-128/GCM(12)/Foo", Syntax::CipherMode);
1 ✔
879
         result.test_str_eq("canonical", gcm_pad.canonical_form(), "GCM(AES-128,12,Foo)");
1 ✔
880

881
         const Botan::AlgorithmSpec plain("GCM(AES-128,12)", Syntax::CipherMode);
1 ✔
882
         result.test_str_eq("canonical", plain.canonical_form(), "GCM(AES-128,12)");
1 ✔
883

884
         // A slash below the top level is an ordinary character
885
         const Botan::AlgorithmSpec inner("AES-128/CBC(Foo/Bar)", Syntax::CipherMode);
1 ✔
886
         result.test_str_eq("canonical", inner.canonical_form(), "CBC(AES-128,Foo/Bar)");
1 ✔
887

888
         const Botan::AlgorithmSpec bare("SHA-256", Syntax::CipherMode);
1 ✔
889
         result.test_str_eq("head", bare.head(), "SHA-256");
1 ✔
890
         result.test_sz_eq("no args", bare.arg_count(), 0);
1 ✔
891

892
         return result;
1 ✔
893
      }
1 ✔
894

895
      static Test::Result parse_invalid_names() {
1 ✔
896
         Test::Result result("AlgorithmSpec invalid names rejected");
1 ✔
897

898
         using Syntax = Botan::AlgorithmSpec::Syntax;
1 ✔
899

900
         const auto verify_rejected = [&](std::string_view name, Syntax syntax) {
46 ✔
901
            result.test_throws<Botan::Invalid_Algorithm_Name>(Botan::fmt("Invalid name '{}' rejected", name), [&] {
90 ✔
902
               const Botan::AlgorithmSpec _parsed(name, syntax);
45 ✔
903
            });
×
904
         };
45 ✔
905

906
         const std::vector<std::string> invalid_in_any_syntax = {
1 ✔
907
            "",        "(",       ")",         ",",        "Foo((A))",         "Foo(A(((B))C))", "Foo()",
908
            "Foo,",    "Foo(",    "Foo)",      "Foo(,)",   "Foo(,A)",          "Foo(A,)",        "Foo(A,,B)",
909
            "Foo(A)B", "Foo(A))", "Foo(A)(B)", "Foo(A),B", "HMAC(SHA-256)Foo",
910
         };
1 ✔
911

912
         for(const auto& tc : invalid_in_any_syntax) {
20 ✔
913
            verify_rejected(tc, Syntax::Standard);
19 ✔
914
            verify_rejected(tc, Syntax::CipherMode);
19 ✔
915
         }
916

917
         const std::vector<std::string> invalid_cipher_modes = {
1 ✔
918
            "AES-128//CBC",
919
            "/Foo",
920
            "Foo/",
921
            "/",
922
            "AES-128/CBC(",
923
            "AES-128/CBC()",
924
            "AES-128/CBC(A)/",
925
         };
1 ✔
926

927
         for(const auto& tc : invalid_cipher_modes) {
8 ✔
928
            verify_rejected(tc, Syntax::CipherMode);
7 ✔
929
         }
930

931
         return result;
1 ✔
932
      }
1 ✔
933

934
      static Test::Result parse_nesting_depth() {
1 ✔
935
         Test::Result result("AlgorithmSpec nesting depth");
1 ✔
936

937
         auto nested = [](size_t depth) {
3 ✔
938
            std::string s = "X";
2 ✔
939
            for(size_t i = 0; i != depth; ++i) {
52 ✔
940
               s = Botan::fmt("F({})", s);
50 ✔
941
            }
942
            return s;
2 ✔
943
         };
×
944

945
         result.test_no_throw("moderate nesting accepted", [&] { const Botan::AlgorithmSpec _parsed(nested(10)); });
3 ✔
946
         result.test_throws<Botan::Invalid_Algorithm_Name>("excessive nesting rejected",
1 ✔
947
                                                           [&] { const Botan::AlgorithmSpec _parsed(nested(40)); });
2 ✔
948

949
         return result;
1 ✔
950
      }
×
951

952
      static Test::Result match_exact() {
1 ✔
953
         Test::Result result("AlgorithmSpec exact match");
1 ✔
954

955
         const Botan::AlgorithmSpec hmac("HMAC(SHA-512)");
1 ✔
956
         if(const auto m = hmac.match("HMAC({hash})")) {
1 ✔
957
            result.test_str_eq("head", m->head(), "HMAC");
1 ✔
958
            result.test_is_true("hash bound", m->has("hash"));
1 ✔
959
            result.test_str_eq("hash", m->str("hash"), "SHA-512");
1 ✔
960
            result.test_str_eq("hash as name", m->name("hash").head(), "SHA-512");
1 ✔
961
         } else {
962
            result.test_failure("HMAC(SHA-512) did not match");
×
963
         }
964

965
         // Extra or missing arguments must not match
966
         const Botan::AlgorithmSpec extra("HMAC(SHA-512,Foo)");
1 ✔
967
         result.test_is_false("extra argument rejected", extra.matches("HMAC({hash})"));
1 ✔
968

969
         const Botan::AlgorithmSpec none("HMAC");
1 ✔
970
         result.test_is_false("missing argument rejected", none.matches("HMAC({hash})"));
1 ✔
971

972
         const Botan::AlgorithmSpec other("CMAC(AES-128)");
1 ✔
973
         result.test_is_false("other head rejected", other.matches("HMAC({hash})"));
1 ✔
974

975
         // A pattern without elements only matches a bare name
976
         const Botan::AlgorithmSpec bare("SHA-256");
1 ✔
977
         result.test_is_true("bare name matches", bare.matches("SHA-256"));
1 ✔
978
         const Botan::AlgorithmSpec with_arg("SHA-256(1)");
1 ✔
979
         result.test_is_false("bare pattern rejects args", with_arg.matches("SHA-256"));
1 ✔
980

981
         return result;
1 ✔
982
      }
1 ✔
983

984
      static Test::Result match_head_alternatives() {
1 ✔
985
         Test::Result result("AlgorithmSpec head alternatives");
1 ✔
986

987
         const Botan::AlgorithmSpec emsa4("EMSA4(SHA-256)");
1 ✔
988
         if(const auto m = emsa4.match("PSS|EMSA-PSS|EMSA4({hash})")) {
1 ✔
989
            result.test_str_eq("matched head", m->head(), "EMSA4");
1 ✔
990
            result.test_str_eq("hash", m->str("hash"), "SHA-256");
1 ✔
991
         } else {
992
            result.test_failure("EMSA4(SHA-256) did not match");
×
993
         }
994

995
         const Botan::AlgorithmSpec sha2("SHA2-256");
1 ✔
996
         result.test_is_true("bare alternative", sha2.matches("SHA-256|SHA2-256"));
1 ✔
997
         const Botan::AlgorithmSpec sha3("SHA-3(256)");
1 ✔
998
         result.test_is_false("other head", sha3.matches("SHA-256|SHA2-256"));
1 ✔
999

1000
         return result;
1 ✔
1001
      }
1 ✔
1002

1003
      static Test::Result match_wildcard_head() {
1 ✔
1004
         Test::Result result("AlgorithmSpec wildcard head");
1 ✔
1005

1006
         const Botan::AlgorithmPattern pattern("*({cipher})");
1 ✔
1007

1008
         const Botan::AlgorithmSpec cbc("CBC(AES-128)");
1 ✔
1009
         if(const auto m = cbc.match(pattern)) {
1 ✔
1010
            result.test_str_eq("head", m->head(), "CBC");
1 ✔
1011
            result.test_str_eq("cipher", m->str("cipher"), "AES-128");
1 ✔
1012
         } else {
1013
            result.test_failure("CBC(AES-128) did not match");
×
1014
         }
1015

1016
         const Botan::AlgorithmSpec other("Anything-At-All(AES-128)");
1 ✔
1017
         if(const auto m = other.match(pattern)) {
1 ✔
1018
            result.test_str_eq("head", m->head(), "Anything-At-All");
1 ✔
1019
         } else {
1020
            result.test_failure("Anything-At-All(AES-128) did not match");
×
1021
         }
1022

1023
         // The elements are still enforced
1024
         const Botan::AlgorithmSpec bare("CBC");
1 ✔
1025
         result.test_is_false("missing argument rejected", bare.matches(pattern));
1 ✔
1026
         const Botan::AlgorithmSpec extra("CBC(AES-128,x)");
1 ✔
1027
         result.test_is_false("extra argument rejected", extra.matches(pattern));
1 ✔
1028

1029
         const Botan::AlgorithmSpec name_only("SHA-256");
1 ✔
1030
         result.test_is_true("bare wildcard matches any bare name", name_only.matches("*"));
1 ✔
1031
         result.test_is_false("bare wildcard rejects arguments", cbc.matches("*"));
1 ✔
1032

1033
         return result;
1 ✔
1034
      }
1 ✔
1035

1036
      static Test::Result match_int_placeholders() {
1 ✔
1037
         Test::Result result("AlgorithmSpec int placeholders");
1 ✔
1038

1039
         const Botan::AlgorithmPattern ccm("CCM({cipher},{tag_len:int=16},{L:int=3})");
1 ✔
1040

1041
         const Botan::AlgorithmSpec defaults("CCM(AES-128)");
1 ✔
1042
         if(const auto m = defaults.match(ccm)) {
1 ✔
1043
            result.test_str_eq("cipher", m->str("cipher"), "AES-128");
1 ✔
1044
            result.test_is_true("tag_len has default", m->has("tag_len"));
1 ✔
1045
            result.test_sz_eq("tag_len default", m->integer("tag_len"), 16);
1 ✔
1046
            result.test_str_eq("tag_len default text", m->str("tag_len"), "16");
1 ✔
1047
            result.test_sz_eq("L default", m->integer("L"), 3);
1 ✔
1048
         } else {
1049
            result.test_failure("CCM(AES-128) did not match");
×
1050
         }
1051

1052
         const Botan::AlgorithmSpec partial("CCM(AES-128,8)");
1 ✔
1053
         if(const auto m = partial.match(ccm)) {
1 ✔
1054
            result.test_sz_eq("tag_len", m->integer("tag_len"), 8);
1 ✔
1055
            result.test_sz_eq("L default", m->integer("L"), 3);
1 ✔
1056
         } else {
1057
            result.test_failure("CCM(AES-128,8) did not match");
×
1058
         }
1059

1060
         const Botan::AlgorithmSpec full("CCM(AES-128,8,4)");
1 ✔
1061
         if(const auto m = full.match(ccm)) {
1 ✔
1062
            result.test_sz_eq("tag_len", m->integer("tag_len"), 8);
1 ✔
1063
            result.test_sz_eq("L", m->integer("L"), 4);
1 ✔
1064
         } else {
1065
            result.test_failure("CCM(AES-128,8,4) did not match");
×
1066
         }
1067

1068
         const std::vector<std::string> rejected = {
1 ✔
1069
            "CCM(AES-128,08)",
1070
            "CCM(AES-128,+8)",
1071
            "CCM(AES-128,-8)",
1072
            "CCM(AES-128,8 )",
1073
            "CCM(AES-128,x)",
1074
            "CCM(AES-128,8(1))",
1075
            "CCM(AES-128,8,4,5)",
1076
            "CCM(AES-128,99999999999999999999999)",
1077
         };
1 ✔
1078

1079
         for(const auto& tc : rejected) {
9 ✔
1080
            const Botan::AlgorithmSpec spec(tc);
8 ✔
1081
            result.test_is_false(Botan::fmt("'{}' rejected", tc), spec.matches(ccm));
8 ✔
1082
         }
8 ✔
1083

1084
         return result;
1 ✔
1085
      }
1 ✔
1086

1087
      static Test::Result match_optional_placeholders() {
1 ✔
1088
         Test::Result result("AlgorithmSpec optional placeholders");
1 ✔
1089

1090
         const Botan::AlgorithmPattern cfb("CFB({cipher},{feedback_bits:int?})");
1 ✔
1091

1092
         const Botan::AlgorithmSpec absent("CFB(AES-128)");
1 ✔
1093
         if(const auto m = absent.match(cfb)) {
1 ✔
1094
            result.test_str_eq("cipher", m->str("cipher"), "AES-128");
1 ✔
1095
            result.test_is_false("feedback_bits unbound", m->has("feedback_bits"));
1 ✔
1096
            result.test_throws<Botan::Internal_Error>("unbound integer() throws", [&] { m->integer("feedback_bits"); });
2 ✔
1097
            result.test_throws<Botan::Internal_Error>("unbound str() throws", [&] { m->str("feedback_bits"); });
2 ✔
1098
         } else {
1099
            result.test_failure("CFB(AES-128) did not match");
×
1100
         }
1101

1102
         const Botan::AlgorithmSpec present("CFB(AES-128,8)");
1 ✔
1103
         if(const auto m = present.match(cfb)) {
1 ✔
1104
            result.test_is_true("feedback_bits bound", m->has("feedback_bits"));
1 ✔
1105
            result.test_sz_eq("feedback_bits", m->integer("feedback_bits"), 8);
1 ✔
1106
         } else {
1107
            result.test_failure("CFB(AES-128,8) did not match");
×
1108
         }
1109

1110
         // An empty str default is permitted
1111
         const Botan::AlgorithmSpec no_pers("Skein-512(256)");
1 ✔
1112
         if(const auto m = no_pers.match("Skein-512({outbits:int=512},{personalization:str=})")) {
1 ✔
1113
            result.test_sz_eq("outbits", m->integer("outbits"), 256);
1 ✔
1114
            result.test_is_true("personalization bound to default", m->has("personalization"));
1 ✔
1115
            result.test_str_eq("personalization empty", m->str("personalization"), "");
1 ✔
1116
         } else {
1117
            result.test_failure("Skein-512(256) did not match");
×
1118
         }
1119

1120
         return result;
1 ✔
1121
      }
1 ✔
1122

1123
      static Test::Result match_str_and_name_placeholders() {
1 ✔
1124
         Test::Result result("AlgorithmSpec str and name placeholders");
1 ✔
1125

1126
         const Botan::AlgorithmSpec spec("Foo(Bar(Baz,Qux),Quux(1))");
1 ✔
1127
         if(const auto m = spec.match("Foo({s:str},{n})")) {
1 ✔
1128
            result.test_str_eq("str binds verbatim", m->str("s"), "Bar(Baz,Qux)");
1 ✔
1129
            result.test_throws<Botan::Internal_Error>("name() on str placeholder throws", [&] { m->name("s"); });
2 ✔
1130
            result.test_throws<Botan::Internal_Error>("integer() on str placeholder throws", [&] { m->integer("s"); });
2 ✔
1131

1132
            result.test_str_eq("name text", m->str("n"), "Quux(1)");
1 ✔
1133
            const auto& n = m->name("n");
1 ✔
1134
            result.test_str_eq("name head", n.head(), "Quux");
1 ✔
1135
            result.test_sz_eq("name args", n.arg_count(), 1);
1 ✔
1136
            result.test_is_true("name can be matched again", n.matches("Quux({x:int})"));
1 ✔
1137
         } else {
1138
            result.test_failure("Foo(Bar(Baz,Qux),Quux(1)) did not match");
×
1139
         }
1140

1141
         return result;
1 ✔
1142
      }
1 ✔
1143

1144
      static Test::Result match_variadic() {
1 ✔
1145
         Test::Result result("AlgorithmSpec variadic placeholders");
1 ✔
1146

1147
         const Botan::AlgorithmPattern hss("HSS-LMS({hash},{layer}...)");
1 ✔
1148

1149
         const Botan::AlgorithmSpec two("HSS-LMS(SHA-256,HW(5,1),HW(10,4))");
1 ✔
1150
         if(const auto m = two.match(hss)) {
1 ✔
1151
            result.test_str_eq("hash", m->str("hash"), "SHA-256");
1 ✔
1152
            const auto layers = m->rest("layer");
1 ✔
1153
            result.test_sz_eq("two layers", layers.size(), 2);
1 ✔
1154
            result.test_str_eq("layer 0", layers[0].to_string(), "HW(5,1)");
1 ✔
1155
            result.test_str_eq("layer 1", layers[1].to_string(), "HW(10,4)");
1 ✔
1156
            result.test_is_true("layer matches", layers[1].matches("HW({h:int},{w:int})"));
1 ✔
1157
            result.test_throws<Botan::Internal_Error>("str() on variadic throws", [&] { m->str("layer"); });
2 ✔
1158
            result.test_throws<Botan::Internal_Error>("rest() on non-variadic throws", [&] { m->rest("hash"); });
2 ✔
1159
         } else {
1160
            result.test_failure("HSS-LMS with two layers did not match");
×
1161
         }
1162

1163
         const Botan::AlgorithmSpec one("HSS-LMS(SHA-256,HW(5,1))");
1 ✔
1164
         if(const auto m = one.match(hss)) {
1 ✔
1165
            result.test_sz_eq("one layer", m->rest("layer").size(), 1);
1 ✔
1166
         } else {
1167
            result.test_failure("HSS-LMS with one layer did not match");
×
1168
         }
1169

1170
         // Variadic means one or more
1171
         const Botan::AlgorithmSpec zero("HSS-LMS(SHA-256)");
1 ✔
1172
         result.test_is_false("variadic requires at least one", zero.matches(hss));
1 ✔
1173

1174
         return result;
1 ✔
1175
      }
1 ✔
1176

1177
      static Test::Result match_literals() {
1 ✔
1178
         Test::Result result("AlgorithmSpec literals");
1 ✔
1179

1180
         const Botan::AlgorithmPattern plain("Foo(Bar,{x})");
1 ✔
1181
         const Botan::AlgorithmSpec ok("Foo(Bar,A)");
1 ✔
1182
         if(const auto m = ok.match(plain)) {
1 ✔
1183
            result.test_str_eq("x", m->str("x"), "A");
1 ✔
1184
         } else {
1185
            result.test_failure("Foo(Bar,A) did not match");
×
1186
         }
1187
         const Botan::AlgorithmSpec wrong_literal("Foo(Baz,A)");
1 ✔
1188
         result.test_is_false("wrong literal rejected", wrong_literal.matches(plain));
1 ✔
1189
         const Botan::AlgorithmSpec literal_with_args("Foo(Bar(Z),A)");
1 ✔
1190
         result.test_is_false("literal with args rejected", literal_with_args.matches(plain));
1 ✔
1191

1192
         const Botan::AlgorithmPattern nested("Foo(Bar({x}),{y})");
1 ✔
1193
         const Botan::AlgorithmSpec nested_ok("Foo(Bar(A),B)");
1 ✔
1194
         if(const auto m = nested_ok.match(nested)) {
1 ✔
1195
            result.test_str_eq("x", m->str("x"), "A");
1 ✔
1196
            result.test_str_eq("y", m->str("y"), "B");
1 ✔
1197
         } else {
1198
            result.test_failure("Foo(Bar(A),B) did not match");
×
1199
         }
1200

1201
         const std::vector<std::string> rejected = {
1 ✔
1202
            "Foo(Bar,B)",
1203
            "Foo(Baz(A),B)",
1204
            "Foo(Bar(A,C),B)",
1205
            "Foo(Bar(A))",
1206
            "Foo(Bar(A),B,C)",
1207
         };
1 ✔
1208
         for(const auto& tc : rejected) {
6 ✔
1209
            const Botan::AlgorithmSpec spec(tc);
5 ✔
1210
            result.test_is_false(Botan::fmt("'{}' rejected", tc), spec.matches(nested));
5 ✔
1211
         }
5 ✔
1212

1213
         return result;
1 ✔
1214
      }
1 ✔
1215

1216
      static Test::Result match_optional_literals() {
1 ✔
1217
         Test::Result result("AlgorithmSpec optional literals");
1 ✔
1218

1219
         const Botan::AlgorithmPattern pattern("Foo([Bar],{x?})");
1 ✔
1220

1221
         const Botan::AlgorithmSpec bare("Foo");
1 ✔
1222
         if(const auto m = bare.match(pattern)) {
1 ✔
1223
            result.test_is_false("x unbound", m->has("x"));
1 ✔
1224
         } else {
1225
            result.test_failure("Foo did not match");
×
1226
         }
1227

1228
         const Botan::AlgorithmSpec literal_only("Foo(Bar)");
1 ✔
1229
         if(const auto m = literal_only.match(pattern)) {
1 ✔
1230
            result.test_is_false("x unbound", m->has("x"));
1 ✔
1231
         } else {
1232
            result.test_failure("Foo(Bar) did not match");
×
1233
         }
1234

1235
         const Botan::AlgorithmSpec both("Foo(Bar,A)");
1 ✔
1236
         if(const auto m = both.match(pattern)) {
1 ✔
1237
            result.test_str_eq("x", m->str("x"), "A");
1 ✔
1238
         } else {
1239
            result.test_failure("Foo(Bar,A) did not match");
×
1240
         }
1241

1242
         // The literal is skipped if the argument is something else
1243
         const Botan::AlgorithmSpec skipped("Foo(A)");
1 ✔
1244
         if(const auto m = skipped.match(pattern)) {
1 ✔
1245
            result.test_str_eq("x", m->str("x"), "A");
1 ✔
1246
         } else {
1247
            result.test_failure("Foo(A) did not match");
×
1248
         }
1249

1250
         // Including if it has the same head but carries arguments
1251
         const Botan::AlgorithmSpec same_head("Foo(Bar(Z))");
1 ✔
1252
         if(const auto m = same_head.match(pattern)) {
1 ✔
1253
            result.test_str_eq("x", m->str("x"), "Bar(Z)");
1 ✔
1254
         } else {
1255
            result.test_failure("Foo(Bar(Z)) did not match");
×
1256
         }
1257

1258
         const Botan::AlgorithmSpec too_many("Foo(A,B)");
1 ✔
1259
         result.test_is_false("too many arguments rejected", too_many.matches(pattern));
1 ✔
1260
         const Botan::AlgorithmSpec too_many2("Foo(Bar,A,B)");
1 ✔
1261
         result.test_is_false("too many arguments rejected", too_many2.matches(pattern));
1 ✔
1262

1263
         return result;
1 ✔
1264
      }
1 ✔
1265

1266
      static Test::Result match_accessor_errors() {
1 ✔
1267
         Test::Result result("AlgorithmSpec accessor errors");
1 ✔
1268

1269
         const Botan::AlgorithmSpec spec("HMAC(SHA-256)");
1 ✔
1270
         if(const auto m = spec.match("HMAC({hash})")) {
1 ✔
1271
            result.test_throws<Botan::Internal_Error>("has() unknown variable", [&] { m->has("nope"); });
2 ✔
1272
            result.test_throws<Botan::Internal_Error>("str() unknown variable", [&] { m->str("nope"); });
2 ✔
1273
            result.test_throws<Botan::Internal_Error>("integer() unknown variable", [&] { m->integer("nope"); });
2 ✔
1274
            result.test_throws<Botan::Internal_Error>("name() unknown variable", [&] { m->name("nope"); });
2 ✔
1275
            result.test_throws<Botan::Internal_Error>("rest() unknown variable", [&] { m->rest("nope"); });
2 ✔
1276
            result.test_throws<Botan::Internal_Error>("integer() on name placeholder", [&] { m->integer("hash"); });
2 ✔
1277
            result.test_throws<Botan::Internal_Error>("rest() on name placeholder", [&] { m->rest("hash"); });
2 ✔
1278
         } else {
1279
            result.test_failure("HMAC(SHA-256) did not match");
×
1280
         }
1281

1282
         return result;
1 ✔
1283
      }
1 ✔
1284
};
1285

1286
BOTAN_REGISTER_TEST("utils", "algorithm_spec", AlgorithmSpec_Tests);
1287

1288
class BitOps_Tests final : public Test {
1 ✔
1289
   public:
1290
      std::vector<Test::Result> run() override {
1 ✔
1291
         std::vector<Test::Result> results;
1 ✔
1292

1293
         results.push_back(test_power_of_2());
2 ✔
1294
         results.push_back(test_ctz());
2 ✔
1295
         results.push_back(test_sig_bytes());
2 ✔
1296
         results.push_back(test_popcount());
2 ✔
1297
         results.push_back(test_reverse_bits());
2 ✔
1298

1299
         return results;
1 ✔
1300
      }
×
1301

1302
   private:
1303
      template <typename T>
1304
      void test_ctz(Test::Result& result, T val, size_t expected) {
6 ✔
1305
         Botan::CT::poison(val);
1306
         const size_t computed = Botan::ctz<T>(val);
6 ✔
1307
         Botan::CT::unpoison_all(computed, val);
1308
         result.test_sz_eq("ctz(" + std::to_string(val) + ")", computed, expected);
24 ✔
1309
      }
6 ✔
1310

1311
      Test::Result test_ctz() {
1 ✔
1312
         Test::Result result("ctz");
1 ✔
1313
         test_ctz<uint32_t>(result, 0, 32);
1 ✔
1314
         test_ctz<uint32_t>(result, 1, 0);
1 ✔
1315
         test_ctz<uint32_t>(result, 0x80, 7);
1 ✔
1316
         test_ctz<uint32_t>(result, 0x8000000, 27);
1 ✔
1317
         test_ctz<uint32_t>(result, 0x8100000, 20);
1 ✔
1318
         test_ctz<uint32_t>(result, 0x80000000, 31);
1 ✔
1319

1320
         return result;
1 ✔
1321
      }
×
1322

1323
      template <typename T>
1324
      void test_sig_bytes(Test::Result& result, T val, size_t expected) {
14 ✔
1325
         Botan::CT::poison(val);
1326
         const size_t computed = Botan::significant_bytes<T>(val);
14 ✔
1327
         Botan::CT::unpoison_all(computed, val);
1328
         result.test_sz_eq("significant_bytes(" + std::to_string(val) + ")", computed, expected);
56 ✔
1329
      }
14 ✔
1330

1331
      Test::Result test_sig_bytes() {
1 ✔
1332
         Test::Result result("significant_bytes");
1 ✔
1333
         test_sig_bytes<uint32_t>(result, 0, 0);
1 ✔
1334
         test_sig_bytes<uint32_t>(result, 1, 1);
1 ✔
1335
         test_sig_bytes<uint32_t>(result, 0x80, 1);
1 ✔
1336
         test_sig_bytes<uint32_t>(result, 255, 1);
1 ✔
1337
         test_sig_bytes<uint32_t>(result, 256, 2);
1 ✔
1338
         test_sig_bytes<uint32_t>(result, 65535, 2);
1 ✔
1339
         test_sig_bytes<uint32_t>(result, 65536, 3);
1 ✔
1340
         test_sig_bytes<uint32_t>(result, 0x80000000, 4);
1 ✔
1341

1342
         test_sig_bytes<uint64_t>(result, 0, 0);
1 ✔
1343
         test_sig_bytes<uint64_t>(result, 1, 1);
1 ✔
1344
         test_sig_bytes<uint64_t>(result, 0x80, 1);
1 ✔
1345
         test_sig_bytes<uint64_t>(result, 256, 2);
1 ✔
1346
         test_sig_bytes<uint64_t>(result, 0x80000000, 4);
1 ✔
1347
         test_sig_bytes<uint64_t>(result, 0x100000000, 5);
1 ✔
1348

1349
         return result;
1 ✔
1350
      }
×
1351

1352
      template <typename T>
1353
      void test_power_of_2(Test::Result& result, T val, bool expected) {
15 ✔
1354
         result.test_bool_eq("power_of_2(" + std::to_string(val) + ")", Botan::is_power_of_2<T>(val), expected);
75 ✔
1355
      }
15 ✔
1356

1357
      Test::Result test_power_of_2() {
1 ✔
1358
         Test::Result result("is_power_of_2");
1 ✔
1359

1360
         test_power_of_2<uint32_t>(result, 0, false);
1 ✔
1361
         test_power_of_2<uint32_t>(result, 1, false);
1 ✔
1362
         test_power_of_2<uint32_t>(result, 2, true);
1 ✔
1363
         test_power_of_2<uint32_t>(result, 3, false);
1 ✔
1364
         test_power_of_2<uint32_t>(result, 0x8000, true);
1 ✔
1365
         test_power_of_2<uint32_t>(result, 0x8001, false);
1 ✔
1366
         test_power_of_2<uint32_t>(result, 0x8000000, true);
1 ✔
1367

1368
         test_power_of_2<uint64_t>(result, 0, false);
1 ✔
1369
         test_power_of_2<uint64_t>(result, 1, false);
1 ✔
1370
         test_power_of_2<uint64_t>(result, 2, true);
1 ✔
1371
         test_power_of_2<uint64_t>(result, 3, false);
1 ✔
1372
         test_power_of_2<uint64_t>(result, 0x8000, true);
1 ✔
1373
         test_power_of_2<uint64_t>(result, 0x8001, false);
1 ✔
1374
         test_power_of_2<uint64_t>(result, 0x8000000, true);
1 ✔
1375
         test_power_of_2<uint64_t>(result, 0x100000000000, true);
1 ✔
1376

1377
         return result;
1 ✔
1378
      }
×
1379

1380
      template <typename T>
1381
      auto pc(T val) -> decltype(Botan::ct_popcount(val)) {
2 ✔
1382
         return Botan::ct_popcount(val);
6 ✔
1383
      }
1384

1385
      template <typename T>
1386
      auto random_pc(Test::Result& result) {
4 ✔
1387
         const auto n = Botan::load_le<T>(Test::rng().random_array<sizeof(T)>());
4 ✔
1388
         result.test_sz_eq(Botan::fmt("popcount({}) == {}", n, std::popcount(n)), pc(n), std::popcount(n));
4 ✔
1389
      }
4 ✔
1390

1391
      Test::Result test_popcount() {
1 ✔
1392
         Test::Result result("popcount");
1 ✔
1393

1394
         result.test_u8_eq("popcount<uint8_t>(0)", pc<uint8_t>(0), 0);
1 ✔
1395
         result.test_u8_eq("popcount<uint16_t>(0)", pc<uint16_t>(0), 0);
1 ✔
1396
         result.test_u8_eq("popcount<uint32_t>(0)", pc<uint32_t>(0), 0);
1 ✔
1397
         result.test_u8_eq("popcount<uint64_t>(0)", pc<uint64_t>(0), 0);
1 ✔
1398

1399
         result.test_u8_eq("popcount<uint8_t>(1)", pc<uint8_t>(1), 1);
1 ✔
1400
         result.test_u8_eq("popcount<uint16_t>(1)", pc<uint16_t>(1), 1);
1 ✔
1401
         result.test_u8_eq("popcount<uint32_t>(1)", pc<uint32_t>(1), 1);
1 ✔
1402
         result.test_u8_eq("popcount<uint64_t>(1)", pc<uint64_t>(1), 1);
1 ✔
1403

1404
         result.test_u8_eq("popcount<uint8_t>(0xAA)", pc<uint8_t>(0xAA), 4);
1 ✔
1405
         result.test_u8_eq("popcount<uint16_t>(0xAAAA)", pc<uint16_t>(0xAAAA), 8);
1 ✔
1406
         result.test_u8_eq("popcount<uint32_t>(0xAAAA...)", pc<uint32_t>(0xAAAAAAAA), 16);
1 ✔
1407
         result.test_u8_eq("popcount<uint64_t>(0xAAAA...)", pc<uint64_t>(0xAAAAAAAAAAAAAAAA), 32);
1 ✔
1408

1409
         result.test_u8_eq("popcount<uint8_t>(0xFF)", pc<uint8_t>(0xFF), 8);
1 ✔
1410
         result.test_u8_eq("popcount<uint16_t>(0xFFFF)", pc<uint16_t>(0xFFFF), 16);
1 ✔
1411
         result.test_u8_eq("popcount<uint32_t>(0xFFFF...)", pc<uint32_t>(0xFFFFFFFF), 32);
1 ✔
1412
         result.test_u8_eq("popcount<uint64_t>(0xFFFF...)", pc<uint64_t>(0xFFFFFFFFFFFFFFFF), 64);
1 ✔
1413

1414
         random_pc<uint8_t>(result);
1 ✔
1415
         random_pc<uint16_t>(result);
1 ✔
1416
         random_pc<uint32_t>(result);
1 ✔
1417
         random_pc<uint64_t>(result);
1 ✔
1418

1419
         return result;
1 ✔
1420
      }
×
1421

1422
      Test::Result test_reverse_bits() {
1 ✔
1423
         Test::Result result("reverse_bits");
1 ✔
1424

1425
         result.test_u8_eq("rev(0u8)", Botan::ct_reverse_bits<uint8_t>(0b00000000), 0b00000000);
1 ✔
1426
         result.test_u8_eq("rev(1u8)", Botan::ct_reverse_bits<uint8_t>(0b01010101), 0b10101010);
1 ✔
1427
         result.test_u8_eq("rev(2u8)", Botan::ct_reverse_bits<uint8_t>(0b01001011), 0b11010010);
1 ✔
1428

1429
         result.test_u16_eq("rev(0u16)", Botan::ct_reverse_bits<uint16_t>(0b0000000000000000), 0b0000000000000000);
1 ✔
1430
         result.test_u16_eq("rev(1u16)", Botan::ct_reverse_bits<uint16_t>(0b0101010101010101), 0b1010101010101010);
1 ✔
1431
         result.test_u16_eq("rev(2u16)", Botan::ct_reverse_bits<uint16_t>(0b0100101101011010), 0b0101101011010010);
1 ✔
1432

1433
         result.test_u32_eq("rev(0u32)", Botan::ct_reverse_bits<uint32_t>(0xFFFFFFFF), 0xFFFFFFFF);
1 ✔
1434
         result.test_u32_eq("rev(1u32)", Botan::ct_reverse_bits<uint32_t>(0x55555555), 0xAAAAAAAA);
1 ✔
1435
         result.test_u32_eq("rev(2u32)", Botan::ct_reverse_bits<uint32_t>(0x4B6A2C1D), 0xB83456D2);
1 ✔
1436

1437
         result.test_u64_eq("rev(0u64)", Botan::ct_reverse_bits<uint64_t>(0xF0E0D0C005040302), 0x40C020A0030B070F);
1 ✔
1438
         result.test_u64_eq("rev(1u64)", Botan::ct_reverse_bits<uint64_t>(0x5555555555555555), 0xAAAAAAAAAAAAAAAA);
1 ✔
1439
         result.test_u64_eq("rev(2u64)", Botan::ct_reverse_bits<uint64_t>(0x4B6A2C1D5E7F8A90), 0x951FE7AB83456D2);
1 ✔
1440

1441
         return result;
1 ✔
1442
      }
×
1443
};
1444

1445
BOTAN_REGISTER_TEST("utils", "bit_ops", BitOps_Tests);
1446

1447
#if defined(BOTAN_HAS_POLY_DBL)
1448

1449
class Poly_Double_Tests final : public Text_Based_Test {
×
1450
   public:
1451
      Poly_Double_Tests() : Text_Based_Test("poly_dbl.vec", "In,Out") {}
2 ✔
1452

1453
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
82 ✔
1454
         Test::Result result("Polynomial doubling");
82 ✔
1455
         const std::vector<uint8_t> in = vars.get_req_bin("In");
82 ✔
1456
         const std::vector<uint8_t> out = vars.get_req_bin("Out");
82 ✔
1457

1458
         std::vector<uint8_t> b = in;
82 ✔
1459
         Botan::poly_double_n(b.data(), b.size());
82 ✔
1460

1461
         result.test_bin_eq("Expected value", b, out);
82 ✔
1462
         return result;
82 ✔
1463
      }
246 ✔
1464
};
1465

1466
BOTAN_REGISTER_TEST("utils", "poly_dbl", Poly_Double_Tests);
1467

1468
#endif
1469

1470
class Version_Tests final : public Test {
1 ✔
1471
   public:
1472
      std::vector<Test::Result> run() override {
1 ✔
1473
         Test::Result result("Versions");
1 ✔
1474

1475
         result.test_u32_eq("Version datestamp matches macro", Botan::version_datestamp(), BOTAN_VERSION_DATESTAMP);
1 ✔
1476

1477
         const char* version_cstr = Botan::version_cstr();
1 ✔
1478
         const std::string version_str = Botan::version_string();
1 ✔
1479
         result.test_str_eq("Same version string", version_str, version_cstr);
1 ✔
1480

1481
         const char* sversion_cstr = Botan::short_version_cstr();
1 ✔
1482
         const std::string sversion_str = Botan::short_version_string();
1 ✔
1483
         result.test_str_eq("Same short version string", sversion_str, sversion_cstr);
1 ✔
1484

1485
         const auto expected_sversion =
1 ✔
1486
            Botan::fmt("{}.{}.{}", BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH);
1 ✔
1487

1488
         // May have a suffix eg 4.0.0-rc2
1489
         result.test_is_true("Short version string has expected format", sversion_str.starts_with(expected_sversion));
1 ✔
1490

1491
         const std::string version_check_ok =
1 ✔
1492
            Botan::runtime_version_check(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH);
1 ✔
1493

1494
         result.test_is_true("Correct version no warning", version_check_ok.empty());
1 ✔
1495

1496
         const std::string version_check_bad = Botan::runtime_version_check(1, 19, 42);
1 ✔
1497

1498
         const std::string expected_error =
1 ✔
1499
            "Warning: linked version (" + sversion_str + ") does not match version built against (1.19.42)\n";
2 ✔
1500

1501
         result.test_str_eq("Expected warning text", version_check_bad, expected_error);
1 ✔
1502

1503
         return {result};
3 ✔
1504
      }
2 ✔
1505
};
1506

1507
BOTAN_REGISTER_TEST("utils", "versioning", Version_Tests);
1508

1509
class Date_Format_Tests final : public Text_Based_Test {
×
1510
   public:
1511
      Date_Format_Tests() : Text_Based_Test("dates.vec", "Date") {}
2 ✔
1512

1513
      static std::vector<uint32_t> parse_date(const std::string& s) {
16 ✔
1514
         const std::vector<std::string> parts = Botan::split_on(s, ',');
16 ✔
1515
         if(parts.size() != 6) {
16 ✔
1516
            throw Test_Error("Bad date format '" + s + "'");
×
1517
         }
1518

1519
         std::vector<uint32_t> u32s;
16 ✔
1520
         u32s.reserve(parts.size());
16 ✔
1521
         for(const auto& sub : parts) {
112 ✔
1522
            u32s.push_back(Botan::to_u32bit(sub));
96 ✔
1523
         }
1524
         return u32s;
16 ✔
1525
      }
16 ✔
1526

1527
      Test::Result run_one_test(const std::string& type, const VarMap& vars) override {
16 ✔
1528
         const std::string date_str = vars.get_req_str("Date");
16 ✔
1529
         Test::Result result("Date parsing");
16 ✔
1530

1531
         const std::vector<uint32_t> d = parse_date(date_str);
16 ✔
1532

1533
         if(type == "valid" || type == "valid.not_std" || type == "valid.64_bit_time_t") {
16 ✔
1534
            const Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]);
16 ✔
1535
            result.test_u32_eq(date_str + " year", c.year(), d[0]);
16 ✔
1536
            result.test_u32_eq(date_str + " month", c.month(), d[1]);
16 ✔
1537
            result.test_u32_eq(date_str + " day", c.day(), d[2]);
16 ✔
1538
            result.test_u32_eq(date_str + " hour", c.hour(), d[3]);
16 ✔
1539
            result.test_u32_eq(date_str + " minute", c.minutes(), d[4]);
16 ✔
1540
            result.test_u32_eq(date_str + " second", c.seconds(), d[5]);
16 ✔
1541

1542
            if(type == "valid.not_std" ||
16 ✔
1543
               (type == "valid.64_bit_time_t" && c.year() > 2037 && sizeof(std::time_t) == 4)) {
1544
               result.test_throws("valid but out of std::timepoint range", [c]() { c.to_std_timepoint(); });
6 ✔
1545
            } else {
1546
               const Botan::calendar_point c2(c.to_std_timepoint());
13 ✔
1547
               result.test_u32_eq(date_str + " year", c2.year(), d[0]);
13 ✔
1548
               result.test_u32_eq(date_str + " month", c2.month(), d[1]);
13 ✔
1549
               result.test_u32_eq(date_str + " day", c2.day(), d[2]);
13 ✔
1550
               result.test_u32_eq(date_str + " hour", c2.hour(), d[3]);
13 ✔
1551
               result.test_u32_eq(date_str + " minute", c2.minutes(), d[4]);
13 ✔
1552
               result.test_u32_eq(date_str + " second", c2.seconds(), d[5]);
26 ✔
1553
            }
1554
         } else if(type == "invalid") {
×
1555
            result.test_throws("invalid date",
×
1556
                               [d]() { const Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]); });
×
1557
         } else {
1558
            throw Test_Error("Unexpected header '" + type + "' in date format tests");
×
1559
         }
1560

1561
         return result;
32 ✔
1562
      }
190 ✔
1563
};
1564

1565
BOTAN_REGISTER_TEST("utils", "util_dates", Date_Format_Tests);
1566

1567
class Charset_Tests final : public Text_Based_Test {
×
1568
   public:
1569
      Charset_Tests() : Text_Based_Test("charset.vec", "In,Out") {}
2 ✔
1570

1571
      Test::Result run_one_test(const std::string& type, const VarMap& vars) override {
53 ✔
1572
         Test::Result result("Charset");
53 ✔
1573

1574
         const std::vector<uint8_t> in = vars.get_req_bin("In");
53 ✔
1575

1576
         const auto in_sv = std::string_view(reinterpret_cast<const char*>(in.data()), in.size());
53 ✔
1577

1578
         if(type == "UTF8-UCS2-INVALID") {
53 ✔
1579
            result.test_throws<Botan::Decoding_Error>("utf8_to_ucs2 rejects invalid input",
13 ✔
1580
                                                      [&] { Botan::utf8_to_ucs2(in_sv); });
26 ✔
1581
            return result;
13 ✔
1582
         }
1583

1584
         if(type == "UTF8-UCS4-INVALID") {
40 ✔
1585
            result.test_throws<Botan::Decoding_Error>("utf8_to_ucs4 rejects invalid input",
10 ✔
1586
                                                      [&] { Botan::utf8_to_ucs4(in_sv); });
20 ✔
1587
            return result;
10 ✔
1588
         }
1589

1590
         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
30 ✔
1591

1592
         std::string converted;
30 ✔
1593

1594
         if(type == "UCS2-UTF8") {
30 ✔
1595
            converted = Botan::ucs2_to_utf8(in);
4 ✔
1596
         } else if(type == "UCS4-UTF8") {
26 ✔
1597
            converted = Botan::ucs4_to_utf8(in);
1 ✔
1598
         } else if(type == "UTF8-UCS2") {
25 ✔
1599
            std::vector<uint8_t> ucs2 = Botan::utf8_to_ucs2(in_sv);
11 ✔
1600
            converted = std::string(ucs2.begin(), ucs2.end());
22 ✔
1601
         } else if(type == "UTF8-UCS4") {
25 ✔
1602
            std::vector<uint8_t> ucs4 = Botan::utf8_to_ucs4(in_sv);
11 ✔
1603
            converted = std::string(ucs4.begin(), ucs4.end());
22 ✔
1604
         } else if(type == "LATIN1-UTF8") {
14 ✔
1605
            converted = Botan::latin1_to_utf8(in);
3 ✔
1606
         } else {
1607
            throw Test_Error("Unexpected header '" + type + "' in charset tests");
×
1608
         }
1609

1610
         result.test_bin_eq(
30 ✔
1611
            "string converted successfully", std::vector<uint8_t>(converted.begin(), converted.end()), expected);
30 ✔
1612

1613
         return result;
30 ✔
1614
      }
113 ✔
1615
};
1616

1617
BOTAN_REGISTER_TEST("utils", "charset", Charset_Tests);
1618

1619
class Escape_Control_Chars_Tests final : public Test {
1 ✔
1620
   public:
1621
      std::vector<Test::Result> run() override {
1 ✔
1622
         Test::Result result("escape_control_chars");
1 ✔
1623

1624
         result.test_str_eq("newline", Botan::escape_control_chars("a\nb"), "a\\x0Ab");
1 ✔
1625
         result.test_str_eq("escape", Botan::escape_control_chars("\x1b[0m"), "\\x1B[0m");
1 ✔
1626
         result.test_str_eq("embedded NUL", Botan::escape_control_chars(std::string("a\0b", 3)), "a\\x00b");
2 ✔
1627
         result.test_str_eq("DEL", Botan::escape_control_chars("\x7f"), "\\x7F");
1 ✔
1628
         result.test_str_eq("C1 control U+009B", Botan::escape_control_chars("\xc2\x9b"), "\\xC2\\x9B");
1 ✔
1629
         result.test_str_eq("printable ASCII ok", Botan::escape_control_chars("Hello.123"), "Hello.123");
1 ✔
1630
         result.test_str_eq("printable UTF-8 ok", Botan::escape_control_chars("Fräulein"), "Fräulein");
1 ✔
1631
         result.test_str_eq("U+00A0 is not a control", Botan::escape_control_chars("\xc2\xa0"), "\xc2\xa0");
1 ✔
1632

1633
         return {result};
3 ✔
1634
      }
3 ✔
1635
};
1636

1637
BOTAN_REGISTER_TEST("utils", "escape_control_chars", Escape_Control_Chars_Tests);
1638

1639
#if defined(BOTAN_HAS_IPV4_ADDRESS)
1640

1641
class IPv4_Parsing_Tests final : public Text_Based_Test {
×
1642
   public:
1643
      IPv4_Parsing_Tests() : Text_Based_Test("utils/ipv4.vec", "IPv4") {}
2 ✔
1644

1645
      Test::Result run_one_test(const std::string& status, const VarMap& vars) override {
47 ✔
1646
         Test::Result result("IPv4 parsing");
47 ✔
1647

1648
         const std::string input = vars.get_req_str("IPv4");
47 ✔
1649
         const bool valid = (status == "Valid");
47 ✔
1650

1651
         auto ipv4 = Botan::IPv4Address::from_string(input);
47 ✔
1652

1653
         result.test_bool_eq("IPv4Address::from_string accepts only valid", ipv4.has_value(), valid);
47 ✔
1654

1655
         if(ipv4) {
47 ✔
1656
            const std::string rt = ipv4->to_string();
13 ✔
1657
            result.test_str_eq("IPv4Address::from_string and IPv4Address::to_string round trip", input, rt);
13 ✔
1658
         }
13 ✔
1659

1660
         return result;
47 ✔
1661
      }
47 ✔
1662
};
1663

1664
BOTAN_REGISTER_TEST("utils", "ipv4_parse", IPv4_Parsing_Tests);
1665

1666
class IPv4_Subnet_Parsing_Tests final : public Text_Based_Test {
×
1667
   public:
1668
      IPv4_Subnet_Parsing_Tests() : Text_Based_Test("utils/ipv4_subnet.vec", "IPv4Subnet", "LastAddress") {}
2 ✔
1669

1670
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
19 ✔
1671
         Test::Result result("IPv4 subnet parsing");
19 ✔
1672

1673
         const std::string input = vars.get_req_str("IPv4Subnet");
19 ✔
1674
         const bool valid = (header == "Valid");
19 ✔
1675

1676
         const auto subnet = Botan::IPv4Subnet::from_string(input);
19 ✔
1677

1678
         result.test_bool_eq("IPv4Subnet::from_string accepts only valid", subnet.has_value(), valid);
19 ✔
1679

1680
         if(subnet) {
19 ✔
1681
            result.test_str_eq(
6 ✔
1682
               "IPv4Subnet::from_string and IPv4Subnet::to_string round trip", subnet->to_string(), input);
6 ✔
1683

1684
            const auto last = Botan::IPv4Address::from_string(vars.get_req_str("LastAddress"));
6 ✔
1685
            result.test_is_true("LastAddress parses", last.has_value());
6 ✔
1686
            if(last) {
6 ✔
1687
               result.test_str_eq("IPv4Subnet::last_address", subnet->last_address().to_string(), last->to_string());
6 ✔
1688
            }
1689
         }
1690

1691
         return result;
19 ✔
1692
      }
19 ✔
1693
};
1694

1695
BOTAN_REGISTER_TEST("utils", "ipv4_subnet_parse", IPv4_Subnet_Parsing_Tests);
1696

1697
#endif
1698

1699
#if defined(BOTAN_HAS_IPV6_ADDRESS)
1700

1701
class IPv6_Parsing_Tests final : public Text_Based_Test {
×
1702
   public:
1703
      IPv6_Parsing_Tests() : Text_Based_Test("utils/ipv6.vec", "IPv6") {}
2 ✔
1704

1705
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
50 ✔
1706
         Test::Result result("IPv6 parsing");
50 ✔
1707

1708
         const std::string input = vars.get_req_str("IPv6");
50 ✔
1709
         const bool valid = (header == "Valid");
50 ✔
1710

1711
         auto ipv6 = Botan::IPv6Address::from_string(input);
50 ✔
1712

1713
         result.test_bool_eq("IPv6Address::from_string accepts only valid", ipv6.has_value(), valid);
50 ✔
1714

1715
         if(ipv6) {
50 ✔
1716
            const std::string rt = ipv6->to_string();
15 ✔
1717
            result.test_str_eq("IPv6Address to_string and from_string round trip", input, rt);
15 ✔
1718
         }
15 ✔
1719

1720
         return result;
50 ✔
1721
      }
50 ✔
1722
};
1723

1724
BOTAN_REGISTER_TEST("utils", "ipv6_parse", IPv6_Parsing_Tests);
1725

1726
class IPv6_Noncanonical_Parsing_Tests final : public Text_Based_Test {
×
1727
   public:
1728
      IPv6_Noncanonical_Parsing_Tests() : Text_Based_Test("utils/ipv6_nc.vec", "Input,Canonical") {}
2 ✔
1729

1730
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
23 ✔
1731
         Test::Result result("IPv6 parsing of non-canonical form");
23 ✔
1732

1733
         const std::string input_str = vars.get_req_str("Input");
23 ✔
1734
         const std::string canonical_str = vars.get_req_str("Canonical");
23 ✔
1735

1736
         const auto ipv6 = Botan::IPv6Address::from_string(input_str);
23 ✔
1737
         const auto canonical = Botan::IPv6Address::from_string(canonical_str);
23 ✔
1738

1739
         result.test_is_true("IPv6 non-canonical parsing worked", ipv6.has_value());
23 ✔
1740
         result.test_is_true("IPv6 canonical parsing worked", canonical.has_value());
23 ✔
1741

1742
         if(ipv6.has_value() && canonical.has_value()) {
23 ✔
1743
            result.test_is_true("IPv6 non-canonical decoding", *ipv6 == *canonical);
23 ✔
1744
            result.test_str_eq("IPv6 to_string is canonical", ipv6->to_string(), canonical_str);
23 ✔
1745
         }
1746

1747
         return result;
23 ✔
1748
      }
23 ✔
1749
};
1750

1751
BOTAN_REGISTER_TEST("utils", "ipv6_parse_non_canonical", IPv6_Noncanonical_Parsing_Tests);
1752

1753
class IPv6_Subnet_Parsing_Tests final : public Text_Based_Test {
×
1754
   public:
1755
      IPv6_Subnet_Parsing_Tests() : Text_Based_Test("utils/ipv6_subnet.vec", "IPv6Subnet", "LastAddress") {}
2 ✔
1756

1757
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
25 ✔
1758
         Test::Result result("IPv6 subnet parsing");
25 ✔
1759

1760
         const std::string input = vars.get_req_str("IPv6Subnet");
25 ✔
1761
         const bool valid = (header == "Valid");
25 ✔
1762

1763
         const auto subnet = Botan::IPv6Subnet::from_string(input);
25 ✔
1764

1765
         result.test_bool_eq("IPv6Subnet::from_string accepts only valid", subnet.has_value(), valid);
25 ✔
1766

1767
         if(subnet) {
25 ✔
1768
            result.test_str_eq(
7 ✔
1769
               "IPv6Subnet::from_string and IPv6Subnet::to_string round trip", subnet->to_string(), input);
7 ✔
1770

1771
            const auto last = Botan::IPv6Address::from_string(vars.get_req_str("LastAddress"));
7 ✔
1772
            result.test_is_true("LastAddress parses", last.has_value());
7 ✔
1773
            if(last) {
7 ✔
1774
               result.test_str_eq("IPv6Subnet::last_address", subnet->last_address().to_string(), last->to_string());
7 ✔
1775
            }
1776
         }
1777

1778
         return result;
25 ✔
1779
      }
25 ✔
1780
};
1781

1782
BOTAN_REGISTER_TEST("utils", "ipv6_subnet_parse", IPv6_Subnet_Parsing_Tests);
1783

1784
#endif
1785

1786
class ReadKV_Tests final : public Text_Based_Test {
×
1787
   public:
1788
      ReadKV_Tests() : Text_Based_Test("utils/read_kv.vec", "Input,Expected") {}
2 ✔
1789

1790
      Test::Result run_one_test(const std::string& status, const VarMap& vars) override {
16 ✔
1791
         Test::Result result("read_kv");
16 ✔
1792

1793
         const bool is_valid = (status == "Valid");
16 ✔
1794

1795
         const std::string input = vars.get_req_str("Input");
16 ✔
1796
         const std::string expected = vars.get_req_str("Expected");
16 ✔
1797

1798
         if(is_valid) {
16 ✔
1799
            confirm_kv(result, Botan::read_kv(input), split_group(expected));
14 ✔
1800
         } else {
1801
            // In this case "expected" is the expected exception message
1802
            result.test_throws("Invalid key value input throws exception", expected, [&]() { Botan::read_kv(input); });
18 ✔
1803
         }
1804
         return result;
16 ✔
1805
      }
16 ✔
1806

1807
   private:
1808
      static std::vector<std::string> split_group(const std::string& str) {
7 ✔
1809
         std::vector<std::string> elems;
7 ✔
1810
         if(str.empty()) {
7 ✔
1811
            return elems;
1812
         }
1813

1814
         std::string substr;
6 ✔
1815
         for(const char c : str) {
115 ✔
1816
            if(c == '|') {
109 ✔
1817
               elems.push_back(substr);
16 ✔
1818
               substr.clear();
16 ✔
1819
            } else {
1820
               substr += c;
202 ✔
1821
            }
1822
         }
1823

1824
         if(!substr.empty()) {
6 ✔
1825
            elems.push_back(substr);
6 ✔
1826
         }
1827

1828
         return elems;
6 ✔
1829
      }
6 ✔
1830

1831
      static void confirm_kv(Test::Result& result,
7 ✔
1832
                             const std::map<std::string, std::string>& kv,
1833
                             const std::vector<std::string>& expected) {
1834
         if(!result.test_sz_eq("expected size", expected.size() % 2, size_t(0))) {
7 ✔
1835
            return;
1836
         }
1837

1838
         for(size_t i = 0; i != expected.size(); i += 2) {
18 ✔
1839
            const auto j = kv.find(expected[i]);
11 ✔
1840
            if(result.test_is_true("Found key", j != kv.end())) {
11 ✔
1841
               result.test_str_eq("Matching value", j->second, expected[i + 1]);
11 ✔
1842
            }
1843
         }
1844

1845
         result.test_sz_eq("KV has same size as expected", kv.size(), expected.size() / 2);
7 ✔
1846
      }
1847
};
1848

1849
BOTAN_REGISTER_TEST("utils", "util_read_kv", ReadKV_Tests);
1850

1851
#if defined(BOTAN_HAS_CPUID)
1852

1853
class CPUID_Tests final : public Test {
1 ✔
1854
   public:
1855
      std::vector<Test::Result> run() override {
1 ✔
1856
         Test::Result result("CPUID");
1 ✔
1857

1858
         const std::string cpuid_string = Botan::CPUID::to_string();
1 ✔
1859
         result.test_success("CPUID::to_string doesn't crash");
1 ✔
1860

1861
         for(size_t b = 0; b != 32; ++b) {
33 ✔
1862
            try {
32 ✔
1863
               const auto bit = static_cast<uint32_t>(1) << b;
32 ✔
1864
               // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
1865
               const auto feat = Botan::CPUID::Feature(static_cast<Botan::CPUID::Feature::Bit>(bit));
32 ✔
1866

1867
               const std::string feat_str = feat.to_string();
32 ✔
1868

1869
               result.test_is_true("Feature string is not empty", !feat_str.empty());
20 ✔
1870

1871
               if(auto from_str = Botan::CPUID::Feature::from_string(feat_str)) {
20 ✔
1872
                  result.test_u32_eq("Feature::from_string returns expected bit", from_str->as_u32(), bit);
20 ✔
1873
               } else {
1874
                  result.test_failure(
×
1875
                     Botan::fmt("Feature::from_string didn't recognize its own output ({})", feat_str));
×
1876
               }
1877
            } catch(Botan::Invalid_State&) {
32 ✔
1878
               // This will thrown if the bit is not a valid one
1879
            }
12 ✔
1880
         }
1881

1882
   #if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY)
1883

1884
         const auto bit = Botan::CPUID::Feature::SSE2;
1 ✔
1885

1886
         if(Botan::CPUID::has(bit)) {
1 ✔
1887
            result.test_is_true("Output string includes sse2", cpuid_string.find("sse2") != std::string::npos);
1 ✔
1888

1889
            Botan::CPUID::clear_cpuid_bit(bit);
1 ✔
1890

1891
            result.test_is_false("After clearing cpuid bit, CPUID::has for SSE2 returns false", Botan::CPUID::has(bit));
1 ✔
1892

1893
            Botan::CPUID::initialize();  // reset state
1 ✔
1894
            result.test_is_true("After reinitializing, CPUID::has for SSE2 returns true again", Botan::CPUID::has(bit));
1 ✔
1895
         }
1896
   #else
1897
         BOTAN_UNUSED(cpuid_string);
1898
   #endif
1899

1900
         return {result};
3 ✔
1901
      }
2 ✔
1902
};
1903

1904
BOTAN_REGISTER_SERIALIZED_TEST("utils", "cpuid", CPUID_Tests);
1905

1906
#endif
1907

1908
#if defined(BOTAN_HAS_UUID)
1909

1910
class UUID_Tests : public Test {
1 ✔
1911
   public:
1912
      std::vector<Test::Result> run() override {
1 ✔
1913
         Test::Result result("UUID");
1 ✔
1914

1915
         const Botan::UUID empty_uuid;
1 ✔
1916
         const Botan::UUID random_uuid1(this->rng());
1 ✔
1917
         const Botan::UUID random_uuid2(this->rng());
1 ✔
1918
         const Botan::UUID loaded_uuid(std::vector<uint8_t>(16, 4));
1 ✔
1919

1920
         result.test_throws("Cannot load wrong number of bytes",
1 ✔
1921
                            []() { const Botan::UUID u(std::vector<uint8_t>(15)); });
1 ✔
1922

1923
         result.test_is_false("Empty UUID is empty", empty_uuid.is_valid());
1 ✔
1924
         result.test_is_true("Empty UUID equals another empty UUID", empty_uuid == Botan::UUID());
1 ✔
1925

1926
         result.test_throws("Empty UUID cannot become a string", [&]() { empty_uuid.to_string(); });
2 ✔
1927

1928
         result.test_is_true("Random UUID not empty", random_uuid1.is_valid());
1 ✔
1929
         result.test_is_true("Random UUID not empty", random_uuid2.is_valid());
1 ✔
1930

1931
         result.test_is_true("Random UUIDs are distinct", random_uuid1 != random_uuid2);
1 ✔
1932
         result.test_is_true("Random UUIDs not equal to empty", random_uuid1 != empty_uuid);
1 ✔
1933

1934
         const std::string uuid4_str = loaded_uuid.to_string();
1 ✔
1935
         result.test_str_eq("String matches expected", uuid4_str, "04040404-0404-0404-0404-040404040404");
1 ✔
1936

1937
         const std::string uuid_r1_str = random_uuid1.to_string();
1 ✔
1938
         result.test_is_true("UUID from string matches", Botan::UUID(uuid_r1_str) == random_uuid1);
1 ✔
1939

1940
         class AllSame_RNG : public Botan::RandomNumberGenerator {
×
1941
            public:
1942
               explicit AllSame_RNG(uint8_t b) : m_val(b) {}
2 ✔
1943

1944
               void fill_bytes_with_input(std::span<uint8_t> output, std::span<const uint8_t> /* ignored */) override {
2 ✔
1945
                  std::fill(output.begin(), output.end(), m_val);
2 ✔
1946
               }
2 ✔
1947

1948
               std::string name() const override { return "zeros"; }
×
1949

1950
               bool accepts_input() const override { return false; }
×
1951

1952
               void clear() override {}
×
1953

1954
               bool is_seeded() const override { return true; }
×
1955

1956
            private:
1957
               uint8_t m_val;
1958
         };
1959

1960
         AllSame_RNG zeros(0x00);
1 ✔
1961
         const Botan::UUID zero_uuid(zeros);
1 ✔
1962
         result.test_str_eq(
1 ✔
1963
            "Zero UUID matches expected", zero_uuid.to_string(), "00000000-0000-4000-8000-000000000000");
1 ✔
1964

1965
         AllSame_RNG ones(0xFF);
1 ✔
1966
         const Botan::UUID ones_uuid(ones);
1 ✔
1967
         result.test_str_eq(
1 ✔
1968
            "Ones UUID matches expected", ones_uuid.to_string(), "FFFFFFFF-FFFF-4FFF-BFFF-FFFFFFFFFFFF");
1 ✔
1969

1970
         return {result};
3 ✔
1971
      }
6 ✔
1972
};
1973

1974
BOTAN_REGISTER_TEST("utils", "uuid", UUID_Tests);
1975

1976
#endif
1977

1978
class Formatter_Tests : public Test {
1 ✔
1979
   public:
1980
      std::vector<Test::Result> run() override {
1 ✔
1981
         Test::Result result("Format utility");
1 ✔
1982

1983
         /*
1984
         In a number of these tests, we are not strictly depending on the
1985
         behavior, for instance checking `fmt("{}") == "{}"` is more about
1986
         checking that we don't crash, rather than we return that precise string.
1987
         */
1988

1989
         result.test_str_eq("test 1", Botan::fmt("hi"), "hi");
1 ✔
1990
         result.test_str_eq("test 2", Botan::fmt("ignored", 5), "ignored");
1 ✔
1991
         result.test_str_eq("test 3", Botan::fmt("answer is {}", 42), "answer is 42");
1 ✔
1992
         result.test_str_eq("test 4", Botan::fmt("{", 5), "{");
1 ✔
1993
         result.test_str_eq("test 4", Botan::fmt("{}"), "{}");
1 ✔
1994
         result.test_str_eq("test 5", Botan::fmt("{} == '{}'", 5, "five"), "5 == 'five'");
1 ✔
1995

1996
         return {result};
3 ✔
1997
      }
2 ✔
1998
};
1999

2000
BOTAN_REGISTER_TEST("utils", "fmt", Formatter_Tests);
2001

2002
class ScopedCleanup_Tests : public Test {
1 ✔
2003
   public:
2004
      std::vector<Test::Result> run() override {
1 ✔
2005
         return {
1 ✔
2006
            CHECK("leaving a scope results in cleanup",
2007
                  [](Test::Result& result) {
1 ✔
2008
                     bool ran = false;
1 ✔
2009
                     {
1 ✔
2010
                        const auto clean = Botan::scoped_cleanup([&] { ran = true; });
1 ✔
2011
                     }
1 ✔
2012
                     result.test_is_true("cleanup ran", ran);
1 ✔
2013
                  }),
1 ✔
2014

2015
            CHECK("leaving a function, results in cleanup",
2016
                  [](Test::Result& result) {
1 ✔
2017
                     bool ran = false;
1 ✔
2018
                     bool fn_called = false;
1 ✔
2019
                     const auto fn = [&] {
2 ✔
2020
                        const auto clean = Botan::scoped_cleanup([&] { ran = true; });
1 ✔
2021
                        fn_called = true;
1 ✔
2022
                     };
2 ✔
2023

2024
                     result.test_is_true("cleanup not yet ran", !ran);
1 ✔
2025
                     fn();
1 ✔
2026
                     result.test_is_true("fn called", fn_called);
1 ✔
2027
                     result.test_is_true("cleanup ran", ran);
1 ✔
2028
                  }),
1 ✔
2029

2030
            CHECK("stack unwinding results in cleanup",
2031
                  [](Test::Result& result) {
1 ✔
2032
                     bool ran = false;
1 ✔
2033
                     bool fn_called = false;
1 ✔
2034
                     bool exception_caught = false;
1 ✔
2035
                     const auto fn = [&] {
2 ✔
2036
                        const auto clean = Botan::scoped_cleanup([&] { ran = true; });
1 ✔
2037
                        fn_called = true;
1 ✔
2038
                        throw std::runtime_error("test");
1 ✔
2039
                     };
2 ✔
2040

2041
                     result.test_is_true("cleanup not yet ran", !ran);
1 ✔
2042
                     try {
1 ✔
2043
                        fn();
1 ✔
2044
                     } catch(const std::exception&) {
1 ✔
2045
                        exception_caught = true;
1 ✔
2046
                     }
1 ✔
2047

2048
                     result.test_is_true("fn called", fn_called);
1 ✔
2049
                     result.test_is_true("cleanup ran", ran);
1 ✔
2050
                     result.test_is_true("exception caught", exception_caught);
1 ✔
2051
                  }),
1 ✔
2052

2053
            CHECK("cleanup isn't called after disengaging",
2054
                  [](Test::Result& result) {
1 ✔
2055
                     bool ran = false;
1 ✔
2056
                     {
1 ✔
2057
                        auto clean = Botan::scoped_cleanup([&] { ran = true; });
1 ✔
2058
                        clean.disengage();
1 ✔
2059
                     }
1 ✔
2060
                     result.test_is_true("cleanup not ran", !ran);
1 ✔
2061
                  }),
1 ✔
2062

2063
         };
5 ✔
2064
      }
1 ✔
2065
};
2066

2067
BOTAN_REGISTER_TEST("utils", "scoped_cleanup", ScopedCleanup_Tests);
2068

2069
}  // namespace
2070

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

© 2026 Coveralls, Inc