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

randombit / botan / 19212638715

09 Nov 2025 06:26PM UTC coverage: 90.622%. Remained the same
19212638715

Pull #5153

github

web-flow
Merge cd1494490 into e27d33208
Pull Request #5153: Add std::array support to BigInt::serialize function

100612 of 111024 relevant lines covered (90.62%)

12705260.27 hits per line

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

94.25
/src/tests/test_bigint.cpp
1
/*
2
* (C) 2009,2015,2016 Jack Lloyd
3
* (C) 2024           Fabian Albert, René Meusel -  Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_NUMBERTHEORY)
11
   #include "test_rng.h"
12
   #include <botan/bigint.h>
13
   #include <botan/numthry.h>
14
   #include <botan/internal/barrett.h>
15
   #include <botan/internal/ct_utils.h>
16
   #include <botan/internal/divide.h>
17
   #include <botan/internal/fmt.h>
18
   #include <botan/internal/mod_inv.h>
19
   #include <botan/internal/parsing.h>
20
   #include <botan/internal/primality.h>
21
   #include <botan/internal/stl_util.h>
22
#endif
23

24
namespace Botan_Tests {
25

26
namespace {
27

28
#if defined(BOTAN_HAS_NUMBERTHEORY)
29

30
using Botan::BigInt;
31

32
class BigInt_Unit_Tests final : public Test {
×
33
   public:
34
      std::vector<Test::Result> run() override {
1✔
35
         std::vector<Test::Result> results;
1✔
36

37
         results.push_back(test_bigint_sizes());
2✔
38
         results.push_back(test_random_prime());
2✔
39
         results.push_back(test_encode());
2✔
40
         results.push_back(test_bigint_io());
2✔
41
         results.push_back(test_get_substring());
2✔
42

43
         return results;
1✔
44
      }
×
45

46
   private:
47
      static Test::Result test_bigint_sizes() {
1✔
48
         Test::Result result("BigInt size functions");
1✔
49

50
         for(size_t bit : {1, 8, 16, 31, 32, 64, 97, 128, 179, 192, 512, 521}) {
13✔
51
            BigInt a;
12✔
52

53
            a.set_bit(bit);
12✔
54

55
            // Test 2^n and 2^n-1
56
            for(size_t i = 0; i != 2; ++i) {
36✔
57
               const size_t exp_bits = bit + 1 - i;
24✔
58
               result.test_eq("BigInt::bits", a.bits(), exp_bits);
24✔
59
               result.test_eq(
24✔
60
                  "BigInt::bytes", a.bytes(), (exp_bits % 8 == 0) ? (exp_bits / 8) : (exp_bits + 8 - exp_bits % 8) / 8);
24✔
61

62
               if(bit == 1 && i == 1) {
24✔
63
                  result.test_is_eq("BigInt::to_u32bit zero", a.to_u32bit(), static_cast<uint32_t>(1));
2✔
64
               } else if(bit <= 31 || (bit == 32 && i == 1)) {
23✔
65
                  result.test_is_eq(
8✔
66
                     "BigInt::to_u32bit", a.to_u32bit(), static_cast<uint32_t>((uint64_t(1) << bit) - i));
16✔
67
               } else {
68
                  try {
15✔
69
                     a.to_u32bit();
15✔
70
                     result.test_failure("BigInt::to_u32bit roundtripped out of range value");
×
71
                  } catch(std::exception&) {
15✔
72
                     result.test_success("BigInt::to_u32bit rejected out of range");
15✔
73
                  }
15✔
74
               }
75

76
               a--;
24✔
77
            }
78
         }
12✔
79

80
         return result;
1✔
81
      }
×
82

83
      static Test::Result test_random_prime() {
1✔
84
         Test::Result result("BigInt prime generation");
1✔
85

86
         auto rng = Test::new_rng("random_prime");
1✔
87

88
         result.test_throws(
2✔
89
            "Invalid bit size", "random_prime: Can't make a prime of 0 bits", [&]() { Botan::random_prime(*rng, 0); });
2✔
90
         result.test_throws(
2✔
91
            "Invalid bit size", "random_prime: Can't make a prime of 1 bits", [&]() { Botan::random_prime(*rng, 1); });
2✔
92
         result.test_throws("Invalid arg", "random_prime Invalid value for equiv/modulo", [&]() {
2✔
93
            Botan::random_prime(*rng, 2, 1, 0, 2);
1✔
94
         });
×
95

96
         BigInt p = Botan::random_prime(*rng, 2);
1✔
97
         result.confirm("Only two 2-bit primes", p == 2 || p == 3);
4✔
98

99
         p = Botan::random_prime(*rng, 3);
1✔
100
         result.confirm("Only two 3-bit primes", p == 5 || p == 7);
3✔
101

102
         p = Botan::random_prime(*rng, 4);
1✔
103
         result.confirm("Only two 4-bit primes", p == 11 || p == 13);
3✔
104

105
         for(size_t bits = 5; bits <= 32; ++bits) {
29✔
106
            p = Botan::random_prime(*rng, bits);
28✔
107
            result.test_eq("Expected bit size", p.bits(), bits);
28✔
108
            result.test_eq("P is prime", Botan::is_prime(p, *rng), true);
56✔
109
         }
110

111
         const size_t safe_prime_bits = 65;
1✔
112
         const BigInt safe_prime = Botan::random_safe_prime(*rng, safe_prime_bits);
1✔
113
         result.test_eq("Safe prime size", safe_prime.bits(), safe_prime_bits);
1✔
114
         result.confirm("P is prime", Botan::is_prime(safe_prime, *rng));
2✔
115
         result.confirm("(P-1)/2 is prime", Botan::is_prime((safe_prime - 1) / 2, *rng));
2✔
116

117
         return result;
2✔
118
      }
2✔
119

120
      static Test::Result test_encode() {
1✔
121
         Test::Result result("BigInt encoding functions");
1✔
122

123
         const auto n1 = Botan::BigInt::from_u64(0xffff);
1✔
124
         const auto n2 = Botan::BigInt::from_u64(1023);
1✔
125

126
         const auto encoded_n1 = n1.serialize(256);
1✔
127
         const auto encoded_n2 = n2.serialize(256);
1✔
128
         const auto expected = Botan::concat(encoded_n1, encoded_n2);
1✔
129

130
         const auto encoded_n1_n2 = BigInt::encode_fixed_length_int_pair(n1, n2, 256);
1✔
131
         result.test_eq("encode_fixed_length_int_pair", encoded_n1_n2, expected);
2✔
132

133
         for(size_t i = 0; i < 256 - n1.bytes(); ++i) {
255✔
134
            if(encoded_n1[i] != 0) {
254✔
135
               result.test_failure("BigInt::serialize", "no zero byte");
×
136
            }
137
         }
138

139
         return result;
2✔
140
      }
4✔
141

142
      static Test::Result test_get_substring() {
1✔
143
         Test::Result result("BigInt get_substring");
1✔
144

145
         const size_t rbits = 1024;
1✔
146

147
         auto rng = Test::new_rng("get_substring");
1✔
148

149
         const Botan::BigInt r(*rng, rbits);
1✔
150

151
         for(size_t wlen = 1; wlen <= 32; ++wlen) {
33✔
152
            for(size_t offset = 0; offset != rbits + 64; ++offset) {
34,848✔
153
               const uint32_t val = r.get_substring(offset, wlen);
34,816✔
154

155
               Botan::BigInt t = r >> offset;
34,816✔
156
               t.mask_bits(wlen);
34,816✔
157

158
               const uint32_t cmp = t.to_u32bit();
34,816✔
159

160
               result.test_eq("Same value", size_t(val), size_t(cmp));
34,816✔
161
            }
34,816✔
162
         }
163

164
         return result;
2✔
165
      }
2✔
166

167
      static Test::Result test_bigint_io() {
1✔
168
         Test::Result result("BigInt IO operators");
1✔
169

170
         const std::map<std::string, Botan::BigInt> str_to_val = {{"-13", -Botan::BigInt(13)},
2✔
171
                                                                  {"0", Botan::BigInt(0)},
2✔
172
                                                                  {"0x13", Botan::BigInt(0x13)},
2✔
173
                                                                  {"1", Botan::BigInt(1)},
2✔
174
                                                                  {"4294967297", Botan::BigInt(2147483648) * 2 + 1}};
7✔
175

176
         for(const auto& vec : str_to_val) {
6✔
177
            Botan::BigInt n;
5✔
178
            std::istringstream iss;
5✔
179

180
            iss.str(vec.first);
5✔
181
            iss >> n;
5✔
182
            result.test_eq("input '" + vec.first + "'", n, vec.second);
15✔
183
         }
5✔
184

185
         auto check_bigint_formatting = [&](const Botan::BigInt& n,
6✔
186
                                            const std::string& dec,
187
                                            const std::string& hex,
188
                                            const std::string& neg_dec,
189
                                            const std::string& neg_hex) {
190
            std::ostringstream oss;
5✔
191
            oss << n;
5✔
192
            result.test_eq("output decimal", oss.str(), dec);
10✔
193

194
            oss.str("");
10✔
195
            oss << (-n);
5✔
196
            result.test_eq("output negative decimal", oss.str(), neg_dec);
10✔
197

198
            oss.str("");
10✔
199
            oss << std::hex << n;
5✔
200
            result.test_eq("output hex", oss.str(), hex);
10✔
201

202
            oss.str("");
10✔
203
            oss << std::hex << (-n);
5✔
204
            result.test_eq("output negative hex", oss.str(), neg_hex);
10✔
205
         };
5✔
206

207
         check_bigint_formatting(Botan::BigInt(33), "33", "0x21", "-33", "-0x21");
1✔
208
         check_bigint_formatting(Botan::BigInt::from_s32(-33), "-33", "-0x21", "33", "0x21");
1✔
209
         check_bigint_formatting(Botan::BigInt(255), "255", "0xFF", "-255", "-0xFF");
1✔
210
         check_bigint_formatting(Botan::BigInt(0), "0", "0x00", "0", "0x00");
1✔
211
         check_bigint_formatting(Botan::BigInt(5), "5", "0x05", "-5", "-0x05");
1✔
212

213
         result.test_throws("octal output not supported", [&]() {
2✔
214
            Botan::BigInt n(5);
1✔
215
            std::ostringstream oss;
1✔
216
            oss << std::oct << n;
1✔
217
         });
2✔
218

219
         return result;
1✔
220
      }
6✔
221
};
222

223
BOTAN_REGISTER_TEST("math", "bigint_unit", BigInt_Unit_Tests);
224

225
class BigInt_Cmp_Test final : public Text_Based_Test {
×
226
   public:
227
      BigInt_Cmp_Test() : Text_Based_Test("bn/cmp.vec", "X,Y,R") {}
2✔
228

229
      Test::Result run_one_test(const std::string& op, const VarMap& vars) override {
19✔
230
         Test::Result result("BigInt Comparison " + op);
19✔
231

232
         const BigInt x = vars.get_req_bn("X");
19✔
233
         const BigInt y = vars.get_req_bn("Y");
19✔
234
         const bool expected = vars.get_req_bool("R");
19✔
235

236
         if(op == "EQ") {
19✔
237
            result.confirm("Values equal", x == y, expected);
12✔
238
         } else if(op == "LT") {
13✔
239
            result.confirm("Values LT", x < y, expected);
12✔
240

241
            if(expected) {
6✔
242
               result.confirm("If LT then reverse is GT", y >= x);
4✔
243
            } else {
244
               result.confirm("If not LT then GTE", x >= y);
8✔
245
            }
246
         } else if(op == "LTE") {
7✔
247
            result.confirm("Values LTE", x <= y, expected);
14✔
248

249
            if(expected) {
7✔
250
               result.confirm("If LTE then either LT or EQ", x < y || x == y);
13✔
251
            } else {
252
               result.confirm("If not LTE then GT", x > y);
6✔
253
            }
254
         } else {
255
            throw Test_Error("Unknown BigInt comparison type " + op);
×
256
         }
257

258
         return result;
19✔
259
      }
19✔
260
};
261

262
BOTAN_REGISTER_TEST("math", "bn_cmp", BigInt_Cmp_Test);
263

264
class BigInt_Add_Test final : public Text_Based_Test {
×
265
   public:
266
      BigInt_Add_Test() : Text_Based_Test("bn/add.vec", "In1,In2,Output") {}
2✔
267

268
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
78✔
269
         Test::Result result("BigInt Addition");
78✔
270

271
         using Botan::BigInt;
78✔
272

273
         const BigInt a = vars.get_req_bn("In1");
78✔
274
         const BigInt b = vars.get_req_bn("In2");
78✔
275
         const BigInt c = vars.get_req_bn("Output");
78✔
276

277
         result.test_eq("a + b", a + b, c);
156✔
278
         result.test_eq("b + a", b + a, c);
156✔
279

280
         BigInt e = a;
78✔
281
         e += b;
78✔
282
         result.test_eq("a += b", e, c);
78✔
283

284
         e = b;
78✔
285
         e += a;
78✔
286
         result.test_eq("b += a", e, c);
78✔
287

288
         return result;
78✔
289
      }
78✔
290
};
291

292
BOTAN_REGISTER_TEST("math", "bn_add", BigInt_Add_Test);
293

294
class BigInt_Sub_Test final : public Text_Based_Test {
×
295
   public:
296
      BigInt_Sub_Test() : Text_Based_Test("bn/sub.vec", "In1,In2,Output") {}
2✔
297

298
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
77✔
299
         Test::Result result("BigInt Subtraction");
77✔
300

301
         const BigInt a = vars.get_req_bn("In1");
77✔
302
         const BigInt b = vars.get_req_bn("In2");
77✔
303
         const BigInt c = vars.get_req_bn("Output");
77✔
304

305
         result.test_eq("a - b", a - b, c);
154✔
306

307
         BigInt e = a;
77✔
308
         e -= b;
77✔
309
         result.test_eq("a -= b", e, c);
77✔
310

311
         return result;
77✔
312
      }
77✔
313
};
314

315
BOTAN_REGISTER_TEST("math", "bn_sub", BigInt_Sub_Test);
316

317
class BigInt_Mul_Test final : public Text_Based_Test {
×
318
   public:
319
      BigInt_Mul_Test() : Text_Based_Test("bn/mul.vec", "In1,In2,Output") {}
2✔
320

321
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
104✔
322
         Test::Result result("BigInt Multiply");
104✔
323

324
         const BigInt a = vars.get_req_bn("In1");
104✔
325
         const BigInt b = vars.get_req_bn("In2");
104✔
326
         const BigInt c = vars.get_req_bn("Output");
104✔
327

328
         result.test_eq("a * b", a * b, c);
208✔
329
         result.test_eq("b * a", b * a, c);
208✔
330

331
         BigInt e = a;
104✔
332
         e *= b;
104✔
333
         result.test_eq("a *= b", e, c);
104✔
334

335
         e = b;
104✔
336
         e *= a;
104✔
337
         result.test_eq("b *= a", e, c);
104✔
338

339
         return result;
104✔
340
      }
104✔
341
};
342

343
BOTAN_REGISTER_TEST("math", "bn_mul", BigInt_Mul_Test);
344

345
class BigInt_Sqr_Test final : public Text_Based_Test {
×
346
   public:
347
      BigInt_Sqr_Test() : Text_Based_Test("bn/sqr.vec", "Input,Output") {}
2✔
348

349
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
21✔
350
         Test::Result result("BigInt Square");
21✔
351

352
         const BigInt input = vars.get_req_bn("Input");
21✔
353
         const BigInt output = vars.get_req_bn("Output");
21✔
354

355
         result.test_eq("a * a", input * input, output);
42✔
356
         result.test_eq("sqr(a)", square(input), output);
42✔
357

358
         return result;
21✔
359
      }
21✔
360
};
361

362
BOTAN_REGISTER_TEST("math", "bn_sqr", BigInt_Sqr_Test);
363

364
class BigInt_Div_Test final : public Text_Based_Test {
×
365
   public:
366
      BigInt_Div_Test() : Text_Based_Test("bn/divide.vec", "In1,In2,Output") {}
2✔
367

368
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
786✔
369
         Test::Result result("BigInt Divide");
786✔
370

371
         const BigInt a = vars.get_req_bn("In1");
786✔
372
         const BigInt b = vars.get_req_bn("In2");
786✔
373
         const BigInt c = vars.get_req_bn("Output");
786✔
374

375
         result.test_eq("a / b", a / b, c);
1,572✔
376

377
         BigInt e = a;
786✔
378
         e /= b;
786✔
379
         result.test_eq("a /= b", e, c);
786✔
380

381
         if(b.sig_words() == 1 && b.is_positive()) {
786✔
382
            const Botan::word bw = b.word_at(0);
405✔
383
            result.test_eq("Low word correct", Botan::BigInt::from_word(bw), b);
810✔
384

385
            Botan::BigInt ct_q;
405✔
386
            Botan::word ct_r = 0;
405✔
387
            Botan::ct_divide_word(a, bw, ct_q, ct_r);
405✔
388
            result.test_eq("ct_divide_word q", ct_q, c);
405✔
389
            result.test_eq("ct_divide_word r", ct_q * b + ct_r, a);
1,215✔
390
         }
405✔
391

392
         Botan::BigInt ct_q;
786✔
393
         Botan::BigInt ct_r;
786✔
394
         Botan::ct_divide(a, b, ct_q, ct_r);
786✔
395
         result.test_eq("ct_divide q", ct_q, c);
786✔
396
         result.test_eq("ct_divide r", ct_q * b + ct_r, a);
1,572✔
397

398
         return result;
786✔
399
      }
786✔
400
};
401

402
BOTAN_REGISTER_TEST("math", "bn_div", BigInt_Div_Test);
403

404
class BigInt_DivPow2k_Test final : public Test {
×
405
   public:
406
      std::vector<Test::Result> run() override {
1✔
407
         Test::Result result("BigInt divide pow2k");
1✔
408

409
         for(size_t k = 2; k != 128; ++k) {
127✔
410
            testcase(k, 1, result);
126✔
411
            testcase(k, 2, result);
126✔
412
            testcase(k, 4, result);
126✔
413
         }
414

415
         for(size_t k = 4; k != 512; ++k) {
509✔
416
            const BigInt pow2k = BigInt::power_of_2(k);
508✔
417

418
            for(size_t y_bits = k / 2; y_bits <= (k + 2); ++y_bits) {
67,564✔
419
               const BigInt y(rng(), y_bits, false);
67,056✔
420
               if(y.is_zero()) {
134,112✔
421
                  continue;
×
422
               }
423

424
               testcase(k, y, result);
67,056✔
425
            }
67,056✔
426
         }
508✔
427

428
         return {result};
3✔
429
      }
2✔
430

431
   private:
432
      static void testcase(size_t k, const BigInt& y, Test::Result& result) {
67,434✔
433
         const BigInt ct_pow2k = ct_divide_pow2k(k, y);
67,434✔
434
         const BigInt vt_pow2k = vartime_divide_pow2k(k, y);
67,434✔
435
         const BigInt ref = BigInt::power_of_2(k) / y;
67,434✔
436

437
         result.test_eq("ct_divide_pow2k matches Knuth division", ct_pow2k, ref);
67,434✔
438
         result.test_eq("vartime_divide_pow2k matches Knuth division", vt_pow2k, ref);
67,434✔
439
      }
67,434✔
440
};
441

442
BOTAN_REGISTER_TEST("math", "bn_div_pow2k", BigInt_DivPow2k_Test);
443

444
class BigInt_Mod_Test final : public Text_Based_Test {
×
445
   public:
446
      BigInt_Mod_Test() : Text_Based_Test("bn/mod.vec", "In1,In2,Output") {}
2✔
447

448
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
80✔
449
         Test::Result result("BigInt Mod");
80✔
450

451
         const BigInt a = vars.get_req_bn("In1");
80✔
452
         const BigInt b = vars.get_req_bn("In2");
80✔
453
         const BigInt expected = vars.get_req_bn("Output");
80✔
454

455
         result.test_eq("a % b", a % b, expected);
160✔
456

457
         BigInt e = a;
80✔
458
         e %= b;
80✔
459
         result.test_eq("a %= b", e, expected);
80✔
460

461
         if(a.is_positive() && a < (b * b)) {
80✔
462
            auto mod_b_pub = Botan::Barrett_Reduction::for_public_modulus(b);
43✔
463
            result.test_eq("Barrett public", mod_b_pub.reduce(a), expected);
86✔
464

465
            auto mod_b_sec = Botan::Barrett_Reduction::for_secret_modulus(b);
43✔
466
            result.test_eq("Barrett secret", mod_b_sec.reduce(a), expected);
86✔
467
         }
86✔
468

469
         // if b fits into a Botan::word test %= operator for words
470
         if(b.sig_words() == 1) {
80✔
471
            const Botan::word b_word = b.word_at(0);
34✔
472

473
            e = a;
34✔
474
            e %= b_word;
34✔
475
            result.test_eq("a %= b (as word)", e, expected);
34✔
476

477
            result.test_eq("a % b (as word)", a % b_word, expected);
68✔
478

479
            Botan::BigInt ct_q;
34✔
480
            Botan::word ct_r = 0;
34✔
481
            Botan::ct_divide_word(a, b.word_at(0), ct_q, ct_r);
68✔
482
            result.test_eq("ct_divide_u8 r", ct_r, expected);
68✔
483
         }
34✔
484

485
         Botan::BigInt ct_q;
80✔
486
         Botan::BigInt ct_r;
80✔
487
         Botan::ct_divide(a, b, ct_q, ct_r);
80✔
488
         result.test_eq("ct_divide r", ct_r, expected);
80✔
489

490
         return result;
80✔
491
      }
80✔
492
};
493

494
BOTAN_REGISTER_TEST("math", "bn_mod", BigInt_Mod_Test);
495

496
class Barrett_Redc_Test final : public Test {
×
497
   public:
498
      std::vector<Test::Result> run() override {
1✔
499
         Test::Result result("Barrett reduction");
1✔
500

501
         for(size_t t = 0; t != 10000; ++t) {
10,001✔
502
            const auto mod = [&]() {
30,000✔
503
               if(t <= 16) {
10,000✔
504
                  return BigInt::from_u64(t) + 1;
34✔
505
               } else {
506
                  const size_t bits = (t / 4) + 2;
9,983✔
507

508
                  if(t % 4 == 0) {
9,983✔
509
                     return BigInt::power_of_2(bits);
2,495✔
510
                  } else if(t % 4 == 1) {
7,488✔
511
                     return BigInt::power_of_2(bits) - 1;
4,992✔
512
                  } else if(t % 4 == 2) {
4,992✔
513
                     return BigInt::power_of_2(bits) + 1;
4,992✔
514
                  } else {
515
                     Botan::BigInt b;
2,496✔
516
                     b.randomize(rng(), bits, true);
2,496✔
517
                     return b;
2,496✔
518
                  }
2,496✔
519
               }
520
            }();
10,000✔
521

522
            const size_t mod_bits = mod.bits();
10,000✔
523
            auto barrett = Botan::Barrett_Reduction::for_public_modulus(mod);
10,000✔
524

525
            for(size_t i = 0; i != 10; ++i) {
110,000✔
526
               const auto input = [&]() {
×
527
                  Botan::BigInt b;
100,000✔
528
                  b.randomize(rng(), 2 * mod_bits, false);
100,000✔
529
                  return b;
100,000✔
530
               }();
100,000✔
531

532
               const auto reduced_ref = input % mod;
100,000✔
533
               const auto reduced_barrett = barrett.reduce(input);
100,000✔
534

535
               result.test_eq("Barrett reduction matches variable time division", reduced_barrett, reduced_ref);
100,000✔
536
            }
100,000✔
537
         }
10,000✔
538

539
         return {result};
3✔
540
      }
2✔
541
};
542

543
BOTAN_REGISTER_TEST("math", "barrett_redc", Barrett_Redc_Test);
544

545
class BigInt_GCD_Test final : public Text_Based_Test {
×
546
   public:
547
      BigInt_GCD_Test() : Text_Based_Test("bn/gcd.vec", "X,Y,GCD") {}
2✔
548

549
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
182✔
550
         Test::Result result("BigInt GCD");
182✔
551

552
         const BigInt x = vars.get_req_bn("X");
182✔
553
         const BigInt y = vars.get_req_bn("Y");
182✔
554
         const BigInt expected = vars.get_req_bn("GCD");
182✔
555

556
         const BigInt g1 = Botan::gcd(x, y);
182✔
557
         result.test_eq("gcd", g1, expected);
182✔
558

559
         const BigInt g2 = Botan::gcd(y, x);
182✔
560
         result.test_eq("gcd", g2, expected);
182✔
561

562
         return result;
182✔
563
      }
182✔
564
};
565

566
BOTAN_REGISTER_TEST("math", "bn_gcd", BigInt_GCD_Test);
567

568
class BigInt_Jacobi_Test final : public Text_Based_Test {
×
569
   public:
570
      BigInt_Jacobi_Test() : Text_Based_Test("bn/jacobi.vec", "A,N,J") {}
2✔
571

572
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
698✔
573
         Test::Result result("BigInt Jacobi");
698✔
574

575
         const BigInt a = vars.get_req_bn("A");
698✔
576
         const BigInt n = vars.get_req_bn("N");
698✔
577
         const std::string expected = vars.get_req_str("J");
698✔
578

579
         const int32_t j = Botan::jacobi(a, n);
698✔
580

581
         if(j == 0) {
698✔
582
            result.test_eq("jacobi", expected, "0");
314✔
583
         } else if(j == -1) {
541✔
584
            result.test_eq("jacobi", expected, "-1");
614✔
585
         } else {
586
            result.test_eq("jacobi", expected, "1");
468✔
587
         }
588

589
         return result;
1,396✔
590
      }
698✔
591
};
592

593
BOTAN_REGISTER_TEST("math", "bn_jacobi", BigInt_Jacobi_Test);
594

595
class BigInt_Lshift_Test final : public Text_Based_Test {
×
596
   public:
597
      BigInt_Lshift_Test() : Text_Based_Test("bn/lshift.vec", "Value,Shift,Output") {}
2✔
598

599
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
51✔
600
         Test::Result result("BigInt Lshift");
51✔
601

602
         const BigInt value = vars.get_req_bn("Value");
51✔
603
         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
51✔
604
         const BigInt output = vars.get_req_bn("Output");
51✔
605

606
         result.test_eq("a << s", value << shift, output);
102✔
607

608
         BigInt e = value;
51✔
609
         e <<= shift;
51✔
610
         result.test_eq("a <<= s", e, output);
51✔
611

612
         return result;
51✔
613
      }
51✔
614
};
615

616
BOTAN_REGISTER_TEST("math", "bn_lshift", BigInt_Lshift_Test);
617

618
class BigInt_Rshift_Test final : public Text_Based_Test {
×
619
   public:
620
      BigInt_Rshift_Test() : Text_Based_Test("bn/rshift.vec", "Value,Shift,Output") {}
2✔
621

622
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
54✔
623
         Test::Result result("BigInt Rshift");
54✔
624

625
         const BigInt value = vars.get_req_bn("Value");
54✔
626
         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
54✔
627
         const BigInt output = vars.get_req_bn("Output");
54✔
628

629
         result.test_eq("a >> s", value >> shift, output);
108✔
630

631
         BigInt e = value;
54✔
632
         e >>= shift;
54✔
633
         result.test_eq("a >>= s", e, output);
54✔
634

635
         return result;
54✔
636
      }
54✔
637
};
638

639
BOTAN_REGISTER_TEST("math", "bn_rshift", BigInt_Rshift_Test);
640

641
Test::Result test_const_time_left_shift() {
1✔
642
   Test::Result result("BigInt const time shift");
1✔
643
   const size_t bits = Test::run_long_tests() ? 4096 : 2048;
1✔
644

645
   auto rng = Test::new_rng("const_time_left_shift");
1✔
646

647
   result.start_timer();
1✔
648

649
   Botan::BigInt a = Botan::BigInt::with_capacity(bits / sizeof(Botan::word));
1✔
650
   for(size_t i = 0; i < bits; ++i) {
4,097✔
651
      if(rng->next_byte() % 2 == 1) {
4,096✔
652
         a.set_bit(i);
4,096✔
653
      }
654
   }
655

656
   for(size_t i = 0; i < bits; ++i) {
4,097✔
657
      auto ct = a;
4,096✔
658
      auto chk = a;
4,096✔
659
      Botan::CT::poison(ct);
4,096✔
660
      ct.ct_shift_left(i);
4,096✔
661
      Botan::CT::unpoison(ct);
4,096✔
662
      chk <<= i;
4,096✔
663
      result.test_eq(Botan::fmt("ct << {}", i), ct, chk);
4,096✔
664
   }
4,096✔
665

666
   result.end_timer();
1✔
667

668
   return result;
2✔
669
}
2✔
670

671
class BigInt_Powmod_Test final : public Text_Based_Test {
×
672
   public:
673
      BigInt_Powmod_Test() : Text_Based_Test("bn/powmod.vec", "Base,Exponent,Modulus,Output") {}
2✔
674

675
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
47✔
676
         Test::Result result("BigInt Powmod");
47✔
677

678
         const BigInt base = vars.get_req_bn("Base");
47✔
679
         const BigInt exponent = vars.get_req_bn("Exponent");
47✔
680
         const BigInt modulus = vars.get_req_bn("Modulus");
47✔
681
         const BigInt expected = vars.get_req_bn("Output");
47✔
682

683
         result.test_eq("power_mod", Botan::power_mod(base, exponent, modulus), expected);
94✔
684
         return result;
47✔
685
      }
47✔
686
};
687

688
BOTAN_REGISTER_TEST("math", "bn_powmod", BigInt_Powmod_Test);
689

690
class BigInt_IsPrime_Test final : public Text_Based_Test {
×
691
   public:
692
      BigInt_IsPrime_Test() : Text_Based_Test("bn/isprime.vec", "X") {}
2✔
693

694
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
132✔
695
         if(header != "Prime" && header != "NonPrime") {
132✔
696
            throw Test_Error("Bad header for prime test " + header);
×
697
         }
698

699
         const BigInt value = vars.get_req_bn("X");
132✔
700
         const bool is_prime = (header == "Prime");
132✔
701

702
         Test::Result result("BigInt Test " + header);
132✔
703
         result.test_eq("is_prime", Botan::is_prime(value, this->rng()), is_prime);
132✔
704

705
         return result;
132✔
706
      }
132✔
707
};
708

709
BOTAN_REGISTER_TEST("math", "bn_isprime", BigInt_IsPrime_Test);
710

711
class BigInt_IsSquare_Test final : public Text_Based_Test {
×
712
   public:
713
      BigInt_IsSquare_Test() : Text_Based_Test("bn/perfect_square.vec", "X,R") {}
2✔
714

715
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
7✔
716
         const BigInt value = vars.get_req_bn("X");
7✔
717
         const BigInt expected = vars.get_req_bn("R");
7✔
718
         const BigInt computed = Botan::is_perfect_square(value);
7✔
719

720
         Test::Result result("BigInt IsSquare");
7✔
721
         result.test_eq("is_perfect_square", computed, expected);
7✔
722
         return result;
7✔
723
      }
7✔
724
};
725

726
BOTAN_REGISTER_TEST("math", "bn_issquare", BigInt_IsSquare_Test);
727

728
class BigInt_Sqrt_Modulo_Prime_Test final : public Text_Based_Test {
×
729
   public:
730
      BigInt_Sqrt_Modulo_Prime_Test() : Text_Based_Test("bn/sqrt_modulo_prime.vec", "Input,Modulus,Output") {}
2✔
731

732
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
17✔
733
         Test::Result result("BigInt Sqrt Modulo Prime");
17✔
734

735
         const Botan::BigInt a = vars.get_req_bn("Input");
17✔
736
         const Botan::BigInt p = vars.get_req_bn("Modulus");
17✔
737
         const Botan::BigInt exp = vars.get_req_bn("Output");
17✔
738

739
         const Botan::BigInt a_sqrt = Botan::sqrt_modulo_prime(a, p);
17✔
740

741
         result.test_eq("sqrt_modulo_prime", a_sqrt, exp);
17✔
742

743
         if(a_sqrt > 1) {
17✔
744
            const Botan::BigInt a_sqrt2 = (a_sqrt * a_sqrt) % p;
8✔
745
            result.test_eq("square correct", a_sqrt2, a);
8✔
746
         }
8✔
747

748
         return result;
17✔
749
      }
17✔
750
};
751

752
BOTAN_REGISTER_TEST("math", "bn_sqrt_modulo_prime", BigInt_Sqrt_Modulo_Prime_Test);
753

754
class BigInt_InvMod_Test final : public Text_Based_Test {
×
755
   public:
756
      BigInt_InvMod_Test() : Text_Based_Test("bn/invmod.vec", "Input,Modulus,Output") {}
2✔
757

758
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
138✔
759
         Test::Result result("BigInt InvMod");
138✔
760

761
         const Botan::BigInt a = vars.get_req_bn("Input");
138✔
762
         const Botan::BigInt mod = vars.get_req_bn("Modulus");
138✔
763
         const Botan::BigInt expected = vars.get_req_bn("Output");
138✔
764

765
         result.test_eq("inverse_mod", Botan::inverse_mod(a, mod), expected);
276✔
766

767
         if(a < mod && a > 0 && a < mod) {
398✔
768
            auto g = Botan::inverse_mod_general(a, mod);
128✔
769
            if(g.has_value()) {
128✔
770
               result.test_eq("inverse_mod_general", g.value(), expected);
87✔
771
               result.test_eq("inverse works", ((g.value() * a) % mod), BigInt::one());
261✔
772
            } else {
773
               result.confirm("inverse_mod_general", expected.is_zero());
82✔
774
            }
775

776
            if(Botan::is_prime(mod, rng()) && mod != 2) {
136✔
777
               BOTAN_ASSERT_NOMSG(expected > 0);
7✔
778
               result.test_eq("inverse_mod_secret_prime", Botan::inverse_mod_secret_prime(a, mod), expected);
14✔
779
               result.test_eq("inverse_mod_public_prime", Botan::inverse_mod_public_prime(a, mod), expected);
14✔
780
            }
781
         }
128✔
782

783
         return result;
138✔
784
      }
138✔
785
};
786

787
BOTAN_REGISTER_TEST("math", "bn_invmod", BigInt_InvMod_Test);
788

789
class BigInt_Rand_Test final : public Text_Based_Test {
×
790
   public:
791
      BigInt_Rand_Test() : Text_Based_Test("bn/random.vec", "Seed,Min,Max,Output") {}
2✔
792

793
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
4✔
794
         Test::Result result("BigInt Random");
4✔
795

796
         const std::vector<uint8_t> seed = vars.get_req_bin("Seed");
4✔
797
         const Botan::BigInt min = vars.get_req_bn("Min");
4✔
798
         const Botan::BigInt max = vars.get_req_bn("Max");
4✔
799
         const Botan::BigInt expected = vars.get_req_bn("Output");
4✔
800

801
         Fixed_Output_RNG rng(seed);
4✔
802
         Botan::BigInt generated = BigInt::random_integer(rng, min, max);
4✔
803

804
         result.test_eq("random_integer KAT", generated, expected);
4✔
805

806
         return result;
8✔
807
      }
8✔
808
};
809

810
BOTAN_REGISTER_TEST("math", "bn_rand", BigInt_Rand_Test);
811

812
class Lucas_Primality_Test final : public Test {
×
813
   public:
814
      std::vector<Test::Result> run() override {
1✔
815
         const uint32_t lucas_max = (Test::run_long_tests() ? 100000 : 10000) + 1;
1✔
816

817
         // OEIS A217120
818
         std::set<uint32_t> lucas_pp{
1✔
819
            323,   377,   1159,  1829,  3827,  5459,  5777,  9071,  9179,  10877, 11419, 11663, 13919, 14839, 16109,
820
            16211, 18407, 18971, 19043, 22499, 23407, 24569, 25199, 25877, 26069, 27323, 32759, 34943, 35207, 39059,
821
            39203, 39689, 40309, 44099, 46979, 47879, 50183, 51983, 53663, 56279, 58519, 60377, 63881, 69509, 72389,
822
            73919, 75077, 77219, 79547, 79799, 82983, 84419, 86063, 90287, 94667, 97019, 97439,
823
         };
1✔
824

825
         Test::Result result("Lucas primality test");
1✔
826

827
         for(uint32_t i = 3; i <= lucas_max; i += 2) {
50,001✔
828
            auto mod_i = Botan::Barrett_Reduction::for_public_modulus(i);
50,000✔
829
            const bool passes_lucas = Botan::is_lucas_probable_prime(i, mod_i);
50,000✔
830
            const bool is_prime = Botan::is_prime(i, this->rng());
50,000✔
831

832
            const bool is_lucas_pp = (is_prime == false && passes_lucas == true);
50,000✔
833

834
            if(is_lucas_pp) {
50,000✔
835
               result.confirm("Lucas pseudoprime is in list", lucas_pp.count(i) == 1);
171✔
836
            } else {
837
               result.confirm("Lucas non-pseudoprime is not in list", !lucas_pp.contains(i));
149,829✔
838
            }
839
         }
50,000✔
840

841
         return {result};
2✔
842
      }
2✔
843
};
844

845
BOTAN_REGISTER_TEST("math", "bn_lucas", Lucas_Primality_Test);
846

847
class RSA_Compute_Exp_Test : public Test {
×
848
   public:
849
      std::vector<Test::Result> run() override {
1✔
850
         const size_t iter = 4000;
1✔
851

852
         Test::Result result("RSA compute exponent");
1✔
853

854
         const auto e = Botan::BigInt::from_u64(65537);
1✔
855

856
         /*
857
         * Rather than create a fresh p/q for each iteration this test creates
858
         * a pool of primes then selects 2 at random as p/q
859
         */
