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

randombit / botan / 31353843857

10 Aug 2026 02:09AM UTC coverage: 89.751% (+0.03%) from 89.725%
31353843857

push

github

web-flow
Merge pull request #5831 from randombit/jack/scan-name-parsing

SCAN_Name improvements and tests

119895 of 133586 relevant lines covered (89.75%)

10274102.04 hits per line

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

95.76
/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/hex.h>
13
#include <botan/rng.h>
14
#include <botan/version.h>
15
#include <botan/internal/bit_ops.h>
16
#include <botan/internal/calendar.h>
17
#include <botan/internal/charset.h>
18
#include <botan/internal/concat_util.h>
19
#include <botan/internal/ct_utils.h>
20
#include <botan/internal/fmt.h>
21
#include <botan/internal/int_utils.h>
22
#include <botan/internal/loadstor.h>
23
#include <botan/internal/parsing.h>
24
#include <botan/internal/rounding.h>
25
#include <botan/internal/scan_name.h>
26
#include <botan/internal/target_info.h>
27

28
#include <bit>
29

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

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

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

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

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

50
namespace Botan_Tests {
51

52
namespace {
53

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

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

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

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

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

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

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

91
         auto& rng = Test::rng();
1✔
92

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

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

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

106
         return result;
1✔
107
      }
×
108

109
      Test::Result test_checked_mul() {
1✔
110
         Test::Result result("checked_mul");
1✔
111

112
         auto& rng = Test::rng();
1✔
113

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

118
            const uint32_t ref = static_cast<uint32_t>(x) * y;
100✔
119

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

127
         return result;
1✔
128
      }
×
129

130
      Test::Result test_checked_cast() {
1✔
131
         Test::Result result("checked_cast");
1✔
132

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

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

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

144
         return result;
1✔
145
      }
×
146

147
      Test::Result test_round_up() {
1✔
148
         Test::Result result("Util round_up");
1✔
149

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

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

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

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

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

178
         return result;
1✔
179
      }
1✔
180

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

250
         uint8_t outbuf[16] = {0};
1✔
251

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

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

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

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

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

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

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

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

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

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

315
         Botan::store_le(in64, outarr);
1✔
316

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

496
         return result;
2✔
497
      }
10✔
498

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

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

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

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

523
      template <size_t N>
524
      using a = std::array<uint8_t, N>;
525

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

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

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

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

553
         return result;
1✔
554
      }
×
555

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

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

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

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

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

579
         return result;
1✔
580
      }
×
581

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

721
         return result;
1✔
722
      }
×
723

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

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

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

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

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

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

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

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

793
BOTAN_REGISTER_SMOKE_TEST("utils", "util", Utility_Function_Tests);
794

795
class SCAN_Name_Tests final : public Test {
1✔
796
   public:
797
      std::vector<Test::Result> run() override {
1✔
798
         std::vector<Test::Result> results;
1✔
799

800
         results.push_back(parse_bare_name());
2✔
801
         results.push_back(parse_name_with_arg());
2✔
802
         results.push_back(parse_nested_args());
2✔
803
         results.push_back(parse_mode_name());
2✔
804
         results.push_back(parse_nested_names());
2✔
805
         results.push_back(parse_invalid_names());
2✔
806

807
         return results;
1✔
808
      }
×
809

810
   private:
811
      static Test::Result parse_bare_name() {
1✔
812
         Test::Result result("SCAN_Name bare name parse");
1✔
813
         const Botan::SCAN_Name n("SHA-256");
1✔
814
         result.test_str_eq("algo name", n.algo_name(), "SHA-256");
1✔
815
         result.test_sz_eq("no args", n.arg_count(), 0);
1✔
816
         result.test_str_eq("orig", n.to_string(), "SHA-256");
1✔
817
         return result;
1✔
818
      }
1✔
819

820
      static Test::Result parse_name_with_arg() {
1✔
821
         Test::Result result("SCAN_Name parse with arg");
1✔
822
         const Botan::SCAN_Name n("HMAC(SHA-256)");
1✔
823
         result.test_str_eq("algo name", n.algo_name(), "HMAC");
1✔
824
         result.test_sz_eq("one arg", n.arg_count(), 1);
1✔
825
         result.test_str_eq("arg", n.arg(0), "SHA-256");
1✔
826
         return result;
1✔
827
      }
1✔
828

829
      static Test::Result parse_nested_args() {
1✔
830
         Test::Result result("SCAN_Name with nested argument");
1✔
831
         const Botan::SCAN_Name n("Foo(A,B(C))");
1✔
832
         result.test_str_eq("algo name", n.algo_name(), "Foo");
1✔
833
         result.test_sz_eq("two args", n.arg_count(), 2);
1✔
834
         result.test_str_eq("arg0", n.arg(0), "A");
1✔
835
         result.test_str_eq("arg1", n.arg(1), "B(C)");
1✔
836
         return result;
1✔
837
      }
1✔
838

839
      static Test::Result parse_mode_name() {
1✔
840
         Test::Result result("SCAN_Name mode and padding");
1✔
841
         const Botan::SCAN_Name n("AES-128/CBC/PKCS7");
1✔
842
         result.test_str_eq("algo name", n.algo_name(), "AES-128");
1✔
843
         result.test_str_eq("mode", n.cipher_mode(), "CBC");
1✔
844
         result.test_str_eq("mode pad", n.cipher_mode_pad(), "PKCS7");
1✔
845
         return result;
1✔
846
      }
1✔
847

848
      static Test::Result parse_nested_names() {
1✔
849
         Test::Result result("SCAN_Name nesting accepted");
1✔
850
         const Botan::SCAN_Name n("Foo(A(B),C)");
1✔
851
         result.test_str_eq("algo name", n.algo_name(), "Foo");
1✔
852
         result.test_sz_eq("two args", n.arg_count(), 2);
1✔
853
         result.test_str_eq("arg0", n.arg(0), "A(B)");
1✔
854
         result.test_str_eq("arg1", n.arg(1), "C");
1✔
855
         return result;
1✔
856
      }
1✔
857

858
      static void verify_name_is_rejected(Test::Result& result, std::string_view name) {
14✔
859
         result.test_throws<Botan::Invalid_Argument>(Botan::fmt("Invalid name '{}', rejected", name),
28✔
860
                                                     [&] { const Botan::SCAN_Name _parsed(name); });
28✔
861
      }
14✔
862

863
      static Test::Result parse_invalid_names() {
1✔
864
         Test::Result result("SCAN_Name invalid names rejected");
1✔
865

866
         const std::vector<std::string> testcases = {
1✔
867
            "",
868
            "Foo((A))",
869
            "Foo(A(((B))C))",
870
            "Foo()",
871
            "Foo,",
872
            "Foo(",
873
            "Foo)",
874
            "Foo(,)",
875
            "Foo(,A)",
876
            "Foo(A,)",
877
            "Foo(A,,B)",
878
            "AES-128//CBC",
879
            "/Foo",
880
            "Foo/",
881
         };
1✔
882

883
         for(const auto& tc : testcases) {
15✔
884
            verify_name_is_rejected(result, tc);
14✔
885
         }
886
         return result;
1✔
887
      }
1✔
888
};
889

