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

randombit / botan / 25650639339

10 May 2026 07:03PM UTC coverage: 89.326% (-0.002%) from 89.328%
25650639339

push

github

web-flow
Merge pull request #5592 from randombit/jack/bn-hardening

Various BigInt/mp related hardenings and bug fixes

107853 of 120741 relevant lines covered (89.33%)

11294230.95 hits per line

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

95.02
/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/exceptn.h>
14
   #include <botan/numthry.h>
15
   #include <botan/internal/barrett.h>
16
   #include <botan/internal/concat_util.h>
17
   #include <botan/internal/ct_utils.h>
18
   #include <botan/internal/divide.h>
19
   #include <botan/internal/fmt.h>
20
   #include <botan/internal/mod_inv.h>
21
   #include <botan/internal/parsing.h>
22
   #include <botan/internal/primality.h>
23
   #include <set>
24
#endif
25

26
namespace Botan_Tests {
27

28
namespace {
29

30
#if defined(BOTAN_HAS_NUMBERTHEORY)
31

32
using Botan::BigInt;
33

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

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

45
         return results;
1✔
46
      }
×
47

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

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

55
            a.set_bit(bit);
12✔
56

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

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

78
               a--;
24✔
79
            }
80
         }
12✔
81

82
         return result;
1✔
83
      }
×
84

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

88
         auto rng = Test::new_rng("random_prime");
1✔
89

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

98
         BigInt p = Botan::random_prime(*rng, 2);
1✔
99
         result.test_is_true("Only two 2-bit primes", p == 2 || p == 3);
2✔
100

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

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

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

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

119
         for(const size_t modulo : {3, 5, 7, 11}) {
5✔
120
            const size_t equiv = (modulo - 1) / 2;
4✔
121
            const BigInt q = Botan::random_prime(*rng, 64, BigInt::zero(), equiv, modulo);
4✔
122
            result.test_is_true(
8✔
123
               "random_prime returned a prime for equiv=" + std::to_string(equiv) + " modulo=" + std::to_string(modulo),
16✔
124
               Botan::is_prime(q, *rng));
4✔
125
            result.test_bn_eq("congruence holds", q % BigInt::from_word(modulo), BigInt::from_word(equiv));
4✔
126
         }
4✔
127

128
         return result;
2✔
129
      }
2✔
130

131
      static Test::Result test_encode() {
1✔
132
         Test::Result result("BigInt encoding functions");
1✔
133

134
         const auto n1 = Botan::BigInt::from_u64(0xffff);
1✔
135
         const auto n2 = Botan::BigInt::from_u64(1023);
1✔
136

137
         const auto encoded_n1 = n1.serialize(256);
1✔
138
         const auto encoded_n2 = n2.serialize(256);
1✔
139
         const auto expected = Botan::concat(encoded_n1, encoded_n2);
1✔
140

141
         const auto encoded_n1_n2 = BigInt::encode_fixed_length_int_pair(n1, n2, 256);
1✔
142
         result.test_bin_eq("encode_fixed_length_int_pair", encoded_n1_n2, expected);
1✔
143

144
         for(size_t i = 0; i < 256 - n1.bytes(); ++i) {
255✔
145
            if(encoded_n1[i] != 0) {
254✔
146
               result.test_failure("BigInt::serialize", "no zero byte");
×
147
            }
148
         }
149

150
         return result;
2✔
151
      }
1✔
152

153
      static Test::Result test_get_substring() {
1✔
154
         Test::Result result("BigInt get_substring");
1✔
155

156
         const size_t rbits = 1024;
1✔
157

158
         auto rng = Test::new_rng("get_substring");
1✔
159

160
         const Botan::BigInt r(*rng, rbits);
1✔
161

162
         for(size_t wlen = 1; wlen <= 32; ++wlen) {
33✔
163
            for(size_t offset = 0; offset != rbits + 64; ++offset) {
34,848✔
164
               const uint32_t val = r.get_substring(offset, wlen);
34,816✔
165

166
               Botan::BigInt t = r >> offset;
34,816✔
167
               t.mask_bits(wlen);
34,816✔
168

169
               const uint32_t cmp = t.to_u32bit();
34,816✔
170

171
               result.test_sz_eq("Same value", size_t(val), size_t(cmp));
34,816✔
172
            }
34,816✔
173
         }
174

175
         return result;
2✔
176
      }
2✔
177

