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

randombit / botan / 12939720817

23 Jan 2025 11:04PM UTC coverage: 91.208% (+0.006%) from 91.202%
12939720817

Pull #4588

github

web-flow
Merge d6c027109 into 525a35d62
Pull Request #4588: Reduce overhead from computing modular reduction parameters

93607 of 102630 relevant lines covered (91.21%)

11442383.67 hits per line

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

94.12
/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/reducer.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/mp_core.h>
20
   #include <botan/internal/parsing.h>
21
   #include <botan/internal/primality.h>
22
   #include <botan/internal/stl_util.h>
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 {
×
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(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--;
48✔
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);
3✔
99

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

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

106
         for(size_t bits = 5; bits <= 32; ++bits) {
29✔
107
            p = Botan::random_prime(*rng, bits);
56✔
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));
3✔
117

118
         return result;
1✔
119
      }
3✔
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;
1✔
141
      }
6✔
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));
69,632✔
162
            }
34,816✔
163
         }
164

165
         return result;
1✔
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}};
10✔
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
         }
10✔
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");
2✔
209
         check_bigint_formatting(Botan::BigInt::from_s32(-33), "-33", "-0x21", "33", "0x21");
2✔
210
         check_bigint_formatting(Botan::BigInt(255), "255", "0xFF", "-255", "-0xFF");
2✔
211
         check_bigint_formatting(Botan::BigInt(0), "0", "0x00", "0", "0x00");
2✔
212
         check_bigint_formatting(Botan::BigInt(5), "5", "0x05", "-5", "-0x05");
2✔
213

214
         result.test_throws("octal output not supported", [&]() {
2✔
215
            Botan::BigInt n(5);
1✔
216
            std::ostringstream oss;
1✔
217
            oss << std::oct << n;
1✔
218
         });
1✔
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
      }
38✔
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
      }
312✔
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
      }
308✔
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
      }
416✔
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
      }
42✔
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 {
783✔
370
         Test::Result result("BigInt Divide");
783✔
371

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

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

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

382
         if(b.sig_words() == 1) {
783✔
383
            const Botan::word bw = b.word_at(0);
404✔
384
            result.test_eq("bw ok", Botan::BigInt::from_word(bw), b);
808✔
385

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

393
         Botan::BigInt ct_q, ct_r;
783✔
394
         Botan::ct_divide(a, b, ct_q, ct_r);
783✔
395
         result.test_eq("ct_divide q", ct_q, c);
783✔
396
         result.test_eq("ct_divide r", ct_q * b + ct_r, a);
2,349✔
397

398
         return result;
783✔
399
      }
4,696✔
400
};
401

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

404
class BigInt_Mod_Test final : public Text_Based_Test {
×
405
   public:
406
      BigInt_Mod_Test() : Text_Based_Test("bn/mod.vec", "In1,In2,Output") {}
2✔
407

408
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
76✔
409
         Test::Result result("BigInt Mod");
76✔
410

411
         const BigInt a = vars.get_req_bn("In1");
76✔
412
         const BigInt b = vars.get_req_bn("In2");
76✔
413
         const BigInt expected = vars.get_req_bn("Output");
76✔
414

415
         result.test_eq("a % b", a % b, expected);
152✔
416

417
         BigInt e = a;
76✔
418
         e %= b;
76✔
419
         result.test_eq("a %= b", e, expected);
76✔
420

421
         auto mod_b_pub = Botan::Modular_Reducer::for_public_modulus(b);
76✔
422
         result.test_eq("Barrett public", mod_b_pub.reduce(a), expected);
152✔
423

424
         auto mod_b_sec = Botan::Modular_Reducer::for_secret_modulus(b);
76✔
425
         result.test_eq("Barrett secret", mod_b_sec.reduce(a), expected);
152✔
426

427
         // if b fits into a Botan::word test %= operator for words
428
         if(b.sig_words() == 1) {
76✔
429
            const Botan::word b_word = b.word_at(0);
30✔
430

431
            e = a;
30✔
432
            e %= b_word;
30✔
433
            result.test_eq("a %= b (as word)", e, expected);
30✔
434

435
            result.test_eq("a % b (as word)", a % b_word, expected);
60✔
436

437
            Botan::BigInt ct_q;
30✔
438
            Botan::word ct_r;
30✔
439
            Botan::ct_divide_word(a, b.word_at(0), ct_q, ct_r);
60✔
440
            result.test_eq("ct_divide_u8 r", ct_r, expected);
82✔
441
         }
30✔
442

443
         Botan::BigInt ct_q, ct_r;
76✔
444
         Botan::ct_divide(a, b, ct_q, ct_r);
76✔
445
         result.test_eq("ct_divide r", ct_r, expected);
76✔
446

447
         return result;
76✔
448
      }