890
BOTAN_REGISTER_TEST("utils", "scan_name", SCAN_Name_Tests);
891

892
class BitOps_Tests final : public Test {
1✔
893
   public:
894
      std::vector<Test::Result> run() override {
1✔
895
         std::vector<Test::Result> results;
1✔
896

897
         results.push_back(test_power_of_2());
2✔
898
         results.push_back(test_ctz());
2✔
899
         results.push_back(test_sig_bytes());
2✔
900
         results.push_back(test_popcount());
2✔
901
         results.push_back(test_reverse_bits());
2✔
902

903
         return results;
1✔
904
      }
×
905

906
   private:
907
      template <typename T>
908
      void test_ctz(Test::Result& result, T val, size_t expected) {
6✔
909
         Botan::CT::poison(val);
910
         const size_t computed = Botan::ctz<T>(val);
6✔
911
         Botan::CT::unpoison_all(computed, val);
912
         result.test_sz_eq("ctz(" + std::to_string(val) + ")", computed, expected);
24✔
913
      }
6✔
914

915
      Test::Result test_ctz() {
1✔
916
         Test::Result result("ctz");
1✔
917
         test_ctz<uint32_t>(result, 0, 32);
1✔
918
         test_ctz<uint32_t>(result, 1, 0);
1✔
919
         test_ctz<uint32_t>(result, 0x80, 7);
1✔
920
         test_ctz<uint32_t>(result, 0x8000000, 27);
1✔
921
         test_ctz<uint32_t>(result, 0x8100000, 20);
1✔
922
         test_ctz<uint32_t>(result, 0x80000000, 31);
1✔
923

924
         return result;
1✔
925
      }
×
926

927
      template <typename T>
928
      void test_sig_bytes(Test::Result& result, T val, size_t expected) {
14✔
929
         Botan::CT::poison(val);
930
         const size_t computed = Botan::significant_bytes<T>(val);
14✔
931
         Botan::CT::unpoison_all(computed, val);
932
         result.test_sz_eq("significant_bytes(" + std::to_string(val) + ")", computed, expected);
56✔
933
      }
14✔
934

935
      Test::Result test_sig_bytes() {
1✔
936
         Test::Result result("significant_bytes");
1✔
937
         test_sig_bytes<uint32_t>(result, 0, 0);
1✔
938
         test_sig_bytes<uint32_t>(result, 1, 1);
1✔
939
         test_sig_bytes<uint32_t>(result, 0x80, 1);
1✔
940
         test_sig_bytes<uint32_t>(result, 255, 1);
1✔
941
         test_sig_bytes<uint32_t>(result, 256, 2);
1✔
942
         test_sig_bytes<uint32_t>(result, 65535, 2);
1✔
943
         test_sig_bytes<uint32_t>(result, 65536, 3);
1✔
944
         test_sig_bytes<uint32_t>(result, 0x80000000, 4);
1✔
945

946
         test_sig_bytes<uint64_t>(result, 0, 0);
1✔
947
         test_sig_bytes<uint64_t>(result, 1, 1);
1✔
948
         test_sig_bytes<uint64_t>(result, 0x80, 1);
1✔
949
         test_sig_bytes<uint64_t>(result, 256, 2);
1✔
950
         test_sig_bytes<uint64_t>(result, 0x80000000, 4);
1✔
951
         test_sig_bytes<uint64_t>(result, 0x100000000, 5);
1✔
952

953
         return result;
1✔
954
      }
×
955

956
      template <typename T>
957
      void test_power_of_2(Test::Result& result, T val, bool expected) {
15✔
958
         result.test_bool_eq("power_of_2(" + std::to_string(val) + ")", Botan::is_power_of_2<T>(val), expected);
75✔
959
      }
15✔
960

961
      Test::Result test_power_of_2() {
1✔
962
         Test::Result result("is_power_of_2");
1✔
963

964
         test_power_of_2<uint32_t>(result, 0, false);
1✔
965
         test_power_of_2<uint32_t>(result, 1, false);
1✔
966
         test_power_of_2<uint32_t>(result, 2, true);
1✔
967
         test_power_of_2<uint32_t>(result, 3, false);
1✔
968
         test_power_of_2<uint32_t>(result, 0x8000, true);
1✔
969
         test_power_of_2<uint32_t>(result, 0x8001, false);
1✔
970
         test_power_of_2<uint32_t>(result, 0x8000000, true);
1✔
971

972
         test_power_of_2<uint64_t>(result, 0, false);
1✔
973
         test_power_of_2<uint64_t>(result, 1, false);
1✔
974
         test_power_of_2<uint64_t>(result, 2, true);
1✔
975
         test_power_of_2<uint64_t>(result, 3, false);
1✔
976
         test_power_of_2<uint64_t>(result, 0x8000, true);
1✔
977
         test_power_of_2<uint64_t>(result, 0x8001, false);
1✔
978
         test_power_of_2<uint64_t>(result, 0x8000000, true);
1✔
979
         test_power_of_2<uint64_t>(result, 0x100000000000, true);
1✔
980

981
         return result;
1✔
982
      }
×
983

984
      template <typename T>
985
      auto pc(T val) -> decltype(Botan::ct_popcount(val)) {
2✔
986
         return Botan::ct_popcount(val);
6✔
987
      }
988

989
      template <typename T>
990
      auto random_pc(Test::Result& result) {
4✔
991
         auto n = Botan::load_le<T>(Test::rng().random_array<sizeof(T)>());
4✔
992
         result.test_sz_eq(Botan::fmt("popcount({}) == {}", n, std::popcount(n)), pc(n), std::popcount(n));
4✔
993
      }
4✔
994

995
      Test::Result test_popcount() {
1✔
996
         Test::Result result("popcount");
1✔
997

998
         result.test_u8_eq("popcount<uint8_t>(0)", pc<uint8_t>(0), 0);
1✔
999
         result.test_u8_eq("popcount<uint16_t>(0)", pc<uint16_t>(0), 0);
1✔
1000
         result.test_u8_eq("popcount<uint32_t>(0)", pc<uint32_t>(0), 0);
1✔
1001
         result.test_u8_eq("popcount<uint64_t>(0)", pc<uint64_t>(0), 0);
1✔
1002

1003
         result.test_u8_eq("popcount<uint8_t>(1)", pc<uint8_t>(1), 1);
1✔
1004
         result.test_u8_eq("popcount<uint16_t>(1)", pc<uint16_t>(1), 1);
1✔
1005
         result.test_u8_eq("popcount<uint32_t>(1)", pc<uint32_t>(1), 1);
1✔
1006
         result.test_u8_eq("popcount<uint64_t>(1)", pc<uint64_t>(1), 1);
1✔
1007

1008
         result.test_u8_eq("popcount<uint8_t>(0xAA)", pc<uint8_t>(0xAA), 4);
1✔
1009
         result.test_u8_eq("popcount<uint16_t>(0xAAAA)", pc<uint16_t>(0xAAAA), 8);
1✔
1010
         result.test_u8_eq("popcount<uint32_t>(0xAAAA...)", pc<uint32_t>(0xAAAAAAAA), 16);
1✔
1011
         result.test_u8_eq("popcount<uint64_t>(0xAAAA...)", pc<uint64_t>(0xAAAAAAAAAAAAAAAA), 32);
1✔
1012

1013
         result.test_u8_eq("popcount<uint8_t>(0xFF)", pc<uint8_t>(0xFF), 8);
1✔
1014
         result.test_u8_eq("popcount<uint16_t>(0xFFFF)", pc<uint16_t>(0xFFFF), 16);
1✔
1015
         result.test_u8_eq("popcount<uint32_t>(0xFFFF...)", pc<uint32_t>(0xFFFFFFFF), 32);
1✔
1016
         result.test_u8_eq("popcount<uint64_t>(0xFFFF...)", pc<uint64_t>(0xFFFFFFFFFFFFFFFF), 64);
1✔
1017

1018
         random_pc<uint8_t>(result);
1✔
1019
         random_pc<uint16_t>(result);
1✔
1020
         random_pc<uint32_t>(result);
1✔
1021
         random_pc<uint64_t>(result);
1✔
1022

1023
         return result;
1✔
1024
      }
×
1025

1026
      Test::Result test_reverse_bits() {
1✔
1027
         Test::Result result("reverse_bits");
1✔
1028

1029
         result.test_u8_eq("rev(0u8)", Botan::ct_reverse_bits<uint8_t>(0b00000000), 0b00000000);
1✔
1030
         result.test_u8_eq("rev(1u8)", Botan::ct_reverse_bits<uint8_t>(0b01010101), 0b10101010);
1✔
1031
         result.test_u8_eq("rev(2u8)", Botan::ct_reverse_bits<uint8_t>(0b01001011), 0b11010010);
1✔
1032

1033
         result.test_u16_eq("rev(0u16)", Botan::ct_reverse_bits<uint16_t>(0b0000000000000000), 0b0000000000000000);
1✔
1034
         result.test_u16_eq("rev(1u16)", Botan::ct_reverse_bits<uint16_t>(0b0101010101010101), 0b1010101010101010);
1✔
1035
         result.test_u16_eq("rev(2u16)", Botan::ct_reverse_bits<uint16_t>(0b0100101101011010), 0b0101101011010010);
1✔
1036

1037
         result.test_u32_eq("rev(0u32)", Botan::ct_reverse_bits<uint32_t>(0xFFFFFFFF), 0xFFFFFFFF);
1✔
1038
         result.test_u32_eq("rev(1u32)", Botan::ct_reverse_bits<uint32_t>(0x55555555), 0xAAAAAAAA);
1✔
1039
         result.test_u32_eq("rev(2u32)", Botan::ct_reverse_bits<uint32_t>(0x4B6A2C1D), 0xB83456D2);
1✔
1040

1041
         result.test_u64_eq("rev(0u64)", Botan::ct_reverse_bits<uint64_t>(0xF0E0D0C005040302), 0x40C020A0030B070F);
1✔
1042
         result.test_u64_eq("rev(1u64)", Botan::ct_reverse_bits<uint64_t>(0x5555555555555555), 0xAAAAAAAAAAAAAAAA);
1✔
1043
         result.test_u64_eq("rev(2u64)", Botan::ct_reverse_bits<uint64_t>(0x4B6A2C1D5E7F8A90), 0x951FE7AB83456D2);
1✔
1044

1045
         return result;
1✔
1046
      }
×
1047
};
1048