178
      static Test::Result test_bigint_io() {
1✔
179
         Test::Result result("BigInt IO operators");
1✔
180

181
         const std::map<std::string, Botan::BigInt> str_to_val = {{"-13", -Botan::BigInt(13)},
2✔
182
                                                                  {"0", Botan::BigInt(0)},
2✔
183
                                                                  {"0x13", Botan::BigInt(0x13)},
2✔
184
                                                                  {"1", Botan::BigInt(1)},
2✔
185
                                                                  {"4294967297", Botan::BigInt(2147483648) * 2 + 1}};
7✔
186

187
         for(const auto& vec : str_to_val) {
6✔
188
            Botan::BigInt n;
5✔
189
            std::istringstream iss;
5✔
190

191
            iss.str(vec.first);
5✔
192
            iss >> n;
5✔
193
            result.test_bn_eq("input '" + vec.first + "'", n, vec.second);
15✔
194
         }
5✔
195

196
         auto check_bigint_formatting = [&](const Botan::BigInt& n,
6✔
197
                                            const std::string& dec,
198
                                            const std::string& hex,
199
                                            const std::string& neg_dec,
200
                                            const std::string& neg_hex) {
201
            std::ostringstream oss;
5✔
202
            oss << n;
5✔
203
            result.test_str_eq("output decimal", oss.str(), dec);
5✔
204

205
            oss.str("");
10✔
206
            oss << (-n);
5✔
207
            result.test_str_eq("output negative decimal", oss.str(), neg_dec);
5✔
208

209
            oss.str("");
10✔
210
            oss << std::hex << n;
5✔
211
            result.test_str_eq("output hex", oss.str(), hex);
5✔
212

213
            oss.str("");
10✔
214
            oss << std::hex << (-n);
5✔
215
            result.test_str_eq("output negative hex", oss.str(), neg_hex);
5✔
216
         };
5✔
217

218
         check_bigint_formatting(Botan::BigInt(33), "33", "0x21", "-33", "-0x21");
1✔
219
         check_bigint_formatting(Botan::BigInt::from_s32(-33), "-33", "-0x21", "33", "0x21");
1✔
220
         check_bigint_formatting(Botan::BigInt(255), "255", "0xFF", "-255", "-0xFF");
1✔
221
         check_bigint_formatting(Botan::BigInt(0), "0", "0x00", "0", "0x00");
1✔
222
         check_bigint_formatting(Botan::BigInt(5), "5", "0x05", "-5", "-0x05");
1✔
223

224
         result.test_throws("octal output not supported", [&]() {
1✔
225
            const Botan::BigInt n(5);
1✔
226
            std::ostringstream oss;
1✔
227
            oss << std::oct << n;
1✔
228
         });
2✔
229

230
         return result;
1✔
231
      }
6✔
232
};
233

234
BOTAN_REGISTER_TEST("math", "bigint_unit", BigInt_Unit_Tests);
235

236
class BigInt_Cmp_Test final : public Text_Based_Test {
×
237
   public:
238
      BigInt_Cmp_Test() : Text_Based_Test("bn/cmp.vec", "X,Y,R") {}
2✔
239

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

243
         const BigInt x = vars.get_req_bn("X");
19✔
244
         const BigInt y = vars.get_req_bn("Y");
19✔
245
         const bool expected = vars.get_req_bool("R");
19✔
246

247
         if(op == "EQ") {
19✔
248
            result.test_bool_eq("Values equal", x == y, expected);
6✔
249
         } else if(op == "LT") {
13✔
250
            result.test_bool_eq("Values LT", x < y, expected);
6✔
251

252
            if(expected) {
6✔
253
               result.test_is_true("If LT then reverse is GT", y >= x);
2✔
254
            } else {
255
               result.test_is_true("If not LT then GTE", x >= y);
4✔
256
            }
257
         } else if(op == "LTE") {
7✔
258
            result.test_bool_eq("Values LTE", x <= y, expected);
7✔
259

260
            if(expected) {
7✔
261
               result.test_is_true("If LTE then either LT or EQ", x < y || x == y);
9✔
262
            } else {
263
               result.test_is_true("If not LTE then GT", x > y);
3✔
264
            }
265
         } else {
266
            throw Test_Error("Unknown BigInt comparison type " + op);
×
267
         }
268

269
         return result;
19✔
270
      }
19✔
271
};
272

273
BOTAN_REGISTER_TEST("math", "bn_cmp", BigInt_Cmp_Test);
274

275
class BigInt_Add_Test final : public Text_Based_Test {
×
276
   public:
277
      BigInt_Add_Test() : Text_Based_Test("bn/add.vec", "In1,In2,Output") {}
2✔
278

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

282
         using Botan::BigInt;
78✔
283

284
         const BigInt a = vars.get_req_bn("In1");
78✔
285
         const BigInt b = vars.get_req_bn("In2");
78✔
286
         const BigInt c = vars.get_req_bn("Output");
78✔
287

288
         result.test_bn_eq("a + b", a + b, c);
78✔
289
         result.test_bn_eq("b + a", b + a, c);
78✔
290

291
         BigInt e = a;
78✔
292
         e += b;
78✔
293
         result.test_bn_eq("a += b", e, c);
78✔
294

295
         e = b;
78✔
296
         e += a;
78✔
297
         result.test_bn_eq("b += a", e, c);
78✔
298

299
         return result;
78✔
300
      }
78✔
301
};
302

303
BOTAN_REGISTER_TEST("math", "bn_add", BigInt_Add_Test);
304

305
class BigInt_Sub_Test final : public Text_Based_Test {
×
306
   public:
307
      BigInt_Sub_Test() : Text_Based_Test("bn/sub.vec", "In1,In2,Output") {}
2✔
308

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

312
         const BigInt a = vars.get_req_bn("In1");
77✔
313
         const BigInt b = vars.get_req_bn("In2");
77✔
314
         const BigInt c = vars.get_req_bn("Output");
77✔
315

316
         result.test_bn_eq("a - b", a - b, c);
77✔
317

318
         BigInt e = a;
77✔
319
         e -= b;
77✔
320
         result.test_bn_eq("a -= b", e, c);
77✔
321

322
         return result;
77✔
323
      }