453✔
449
};
450

451
BOTAN_REGISTER_TEST("math", "bn_mod", BigInt_Mod_Test);
452

453
class BigInt_GCD_Test final : public Text_Based_Test {
×
454
   public:
455
      BigInt_GCD_Test() : Text_Based_Test("bn/gcd.vec", "X,Y,GCD") {}
2✔
456

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

460
         const BigInt x = vars.get_req_bn("X");
182✔
461
         const BigInt y = vars.get_req_bn("Y");
182✔
462
         const BigInt expected = vars.get_req_bn("GCD");
182✔
463

464
         const BigInt g1 = Botan::gcd(x, y);
182✔
465
         result.test_eq("gcd", g1, expected);
182✔
466

467
         const BigInt g2 = Botan::gcd(y, x);
182✔
468
         result.test_eq("gcd", g2, expected);
182✔
469

470
         return result;
182✔
471
      }
910✔
472
};
473

474
BOTAN_REGISTER_TEST("math", "bn_gcd", BigInt_GCD_Test);
475

476
class BigInt_Jacobi_Test final : public Text_Based_Test {
×
477
   public:
478
      BigInt_Jacobi_Test() : Text_Based_Test("bn/jacobi.vec", "A,N,J") {}
2✔
479

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

483
         const BigInt a = vars.get_req_bn("A");
698✔
484
         const BigInt n = vars.get_req_bn("N");
698✔
485
         const std::string expected = vars.get_req_str("J");
698✔
486

487
         const int32_t j = Botan::jacobi(a, n);
698✔
488

489
         if(j == 0) {
698✔
490
            result.test_eq("jacobi", expected, "0");
314✔
491
         } else if(j == -1) {
541✔
492
            result.test_eq("jacobi", expected, "-1");
614✔
493
         } else {
494
            result.test_eq("jacobi", expected, "1");
468✔
495
         }
496

497
         return result;
698✔
498
      }
2,094✔
499
};
500

501
BOTAN_REGISTER_TEST("math", "bn_jacobi", BigInt_Jacobi_Test);
502

503
class BigInt_Lshift_Test final : public Text_Based_Test {
×
504
   public:
505
      BigInt_Lshift_Test() : Text_Based_Test("bn/lshift.vec", "Value,Shift,Output") {}
2✔
506

507
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
50✔
508
         Test::Result result("BigInt Lshift");
50✔
509

510
         const BigInt value = vars.get_req_bn("Value");
50✔
511
         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
100✔
512
         const BigInt output = vars.get_req_bn("Output");
50✔
513

514
         result.test_eq("a << s", value << shift, output);
100✔
515

516
         BigInt e = value;
50✔
517
         e <<= shift;
50✔
518
         result.test_eq("a <<= s", e, output);
50✔
519

520
         return result;
50✔
521
      }
150✔
522
};
523

524
BOTAN_REGISTER_TEST("math", "bn_lshift", BigInt_Lshift_Test);
525

526
class BigInt_Rshift_Test final : public Text_Based_Test {
×
527
   public:
528
      BigInt_Rshift_Test() : Text_Based_Test("bn/rshift.vec", "Value,Shift,Output") {}
2✔
529

530
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
53✔
531
         Test::Result result("BigInt Rshift");
53✔
532

533
         const BigInt value = vars.get_req_bn("Value");
53✔
534
         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
106✔
535
         const BigInt output = vars.get_req_bn("Output");
53✔
536

537
         result.test_eq("a >> s", value >> shift, output);
106✔
538

539
         BigInt e = value;
53✔
540
         e >>= shift;
53✔
541
         result.test_eq("a >>= s", e, output);
53✔
542

543
         return result;
53✔
544
      }
