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

randombit / botan / 5133556677

31 May 2023 02:11PM UTC coverage: 91.735% (-0.3%) from 92.012%
5133556677

Pull #3568

github

web-flow
Merge de48a2eb6 into 1cbeffafb
Pull Request #3568: Change clang-format AllowShortBlocksOnASingleLine from true to Empty

76059 of 82912 relevant lines covered (91.73%)

12004312.75 hits per line

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

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

8
#include "test_rng.h"
9
#include "tests.h"
10

11
#if defined(BOTAN_HAS_STATEFUL_RNG)
12
   #include <botan/stateful_rng.h>
13
#endif
14

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

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

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

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

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

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

39
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
40
   #include <sys/wait.h>
41
   #include <unistd.h>
42
#endif
43

44
namespace Botan_Tests {
45

46
namespace {
47

48
#if defined(BOTAN_HAS_STATEFUL_RNG)
49

50
class Stateful_RNG_Tests : public Test {
2✔
51
   public:
52
      std::vector<Test::Result> run() override {
2✔
53
         std::vector<Test::Result> results;
2✔
54
         results.push_back(test_reseed_kat());
4✔
55
         results.push_back(test_reseed());
4✔
56
         results.push_back(test_reseed_interval_limits());
4✔
57
         results.push_back(test_max_number_of_bytes_per_request());
4✔
58
         results.push_back(test_broken_entropy_input());
4✔
59
         results.push_back(test_check_nonce());
4✔
60
         results.push_back(test_prediction_resistance());
4✔
61
         results.push_back(test_randomize_with_ts_input());
4✔
62
         results.push_back(test_security_level());
4✔
63
         results.push_back(test_input_output_edge_cases());
4✔
64

65
         /*
66
         * This test uses the library in both parent and child processes. But
67
         * this causes a race with other threads, where if any other test thread
68
         * is holding the mlock pool mutex, it is killed after the fork. Then,
69
         * in the child, any attempt to allocate or free memory will cause a
70
         * deadlock.
71
         */
72
         if(Test::options().test_threads() == 1) {
2✔
73
            results.push_back(test_fork_safety());
×
74
         }
75

76
         return results;
2✔
77
      }
×
78

79
   protected:
80
      virtual std::string rng_name() const = 0;
81

82
      virtual std::unique_ptr<Botan::Stateful_RNG> create_rng(Botan::RandomNumberGenerator* underlying_rng,
83
                                                              Botan::Entropy_Sources* underlying_es,
84
                                                              size_t reseed_interval) = 0;
85

86
      std::unique_ptr<Botan::Stateful_RNG> make_rng(Botan::RandomNumberGenerator& underlying_rng,
14✔
87
                                                    size_t reseed_interval = 1024) {
88
         return create_rng(&underlying_rng, nullptr, reseed_interval);
14✔
89
      }
90

91
      std::unique_ptr<Botan::Stateful_RNG> make_rng(Botan::Entropy_Sources& underlying_srcs,
4✔
92
                                                    size_t reseed_interval = 1024) {
93
         return create_rng(nullptr, &underlying_srcs, reseed_interval);
4✔
94
      }
95

96
      std::unique_ptr<Botan::Stateful_RNG> make_rng(Botan::RandomNumberGenerator& underlying_rng,
6✔
97
                                                    Botan::Entropy_Sources& underlying_srcs,
98
                                                    size_t reseed_interval = 1024) {
99
         return create_rng(&underlying_rng, &underlying_srcs, reseed_interval);
6✔
100
      }
101

102
      virtual Test::Result test_reseed_kat() = 0;
103

104
      virtual Test::Result test_security_level() = 0;
105

106
      virtual Test::Result test_max_number_of_bytes_per_request() = 0;
107

108
      virtual Test::Result test_reseed_interval_limits() = 0;
109

110
   private:
111
      Test::Result test_reseed() {
2✔
112
         Test::Result result(rng_name() + " Reseed");
2✔
113

114
         // test reseed_interval is enforced
115
         Request_Counting_RNG counting_rng;
2✔
116

117
         auto rng = make_rng(counting_rng, 2);
2✔
118

119
         rng->random_vec(7);
2✔
120
         result.test_eq("initial seeding", counting_rng.randomize_count(), 1);
2✔
121
         rng->random_vec(9);
2✔
122
         result.test_eq("still initial seed", counting_rng.randomize_count(), 1);
2✔
123

124
         rng->random_vec(1);
2✔
125
         result.test_eq("first reseed", counting_rng.randomize_count(), 2);
2✔
126
         rng->random_vec(15);
2✔
127
         result.test_eq("still first reseed", counting_rng.randomize_count(), 2);
2✔
128

129
         rng->random_vec(15);
2✔
130
         result.test_eq("second reseed", counting_rng.randomize_count(), 3);
2✔
131
         rng->random_vec(1);
2✔
132
         result.test_eq("still second reseed", counting_rng.randomize_count(), 3);
2✔
133

134
         if(rng->max_number_of_bytes_per_request() > 0) {
2✔
135
            // request > max_number_of_bytes_per_request, do reseeds occur?
136
            rng->random_vec(64 * 1024 + 1);
1✔
137
            result.test_eq("request exceeds output limit", counting_rng.randomize_count(), 4);
1✔
138

139
            rng->random_vec(9 * 64 * 1024 + 1);
1✔
140
            result.test_eq("request exceeds output limit", counting_rng.randomize_count(), 9);
2✔
141
         }
142

143
         return result;
4✔
144
      }
2✔
145

146
      Test::Result test_broken_entropy_input() {
2✔
147
         Test::Result result(rng_name() + " Broken Entropy Input");
2✔
148

149
         class Broken_Entropy_Source final : public Botan::Entropy_Source {
2✔
150
            public:
151
               std::string name() const override { return "Broken Entropy Source"; }
×
152

153
               size_t poll(Botan::RandomNumberGenerator& /*rng*/) override {
4✔
154
                  throw Botan::Not_Implemented("polling not available");
4✔
155
               }
156
         };
157

158
         class Insufficient_Entropy_Source final : public Botan::Entropy_Source {
2✔
159
            public:
160
               std::string name() const override { return "Insufficient Entropy Source"; }
×
161

162
               size_t poll(Botan::RandomNumberGenerator& /*rng*/) override { return 0; }
2✔
163
         };
164

165
         // make sure no output is generated when the entropy input source is broken
166

167
         // underlying_rng throws exception
168
         Botan::Null_RNG broken_entropy_input_rng;
2✔
169
         result.test_eq("Null_RNG not seeded", broken_entropy_input_rng.is_seeded(), false);
2✔
170
         auto rng_with_broken_rng = make_rng(broken_entropy_input_rng);
2✔
171

172
         result.test_throws("broken underlying rng", [&rng_with_broken_rng]() { rng_with_broken_rng->random_vec(16); });
6✔
173

174
         // entropy_sources throw exception
175
         auto broken_entropy_source_1 = std::make_unique<Broken_Entropy_Source>();
2✔
176
         auto broken_entropy_source_2 = std::make_unique<Broken_Entropy_Source>();
2✔
177

178
         Botan::Entropy_Sources broken_entropy_sources;
2✔
179
         broken_entropy_sources.add_source(std::move(broken_entropy_source_1));
2✔
180
         broken_entropy_sources.add_source(std::move(broken_entropy_source_2));
2✔
181

182
         auto rng_with_broken_es = make_rng(broken_entropy_sources);
2✔
183
         result.test_throws("broken entropy sources", [&rng_with_broken_es]() { rng_with_broken_es->random_vec(16); });
6✔
184

185
         // entropy source returns insufficient entropy
186
         Botan::Entropy_Sources insufficient_entropy_sources;
2✔
187
         auto insufficient_entropy_source = std::make_unique<Insufficient_Entropy_Source>();
2✔
188
         insufficient_entropy_sources.add_source(std::move(insufficient_entropy_source));
2✔
189

190
         auto rng_with_insufficient_es = make_rng(insufficient_entropy_sources);
2✔
191
         result.test_throws("insufficient entropy source",
4✔
192
                            [&rng_with_insufficient_es]() { rng_with_insufficient_es->random_vec(16); });
2✔
193

194
         // one of or both underlying_rng and entropy_sources throw exception
195

196
         auto rng_with_broken_rng_and_good_es =
2✔
197
            make_rng(broken_entropy_input_rng, Botan::Entropy_Sources::global_sources());
2✔
198

199
         result.test_throws("broken underlying rng but good entropy sources",
4✔
200
                            [&rng_with_broken_rng_and_good_es]() { rng_with_broken_rng_and_good_es->random_vec(16); });
2✔
201

202
         auto rng_with_good_rng_and_broken_es = make_rng(Test::rng(), broken_entropy_sources);
2✔
203

204
         result.test_throws("good underlying rng but broken entropy sources",
4✔
205
                            [&rng_with_good_rng_and_broken_es]() { rng_with_good_rng_and_broken_es->random_vec(16); });
2✔
206

207
         auto rng_with_broken_rng_and_broken_es = make_rng(broken_entropy_input_rng, broken_entropy_sources);
2✔
208

209
         result.test_throws("underlying rng and entropy sources broken", [&rng_with_broken_rng_and_broken_es]() {
4✔
210
            rng_with_broken_rng_and_broken_es->random_vec(16);
2✔
211
         });
×
212

213
         return result;
4✔
214
      }
12✔
215

216
      Test::Result test_check_nonce() {
2✔
217
         Test::Result result(rng_name() + " Nonce Check");
2✔
218

219
         // make sure the nonce has at least security_strength bits
220
         auto rng = create_rng(nullptr, nullptr, 0);
2✔
221

222
         for(size_t nonce_size : {0, 4, 8, 16, 31, 32, 34, 64}) {
18✔
223
            rng->clear();
16✔
224
            result.test_eq("not seeded", rng->is_seeded(), false);
16✔
225

226
            const std::vector<uint8_t> nonce(nonce_size);
16✔
227
            rng->initialize_with(nonce.data(), nonce.size());
16✔
228

229
            if(nonce_size < rng->security_level() / 8) {
16✔
230
               result.test_eq("not seeded", rng->is_seeded(), false);
10✔
231
               result.test_throws("invalid nonce size", [&rng]() { rng->random_vec(32); });
40✔
232
            } else {
233
               result.test_eq("is seeded", rng->is_seeded(), true);
6✔
234
               rng->random_vec(32);
12✔
235
            }
236
         }
16✔
237

238
         return result;
2✔
239
      }
2✔
240

241
      Test::Result test_prediction_resistance() {
2✔
242
         Test::Result result(rng_name() + " Prediction Resistance");
2✔
243

244
         // set reseed_interval = 1, forcing a reseed for every RNG request
245
         Request_Counting_RNG counting_rng;
2✔
246
         auto rng = make_rng(counting_rng, 1);
2✔
247

248
         rng->random_vec(16);
2✔
249
         result.test_eq("first request", counting_rng.randomize_count(), size_t(1));
2✔
250

251
         rng->random_vec(16);
2✔
252
         result.test_eq("second request", counting_rng.randomize_count(), size_t(2));
2✔
253

254
         rng->random_vec(16);
2✔
255
         result.test_eq("third request", counting_rng.randomize_count(), size_t(3));
2✔
256

257
         return result;
4✔
258
      }
2✔
259

260
      Test::Result test_fork_safety() {
×
261
         Test::Result result(rng_name() + " Fork Safety");
×
262

263
   #if defined(BOTAN_TARGET_OS_HAS_POSIX1)
264
         const size_t reseed_interval = 1024;
×
265

266
         // make sure rng is reseeded after every fork
267
         Request_Counting_RNG counting_rng;
×
268
         auto rng = make_rng(counting_rng, reseed_interval);
×
269

270
         rng->random_vec(16);
×
271
         result.test_eq("first request", counting_rng.randomize_count(), size_t(1));
×
272

273
         // fork and request from parent and child, both should output different sequences
274
         size_t count = counting_rng.randomize_count();
×
275
         Botan::secure_vector<uint8_t> parent_bytes(16), child_bytes(16);
×
276
         int fd[2];
×
277
         int rc = ::pipe(fd);
×
278
         if(rc != 0) {
×
279
            result.test_failure("failed to create pipe");
×
280
         }
281

282
         pid_t pid = ::fork();
×
283
         if(pid == -1) {
×
284
      #if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
285
            result.test_note("failed to fork process");
286
      #else
287
            result.test_failure("failed to fork process");
×
288
      #endif
289
            return result;
×
290
         } else if(pid != 0) {
×
291
            // parent process, wait for randomize_count from child's rng
292
            ::close(fd[1]);  // close write end in parent
×
293
            ssize_t got = ::read(fd[0], &count, sizeof(count));
×
294

295
            if(got > 0) {
×
296
               result.test_eq("expected bytes from child", got, sizeof(count));
×
297
               result.test_eq("parent not reseeded", counting_rng.randomize_count(), 1);
×
298
               result.test_eq("child reseed occurred", count, 2);
×
299
            } else {
300
               result.test_failure("Failed to read count size from child process");
×
301
            }
302

303
            parent_bytes = rng->random_vec(16);
×
304
            got = ::read(fd[0], &child_bytes[0], child_bytes.size());
×
305

306
            if(got > 0) {
×
307
               result.test_eq("expected bytes from child", got, child_bytes.size());
×
308
               result.test_ne("parent and child output sequences differ", parent_bytes, child_bytes);
×
309
            } else {
310
               result.test_failure("Failed to read RNG bytes from child process");
×
311
            }
312
            ::close(fd[0]);  // close read end in parent
×
313

314
            // wait for the child to exit
315
            int status = 0;
×
316
            ::waitpid(pid, &status, 0);
×
317
         } else {
318
            // child process, send randomize_count and first output sequence back to parent
319
            ::close(fd[0]);  // close read end in child
×
320
            rng->randomize(&child_bytes[0], child_bytes.size());
×
321
            count = counting_rng.randomize_count();
×
322
            ssize_t written = ::write(fd[1], &count, sizeof(count));
×
323
            BOTAN_UNUSED(written);
×
324
            try {
×
325
               rng->randomize(&child_bytes[0], child_bytes.size());
×
326
            } catch(std::exception& e) {
×
327
               static_cast<void>(fprintf(stderr, "%s", e.what()));
×
328
            }
×
329
            written = ::write(fd[1], &child_bytes[0], child_bytes.size());
×
330
            BOTAN_UNUSED(written);
×
331
            ::close(fd[1]);  // close write end in child
×
332

333
            /*
334
            * We can't call exit because it causes the mlock pool to be freed (#602)
335
            * We can't call _exit because it makes valgrind think we leaked memory.
336
            * So instead we execute something that will return 0 for us.
337
            */
338
            ::execl("/bin/true", "true", NULL);
×
339
            ::_exit(0);  // just in case /bin/true isn't available (sandbox?)
×
340
         }
341
   #endif
342
         return result;
×
343
      }
×
344

345
      Test::Result test_randomize_with_ts_input() {
2✔
346
         Test::Result result(rng_name() + " Randomize With Timestamp Input");
2✔
347

348
         const size_t request_bytes = 64;
2✔
349
         const std::vector<uint8_t> seed(128);
2✔
350

351
         // check that randomize_with_ts_input() creates different output based on a timestamp
352
         // and possibly additional data, such as process id even with identical seeds
353
         Fixed_Output_RNG fixed_output_rng1(seed);
2✔
354
         Fixed_Output_RNG fixed_output_rng2(seed);
2✔
355

356
         auto rng1 = make_rng(fixed_output_rng1);
2✔
357
         auto rng2 = make_rng(fixed_output_rng2);
2✔
358

359
         Botan::secure_vector<uint8_t> output1(request_bytes);
2✔
360
         Botan::secure_vector<uint8_t> output2(request_bytes);
2✔
361

362
         rng1->randomize(output1.data(), output1.size());
2✔
363
         rng2->randomize(output2.data(), output2.size());
2✔
364

365
         result.test_eq("equal output due to same seed", output1, output2);
2✔
366

367
         rng1->randomize_with_ts_input(output1.data(), output1.size());
2✔
368
         rng2->randomize_with_ts_input(output2.data(), output2.size());
2✔
369

370
         result.test_ne("output differs due to different timestamp", output1, output2);
4✔
371

372
         return result;
2✔
373
      }
10✔
374

375
      Test::Result test_input_output_edge_cases() {
2✔
376
         Test::Result result(rng_name() + " randomize");
2✔
377

378
         const std::vector<uint8_t> seed(128);
2✔
379
         Fixed_Output_RNG fixed_output_rng(seed);
2✔
380

381
         auto rng = make_rng(fixed_output_rng);
2✔
382

383
         for(size_t i = 0; i != 4096; ++i) {
8,194✔
384
            std::vector<uint8_t> buf(i);
8,192✔
385
            rng->randomize(buf.data(), buf.size());
8,192✔
386
            rng->add_entropy(buf.data(), buf.size());
8,192✔
387

388
            result.test_success("RNG accepted input and output length");
16,384✔
389
         }
8,192✔
390

391
         return result;
2✔
392
      }
4✔
393
};
394

395
#endif
396

397
#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_32)
398

399
class HMAC_DRBG_Unit_Tests final : public Stateful_RNG_Tests {
×
400
   public:
401
      std::string rng_name() const override { return "HMAC_DRBG"; }
6✔
402

403
      std::unique_ptr<Botan::Stateful_RNG> create_rng(Botan::RandomNumberGenerator* underlying_rng,
13✔
404
                                                      Botan::Entropy_Sources* underlying_es,
405
                                                      size_t reseed_interval) override {
406
         std::unique_ptr<Botan::MessageAuthenticationCode> mac =
13✔
407
            Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
13✔
408

409
         if(underlying_rng && underlying_es) {
13✔
410
            return std::make_unique<Botan::HMAC_DRBG>(std::move(mac), *underlying_rng, *underlying_es, reseed_interval);
3✔
411
         } else if(underlying_rng) {
10✔
412
            return std::make_unique<Botan::HMAC_DRBG>(std::move(mac), *underlying_rng, reseed_interval);
7✔
413
         } else if(underlying_es) {
3✔
414
            return std::make_unique<Botan::HMAC_DRBG>(std::move(mac), *underlying_es, reseed_interval);
2✔
415
         } else if(reseed_interval == 0) {
1✔
416
            return std::make_unique<Botan::HMAC_DRBG>(std::move(mac));
1✔
417
         } else {
418
            throw Test_Error("Invalid reseed interval in HMAC_DRBG unit test");
×
419
         }
420
      }
13✔
421

422
      Test::Result test_max_number_of_bytes_per_request() override {
1✔
423
         Test::Result result("HMAC_DRBG max_number_of_bytes_per_request");
1✔
424

425
         const std::string mac_string = "HMAC(SHA-256)";
1✔
426

427
         Request_Counting_RNG counting_rng;
1✔
428

429
         result.test_throws(
2✔
430
            "HMAC_DRBG does not accept 0 for max_number_of_bytes_per_request", [&mac_string, &counting_rng]() {
1✔
431
               Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 0);
1✔
432
            });
×
433

434
         result.test_throws("HMAC_DRBG does not accept values higher than 64KB for max_number_of_bytes_per_request",
2✔
435
                            [&mac_string, &counting_rng]() {
1✔
436
                               Botan::HMAC_DRBG failing_rng(
1✔
437
                                  Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 64 * 1024 + 1);
1✔
438
                            });
×
439

440
         // set reseed_interval to 1 so we can test that a long request is split
441
         // into multiple, max_number_of_bytes_per_request long requests
442
         // for each smaller request, reseed_check() calls counting_rng::randomize(),
443
         // which we can compare with
444
         Botan::HMAC_DRBG rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 1, 64);
1✔
445

446
         rng.random_vec(63);
1✔
447
         result.test_eq("one request", counting_rng.randomize_count(), 1);
1✔
448

449
         rng.clear();
1✔
450
         counting_rng.clear();
1✔
451

452
         rng.random_vec(64);
1✔
453
         result.test_eq("one request", counting_rng.randomize_count(), 1);
1✔
454

455
         rng.clear();
1✔
456
         counting_rng.clear();
1✔
457

458
         rng.random_vec(65);
1✔
459
         result.test_eq("two requests", counting_rng.randomize_count(), 2);
1✔
460

461
         rng.clear();
1✔
462
         counting_rng.clear();
1✔
463

464
         rng.random_vec(1025);
1✔
465
         result.test_eq("17 requests", counting_rng.randomize_count(), 17);
1✔
466

467
         return result;
2✔
468
      }
1✔
469

470
      Test::Result test_reseed_interval_limits() override {
1✔
471
         Test::Result result("HMAC_DRBG reseed_interval limits");
1✔
472

473
         const std::string mac_string = "HMAC(SHA-256)";
1✔
474

475
         Request_Counting_RNG counting_rng;
1✔
476

477
         result.test_throws("HMAC_DRBG does not accept 0 for reseed_interval", [&mac_string, &counting_rng]() {
2✔
478
            Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 0);
1✔
479
         });
×
480

481
         result.test_throws("HMAC_DRBG does not accept values higher than 2^24 for reseed_interval",
2✔
482
                            [&mac_string, &counting_rng]() {
1✔
483
                               Botan::HMAC_DRBG failing_rng(Botan::MessageAuthenticationCode::create(mac_string),
1✔
484
                                                            counting_rng,
485
                                                            (static_cast<size_t>(1) << 24) + 1);
1✔
486
                            });
×
487

488
         return result;
1✔
489
      }