1049
BOTAN_REGISTER_TEST("utils", "bit_ops", BitOps_Tests);
1050

1051
#if defined(BOTAN_HAS_POLY_DBL)
1052

1053
class Poly_Double_Tests final : public Text_Based_Test {
×
1054
   public:
1055
      Poly_Double_Tests() : Text_Based_Test("poly_dbl.vec", "In,Out") {}
2✔
1056

1057
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
82✔
1058
         Test::Result result("Polynomial doubling");
82✔
1059
         const std::vector<uint8_t> in = vars.get_req_bin("In");
82✔
1060
         const std::vector<uint8_t> out = vars.get_req_bin("Out");
82✔
1061

1062
         std::vector<uint8_t> b = in;
82✔
1063
         Botan::poly_double_n(b.data(), b.size());
82✔
1064

1065
         result.test_bin_eq("Expected value", b, out);
82✔
1066
         return result;
82✔
1067
      }
246✔
1068
};
1069

1070
BOTAN_REGISTER_TEST("utils", "poly_dbl", Poly_Double_Tests);
1071

1072
#endif
1073

1074
class Version_Tests final : public Test {
1✔
1075
   public:
1076
      std::vector<Test::Result> run() override {
1✔
1077
         Test::Result result("Versions");
1✔
1078

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

1081
         const char* version_cstr = Botan::version_cstr();
1✔
1082
         const std::string version_str = Botan::version_string();
1✔
1083
         result.test_str_eq("Same version string", version_str, std::string(version_cstr));
1✔
1084

1085
         const char* sversion_cstr = Botan::short_version_cstr();
1✔
1086
         const std::string sversion_str = Botan::short_version_string();
1✔
1087
         result.test_str_eq("Same short version string", sversion_str, std::string(sversion_cstr));
1✔
1088

1089
         const auto expected_sversion =
1✔
1090
            Botan::fmt("{}.{}.{}", BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH);
1✔
1091

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

1095
         const std::string version_check_ok =
1✔
1096
            Botan::runtime_version_check(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH);
1✔
1097

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

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

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

1105
         result.test_str_eq("Expected warning text", version_check_bad, expected_error);
1✔
1106

1107
         return {result};
3✔
1108
      }
2✔
1109
};
1110