159✔
545
};
546

547
BOTAN_REGISTER_TEST("math", "bn_rshift", BigInt_Rshift_Test);
548

549
Test::Result test_const_time_left_shift() {
1✔
550
   Test::Result result("BigInt const time shift");
1✔
551
   const size_t bits = Test::run_long_tests() ? 4096 : 2048;
1✔
552

553
   auto rng = Test::new_rng("const_time_left_shift");
1✔
554

555
   result.start_timer();
1✔
556

557
   Botan::BigInt a = Botan::BigInt::with_capacity(bits / sizeof(Botan::word));
1✔
558
   for(size_t i = 0; i < bits; ++i) {
4,097✔
559
      if(rng->next_byte() & 1) {
4,096✔
560
         a.set_bit(i);
4,096✔
561
      }
562
   }
563

564
   for(size_t i = 0; i < bits; ++i) {
4,097✔
565
      auto ct = a;
4,096✔
566
      auto chk = a;
4,096✔
567
      Botan::CT::poison(ct);
4,096✔
568
      ct.ct_shift_left(i);
4,096✔
569
      Botan::CT::unpoison(ct);
4,096✔
570
      chk <<= i;
4,096✔
571
      result.test_eq(Botan::fmt("ct << {}", i), ct, chk);
8,192✔
572
   }
8,192✔
573

574
   result.end_timer();
1✔
575

576
   return result;
1✔
577
}
2✔
578

579
class BigInt_Powmod_Test final : public Text_Based_Test {
×
580
   public:
581
      BigInt_Powmod_Test() : Text_Based_Test("bn/powmod.vec", "Base,Exponent,Modulus,Output") {}
2✔
582

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

586
         const BigInt base = vars.get_req_bn("Base");
47✔
587
         const BigInt exponent = vars.get_req_bn("Exponent");
47✔
588
         const BigInt modulus = vars.get_req_bn("Modulus");
47✔
589
         const BigInt expected = vars.get_req_bn("Output");
47✔
590

591
         result.test_eq("power_mod", Botan::power_mod(base, exponent, modulus), expected);
94✔
592
         return result;
47✔
593
      }
188✔
594
};
595

596
BOTAN_REGISTER_TEST("math", "bn_powmod", BigInt_Powmod_Test);
597

598
class BigInt_IsPrime_Test final : public Text_Based_Test {
×
599
   public:
600
      BigInt_IsPrime_Test() : Text_Based_Test("bn/isprime.vec", "X") {}
2✔
601

602
      Test::Result run_one_test(const std::string& header, const VarMap& vars) override {
132✔
603
         if(header != "Prime" && header != "NonPrime") {
132✔
604
            throw Test_Error("Bad header for prime test " + header);
×
605
         }
606

607
         const BigInt value = vars.get_req_bn("X");
132✔
608
         const bool is_prime = (header == "Prime");
132✔
609

610
         Test::Result result("BigInt Test " + header);
132✔
611
         result.test_eq("is_prime", Botan::is_prime(value, this->rng()), is_prime);
132✔
612

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

617
BOTAN_REGISTER_TEST("math", "bn_isprime", BigInt_IsPrime_Test);
618

619
class BigInt_IsSquare_Test final : public Text_Based_Test {
×
620
   public:
621
      BigInt_IsSquare_Test() : Text_Based_Test("bn/perfect_square.vec", "X,R") {}
2✔
622

623
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
7✔
624
         const BigInt value = vars.get_req_bn("X");
7✔
625
         const BigInt expected = vars.get_req_bn("R");
7✔
626
         const BigInt computed = Botan::is_perfect_square(value);
7✔
627

628
         Test::Result result("BigInt IsSquare");
7✔
629
         result.test_eq("is_perfect_square", computed, expected);
7✔
630
         return result;
7✔
631
      }
21✔
632
};
633

634
BOTAN_REGISTER_TEST("math", "bn_issquare", BigInt_IsSquare_Test);
635

636
class BigInt_Sqrt_Modulo_Prime_Test final : public Text_Based_Test {
×
637
   public:
638
      BigInt_Sqrt_Modulo_Prime_Test() : Text_Based_Test("bn/sqrt_modulo_prime.vec", "Input,Modulus,Output") {}
2✔
639

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

643
         const Botan::BigInt a = vars.get_req_bn("Input");
17✔
644
         const Botan::BigInt p = vars.get_req_bn("Modulus");
17✔
645
         const Botan::BigInt exp = vars.get_req_bn("Output");
17✔
646

647
         const Botan::BigInt a_sqrt = Botan::sqrt_modulo_prime(a, p);
17✔
648

649
         result.test_eq("sqrt_modulo_prime", a_sqrt, exp);
17✔
650

651
         if(a_sqrt > 1) {
17✔
652
            const Botan::BigInt a_sqrt2 = (a_sqrt * a_sqrt) % p;
8✔
653
            result.test_eq("square correct", a_sqrt2, a);
16✔
654
         }
8✔
655

656
         return result;
17✔
657
      }
68✔
658
};
659