1✔
490

491
      Test::Result test_security_level() override {
1✔
492
         Test::Result result("HMAC_DRBG Security Level");
1✔
493

494
         std::vector<std::string> approved_hash_fns{"SHA-1", "SHA-224", "SHA-256", "SHA-512/256", "SHA-384", "SHA-512"};
7✔
495
         std::vector<uint32_t> security_strengths{128, 192, 256, 256, 256, 256};
1✔
496

497
         for(size_t i = 0; i < approved_hash_fns.size(); ++i) {
7✔
498
            std::string hash_fn = approved_hash_fns[i];
6✔
499
            std::string mac_name = "HMAC(" + hash_fn + ")";
6✔
500
            auto mac = Botan::MessageAuthenticationCode::create(mac_name);
6✔
501
            if(!mac) {
6✔
502
               result.note_missing(mac_name);
1✔
503
               continue;
1✔
504
            }
505

506
            Botan::HMAC_DRBG rng(std::move(mac));
5✔
507
            result.test_eq(hash_fn + " security level", rng.security_level(), security_strengths[i]);
5✔
508
         }
7✔
509

510
         return result;
2✔
511
      }
1✔
512

513
      Test::Result test_reseed_kat() override {
1✔
514
         Test::Result result("HMAC_DRBG Reseed KAT");
1✔
515

516
         Request_Counting_RNG counting_rng;
1✔
517
         auto rng = make_rng(counting_rng, 2);
1✔
518

519
         const Botan::secure_vector<uint8_t> seed_input(
1✔
520
            {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
521
             0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF});
1✔
522

523
         result.test_eq("is_seeded", rng->is_seeded(), false);
1✔
524

525
         rng->initialize_with(seed_input.data(), seed_input.size());
1✔
526

527
         Botan::secure_vector<uint8_t> out(32);
1✔
528

529
         rng->randomize(out.data(), out.size());
1✔
530
         result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(0));