1111
BOTAN_REGISTER_TEST("utils", "versioning", Version_Tests);
1112

1113
class Date_Format_Tests final : public Text_Based_Test {
×
1114
   public:
1115
      Date_Format_Tests() : Text_Based_Test("dates.vec", "Date") {}
2✔
1116

1117
      static std::vector<uint32_t> parse_date(const std::string& s) {
16✔
1118
         const std::vector<std::string> parts = Botan::split_on(s, ',');
16✔
1119
         if(parts.size() != 6) {
16✔
1120
            throw Test_Error("Bad date format '" + s + "'");
×
1121
         }
1122

1123
         std::vector<uint32_t> u32s;
16✔
1124
         u32s.reserve(parts.size());
16✔
1125
         for(const auto& sub : parts) {
112✔
1126
            u32s.push_back(Botan::to_u32bit(sub));
96✔
1127
         }
1128
         return u32s;
16✔
1129
      }
16✔
1130

1131
      Test::Result run_one_test(const std::string& type, const VarMap& vars) override {
16✔
1132
         const std::string date_str = vars.get_req_str("Date");
16✔
1133
         Test::Result result("Date parsing");
16✔
1134

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

1137
         if(type == "valid" || type == "valid.not_std" || type == "valid.64_bit_time_t") {
16✔
1138
            const Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]);
16✔
1139
            result.test_u32_eq(date_str + " year", c.year(), d[0]);
16✔
1140
            result.test_u32_eq(date_str + " month", c.month(), d[1]);
16✔
1141
            result.test_u32_eq(date_str + " day", c.day(), d[2]);
16✔
1142
            result.test_u32_eq(date_str + " hour", c.hour(), d[3]);
16✔
1143
            result.test_u32_eq(date_str + " minute", c.minutes(), d[4]);
16✔
1144
            result.test_u32_eq(date_str + " second", c.seconds(), d[5]);
16✔
1145

1146
            if(type == "valid.not_std" ||
16✔
1147
               (type == "valid.64_bit_time_t" && c.year() > 2037 && sizeof(std::time_t) == 4)) {
1148
               result.test_throws("valid but out of std::timepoint range", [c]() { c.to_std_timepoint(); });
6✔
1149
            } else {
1150
               const Botan::calendar_point c2(c.to_std_timepoint());
13✔
1151
               result.test_u32_eq(date_str + " year", c2.year(), d[0]);
13✔
1152
               result.test_u32_eq(date_str + " month", c2.month(), d[1]);
13✔
1153
               result.test_u32_eq(date_str + " day", c2.day(), d[2]);
13✔
1154
               result.test_u32_eq(date_str + " hour", c2.hour(), d[3]);
13✔
1155
               result.test_u32_eq(date_str + " minute", c2.minutes(), d[4]);
13✔
1156
               result.test_u32_eq(date_str + " second", c2.seconds(), d[5]);
26✔
1157
            }
1158
         } else if(type == "invalid") {
×
1159
            result.test_throws("invalid date",
×
1160
                               [d]() { const Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]); });
