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

randombit / botan / 21780928802

07 Feb 2026 01:36PM UTC coverage: 90.068% (+0.003%) from 90.065%
21780928802

Pull #5295

github

web-flow
Merge cbabeb61a into ebf8f0044
Pull Request #5295: Reduce header dependencies in tests and cli

102234 of 113508 relevant lines covered (90.07%)

11464546.51 hits per line

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

95.26
/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/ct_utils.h>
17
   #include <botan/internal/divide.h>
18
   #include <botan/internal/fmt.h>
19
   #include <botan/internal/mod_inv.h>
20
   #include <botan/internal/parsing.h>
21
   #include <botan/internal/primality.h>
22
   #include <botan/internal/stl_util.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_eq("BigInt::bits", a.bits(), exp_bits);
24✔
61
               result.test_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_is_eq("BigInt::to_u32bit zero", a.to_u32bit(), static_cast<uint32_t>(1));
2✔
66
               } else if(bit <= 31 || (bit == 32 && i == 1)) {
23✔
67
                  result.test_is_eq(
8✔
68
                     "BigInt::to_u32bit", a.to_u32bit(), static_cast<uint32_t>((uint64_t(1) << bit) - i));
16✔
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(
2✔
91
            "Invalid bit size", "random_prime: Can't make a prime of 0 bits", [&]() { Botan::random_prime(*rng, 0); });
2✔
92
         result.test_throws(
2✔
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", [&]() {
2✔
95
            Botan::random_prime(*rng, 2, 1, 0, 2);
1✔
96
         });
×
97

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

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

104
         p = Botan::random_prime(*rng, 4);
1✔
105
         result.confirm("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_eq("Expected bit size", p.bits(), bits);
28✔
110
            result.test_eq("P is prime", Botan::is_prime(p, *rng), true);
56✔
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_eq("Safe prime size", safe_prime.bits(), safe_prime_bits);
1✔
116
         result.confirm("P is prime", Botan::is_prime(safe_prime, *rng));
2✔
117
         result.confirm("(P-1)/2 is prime", Botan::is_prime((safe_prime - 1) / 2, *rng));
2✔
118

119
         return result;
2✔
120
      }
2✔
121

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

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

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

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

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

141
         return result;
2✔
142
      }
4✔
143

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

147
         const size_t rbits = 1024;
1✔
148

149
         auto rng = Test::new_rng("get_substring");
1✔
150

151
         const Botan::BigInt r(*rng, rbits);
1✔
152

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

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

160
               const uint32_t cmp = t.to_u32bit();
34,816✔
161

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

166
         return result;
2✔
167
      }
2✔
168

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

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

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

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

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

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

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

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

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

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

221
         return result;
1✔
222
      }
6✔
223
};
224

225
BOTAN_REGISTER_TEST("math", "bigint_unit", BigInt_Unit_Tests);
226

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

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

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

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

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

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

260
         return result;
19✔
261
      }
19✔
262
};
263

264
BOTAN_REGISTER_TEST("math", "bn_cmp", BigInt_Cmp_Test);
265

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

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

273
         using Botan::BigInt;
78✔
274

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

279
         result.test_eq("a + b", a + b, c);
156✔
280
         result.test_eq("b + a", b + a, c);
156✔
281

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

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

290
         return result;
78✔
291
      }
78✔
292
};
293

294
BOTAN_REGISTER_TEST("math", "bn_add", BigInt_Add_Test);
295

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

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

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

307
         result.test_eq("a - b", a - b, c);
154✔
308

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

313
         return result;
77✔
314
      }
77✔
315
};
316

317
BOTAN_REGISTER_TEST("math", "bn_sub", BigInt_Sub_Test);
318

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

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

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

330
         result.test_eq("a * b", a * b, c);
208✔
331
         result.test_eq("b * a", b * a, c);
208✔
332

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

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

341
         return result;
104✔
342
      }
104✔
343
};
344

345
BOTAN_REGISTER_TEST("math", "bn_mul", BigInt_Mul_Test);
346

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

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

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

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

360
         return result;
21✔
361
      }
21✔
362
};
363

364
BOTAN_REGISTER_TEST("math", "bn_sqr", BigInt_Sqr_Test);
365

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

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

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

377
         result.test_eq("a / b", a / b, c);
1,572✔
378

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

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

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

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

400
         return result;
786✔
401
      }
786✔
402
};
403

404
BOTAN_REGISTER_TEST("math", "bn_div", BigInt_Div_Test);
405

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

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

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

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

426
               testcase(k, y, result);
67,054✔
427
            }
67,056✔
428
         }
508✔
429

430
         return {result};
3✔
431
      }
2✔
432

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

439
         result.test_eq("ct_divide_pow2k matches Knuth division", ct_pow2k, ref);
67,432✔
440
         result.test_eq("vartime_divide_pow2k matches Knuth division", vt_pow2k, ref);
67,432✔
441
      }
67,432✔
442
};
443

444
BOTAN_REGISTER_TEST("math", "bn_div_pow2k", BigInt_DivPow2k_Test);
445

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

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

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