1✔
531
         result.test_eq("out before reseed", out, "48D3B45AAB65EF92CCFCB9427EF20C90297065ECC1B8A525BFE4DC6FF36D0E38");
1✔
532

533
         // reseed must happen here
534
         rng->randomize(out.data(), out.size());
1✔
535
         result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(1));
1✔
536
         result.test_eq("out after reseed", out, "2F8FCA696832C984781123FD64F4B20C7379A25C87AB29A21C9BF468B0081CE2");
1✔
537

538
         return result;
2✔
539
      }
3✔
540
};
541

542
std::vector<Test::Result> hmac_drbg_multiple_requests() {
1✔
543
   auto null_rng = Botan::Null_RNG();
1✔
544
   constexpr auto rng_max_output = 1024;
1✔
545
   const auto seed = Botan::hex_decode("deadbeefbaadcafedeadbeefbaadcafedeadbeefbaadcafedeadbeefbaadcafe");
1✔
546

547
   auto make_seeded_rng = [&](size_t reseed_interval) {
5✔
548
      auto rng = std::make_unique<Botan::HMAC_DRBG>(Botan::MessageAuthenticationCode::create("HMAC(SHA-256)"),
8✔
549
                                                    null_rng,
4✔
550
                                                    reseed_interval + 1 /* off by one */,
8✔
551
                                                    rng_max_output);
4✔
552
      rng->add_entropy(seed);
4✔
553
      return rng;
4✔
554
   };
×
555

556
   return {Botan_Tests::CHECK("bulk and split output without input",
1✔
557
                              [&](auto& result) {
1✔
558
                                 auto rng1 = make_seeded_rng(2);
1✔
559
                                 auto rng2 = make_seeded_rng(2);
1✔
560

561
                                 result.confirm("RNG 1 is seeded and ready to go", rng1->is_seeded());
3✔
562
                                 result.confirm("RNG 2 is seeded and ready to go", rng2->is_seeded());
3✔
563

564
                                 auto bulk = rng1->random_vec<std::vector<uint8_t>>(2 * rng_max_output);
1✔
565

566
                                 auto split1 = rng2->random_vec<std::vector<uint8_t>>(rng_max_output);
1✔
567
                                 auto split2 = rng2->random_vec<std::vector<uint8_t>>(rng_max_output);
1✔
568
                                 split1.insert(split1.end(), split2.begin(), split2.end());
1✔
569

570
                                 result.test_eq("Output is equal, regardless bulk request", bulk, split1);
1✔
571

572
                                 return result;
2✔
573
                              }),
3✔
574

575
           Botan_Tests::CHECK("bulk and split output with input", [&](auto& result) {
1✔
576
              auto rng1 = make_seeded_rng(3);
1✔
577
              auto rng2 = make_seeded_rng(3);
1✔
578

579
              result.confirm("RNG 1 is seeded and ready to go", rng1->is_seeded());
3✔
580
              result.confirm("RNG 2 is seeded and ready to go", rng2->is_seeded());
3✔
581

582
              std::vector<uint8_t> bulk(3 * rng_max_output);
1✔
583
              rng1->randomize_with_input(bulk, seed);
2✔
584

585
              std::vector<uint8_t> split(3 * rng_max_output);
1✔
586
              std::span<uint8_t> split_span(split);
1✔
587
              rng2->randomize_with_input(split_span.subspan(0, rng_max_output), seed);
2✔
588
              rng2->randomize_with_input(split_span.subspan(rng_max_output, rng_max_output), {});
2✔
589
              rng2->randomize_with_input(split_span.subspan(2 * rng_max_output), {});
2✔
590

591
              result.test_eq("Output is equal, regardless bulk request", bulk, split);
1✔
592

593
              return result;
2✔
594
           })};
6✔
595
}
1✔
596