77✔
324
};
325

326
BOTAN_REGISTER_TEST("math", "bn_sub", BigInt_Sub_Test);
327

328
class BigInt_Mul_Test final : public Text_Based_Test {
×
329
   public:
330
      BigInt_Mul_Test() : Text_Based_Test("bn/mul.vec", "In1,In2,Output") {}
2✔
331

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

335
         const BigInt a = vars.get_req_bn("In1");
104✔
336
         const BigInt b = vars.get_req_bn("In2");
104✔
337
         const BigInt c = vars.get_req_bn("Output");
104✔
338

339
         result.test_bn_eq("a * b", a * b, c);
104✔
340
         result.test_bn_eq("b * a", b * a, c);
104✔
341

342
         BigInt e = a;
104✔
343
         e *= b;
104✔
344
         result.test_bn_eq("a *= b", e, c);
104✔
345

346
         e = b;
104✔
347
         e *= a;
104✔
348
         result.test_bn_eq("b *= a", e, c);
104✔
349

350
         return result;
104✔
351
      }
104✔
352
};
353

354
BOTAN_REGISTER_TEST("math", "bn_mul", BigInt_Mul_Test);
355

356
class BigInt_Sqr_Test final : public Text_Based_Test {
×
357
   public:
358
      BigInt_Sqr_Test() : Text_Based_Test("bn/sqr.vec", "Input,Output") {}
2✔
359

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

363
         const BigInt input = vars.get_req_bn("Input");
21✔
364
         const BigInt output = vars.get_req_bn("Output");
21✔
365

366
         result.test_bn_eq("a * a", input * input, output);
21✔
367
         result.test_bn_eq("sqr(a)", square(input), output);
21✔
368

369
         return result;
21✔
370
      }
21✔
371
};
372

373
BOTAN_REGISTER_TEST("math", "bn_sqr", BigInt_Sqr_Test);
374

375
class BigInt_Div_Test final : public Text_Based_Test {
×
376
   public:
377
      BigInt_Div_Test() : Text_Based_Test("bn/divide.vec", "In1,In2,Output") {}
2✔
378

379
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
792✔
380
         Test::Result result("BigInt Divide");
792✔
381

382
         const BigInt a = vars.get_req_bn("In1");
792✔
383
         const BigInt b = vars.get_req_bn("In2");
792✔
384
         const BigInt c = vars.get_req_bn("Output");
792✔
385

386
         result.test_bn_eq("a / b", a / b, c);
792✔
387

388
         BigInt e = a;
792✔
389
         e /= b;
792✔
390
         result.test_bn_eq("a /= b", e, c);
792✔
391

392
         if(b.sig_words() == 1 && b.signum() >= 0) {
792✔
393
            const Botan::word bw = b.word_at(0);
405✔
394
            result.test_bn_eq("Low word correct", Botan::BigInt::from_word(bw), b);
405✔
395

396
            Botan::BigInt ct_q;
405✔
397
            Botan::word ct_r = 0;
405✔
398
            Botan::ct_divide_word(a, bw, ct_q, ct_r);
405✔
399
            result.test_bn_eq("ct_divide_word q", ct_q, c);
405✔
400
            result.test_bn_eq("ct_divide_word r", ct_q * b + ct_r, a);
810✔
401
         }
405✔
402

403
         Botan::BigInt ct_q;
792✔
404
         Botan::BigInt ct_r;
792✔
405
         Botan::ct_divide(a, b, ct_q, ct_r);
792✔
406
         result.test_bn_eq("ct_divide q", ct_q, c);
792✔
407
         result.test_bn_eq("ct_divide r", ct_q * b + ct_r, a);
792✔
408

409
         return result;
792✔
410
      }
792✔
411
};
412

413
BOTAN_REGISTER_TEST("math", "bn_div", BigInt_Div_Test);
414

415
class BigInt_DivPow2k_Test final : public Test {
1✔
416
   public:
417
      std::vector<Test::Result> run() override {
1✔
418
         Test::Result result("BigInt divide pow2k");
1✔
419

420
         for(size_t k = 2; k != 128; ++k) {
127✔
421
            testcase(k, 1, result);
126✔
422
            testcase(k, 2, result);
126✔
423
            testcase(k, 4, result);
126✔
424
         }
425

426
         for(size_t k = 4; k != 512; ++k) {
509✔
427
            const BigInt pow2k = BigInt::power_of_2(k);
508✔
428

429
            for(size_t y_bits = k / 2; y_bits <= (k + 2); ++y_bits) {
67,564✔
430
               const BigInt y(rng(), y_bits, false);
67,056✔
431
               if(y.is_zero()) {
134,112✔
432
                  continue;
2✔
433
               }
434

435
               testcase(k, y, result);
67,054✔
436
            }
67,056✔
437
         }
508✔
438

439
         return {result};
3✔
440
      }
2✔
441

442
   private:
443
      static void testcase(size_t k, const BigInt& y, Test::Result& result) {
67,432✔
444
         const BigInt ct_pow2k = ct_divide_pow2k(k, y);
67,432✔
445
         const BigInt vt_pow2k = vartime_divide_pow2k(k, y);
67,432✔
446
         const BigInt ref = BigInt::power_of_2(k) / y;
67,432✔
447

448
         result.test_bn_eq("ct_divide_pow2k matches Knuth division", ct_pow2k, ref);
67,432✔
449
         result.test_bn_eq("vartime_divide_pow2k matches Knuth division", vt_pow2k, ref);
67,432✔
450
      }
67,432✔
451
};
452