860

861
         const auto random_primes = [&]() {
3✔
862
            std::vector<Botan::BigInt> rp;
1✔
863
            for(size_t i = 0; i != iter / 10; ++i) {
401✔
864
               size_t bits = (128 + (i % 1024)) % 4096;
400✔
865
               auto p = Botan::random_prime(rng(), bits);
400✔
866
               if(gcd(p - 1, e) == 1) {
800✔
867
                  rp.push_back(p);
400✔
868
               }
869
            }
400✔
870
            return rp;
1✔
871
         }();
1✔
872

873
         for(size_t i = 0; i != iter; ++i) {
4,001✔
874
            const size_t p_idx = random_index(rng(), random_primes.size());
4,000✔
875
            const size_t q_idx = random_index(rng(), random_primes.size());
4,000✔
876

877
            if(p_idx == q_idx) {
4,000✔
878
               continue;
8✔
879
            }
880

881
            const auto& p = random_primes[p_idx];
3,992✔
882
            const auto& q = random_primes[q_idx];
3,992✔
883

884
            auto phi_n = lcm(p - 1, q - 1);
3,992✔
885

886
            auto d = Botan::compute_rsa_secret_exponent(e, phi_n, p, q);
3,992✔
887

888
            auto ed_mod_phi_n = (e * d) % phi_n;
3,992✔
889

890
            result.test_eq("compute_rsa_secret_exponent returned inverse", ed_mod_phi_n, Botan::BigInt::one());
7,984✔
891
         }