597
BOTAN_REGISTER_TEST("rng", "hmac_drbg_unit", HMAC_DRBG_Unit_Tests);
598
BOTAN_REGISTER_TEST_FN("rng", "hmac_drbg_multi_requst", hmac_drbg_multiple_requests);
599

600
#endif
601

602
#if defined(BOTAN_HAS_CHACHA_RNG)
603

604
class ChaCha_RNG_Unit_Tests final : public Stateful_RNG_Tests {
×
605
   public:
606
      std::string rng_name() const override { return "ChaCha_RNG"; }
6✔
607

608
      std::unique_ptr<Botan::Stateful_RNG> create_rng(Botan::RandomNumberGenerator* underlying_rng,
13✔
609
                                                      Botan::Entropy_Sources* underlying_es,
610
                                                      size_t reseed_interval) override {
611
         if(underlying_rng && underlying_es) {
13✔
612
            return std::make_unique<Botan::ChaCha_RNG>(*underlying_rng, *underlying_es, reseed_interval);
3✔
613
         } else if(underlying_rng) {
10✔
614
            return std::make_unique<Botan::ChaCha_RNG>(*underlying_rng, reseed_interval);
7✔
615
         } else if(underlying_es) {
3✔
616
            return std::make_unique<Botan::ChaCha_RNG>(*underlying_es, reseed_interval);
2✔
617
         } else if(reseed_interval == 0) {
1✔
618
            return std::make_unique<Botan::ChaCha_RNG>();
1✔
619
         } else {
620
            throw Test_Error("Invalid reseed interval in ChaCha_RNG unit test");
×
621
         }
622
      }
623

624
      Test::Result test_security_level() override {
1✔
625
         Test::Result result("ChaCha_RNG Security Level");
1✔
626
         Botan::ChaCha_RNG rng;
1✔
627
         result.test_eq("Expected security level", rng.security_level(), size_t(256));
1✔
628
         return result;
1✔
629
      }
1✔
630

631
      Test::Result test_max_number_of_bytes_per_request() override {
1✔
632
         Test::Result result("ChaCha_RNG max_number_of_bytes_per_request");
1✔
633
         // ChaCha_RNG doesn't have this notion
634
         return result;
1✔
635
      }
636

637
      Test::Result test_reseed_interval_limits() override {
1✔
638
         Test::Result result("ChaCha_RNG reseed_interval limits");
1✔
639
         // ChaCha_RNG doesn't apply any limits to reseed_interval
640
         return result;
1✔
641
      }
642

643
      Test::Result test_reseed_kat() override {
1✔
644
         Test::Result result("ChaCha_RNG Reseed KAT");
1✔
645

646
         Request_Counting_RNG counting_rng;
1✔
647
         auto rng = make_rng(counting_rng, 2);
1✔
648

649
         const Botan::secure_vector<uint8_t> seed_input(32);
1✔
650

651
         result.test_eq("is_seeded", rng->is_seeded(), false);
1✔
652

653
         rng->initialize_with(seed_input.data(), seed_input.size());
1✔
654

655
         Botan::secure_vector<uint8_t> out(32);
1✔
656

657
         rng->randomize(out.data(), out.size());
1✔
658
         result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(0));
1✔
659
         result.test_eq("out before reseed", out, "1F0E6F13429D5073B59C057C37CBE9587740A0A894D247E2596C393CE91DDC6F");
1✔
660

661
         // reseed must happen here
662
         rng->randomize(out.data(), out.size());
1✔
663
         result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(1));