660
BOTAN_REGISTER_TEST("math", "bn_sqrt_modulo_prime", BigInt_Sqrt_Modulo_Prime_Test);
661

662
class BigInt_InvMod_Test final : public Text_Based_Test {
×
663
   public:
664
      BigInt_InvMod_Test() : Text_Based_Test("bn/invmod.vec", "Input,Modulus,Output") {}
2✔
665

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

669
         const Botan::BigInt a = vars.get_req_bn("Input");
138✔
670
         const Botan::BigInt mod = vars.get_req_bn("Modulus");
138✔
671
         const Botan::BigInt expected = vars.get_req_bn("Output");
138✔
672

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

675
         if(a < mod && a > 0 && a < mod) {
398✔
676
            auto g = Botan::inverse_mod_general(a, mod);
128✔
677
            if(g.has_value()) {
128✔
678
               result.test_eq("inverse_mod_general", g.value(), expected);
87✔
679
               result.test_eq("inverse works", ((g.value() * a) % mod), BigInt::one());
522✔
680
            } else {
681
               result.confirm("inverse_mod_general", expected.is_zero());
82✔
682
            }
683

684
            if(Botan::is_prime(mod, rng()) && mod != 2) {
136✔
685
               BOTAN_ASSERT_NOMSG(expected > 0);
7✔
686
               result.test_eq("inverse_mod_secret_prime", Botan::inverse_mod_secret_prime(a, mod), expected);
14✔
687
               result.test_eq("inverse_mod_public_prime", Botan::inverse_mod_public_prime(a, mod), expected);
21✔
688
            }
689
         }
128✔
690

691
         return result;
138✔
692
      }
414✔
693
};
694

695
BOTAN_REGISTER_TEST("math", "bn_invmod", BigInt_InvMod_Test);
696

697
class BigInt_Rand_Test final : public Text_Based_Test {
×
698
   public:
699
      BigInt_Rand_Test() : Text_Based_Test("bn/random.vec", "Seed,Min,Max,Output") {}
2✔
700

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

704
         const std::vector<uint8_t> seed = vars.get_req_bin("Seed");
4✔
705
         const Botan::BigInt min = vars.get_req_bn("Min");
4✔
706
         const Botan::BigInt max = vars.get_req_bn("Max");
4✔
707
         const Botan::BigInt expected = vars.get_req_bn("Output");
4✔
708

709
         Fixed_Output_RNG rng(seed);
4✔
710
         Botan::BigInt generated = BigInt::random_integer(rng, min, max);
4✔
711

712
         result.test_eq("random_integer KAT", generated, expected);
4✔
713

714
         return result;
4✔
715
      }
20✔
716
};
717

718
BOTAN_REGISTER_TEST("math", "bn_rand", BigInt_Rand_Test);
719