3,992✔
892

893
         return {result};
3✔
894
      }
2✔
895
};
896

897
BOTAN_REGISTER_TEST("math", "rsa_compute_d", RSA_Compute_Exp_Test);
898

899
class DSA_ParamGen_Test final : public Text_Based_Test {
×
900
   public:
901
      DSA_ParamGen_Test() : Text_Based_Test("bn/dsa_gen.vec", "P,Q,Counter,Seed") {}
2✔
902

903
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
20✔
904
         const std::vector<uint8_t> seed = vars.get_req_bin("Seed");
20✔
905
         const size_t offset = vars.get_req_sz("Counter");
20✔
906

907
         const Botan::BigInt exp_P = vars.get_req_bn("P");
20✔
908
         const Botan::BigInt exp_Q = vars.get_req_bn("Q");
20✔
909

910
         const std::vector<std::string> header_parts = Botan::split_on(header, ',');
20✔
911

912
         if(header_parts.size() != 2) {
20✔
913
            throw Test_Error("Unexpected header '" + header + "' in DSA param gen test");
×
914
         }
915

916
         const size_t p_bits = Botan::to_u32bit(header_parts[1]);
20✔
917
         const size_t q_bits = Botan::to_u32bit(header_parts[0]);
20✔
918

919
         Test::Result result("DSA Parameter Generation");
20✔
920

921
         try {
20✔
922
            Botan::BigInt gen_P;
20✔
923
            Botan::BigInt gen_Q;
20✔
924
            if(Botan::generate_dsa_primes(this->rng(), gen_P, gen_Q, p_bits, q_bits, seed, offset)) {
20✔
925
               result.test_eq("P", gen_P, exp_P);
20✔
926
               result.test_eq("Q", gen_Q, exp_Q);
40✔
927
            } else {
928
               result.test_failure("Seed did not generate a DSA parameter");
×
929
            }
930
         } catch(Botan::Lookup_Error&) {}
20✔
931

932
         return result;
40✔
933
      }