453
BOTAN_REGISTER_TEST("math", "bn_div_pow2k", BigInt_DivPow2k_Test);
454

455
class BigInt_Mod_Test final : public Text_Based_Test {
×
456
   public:
457
      BigInt_Mod_Test() : Text_Based_Test("bn/mod.vec", "In1,In2,Output") {}
2✔
458

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

462
         const BigInt a = vars.get_req_bn("In1");
80✔
463
         const BigInt b = vars.get_req_bn("In2");
80✔
464
         const BigInt expected = vars.get_req_bn("Output");
80✔
465

466
         result.test_bn_eq("a % b", a % b, expected);
80✔
467

468
         BigInt e = a;
80✔
469
         e %= b;
80✔
470
         result.test_bn_eq("a %= b", e, expected);
80✔
471

472
         if(a.signum() >= 0 && a < (b * b)) {
80✔
473
            auto mod_b_pub = Botan::Barrett_Reduction::for_public_modulus(b);
43✔
474
            result.test_bn_eq("Barrett public", mod_b_pub.reduce(a), expected);
43✔
475

476
            auto mod_b_sec = Botan::Barrett_Reduction::for_secret_modulus(b);
43✔
477
            result.test_bn_eq("Barrett secret", mod_b_sec.reduce(a), expected);
43✔
478
         }
86✔
479

480
         // if b fits into a Botan::word test %= operator for words
481
         if(b.sig_words() == 1) {
80✔
482
            const Botan::word b_word = b.word_at(0);
34✔
483

484
            e = a;
34✔
485
            e %= b_word;
34✔
486
            result.test_bn_eq("a %= b (as word)", e, expected);
34✔
487

488
            result.test_bn_eq("a % b (as word)", a % b_word, expected);
34✔
489

490
            Botan::BigInt ct_q;
34✔
491
            Botan::word ct_r = 0;
34✔
492
            Botan::ct_divide_word(a, b.word_at(0), ct_q, ct_r);
68✔
493
            result.test_bn_eq("ct_divide_u8 r", ct_r, expected);
34✔
494
         }
34✔
495

496
         Botan::BigInt ct_q;
80✔
497
         Botan::BigInt ct_r;
80✔
498
         Botan::ct_divide(a, b, ct_q, ct_r);
80✔
499
         result.test_bn_eq("ct_divide r", ct_r, expected);
80✔
500

501
         return result;
80✔
502
      }
80✔
503
};
504

505
BOTAN_REGISTER_TEST("math", "bn_mod", BigInt_Mod_Test);
506

507
class Barrett_Redc_Test final : public Test {
1✔
508
   public:
509
      std::vector<Test::Result> run() override {
1✔
510
         Test::Result result("Barrett reduction");
1✔
511

512
         for(size_t t = 0; t != 10000; ++t) {
10,001✔
513
            const auto mod = [&]() {
30,000✔
514
               if(t <= 16) {
10,000✔
515
                  return BigInt::from_u64(t) + 1;
34✔
516
               } else {
517
                  const size_t bits = (t / 4) + 2;
9,983✔
518

519
                  if(t % 4 == 0) {
9,983✔
520
                     return BigInt::power_of_2(bits);
2,495✔
521
                  } else if(t % 4 == 1) {
7,488✔
522
                     return BigInt::power_of_2(bits) - 1;
4,992✔
523
                  } else if(t % 4 == 2) {
4,992✔
524
                     return BigInt::power_of_2(bits) + 1;
4,992✔
525
                  } else {
526
                     Botan::BigInt b;
2,496✔
527
                     b.randomize(rng(), bits, true);
2,496✔
528
                     return b;
2,496✔
529
                  }
2,496✔
530
               }
531
            }();
10,000✔
532

533
            const size_t mod_bits = mod.bits();
10,000✔
534
            auto barrett = Botan::Barrett_Reduction::for_public_modulus(mod);
10,000✔
535

536
            for(size_t i = 0; i != 10; ++i) {
110,000✔
537
               const auto input = [&]() {
×
538
                  Botan::BigInt b;
100,000✔
539
                  b.randomize(rng(), 2 * mod_bits, false);
100,000✔
540
                  return b;
100,000✔
541
               }();
100,000✔
542

543
               const auto reduced_ref = input % mod;
100,000✔
544
               const auto reduced_barrett = barrett.reduce(input);
100,000✔
545

546
               result.test_bn_eq("Barrett reduction matches variable time division", reduced_barrett, reduced_ref);
100,000✔
547
            }
100,000✔
548
         }
10,000✔
549

550
         return {result};
3✔
551
      }
2✔
552
};
553

554
BOTAN_REGISTER_TEST("math", "barrett_redc", Barrett_Redc_Test);
555