457
         result.test_eq("a % b", a % b, expected);
160✔
458

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

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

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

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

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

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

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

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

492
         return result;
80✔
493
      }
80✔
494
};
495

496
BOTAN_REGISTER_TEST("math", "bn_mod", BigInt_Mod_Test);
497

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

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

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

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

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

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

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

541
         return {result};
3✔
542
      }
2✔
543
};
544

545
BOTAN_REGISTER_TEST("math", "barrett_redc", Barrett_Redc_Test);
546

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

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

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

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

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

564
         return result;
182✔
565
      }
182✔
566
};
567

568
BOTAN_REGISTER_TEST("math", "bn_gcd", BigInt_GCD_Test);
569

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

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

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

581
         const int32_t j = Botan::jacobi(a, n);
698✔
582

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

591
         return result;
1,396✔
592
      }
698✔
593
};
594

595
BOTAN_REGISTER_TEST("math", "bn_jacobi", BigInt_Jacobi_Test);
596

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

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

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

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

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

614
         return result;
51✔
615
      }
51✔
616
};
617

618
BOTAN_REGISTER_TEST("math", "bn_lshift", BigInt_Lshift_Test);
619

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

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

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

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

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

637
         return result;
54✔
638
      }
54✔
639
};
640

641
BOTAN_REGISTER_TEST("math", "bn_rshift", BigInt_Rshift_Test);
642

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

647
   auto rng = Test::new_rng("const_time_left_shift");
1✔
648

649
   result.start_timer();
1✔
650

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

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

668
   result.end_timer();
1✔
669

670
   return result;
2✔
671
}
2✔
672

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

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

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

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

690
BOTAN_REGISTER_TEST("math", "bn_powmod", BigInt_Powmod_Test);
691

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

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

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

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

707
         return result;
132✔
708
      }
132✔
709
};
710

711
BOTAN_REGISTER_TEST("math", "bn_isprime", BigInt_IsPrime_Test);
712

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

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

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

728
BOTAN_REGISTER_TEST("math", "bn_issquare", BigInt_IsSquare_Test);
729

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

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

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

741
         const Botan::BigInt a_sqrt = Botan::sqrt_modulo_prime(a, p);
17✔
742

743
         result.test_eq("sqrt_modulo_prime", a_sqrt, exp);
17✔
744

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

750
         return result;
17✔
751
      }
17✔
752
};
753

754
BOTAN_REGISTER_TEST("math", "bn_sqrt_modulo_prime", BigInt_Sqrt_Modulo_Prime_Test);
755

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

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

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

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

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

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

785
         return result;
138✔
786
      }
138✔
787
};
788

789
BOTAN_REGISTER_TEST("math", "bn_invmod", BigInt_InvMod_Test);
790

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

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

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

803
         Fixed_Output_RNG rng(seed);
4✔
804
         const Botan::BigInt generated = BigInt::random_integer(rng, min, max);
4✔
805

806
         result.test_eq("random_integer KAT", generated, expected);
4✔
807

808
         return result;
8✔
809
      }
8✔
810
};
811

812
BOTAN_REGISTER_TEST("math", "bn_rand", BigInt_Rand_Test);
813

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

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

827
         Test::Result result("Lucas primality test");
1✔
828

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

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

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

843
         return {result};
2✔
844
      }
2✔
845
};
846

847
BOTAN_REGISTER_TEST("math", "bn_lucas", Lucas_Primality_Test);
848

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

854
         Test::Result result("RSA compute exponent");
1✔
855

856
         const auto e = Botan::BigInt::from_u64(65537);
1✔
857

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

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

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

879
            if(p_idx == q_idx) {
4,000✔
880
               continue;
18✔
881
            }
882

883
            const auto& p = random_primes[p_idx];
3,982✔
884
            const auto& q = random_primes[q_idx];
3,982✔
885

886
            auto phi_n = lcm(p - 1, q - 1);
3,982✔
887

888
            auto d = Botan::compute_rsa_secret_exponent(e, phi_n, p, q);
3,982✔
889

890
            auto ed_mod_phi_n = (e * d) % phi_n;
3,982✔
891

892
            result.test_eq("compute_rsa_secret_exponent returned inverse", ed_mod_phi_n, Botan::BigInt::one());
7,964✔
893
         }
3,982✔
894

895
         return {result};
3✔
896
      }
2✔
897
};
898

899
BOTAN_REGISTER_TEST("math", "rsa_compute_d", RSA_Compute_Exp_Test);
900

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

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

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

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

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

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

921
         Test::Result result("DSA Parameter Generation");
20✔
922

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

934
         return result;
40✔
935
      }
40✔
936
};
937

938
BOTAN_REGISTER_TEST("math", "dsa_param", DSA_ParamGen_Test);
939

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

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

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

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

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

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

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

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

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

978
               const Botan::BigInt b(Botan::hex_decode("FEDCBA9876543210BAADC0FFEE"));
1✔
979

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

990
BOTAN_REGISTER_TEST_FN("math", "bignum_auxiliary", test_const_time_left_shift, test_bigint_serialization);
991

992
#endif
993

994
}  // namespace
995

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