1✔
664
         result.test_eq("out after reseed", out, "F2CAE73F22684D5D773290B48FDCDA0E6C0661EBA0A854AFEC922832BDBB9C49");
1✔
665

666
         return result;
2✔
667
      }
3✔
668
};
669

670
BOTAN_REGISTER_TEST("rng", "chacha_rng_unit", ChaCha_RNG_Unit_Tests);
671

672
#endif
673

674
#if defined(BOTAN_HAS_AUTO_RNG)
675

676
class AutoSeeded_RNG_Tests final : public Test {
×
677
   private:
678
      static Test::Result auto_rng_tests() {
1✔
679
         Test::Result result("AutoSeeded_RNG");
1✔
680

681
         Botan::Entropy_Sources no_entropy_for_you;
1✔
682
         Botan::Null_RNG null_rng;
1✔
683

684
         result.test_eq("Null_RNG is null", null_rng.is_seeded(), false);
1✔
685

686
         try {
1✔
687
            Botan::AutoSeeded_RNG rng(no_entropy_for_you);
1✔
688
            result.test_failure("AutoSeeded_RNG should have rejected useless entropy source");
×
689
         } catch(Botan::PRNG_Unseeded&) {
1✔
690
            result.test_success("AutoSeeded_RNG rejected empty entropy source");
1✔
691
         }
1✔
692

693
         try {
1✔
694
            Botan::AutoSeeded_RNG rng(null_rng);
1✔
695
         } catch(Botan::PRNG_Unseeded&) {
1✔
696
            result.test_success("AutoSeeded_RNG rejected useless RNG");
1✔
697
         }
1✔
698

699
         try {
1✔
700
            Botan::AutoSeeded_RNG rng(null_rng, no_entropy_for_you);
1✔
701
         } catch(Botan::PRNG_Unseeded&) {
1✔
702
            result.test_success("AutoSeeded_RNG rejected useless RNG+entropy sources");
1✔
703
         }
1✔
704

705
         Botan::AutoSeeded_RNG rng;
1✔
706

707
         result.confirm("AutoSeeded_RNG::name", rng.name().starts_with("HMAC_DRBG(HMAC(SHA-"));
4✔
708

709
         result.confirm("AutoSeeded_RNG starts seeded", rng.is_seeded());
2✔
710
         rng.random_vec(16);  // generate and discard output
1✔
711
         rng.clear();
1✔
712
         result.test_eq("AutoSeeded_RNG unseeded after calling clear", rng.is_seeded(), false);
1✔
713

714
         // AutoSeeded_RNG automatically reseeds as required:
715
         rng.random_vec(16);
1✔
716
         result.confirm("AutoSeeded_RNG can be reseeded", rng.is_seeded());
2✔
717

718
         result.confirm("AutoSeeded_RNG ", rng.is_seeded());
2✔
719
         rng.random_vec(16);  // generate and discard output
1✔
720
         rng.clear();
1✔
721
         result.test_eq("AutoSeeded_RNG unseeded after calling clear", rng.is_seeded(), false);
1✔
722

723
         const size_t no_entropy_bits = rng.reseed(no_entropy_for_you, 256, std::chrono::milliseconds(300));
1✔
724
         result.test_eq("AutoSeeded_RNG can't reseed from nothing", no_entropy_bits, 0);
1✔
725
         result.test_eq("AutoSeeded_RNG still unseeded", rng.is_seeded(), false);
1✔
726

727
         rng.random_vec(16);  // generate and discard output
1✔
728
         result.confirm("AutoSeeded_RNG can be reseeded", rng.is_seeded());
2✔
729

730
         for(size_t i = 0; i != 4096; ++i) {
4,097✔
731
            std::vector<uint8_t> buf(i);
4,096✔
732
            rng.randomize(buf.data(), buf.size());
4,096✔
733
            rng.add_entropy(buf.data(), buf.size());
4,096✔
734

735
            result.test_success("AutoSeeded_RNG accepted input and output length");
8,192✔
736
         }
4,096✔
737

738
         rng.clear();
1✔
739

740
         return result;
2✔
741
      }
1✔
742

743
   public:
744
      std::vector<Test::Result> run() override {
1✔
745
         std::vector<Test::Result> results;
1✔
746
         results.push_back(auto_rng_tests());
2✔
747
         return results;
1✔
748
      }
×
749
};
750