×
1161
         } else {
1162
            throw Test_Error("Unexpected header '" + type + "' in date format tests");
×
1163
         }
1164

1165
         return result;
32✔
1166
      }
190✔
1167
};
1168

1169
BOTAN_REGISTER_TEST("utils", "util_dates", Date_Format_Tests);
1170

1171
class Charset_Tests final : public Text_Based_Test {
×
1172
   public:
1173
      Charset_Tests() : Text_Based_Test("charset.vec", "In,Out") {}
2✔
1174

1175
      Test::Result run_one_test(const std::string& type, const VarMap& vars) override {
53✔
1176
         Test::Result result("Charset");
53✔
1177

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

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

1182
         if(type == "UTF8-UCS2-INVALID") {
53✔
1183
            result.test_throws<Botan::Decoding_Error>("utf8_to_ucs2 rejects invalid input",
13✔
1184
                                                      [&] { Botan::utf8_to_ucs2(in_sv); });
26✔
1185
            return result;
13✔
1186
         }
1187

1188
         if(type == "UTF8-UCS4-INVALID") {
40✔
1189
            result.test_throws<Botan::Decoding_Error>("utf8_to_ucs4 rejects invalid input",
10✔
1190
                                                      [&] { Botan::utf8_to_ucs4(in_sv); });
20✔
1191
            return result;
10✔
1192
         }
1193

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

1196
         std::string converted;
30✔
1197

1198
         if(type == "UCS2-UTF8") {
30✔
1199
            converted = Botan::ucs2_to_utf8(in);
4✔
1200
         } else if(type == "UCS4-UTF8") {
26✔
1201
            converted = Botan::ucs4_to_utf8(in);
1✔
1202
         } else if(type == "UTF8-UCS2") {
25✔
1203
            std::vector<uint8_t> ucs2 = Botan::utf8_to_ucs2(in_sv);
11✔
1204
            converted = std::string(ucs2.begin(), ucs2.end());
22✔
1205
         } else if(type == "UTF8-UCS4") {
25✔
1206
            std::vector<uint8_t> ucs4 = Botan::utf8_to_ucs4(in_sv);
11✔
1207
            converted = std::string(ucs4.begin(), ucs4.end());
22✔
1208
         } else if(type == "LATIN1-UTF8") {
14✔
1209
            converted = Botan::latin1_to_utf8(in);
3✔
1210
         } else {
1211
            throw Test_Error("Unexpected header '" + type + "' in charset tests");
×
1212
         }
1213

1214
         result.test_bin_eq(
30✔
1215
            "string converted successfully", std::vector<uint8_t>(converted.begin(), converted.end()), expected);
30✔
1216

1217
         return result;
30✔
1218
      }
113✔
1219
};
1220

1221
BOTAN_REGISTER_TEST("utils", "charset", Charset_Tests);
1222

1223
class Escape_Control_Chars_Tests final : public Test {
1✔
1224
   public:
1225
      std::vector<Test::Result> run() override {
1✔
1226
         Test::Result result("escape_control_chars");
1✔
1227

1228
         result.test_str_eq("newline", Botan::escape_control_chars("a\nb"), "a\\x0Ab");
1✔
1229
         result.test_str_eq("escape", Botan::escape_control_chars("\x1b[0m"), "\\x1B[0m");
1✔
1230
         result.test_str_eq("embedded NUL", Botan::escape_control_chars(std::string("a\0b", 3)), "a\\x00b");
2✔
1231
         result.test_str_eq("DEL", Botan::escape_control_chars("\x7f"), "\\x7F");
1✔
1232
         result.test_str_eq("C1 control U+009B", Botan::escape_control_chars("\xc2\x9b"), "\\xC2\\x9B");
1✔
1233
         result.test_str_eq("printable ASCII ok", Botan::escape_control_chars("Hello.123"), "Hello.123");
1✔
1234
         result.test_str_eq("printable UTF-8 ok", Botan::escape_control_chars("Fräulein"), "Fräulein");
1✔
1235
         result.test_str_eq("U+00A0 is not a control", Botan::escape_control_chars("\xc2\xa0"), "\xc2\xa0");
1✔
1236

1237
         return {result};
3✔
1238
      }
3✔
1239
};
1240

1241
BOTAN_REGISTER_TEST("utils", "escape_control_chars", Escape_Control_Chars_Tests);
1242

1243
#if defined(BOTAN_HAS_IPV4_ADDRESS)
1244

1245
class IPv4_Parsing_Tests final : public Text_Based_Test {
×
1246
   public:
1247
      IPv4_Parsing_Tests() : Text_Based_Test("utils/ipv4.vec", "IPv4") {}
2✔
1248

1249
      Test::Result run_one_test(const std::string& status, const VarMap& vars) override {
47✔
1250
         Test::Result result("IPv4 parsing");
47✔
1251

1252
         const std::string input = vars.get_req_str("IPv4");
47✔
1253
         const bool valid = (status == "Valid");
47✔
1254

1255
         auto ipv4 = Botan::IPv4Address::from_string(input);
47✔
1256

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

1259
         if(ipv4) {
47✔
1260
            const std::string rt = ipv4->to_string();
13✔
1261
            result.test_str_eq("IPv4Address::from_string and IPv4Address::to_string round trip", input, rt);
13✔
1262
         }
13✔
1263

1264
         return result;
47✔
1265
      }
47✔
1266
};
1267