720
class Lucas_Primality_Test final : public Test {
×
721
   public:
722
      std::vector<Test::Result> run() override {
1✔
723
         const uint32_t lucas_max = (Test::run_long_tests() ? 100000 : 6000);
1✔
724

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

733
         Test::Result result("Lucas primality test");
1✔
734

735
         for(uint32_t i = 3; i <= lucas_max; i += 2) {
50,000✔
736
            auto mod_i = Botan::Modular_Reducer::for_public_modulus(i);
49,999✔
737
            const bool passes_lucas = Botan::is_lucas_probable_prime(i, mod_i);
49,999✔
738
            const bool is_prime = Botan::is_prime(i, this->rng());
49,999✔
739

740
            const bool is_lucas_pp = (is_prime == false && passes_lucas == true);
49,999✔
741

742
            if(is_lucas_pp) {
49,999✔
743
               result.confirm("Lucas pseudoprime is in list", lucas_pp.count(i) == 1);
171✔
744
            } else {
745
               result.confirm("Lucas non-pseudoprime is not in list", !lucas_pp.contains(i));
149,826✔
746
            }
747
         }
49,999✔
748

749
         return {result};
2✔
750
      }
2✔
751
};
752

753
BOTAN_REGISTER_TEST("math", "bn_lucas", Lucas_Primality_Test);
754

755
class RSA_Compute_Exp_Test : public Test {
×
756
   public:
757
      std::vector<Test::Result> run() override {
1✔
758
         const size_t iter = 4000;
1✔
759

760
         Test::Result result("RSA compute exponent");
1✔
761

762
         const auto e = Botan::BigInt::from_u64(65537);
1✔
763

764
         /*
765
         * Rather than create a fresh p/q for each iteration this test creates
766
         * a pool of primes then selects 2 at random as p/q
767
         */
768

769
         const auto random_primes = [&]() {
×
770
            std::vector<Botan::BigInt> rp;
1✔
771
            for(size_t i = 0; i != iter / 10; ++i) {
401✔
772
               size_t bits = (128 + (i % 1024)) % 4096;
400✔
773
               auto p = Botan::random_prime(rng(), bits);
400✔
774
               if(gcd(p - 1, e) == 1) {
1,600✔
775
                  rp.push_back(p);
400✔
776
               }
777
            }
400✔
778
            return rp;
1✔
779
         }();
1✔
780

781
         for(size_t i = 0; i != iter; ++i) {
4,001✔
782
            const size_t p_idx = random_index(rng(), random_primes.size());
4,000✔
783
            const size_t q_idx = random_index(rng(), random_primes.size());
4,000✔
784

785
            if(p_idx == q_idx) {
4,000✔
786
               continue;
9✔
787
            }
788

789
            const auto& p = random_primes[p_idx];
3,991✔
790
            const auto& q = random_primes[q_idx];
3,991✔
791

792
            auto phi_n = lcm(p - 1, q - 1);
7,982✔
793

794
            auto d = Botan::compute_rsa_secret_exponent(e, phi_n, p, q);
3,991✔
795

796
            auto one = (e * d) % phi_n;
3,991✔
797

798
            result.test_eq("compute_rsa_secret_exponent returned inverse", (e * d) % phi_n, Botan::BigInt::one());
19,955✔
799
         }
11,973✔
800

801
         return {result};
3✔
802
      }
3✔
803
};
804

805
BOTAN_REGISTER_TEST("math", "rsa_compute_d", RSA_Compute_Exp_Test);
806