556
class BigInt_GCD_Test final : public Text_Based_Test {
×
557
   public:
558
      BigInt_GCD_Test() : Text_Based_Test("bn/gcd.vec", "X,Y,GCD") {}
2✔
559

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

563
         const BigInt x = vars.get_req_bn("X");
182✔
564
         const BigInt y = vars.get_req_bn("Y");
182✔
565
         const BigInt expected = vars.get_req_bn("GCD");
182✔
566

567
         const BigInt g1 = Botan::gcd(x, y);
182✔
568
         result.test_bn_eq("gcd", g1, expected);
182✔
569

570
         const BigInt g2 = Botan::gcd(y, x);
182✔
571
         result.test_bn_eq("gcd", g2, expected);
182✔
572

573
         return result;
182✔
574
      }
182✔
575
};
576

577
BOTAN_REGISTER_TEST("math", "bn_gcd", BigInt_GCD_Test);
578

579
class BigInt_Jacobi_Test final : public Text_Based_Test {
×
580
   public:
581
      BigInt_Jacobi_Test() : Text_Based_Test("bn/jacobi.vec", "A,N,J") {}
2✔
582

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

586
         const BigInt a = vars.get_req_bn("A");
698✔
587
         const BigInt n = vars.get_req_bn("N");
698✔
588
         const std::string expected = vars.get_req_str("J");
698✔
589

590
         const int32_t j = Botan::jacobi(a, n);
698✔
591

592
         if(j == 0) {
698✔
593
            result.test_str_eq("jacobi", expected, "0");
157✔
594
         } else if(j == -1) {
541✔
595
            result.test_str_eq("jacobi", expected, "-1");
307✔
596
         } else {
597
            result.test_str_eq("jacobi", expected, "1");
234✔
598
         }
599

600
         return result;
1,396✔
601
      }
698✔
602
};
603

604
BOTAN_REGISTER_TEST("math", "bn_jacobi", BigInt_Jacobi_Test);
605

606
class BigInt_Lshift_Test final : public Text_Based_Test {
×
607
   public:
608
      BigInt_Lshift_Test() : Text_Based_Test("bn/lshift.vec", "Value,Shift,Output") {}
2✔
609

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

613
         const BigInt value = vars.get_req_bn("Value");
51✔
614
         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
51✔
615
         const BigInt output = vars.get_req_bn("Output");
51✔
616

617
         result.test_bn_eq("a << s", value << shift, output);
51✔
618

619
         BigInt e = value;
51✔
620
         e <<= shift;
51✔
621
         result.test_bn_eq("a <<= s", e, output);
51✔
622

623
         return result;
51✔
624
      }
51✔
625
};
626

627
BOTAN_REGISTER_TEST("math", "bn_lshift", BigInt_Lshift_Test);
628

629
class BigInt_Rshift_Test final : public Text_Based_Test {
×
630
   public:
631
      BigInt_Rshift_Test() : Text_Based_Test("bn/rshift.vec", "Value,Shift,Output") {}
2✔
632

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

636
         const BigInt value = vars.get_req_bn("Value");
54✔
637
         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
54✔
638
         const BigInt output = vars.get_req_bn("Output");
54✔
639

640
         result.test_bn_eq("a >> s", value >> shift, output);
54✔
641

642
         BigInt e = value;
54✔
643
         e >>= shift;
54✔
644
         result.test_bn_eq("a >>= s", e, output);
54✔
645

646
         return result;
54✔
647
      }
54✔
648
};
649

650
BOTAN_REGISTER_TEST("math", "bn_rshift", BigInt_Rshift_Test);
651

652
Test::Result test_const_time_left_shift() {
1✔
653
   Test::Result result("BigInt const time shift");
1✔
654
   const size_t bits = Test::run_long_tests() ? 4096 : 2048;
1✔
655

656
   auto rng = Test::new_rng("const_time_left_shift");
1✔
657

658
   result.start_timer();
1✔
659

660
   Botan::BigInt a = Botan::BigInt::with_capacity(bits / sizeof(Botan::word));
1✔
661
   for(size_t i = 0; i < bits; ++i) {
4,097✔
662
      if(rng->next_byte() % 2 == 1) {
4,096✔
663
         a.set_bit(i);
4,096✔
664
      }
665
   }
666

667
   for(size_t i = 0; i < bits; ++i) {
4,097✔
668
      auto ct = a;
4,096✔
669
      auto chk = a;
4,096✔
670
      Botan::CT::poison(ct);
4,096✔
671
      ct.ct_shift_left(i);
4,096✔
672
      Botan::CT::unpoison(ct);
4,096✔
673
      chk <<= i;
4,096✔
674
      result.test_bn_eq(Botan::fmt("ct << {}", i), ct, chk);
4,096✔
675
   }
4,096✔
676

677
   result.end_timer();
1✔
678

679
   return result;
2✔
680
}
2✔
681

682
class BigInt_Powmod_Test final : public Text_Based_Test {
×
683
   public:
684
      BigInt_Powmod_Test() : Text_Based_Test("bn/powmod.vec", "Base,Exponent,Modulus,Output") {}
2✔
685

686
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
48✔
687
         Test::Result result("BigInt Powmod");
48✔
688

689
         const BigInt base = vars.get_req_bn("Base");
48✔
690
         const BigInt exponent = vars.get_req_bn("Exponent");
48✔
691
         const BigInt modulus = vars.get_req_bn("Modulus");
48✔
692
         const BigInt expected = vars.get_req_bn("Output");
48✔
693

694
         result.test_bn_eq("power_mod", Botan::power_mod(base, exponent, modulus), expected);
48✔
695
         return result;
48✔
696
      }