1268
BOTAN_REGISTER_TEST("utils", "ipv4_parse", IPv4_Parsing_Tests);
1269

1270
class IPv4_Subnet_Parsing_Tests final : public Text_Based_Test {
×
1271
   public:
1272
      IPv4_Subnet_Parsing_Tests() : Text_Based_Test("utils/ipv4_subnet.vec", "IPv4Subnet") {}
2✔
1273

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

1277
         const std::string input = vars.get_req_str("IPv4Subnet");
19✔
1278
         const bool valid = (header == "Valid");
19✔
1279

1280
         const auto subnet = Botan::IPv4Subnet::from_string(input);
19✔
1281

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

1284
         if(subnet) {
19✔
1285
            result.test_str_eq(
6✔
1286
               "IPv4Subnet::from_string and IPv4Subnet::to_string round trip", subnet->to_string(), input);
12✔
1287
         }
1288

1289
         return result;
19✔
1290
      }
19✔
1291
};
1292

1293
BOTAN_REGISTER_TEST("utils", "ipv4_subnet_parse", IPv4_Subnet_Parsing_Tests);
1294

1295
#endif
1296

1297
#if defined(BOTAN_HAS_IPV6_ADDRESS)
1298

1299
class IPv6_Parsing_Tests final : public Text_Based_Test {
×
1300
   public:
1301
      IPv6_Parsing_Tests() : Text_Based_Test("utils/ipv6.vec", "IPv6") {}
2✔
1302

1303
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
50✔
1304
         Test::Result result("IPv6 parsing");
50✔
1305

1306
         const std::string input = vars.get_req_str("IPv6");
50✔
1307
         const bool valid = (header == "Valid");
50✔
1308

1309
         auto ipv6 = Botan::IPv6Address::from_string(input);
50✔
1310

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

1313
         if(ipv6) {
50✔
1314
            const std::string rt = ipv6->to_string();
15✔
1315
            result.test_str_eq("IPv6Address to_string and from_string round trip", input, rt);
15✔
1316
         }
15✔
1317

1318
         return result;
50✔
1319
      }
50✔
1320
};
1321

1322
BOTAN_REGISTER_TEST("utils", "ipv6_parse", IPv6_Parsing_Tests);
1323

1324
class IPv6_Noncanonical_Parsing_Tests final : public Text_Based_Test {
×
1325
   public:
1326
      IPv6_Noncanonical_Parsing_Tests() : Text_Based_Test("utils/ipv6_nc.vec", "Input,Canonical") {}
2✔
1327

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

1331
         const std::string input_str = vars.get_req_str("Input");
23✔
1332
         const std::string canonical_str = vars.get_req_str("Canonical");
23✔
1333

1334
         const auto ipv6 = Botan::IPv6Address::from_string(input_str);
23✔
1335
         const auto canonical = Botan::IPv6Address::from_string(canonical_str);
23✔
1336

1337
         result.test_is_true("IPv6 non-canonical parsing worked", ipv6.has_value());
23✔
1338
         result.test_is_true("IPv6 canonical parsing worked", canonical.has_value());
23✔
1339

1340
         if(ipv6.has_value() && canonical.has_value()) {
23✔
1341
            result.test_is_true("IPv6 non-canonical decoding", *ipv6 == *canonical);
23✔
1342
            result.test_str_eq("IPv6 to_string is canonical", ipv6->to_string(), canonical_str);
23✔
1343
         }
1344

1345
         return result;
23✔
1346
      }
23✔
1347
};
1348

1349
BOTAN_REGISTER_TEST("utils", "ipv6_parse_non_canonical", IPv6_Noncanonical_Parsing_Tests);
1350

1351
class IPv6_Subnet_Parsing_Tests final : public Text_Based_Test {
×
1352
   public:
1353
      IPv6_Subnet_Parsing_Tests() : Text_Based_Test("utils/ipv6_subnet.vec", "IPv6Subnet") {}
2✔
1354

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

1358
         const std::string input = vars.get_req_str("IPv6Subnet");
25✔
1359
         const bool valid = (header == "Valid");
25✔
1360

1361
         const auto subnet = Botan::IPv6Subnet::from_string(input);
25✔
1362

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

1365
         if(subnet) {
25✔
1366
            result.test_str_eq(
7✔
1367
               "IPv6Subnet::from_string and IPv6Subnet::to_string round trip", subnet->to_string(), input);
14✔
1368
         }
1369

1370
         return result;
25✔
1371
      }
25✔
1372
};
1373

1374
BOTAN_REGISTER_TEST("utils", "ipv6_subnet_parse", IPv6_Subnet_Parsing_Tests);
1375

1376
#endif
1377