751
BOTAN_REGISTER_TEST("rng", "auto_rng_unit", AutoSeeded_RNG_Tests);
752

753
#endif
754

755
#if defined(BOTAN_HAS_SYSTEM_RNG)
756

757
class System_RNG_Tests final : public Test {
×
758
   public:
759
      std::vector<Test::Result> run() override {
1✔
760
         Test::Result result("System_RNG");
1✔
761

762
         Botan::System_RNG rng;
1✔
763

764
         result.test_gte("Some non-empty name is returned", rng.name().size(), 1);
3✔
765

766
         result.confirm("System RNG always seeded", rng.is_seeded());
3✔
767
         rng.clear();  // clear is a noop for system rng
1✔
768
         result.confirm("System RNG always seeded", rng.is_seeded());
3✔
769

770
         rng.reseed(Botan::Entropy_Sources::global_sources(), 256, std::chrono::milliseconds(100));
1✔
771

772
         for(size_t i = 0; i != 128; ++i) {
129✔
773
            std::vector<uint8_t> out_buf(i);
128✔
774
            rng.randomize(out_buf.data(), out_buf.size());
128✔
775
            rng.add_entropy(out_buf.data(), out_buf.size());
128✔
776
         }
128✔
777

778
         if(Test::run_long_tests() && Test::run_memory_intensive_tests() && (sizeof(size_t) > 4)) {
1✔
779
            // Pass buffer with a size greater than 32bit
780
            const size_t size32BitsMax = std::numeric_limits<uint32_t>::max();
1✔
781
            const size_t checkSize = 1024;
1✔
782
            std::vector<uint8_t> large_buf(size32BitsMax + checkSize);
1✔
783
            std::memset(large_buf.data() + size32BitsMax, 0xFE, checkSize);
1✔
784

785
            rng.randomize(large_buf.data(), large_buf.size());
1✔
786

787
            std::vector<uint8_t> check_buf(checkSize, 0xFE);
1✔
788

789
            result.confirm("System RNG failed to write after 4GB boundry",
3✔
790
                           std::memcmp(large_buf.data() + size32BitsMax, check_buf.data(), checkSize) != 0);
1✔
791
         }
2✔
792

793
         return std::vector<Test::Result>{result};
2✔
794
      }
1✔
795
};
796