40✔
934
};
935

936
BOTAN_REGISTER_TEST("math", "dsa_param", DSA_ParamGen_Test);
937

938
std::vector<Test::Result> test_bigint_serialization() {
1✔
939
   auto rng = Test::new_rng("test_bigint_serialization");
1✔
940

941
   return {
1✔
942
      CHECK("BigInt binary serialization",
943
            [](Test::Result& res) {
1✔
944
               Botan::BigInt a(0x1234567890ABCDEF);
1✔
945
               auto enc = a.serialize();
1✔
946
               res.test_eq("BigInt::serialize", enc, Botan::hex_decode("1234567890ABCDEF"));
2✔
947

948
               auto enc10 = a.serialize(10);
1✔
949
               res.test_eq("BigInt::serialize", enc10, Botan::hex_decode("00001234567890ABCDEF"));
2✔
950

951
               res.test_throws("BigInt::serialize rejects short output", [&]() { a.serialize(7); });
4✔
952
            }),
2✔
953

954
      CHECK("BigInt truncated/padded binary serialization",
955
            [&](Test::Result& res) {
1✔
956
               Botan::BigInt a(0xFEDCBA9876543210);
1✔
957

958
               std::vector<uint8_t> enc1(a.bytes() - 1);
1✔
959
               a.binary_encode(enc1.data(), enc1.size());
1✔
960
               res.test_eq("BigInt::binary_encode", enc1, Botan::hex_decode("DCBA9876543210"));
2✔
961

962
               std::vector<uint8_t> enc2(a.bytes() - 3);
1✔
963
               a.binary_encode(enc2.data(), enc2.size());
1✔
964
               res.test_eq("BigInt::binary_encode", enc2, Botan::hex_decode("9876543210"));
2✔
965

966
               std::vector<uint8_t> enc3(a.bytes() + 1);
1✔
967
               a.binary_encode(enc3.data(), enc3.size());
1✔
968
               res.test_eq("BigInt::binary_encode", enc3, Botan::hex_decode("00FEDCBA9876543210"));
2✔
969

970
               // make sure that the padding is actually written
971
               std::vector<uint8_t> enc4(a.bytes() + 3);
1✔
972
               rng->randomize(enc4);
1✔
973
               a.binary_encode(enc4.data(), enc4.size());
1✔
974
               res.test_eq("BigInt::binary_encode", enc4, Botan::hex_decode("000000FEDCBA9876543210"));
2✔
975

976
               Botan::BigInt b(Botan::hex_decode("FEDCBA9876543210BAADC0FFEE"));
1✔
977

978
               std::vector<uint8_t> enc5(b.bytes() + 12);
1✔
979
               rng->randomize(enc5);
1✔
980
               b.binary_encode(enc5.data(), enc5.size());
1✔
981
               res.test_eq("BigInt::binary_encode",
2✔
982
                           enc5,
983
                           Botan::hex_decode("000000000000000000000000FEDCBA9876543210BAADC0FFEE"));
2✔
984
            }),
5✔
985
   };
3✔
986
}
2✔
987

988
BOTAN_REGISTER_TEST_FN("math", "bignum_auxiliary", test_const_time_left_shift, test_bigint_serialization);
989

990
#endif
991

992
}  // namespace
993

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

© 2026 Coveralls, Inc