1378
class ReadKV_Tests final : public Text_Based_Test {
×
1379
   public:
1380
      ReadKV_Tests() : Text_Based_Test("utils/read_kv.vec", "Input,Expected") {}
2✔
1381

1382
      Test::Result run_one_test(const std::string& status, const VarMap& vars) override {
16✔
1383
         Test::Result result("read_kv");
16✔
1384

1385
         const bool is_valid = (status == "Valid");
16✔
1386

1387
         const std::string input = vars.get_req_str("Input");
16✔
1388
         const std::string expected = vars.get_req_str("Expected");
16✔
1389

1390
         if(is_valid) {
16✔
1391
            confirm_kv(result, Botan::read_kv(input), split_group(expected));
14✔
1392
         } else {
1393
            // In this case "expected" is the expected exception message
1394
            result.test_throws("Invalid key value input throws exception", expected, [&]() { Botan::read_kv(input); });
18✔
1395
         }
1396
         return result;
16✔
1397
      }
16✔
1398

1399
   private:
1400
      static std::vector<std::string> split_group(const std::string& str) {
7✔
1401
         std::vector<std::string> elems;
7✔
1402
         if(str.empty()) {
7✔
1403
            return elems;
1404
         }
1405

1406
         std::string substr;
6✔
1407
         for(const char c : str) {
115✔
1408
            if(c == '|') {
109✔
1409
               elems.push_back(substr);
16✔
1410
               substr.clear();
16✔
1411
            } else {
1412
               substr += c;
202✔
1413
            }
1414
         }
1415

1416
         if(!substr.empty()) {
6✔
1417
            elems.push_back(substr);
6✔
1418
         }
1419

1420
         return elems;
6✔
1421
      }
6✔
1422

1423
      static void confirm_kv(Test::Result& result,
7✔
1424
                             const std::map<std::string, std::string>& kv,
1425
                             const std::vector<std::string>& expected) {
1426
         if(!result.test_sz_eq("expected size", expected.size() % 2, size_t(0))) {
7✔
1427
            return;
1428
         }
1429

1430
         for(size_t i = 0; i != expected.size(); i += 2) {
18✔
1431
            auto j = kv.find(expected[i]);
11✔
1432
            if(result.test_is_true("Found key", j != kv.end())) {
11✔
1433
               result.test_str_eq("Matching value", j->second, expected[i + 1]);
11✔
1434
            }
1435
         }
1436

1437
         result.test_sz_eq("KV has same size as expected", kv.size(), expected.size() / 2);
7✔
1438
      }
1439
};
1440

1441
BOTAN_REGISTER_TEST("utils", "util_read_kv", ReadKV_Tests);
1442

1443
#if defined(BOTAN_HAS_CPUID)
1444

1445
class CPUID_Tests final : public Test {
1✔
1446
   public:
1447
      std::vector<Test::Result> run() override {
1✔
1448
         Test::Result result("CPUID");
1✔
1449

1450
         const std::string cpuid_string = Botan::CPUID::to_string();
1✔
1451
         result.test_success("CPUID::to_string doesn't crash");
1✔
1452

1453
         for(size_t b = 0; b != 32; ++b) {
33✔
1454
            try {
32✔
1455
               const auto bit = static_cast<uint32_t>(1) << b;
32✔
1456
               // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
1457
               const auto feat = Botan::CPUID::Feature(static_cast<Botan::CPUID::Feature::Bit>(bit));
32✔
1458

1459
               const std::string feat_str = feat.to_string();
32✔
1460

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

1463
               if(auto from_str = Botan::CPUID::Feature::from_string(feat_str)) {
20✔
1464
                  result.test_u32_eq("Feature::from_string returns expected bit", from_str->as_u32(), bit);
20✔
1465
               } else {
1466
                  result.test_failure(
×
1467
                     Botan::fmt("Feature::from_string didn't recognize its own output ({})", feat_str));
×
1468
               }
1469
            } catch(Botan::Invalid_State&) {
32✔
1470
               // This will thrown if the bit is not a valid one
1471
            }
12✔
1472
         }
1473

1474
   #if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY)
1475

1476
         const auto bit = Botan::CPUID::Feature::SSE2;
1✔
1477

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

1481
            Botan::CPUID::clear_cpuid_bit(bit);
1✔
1482

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

1485
            Botan::CPUID::initialize();  // reset state
1✔
1486
            result.test_is_true("After reinitializing, CPUID::has for SSE2 returns true again", Botan::CPUID::has(bit));
1✔
1487
         }
1488
   #else
1489
         BOTAN_UNUSED(cpuid_string);
1490
   #endif
1491

1492
         return {result};
3✔
1493
      }
2✔
1494
};
1495

1496
BOTAN_REGISTER_SERIALIZED_TEST("utils", "cpuid", CPUID_Tests);
1497

1498
#endif
1499

1500
#if defined(BOTAN_HAS_UUID)
1501

1502
class UUID_Tests : public Test {
1✔
1503
   public:
1504
      std::vector<Test::Result> run() override {
1✔
1505
         Test::Result result("UUID");
1✔
1506

1507
         const Botan::UUID empty_uuid;
1✔
1508
         const Botan::UUID random_uuid1(this->rng());
1✔
1509
         const Botan::UUID random_uuid2(this->rng());
1✔
1510
         const Botan::UUID loaded_uuid(std::vector<uint8_t>(16, 4));
1✔
1511

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

1515
         result.test_is_false("Empty UUID is empty", empty_uuid.is_valid());
1✔
1516
         result.test_is_true("Empty UUID equals another empty UUID", empty_uuid == Botan::UUID());
1✔
1517

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

1520
         result.test_is_true("Random UUID not empty", random_uuid1.is_valid());
1✔
1521
         result.test_is_true("Random UUID not empty", random_uuid2.is_valid());
1✔
1522

1523
         result.test_is_true("Random UUIDs are distinct", random_uuid1 != random_uuid2);
1✔
1524
         result.test_is_true("Random UUIDs not equal to empty", random_uuid1 != empty_uuid);
1✔
1525

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

1529
         const std::string uuid_r1_str = random_uuid1.to_string();
1✔
1530
         result.test_is_true("UUID from string matches", Botan::UUID(uuid_r1_str) == random_uuid1);
1✔
1531

1532
         class AllSame_RNG : public Botan::RandomNumberGenerator {
×
1533
            public:
1534
               explicit AllSame_RNG(uint8_t b) : m_val(b) {}
2✔
1535

1536
               void fill_bytes_with_input(std::span<uint8_t> output, std::span<const uint8_t> /* ignored */) override {
2✔
1537
                  std::fill(output.begin(), output.end(), m_val);
2✔
1538
               }
2✔
1539

1540
               std::string name() const override { return "zeros"; }
×
1541

1542
               bool accepts_input() const override { return false; }
×
1543

1544
               void clear() override {}
×
1545

1546
               bool is_seeded() const override { return true; }
×
1547

1548
            private:
1549
               uint8_t m_val;
1550
         };
1551

1552
         AllSame_RNG zeros(0x00);
1✔
1553
         const Botan::UUID zero_uuid(zeros);
1✔
1554
         result.test_str_eq(
1✔
1555
            "Zero UUID matches expected", zero_uuid.to_string(), "00000000-0000-4000-8000-000000000000");
1✔
1556

1557
         AllSame_RNG ones(0xFF);
1✔
1558
         const Botan::UUID ones_uuid(ones);
1✔
1559
         result.test_str_eq(
1✔
1560
            "Ones UUID matches expected", ones_uuid.to_string(), "FFFFFFFF-FFFF-4FFF-BFFF-FFFFFFFFFFFF");
1✔
1561

1562
         return {result};
3✔
1563
      }
6✔
1564
};
1565