797
BOTAN_REGISTER_TEST("rng", "system_rng", System_RNG_Tests);
798

799
#endif
800

801
#if defined(BOTAN_HAS_PROCESSOR_RNG)
802

803
class Processor_RNG_Tests final : public Test {
×
804
   public:
805
      std::vector<Test::Result> run() override {
1✔
806
         Test::Result result("Processor_RNG");
1✔
807

808
         if(Botan::Processor_RNG::available()) {
1✔
809
            Botan::Processor_RNG rng;
1✔
810

811
            result.test_ne("Has a name", rng.name(), "");
2✔
812
            result.confirm("CPU RNG always seeded", rng.is_seeded());
2✔
813
            rng.clear();  // clear is a noop for rdrand
1✔
814
            result.confirm("CPU RNG always seeded", rng.is_seeded());
2✔
815

816
            size_t reseed_bits = rng.reseed(Botan::Entropy_Sources::global_sources(), 256, std::chrono::seconds(1));
1✔
817
            result.test_eq("CPU RNG cannot consume inputs", reseed_bits, size_t(0));
1✔
818

819
            /*
820
            Processor_RNG ignores add_entropy calls - confirm this by passing
821
            an invalid ptr/length field to add_entropy. If it examined its
822
            arguments, it would crash...
823
            */
824
            // NOLINTNEXTLINE(*-no-int-to-ptr)
825
            const uint8_t* invalid_ptr = reinterpret_cast<const uint8_t*>(static_cast<uintptr_t>(0xDEADC0DE));
1✔
826
            const size_t invalid_ptr_len = 64 * 1024;
1✔
827
            rng.add_entropy(invalid_ptr, invalid_ptr_len);
1✔
828

829
            for(size_t i = 0; i != 128; ++i) {
129✔
830
               std::vector<uint8_t> out_buf(i);
128✔
831
               rng.randomize(out_buf.data(), out_buf.size());
128✔
832
            }
128✔
833
         } else {
1✔
834
            result.test_throws("Processor_RNG throws if instruction not available", []() { Botan::Processor_RNG rng; });
×
835
         }
836

837
         return std::vector<Test::Result>{result};
3✔
838
      }
1✔
839
};
840

841
BOTAN_REGISTER_TEST("rng", "processor_rng", Processor_RNG_Tests);
842

843
#endif
844

845
}  // namespace
846

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

© 2025 Coveralls, Inc