48✔
697
};
698

699
BOTAN_REGISTER_TEST("math", "bn_powmod", BigInt_Powmod_Test);
700

701
class BigInt_IsPrime_Test final : public Text_Based_Test {
×
702
   public:
703
      BigInt_IsPrime_Test() : Text_Based_Test("bn/isprime.vec", "X") {}
2✔
704

705
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
132✔
706
         if(header != "Prime" && header != "NonPrime") {
132✔
707
            throw Test_Error("Bad header for prime test " + header);
×
708
         }
709

710
         const BigInt value = vars.get_req_bn("X");
132✔
711
         const bool is_prime = (header == "Prime");
132✔
712

713
         Test::Result result("BigInt Test " + header);
132✔
714
         result.test_bool_eq("is_prime", Botan::is_prime(value, this->rng()), is_prime);
132✔
715

716
         return result;
132✔
717
      }
132✔
718
};
719

720
BOTAN_REGISTER_TEST("math", "bn_isprime", BigInt_IsPrime_Test);
721

722
class BigInt_IsSquare_Test final : public Text_Based_Test {
×
723
   public:
724
      BigInt_IsSquare_Test() : Text_Based_Test("bn/perfect_square.vec", "X,R") {}
2✔
725

726
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
7✔
727
         const BigInt value = vars.get_req_bn("X");
7✔
728
         const BigInt expected = vars.get_req_bn("R");
7✔
729
         const BigInt computed = Botan::is_perfect_square(value);
7✔
730

731
         Test::Result result("BigInt IsSquare");
7✔
732
         result.test_bn_eq("is_perfect_square", computed, expected);
7✔
733
         return result;
7✔
734
      }
7✔
735
};
736

737
BOTAN_REGISTER_TEST("math", "bn_issquare", BigInt_IsSquare_Test);
738

739
class BigInt_Sqrt_Modulo_Prime_Test final : public Text_Based_Test {
×
740
   public:
741
      BigInt_Sqrt_Modulo_Prime_Test() : Text_Based_Test("bn/sqrt_modulo_prime.vec", "Input,Modulus,Output") {}
2✔
742

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

746
         const Botan::BigInt a = vars.get_req_bn("Input");
17✔
747
         const Botan::BigInt p = vars.get_req_bn("Modulus");
17✔
748
         const Botan::BigInt exp = vars.get_req_bn("Output");
17✔
749

750
         const Botan::BigInt a_sqrt = Botan::sqrt_modulo_prime(a, p);
17✔
751

752
         result.test_bn_eq("sqrt_modulo_prime", a_sqrt, exp);
17✔
753

754
         if(a_sqrt > 1) {
17✔
755
            const Botan::BigInt a_sqrt2 = (a_sqrt * a_sqrt) % p;
8✔
756
            result.test_bn_eq("square correct", a_sqrt2, a);
8✔
757
         }
8✔
758

759
         return result;
17✔
760
      }
17✔
761
};
762

763
BOTAN_REGISTER_TEST("math", "bn_sqrt_modulo_prime", BigInt_Sqrt_Modulo_Prime_Test);
764

765
class BigInt_InvMod_Test final : public Text_Based_Test {
×
766
   public:
767
      BigInt_InvMod_Test() : Text_Based_Test("bn/invmod.vec", "Input,Modulus,Output") {}
2✔
768

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

772
         const Botan::BigInt a = vars.get_req_bn("Input");
138✔
773
         const Botan::BigInt mod = vars.get_req_bn("Modulus");
138✔
774
         const Botan::BigInt expected = vars.get_req_bn("Output");
138✔
775

776
         result.test_bn_eq("inverse_mod", Botan::inverse_mod(a, mod), expected);
138✔
777

778
         if(a < mod && a > 0 && a < mod) {
398✔
779
            auto g = Botan::inverse_mod_general(a, mod);
128✔
780
            if(g.has_value()) {
128✔
781
               result.test_bn_eq("inverse_mod_general", g.value(), expected);
87✔
782
               result.test_bn_eq("inverse works", ((g.value() * a) % mod), BigInt::one());
174✔
783
            } else {
784
               result.test_is_true("inverse_mod_general", expected.is_zero());
82✔
785
            }
786

787
            if(Botan::is_prime(mod, rng()) && mod != 2) {
136✔
788
               BOTAN_ASSERT_NOMSG(expected > 0);
7✔
789
               result.test_bn_eq("inverse_mod_secret_prime", Botan::inverse_mod_secret_prime(a, mod), expected);
7✔
790
               result.test_bn_eq("inverse_mod_public_prime", Botan::inverse_mod_public_prime(a, mod), expected);
7✔
791
            }
792
         }
128✔
793

794
         return result;
138✔
795
      }
138✔
796
};
797

798
BOTAN_REGISTER_TEST("math", "bn_invmod", BigInt_InvMod_Test);
799