1566
BOTAN_REGISTER_TEST("utils", "uuid", UUID_Tests);
1567

1568
#endif
1569

1570
class Formatter_Tests : public Test {
1✔
1571
   public:
1572
      std::vector<Test::Result> run() override {
1✔
1573
         Test::Result result("Format utility");
1✔
1574

1575
         /*
1576
         In a number of these tests, we are not strictly depending on the
1577
         behavior, for instance checking `fmt("{}") == "{}"` is more about
1578
         checking that we don't crash, rather than we return that precise string.
1579
         */
1580

1581
         result.test_str_eq("test 1", Botan::fmt("hi"), "hi");
1✔
1582
         result.test_str_eq("test 2", Botan::fmt("ignored", 5), "ignored");
1✔
1583
         result.test_str_eq("test 3", Botan::fmt("answer is {}", 42), "answer is 42");
1✔
1584
         result.test_str_eq("test 4", Botan::fmt("{", 5), "{");
1✔
1585
         result.test_str_eq("test 4", Botan::fmt("{}"), "{}");
1✔
1586
         result.test_str_eq("test 5", Botan::fmt("{} == '{}'", 5, "five"), "5 == 'five'");
1✔
1587

1588
         return {result};
3✔
1589
      }
2✔
1590
};
1591

1592
BOTAN_REGISTER_TEST("utils", "fmt", Formatter_Tests);
1593

1594
class ScopedCleanup_Tests : public Test {
1✔
1595
   public:
1596
      std::vector<Test::Result> run() override {
1✔
1597
         return {
1✔
1598
            CHECK("leaving a scope results in cleanup",
1599
                  [](Test::Result& result) {
1✔
1600
                     bool ran = false;
1✔
1601
                     {
1✔
1602
                        auto clean = Botan::scoped_cleanup([&] { ran = true; });
1✔
1603
                     }
1✔
1604
                     result.test_is_true("cleanup ran", ran);
1✔
1605
                  }),
1✔
1606

1607
            CHECK("leaving a function, results in cleanup",
1608
                  [](Test::Result& result) {
1✔
1609
                     bool ran = false;
1✔
1610
                     bool fn_called = false;
1✔
1611
                     auto fn = [&] {
2✔
1612
                        auto clean = Botan::scoped_cleanup([&] { ran = true; });
1✔
1613
                        fn_called = true;
1✔
1614
                     };
2✔
1615

1616
                     result.test_is_true("cleanup not yet ran", !ran);
1✔
1617
                     fn();
1✔
1618
                     result.test_is_true("fn called", fn_called);
1✔
1619
                     result.test_is_true("cleanup ran", ran);
1✔
1620
                  }),
1✔
1621

1622
            CHECK("stack unwinding results in cleanup",
1623
                  [](Test::Result& result) {
1✔
1624
                     bool ran = false;
1✔
1625
                     bool fn_called = false;
1✔
1626
                     bool exception_caught = false;
1✔
1627
                     auto fn = [&] {
2✔
1628
                        auto clean = Botan::scoped_cleanup([&] { ran = true; });
1✔
1629
                        fn_called = true;
1✔
1630
                        throw std::runtime_error("test");
1✔
1631
                     };
2✔
1632

1633
                     result.test_is_true("cleanup not yet ran", !ran);
1✔
1634
                     try {
1✔
1635
                        fn();
1✔
1636
                     } catch(const std::exception&) {
1✔
1637
                        exception_caught = true;
1✔
1638
                     }
1✔
1639

1640
                     result.test_is_true("fn called", fn_called);
1✔
1641
                     result.test_is_true("cleanup ran", ran);
1✔
1642
                     result.test_is_true("exception caught", exception_caught);
1✔
1643
                  }),
1✔
1644

1645
            CHECK("cleanup isn't called after disengaging",
1646
                  [](Test::Result& result) {
1✔
1647
                     bool ran = false;
1✔
1648
                     {
1✔
1649
                        auto clean = Botan::scoped_cleanup([&] { ran = true; });
1✔
1650
                        clean.disengage();
1✔
1651
                     }
1✔
1652
                     result.test_is_true("cleanup not ran", !ran);
1✔
1653
                  }),
1✔
1654

1655
         };
5✔
1656
      }
1✔
1657
};
1658

1659
BOTAN_REGISTER_TEST("utils", "scoped_cleanup", ScopedCleanup_Tests);
1660

1661
}  // namespace
1662

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