807
class DSA_ParamGen_Test final : public Text_Based_Test {
×
808
   public:
809
      DSA_ParamGen_Test() : Text_Based_Test("bn/dsa_gen.vec", "P,Q,Counter,Seed") {}
2✔
810

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

815
         const Botan::BigInt exp_P = vars.get_req_bn("P");
20✔
816
         const Botan::BigInt exp_Q = vars.get_req_bn("Q");
20✔
817

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

820
         if(header_parts.size() != 2) {
20✔
821
            throw Test_Error("Unexpected header '" + header + "' in DSA param gen test");
×
822
         }
823

824
         const size_t p_bits = Botan::to_u32bit(header_parts[1]);
20✔
825
         const size_t q_bits = Botan::to_u32bit(header_parts[0]);
20✔
826

827
         Test::Result result("DSA Parameter Generation");
20✔
828

829
         try {
20✔
830
            Botan::BigInt gen_P, gen_Q;
20✔
831
            if(Botan::generate_dsa_primes(this->rng(), gen_P, gen_Q, p_bits, q_bits, seed, offset)) {
20✔
832
               result.test_eq("P", gen_P, exp_P);
20✔
833
               result.test_eq("Q", gen_Q, exp_Q);
40✔
834
            } else {
835
               result.test_failure("Seed did not generate a DSA parameter");
×
836
            }
837
         } catch(Botan::Lookup_Error&) {}
40✔
838

839
         return result;
40✔
840
      }
80✔
841
};
842

843
BOTAN_REGISTER_TEST("math", "dsa_param", DSA_ParamGen_Test);
844

845
std::vector<Test::Result> test_bigint_serialization() {
1✔
846
   auto rng = Test::new_rng("test_bigint_serialization");
1✔
847

848
   return {
1✔
849
      CHECK("BigInt binary serialization",
850
            [](Test::Result& res) {
1✔
851
               Botan::BigInt a(0x1234567890ABCDEF);
1✔
852
               auto enc = a.serialize();
1✔
853
               res.test_eq("BigInt::serialize", enc, Botan::hex_decode("1234567890ABCDEF"));
2✔
854

855
               auto enc10 = a.serialize(10);
1✔
856
               res.test_eq("BigInt::serialize", enc10, Botan::hex_decode("00001234567890ABCDEF"));
2✔
857

858
               res.test_throws("BigInt::serialize rejects short output", [&]() { a.serialize(7); });
4✔
859
            }),
3✔
860

861
      CHECK("BigInt truncated/padded binary serialization",
862
            [&](Test::Result& res) {
1✔
863
               Botan::BigInt a(0xFEDCBA9876543210);
1✔
864

865
               std::vector<uint8_t> enc1(a.bytes() - 1);
1✔
866
               a.binary_encode(enc1.data(), enc1.size());
1✔
867
               res.test_eq("BigInt::binary_encode", enc1, Botan::hex_decode("DCBA9876543210"));
2✔
868

869
               std::vector<uint8_t> enc2(a.bytes() - 3);
1✔
870
               a.binary_encode(enc2.data(), enc2.size());
1✔
871
               res.test_eq("BigInt::binary_encode", enc2, Botan::hex_decode("9876543210"));
2✔
872

873
               std::vector<uint8_t> enc3(a.bytes() + 1);
1✔
874
               a.binary_encode(enc3.data(), enc3.size());
1✔
875
               res.test_eq("BigInt::binary_encode", enc3, Botan::hex_decode("00FEDCBA9876543210"));
2✔
876

877
               // make sure that the padding is actually written
878
               std::vector<uint8_t> enc4(a.bytes() + 3);
1✔
879
               rng->randomize(enc4);
1✔
880
               a.binary_encode(enc4.data(), enc4.size());
1✔
881
               res.test_eq("BigInt::binary_encode", enc4, Botan::hex_decode("000000FEDCBA9876543210"));
2✔
882

883
               Botan::BigInt b(Botan::hex_decode("FEDCBA9876543210BAADC0FFEE"));
1✔
884

885
               std::vector<uint8_t> enc5(b.bytes() + 12);
1✔
886
               rng->randomize(enc5);
1✔
887
               b.binary_encode(enc5.data(), enc5.size());
1✔
888
               res.test_eq("BigInt::binary_encode",
2✔
889
                           enc5,
890
                           Botan::hex_decode("000000000000000000000000FEDCBA9876543210BAADC0FFEE"));
2✔
891
            }),
7✔
892
   };
3✔
893
}
2✔
894

895
BOTAN_REGISTER_TEST_FN("math", "bignum_auxiliary", test_const_time_left_shift, test_bigint_serialization);
896

897
#endif
898

899
}  // namespace
900

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