800
class BigInt_Rand_Test final : public Text_Based_Test {
×
801
   public:
802
      BigInt_Rand_Test() : Text_Based_Test("bn/random.vec", "Seed,Min,Max,Output") {}
2✔
803

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

807
         const std::vector<uint8_t> seed = vars.get_req_bin("Seed");
4✔
808
         const Botan::BigInt min = vars.get_req_bn("Min");
4✔
809
         const Botan::BigInt max = vars.get_req_bn("Max");
4✔
810
         const Botan::BigInt expected = vars.get_req_bn("Output");
4✔
811

812
         Fixed_Output_RNG rng(seed);
4✔
813
         const Botan::BigInt generated = BigInt::random_integer(rng, min, max);
4✔
814

815
         result.test_bn_eq("random_integer KAT", generated, expected);
4✔
816

817
         return result;
4✔
818
      }
4✔
819
};
820

821
BOTAN_REGISTER_TEST("math", "bn_rand", BigInt_Rand_Test);
822

823
class Lucas_Primality_Test final : public Test {
1✔
824
   public:
825
      std::vector<Test::Result> run() override {
1✔
826
         const uint32_t lucas_max = (Test::run_long_tests() ? 100000 : 10000) + 1;
1✔
827

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

836
         Test::Result result("Lucas primality test");
1✔
837

838
         for(uint32_t i = 3; i <= lucas_max; i += 2) {
50,001✔
839
            auto mod_i = Botan::Barrett_Reduction::for_public_modulus(i);
50,000✔
840
            const bool passes_lucas = Botan::is_lucas_probable_prime(i, mod_i);
50,000✔
841
            const bool is_prime = Botan::is_prime(i, this->rng());
50,000✔
842

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

845
            if(is_lucas_pp) {
50,000✔
846
               result.test_is_true("Lucas pseudoprime is in list", lucas_pp.contains(i));
114✔
847
            } else {
848
               result.test_is_true("Lucas non-pseudoprime is not in list", !lucas_pp.contains(i));
99,886✔
849
            }
850
         }
50,000✔
851

852
         return {result};
2✔
853
      }
2✔
854
};
855

856
BOTAN_REGISTER_TEST("math", "bn_lucas", Lucas_Primality_Test);
857

858
class RSA_Compute_Exp_Test : public Test {
1✔
859
   public:
860
      std::vector<Test::Result> run() override {
1✔
861
         const size_t iter = 4000;
1✔
862

863
         Test::Result result("RSA compute exponent");
1✔
864

865
         const auto e = Botan::BigInt::from_u64(65537);
1✔
866

867
         /*
868
         * Rather than create a fresh p/q for each iteration this test creates
869
         * a pool of primes then selects 2 at random as p/q
870
         */
871

872
         const auto random_primes = [&]() {
3✔
873
            std::vector<Botan::BigInt> rp;
1✔
874
            for(size_t i = 0; i != iter / 10; ++i) {
401✔
875
               const size_t bits = (128 + (i % 1024)) % 4096;
400✔
876
               auto p = Botan::random_prime(rng(), bits);
400✔
877
               if(gcd(p - 1, e) == 1) {
800✔
878
                  rp.push_back(p);
400✔
879
               }
880
            }
400✔
881
            return rp;
1✔
882
         }();
1✔
883

884
         for(size_t i = 0; i != iter; ++i) {
4,001✔
885
            const size_t p_idx = random_index(rng(), random_primes.size());
4,000✔
886
            const size_t q_idx = random_index(rng(), random_primes.size());
4,000✔
887

888
            if(p_idx == q_idx) {
4,000✔
889
               continue;
10✔
890
            }
891

892
            const auto& p = random_primes[p_idx];
3,990✔
893
            const auto& q = random_primes[q_idx];
3,990✔
894

895
            auto phi_n = lcm(p - 1, q - 1);
3,990✔
896

897
            auto d = Botan::compute_rsa_secret_exponent(e, phi_n, p, q);
3,990✔
898

899
            auto ed_mod_phi_n = (e * d) % phi_n;
3,990✔
900

901
            result.test_bn_eq("compute_rsa_secret_exponent returned inverse", ed_mod_phi_n, Botan::BigInt::one());
3,990✔
902
         }
3,990✔
903

904
         return {result};
3✔
905
      }
2✔
906
};
907

908
BOTAN_REGISTER_TEST("math", "rsa_compute_d", RSA_Compute_Exp_Test);
909

910
class DSA_ParamGen_Test final : public Text_Based_Test {
×
911
   public:
912
      DSA_ParamGen_Test() : Text_Based_Test("bn/dsa_gen.vec", "P,Q,Counter,Seed") {}
2✔
913

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

918
         const Botan::BigInt exp_P = vars.get_req_bn("P");
20✔
919
         const Botan::BigInt exp_Q = vars.get_req_bn("Q");
20✔
920

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

923
         if(header_parts.size() != 2) {
20✔
924
            throw Test_Error("Unexpected header '" + header + "' in DSA param gen test");
×
925
         }
926

927
         const size_t p_bits = Botan::to_u32bit(header_parts[1]);
20✔
928
         const size_t q_bits = Botan::to_u32bit(header_parts[0]);
20✔
929

930
         Test::Result result("DSA Parameter Generation");
20✔
931

932
         try {
20✔
933
            Botan::BigInt gen_P;
20✔
934
            Botan::BigInt gen_Q;
20✔
935
            if(Botan::generate_dsa_primes(this->rng(), gen_P, gen_Q, p_bits, q_bits, seed, offset)) {
20✔
936
               result.test_bn_eq("P", gen_P, exp_P);
20✔
937
               result.test_bn_eq("Q", gen_Q, exp_Q);
20✔
938
            } else {
939
               result.test_failure("Seed did not generate a DSA parameter");
×
940
            }
941
         } catch(Botan::Lookup_Error&) {}
20✔
942

943
         return result;
20✔
944
      }
