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

randombit / botan / 20600514215

30 Dec 2025 10:38AM UTC coverage: 90.424%. Remained the same
20600514215

push

github

web-flow
Merge pull request #5199 from randombit/jack/add-p521-leading-zero-keygen

Add a test for encoding of P-521 keys with a leading zero byte.

101648 of 112413 relevant lines covered (90.42%)

12992413.47 hits per line

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

95.09
/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
   #include <set>
23
#endif
24

25
namespace Botan_Tests {
26

27
namespace {
28

29
#if defined(BOTAN_HAS_NUMBERTHEORY)
30

31
using Botan::BigInt;
32

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

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

44
         return results;
1✔
45
      }
×
46

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

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

54
            a.set_bit(bit);
12✔
55

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

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

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

81
         return result;
1✔
82
      }
×
83

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

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

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

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

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

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

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

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

118
         return result;
2✔
119
      }
2✔
120

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

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

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

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

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

140
         return result;
2✔
141
      }
4✔
142

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

146
         const size_t rbits = 1024;
1✔
147

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

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

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

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

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

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

165
         return result;
2✔
166
      }
2✔
167

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

272
         using Botan::BigInt;
78✔
273

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

648
   result.start_timer();
1✔
649

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

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

667
   result.end_timer();
1✔
668

669
   return result;
2✔
670
}
2✔
671

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

885
            auto phi_n = lcm(p - 1, q - 1);
3,993✔
886

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

889
            auto ed_mod_phi_n = (e * d) % phi_n;
3,993✔
890

891
            result.test_eq("compute_rsa_secret_exponent returned inverse", ed_mod_phi_n, Botan::BigInt::one());
7,986✔
892
         }
3,993✔
893

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

991
#endif
992

993
}  // namespace
994

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