20✔
945
};
946

947
BOTAN_REGISTER_TEST("math", "dsa_param", DSA_ParamGen_Test);
948

949
std::vector<Test::Result> test_bigint_serialization() {
1✔
950
   auto rng = Test::new_rng("test_bigint_serialization");
1✔
951

952
   return {
1✔
953
      CHECK("BigInt binary serialization",
954
            [](Test::Result& res) {
1✔
955
               Botan::BigInt a(0x1234567890ABCDEF);
1✔
956
               auto enc = a.serialize();
1✔
957
               res.test_bin_eq("BigInt::serialize", enc, "1234567890ABCDEF");
1✔
958

959
               auto enc10 = a.serialize(10);
1✔
960
               res.test_bin_eq("BigInt::serialize", enc10, "00001234567890ABCDEF");
1✔
961

962
               res.test_throws("BigInt::serialize rejects short output", [&]() { a.serialize(7); });
2✔
963
            }),
1✔
964

965
      CHECK("BigInt truncated/padded binary serialization",
966
            [&](Test::Result& res) {
1✔
967
               const Botan::BigInt a(0xFEDCBA9876543210);
1✔
968

969
               std::vector<uint8_t> enc1(a.bytes() - 1);
1✔
970
               a.binary_encode(enc1.data(), enc1.size());
1✔
971
               res.test_bin_eq("BigInt::binary_encode", enc1, "DCBA9876543210");
1✔
972

973
               std::vector<uint8_t> enc2(a.bytes() - 3);
1✔
974
               a.binary_encode(enc2.data(), enc2.size());
1✔
975
               res.test_bin_eq("BigInt::binary_encode", enc2, "9876543210");
1✔
976

977
               std::vector<uint8_t> enc3(a.bytes() + 1);
1✔
978
               a.binary_encode(enc3.data(), enc3.size());
1✔
979
               res.test_bin_eq("BigInt::binary_encode", enc3, "00FEDCBA9876543210");
1✔
980

981
               // make sure that the padding is actually written
982
               std::vector<uint8_t> enc4(a.bytes() + 3);
1✔
983
               rng->randomize(enc4);
1✔
984
               a.binary_encode(enc4.data(), enc4.size());
1✔
985
               res.test_bin_eq("BigInt::binary_encode", enc4, "000000FEDCBA9876543210");
1✔
986

987
               const Botan::BigInt b("0xFEDCBA9876543210BAADC0FFEE");
1✔
988

989
               std::vector<uint8_t> enc5(b.bytes() + 12);
1✔
990
               rng->randomize(enc5);
1✔
991
               b.binary_encode(enc5.data(), enc5.size());
1✔
992
               res.test_bin_eq("BigInt::binary_encode", enc5, "000000000000000000000000FEDCBA9876543210BAADC0FFEE");
1✔
993
            }),
1✔
994
   };
3✔
995
}
2✔
996

997
BOTAN_REGISTER_TEST_FN("math", "bignum_auxiliary", test_const_time_left_shift, test_bigint_serialization);
998

999
class BigInt_FromRadix_Tests final : public Text_Based_Test {
×
1000
   public:
1001
      BigInt_FromRadix_Tests() : Text_Based_Test("bn/from_radix.vec", "Input,Radix,Output") {}
2✔
1002

1003
      bool clear_between_callbacks() const override { return false; }
69✔
1004

1005
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
69✔
1006
         Test::Result result("BigInt from_radix_digits");
69✔
1007

1008
         const std::string input = vars.get_req_str("Input");
69✔
1009
         const size_t radix = vars.get_req_sz("Radix");
69✔
1010
         const std::string expected_output = vars.get_req_str("Output");
69✔
1011

1012
         if(header == "Valid") {
69✔
1013
            const Botan::BigInt n = Botan::BigInt::from_radix_digits(input, radix);
60✔
1014
            const auto serialized = n.serialize();
60✔
1015
            result.test_bin_eq("from_radix_digits", serialized, expected_output);
60✔
1016
         } else if(header == "Invalid") {
69✔
1017
            try {
9✔
1018
               Botan::BigInt::from_radix_digits(input, radix);
9✔
1019
               result.test_failure("from_radix_digits should have thrown");
×
1020
            } catch(const std::exception& e) {
9✔
1021
               const std::string msg = e.what();
9✔
1022
               result.test_is_true("exception message contains '" + expected_output + "'",
27✔
1023
                                   msg.find(expected_output) != std::string::npos);
9✔
1024
            }
9✔
1025
         } else {
1026
            result.test_failure("Unknown header " + header);
×
1027
         }
1028

1029
         return result;
69✔
1030
      }
78✔
1031
};
1032

1033
BOTAN_REGISTER_TEST("math", "bn_from_radix", BigInt_FromRadix_Tests);
1034

1035
#endif
1036

1037
}  // namespace
1038

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