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

randombit / botan / 12869303872

20 Jan 2025 01:40PM UTC coverage: 91.213% (+0.01%) from 91.202%
12869303872

push

github

web-flow
Merge pull request #4569 from randombit/jack/mod-inv-distinguish-cases

When computing modular inverses distingush which case we are in

93546 of 102558 relevant lines covered (91.21%)

11542300.02 hits per line

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

94.1
/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);
3✔
102

103
         p = Botan::random_prime(*rng, 4);
2✔
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);
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
         const Botan::Modular_Reducer mod_b(b);
76✔
422
         result.test_eq("Barrett", mod_b.reduce(a), expected);
152✔
423

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

428
            e = a;
30✔
429
            e %= b_word;
30✔
430
            result.test_eq("a %= b (as word)", e, expected);
30✔
431

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

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

440
         Botan::BigInt ct_q, ct_r;
76✔
441
         Botan::ct_divide(a, b, ct_q, ct_r);
76✔
442
         result.test_eq("ct_divide r", ct_r, expected);
76✔
443

444
         return result;
76✔
445
      }
453✔
446
};
447

448
BOTAN_REGISTER_TEST("math", "bn_mod", BigInt_Mod_Test);
449

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

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

457
         const BigInt x = vars.get_req_bn("X");
182✔
458
         const BigInt y = vars.get_req_bn("Y");
182✔
459
         const BigInt expected = vars.get_req_bn("GCD");
182✔
460

461
         const BigInt g1 = Botan::gcd(x, y);
182✔
462
         result.test_eq("gcd", g1, expected);
182✔
463

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

467
         return result;
182✔
468
      }
910✔
469
};
470

471
BOTAN_REGISTER_TEST("math", "bn_gcd", BigInt_GCD_Test);
472

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

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

480
         const BigInt a = vars.get_req_bn("A");
698✔
481
         const BigInt n = vars.get_req_bn("N");
698✔
482
         const std::string expected = vars.get_req_str("J");
698✔
483

484
         const int32_t j = Botan::jacobi(a, n);
698✔
485

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

494
         return result;
698✔
495
      }
2,094✔
496
};
497

498
BOTAN_REGISTER_TEST("math", "bn_jacobi", BigInt_Jacobi_Test);
499

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

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

507
         const BigInt value = vars.get_req_bn("Value");
50✔
508
         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
100✔
509
         const BigInt output = vars.get_req_bn("Output");
50✔
510

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

513
         BigInt e = value;
50✔
514
         e <<= shift;
50✔
515
         result.test_eq("a <<= s", e, output);
50✔
516

517
         return result;
50✔
518
      }
150✔
519
};
520

521
BOTAN_REGISTER_TEST("math", "bn_lshift", BigInt_Lshift_Test);
522

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

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

530
         const BigInt value = vars.get_req_bn("Value");
53✔
531
         const size_t shift = vars.get_req_bn("Shift").to_u32bit();
106✔
532
         const BigInt output = vars.get_req_bn("Output");
53✔
533

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

536
         BigInt e = value;
53✔
537
         e >>= shift;
53✔
538
         result.test_eq("a >>= s", e, output);
53✔
539

540
         return result;
53✔
541
      }
159✔
542
};
543

544
BOTAN_REGISTER_TEST("math", "bn_rshift", BigInt_Rshift_Test);
545

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

550
   auto rng = Test::new_rng("const_time_left_shift");
1✔
551

552
   result.start_timer();
1✔
553

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

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

571
   result.end_timer();
1✔
572

573
   return result;
1✔
574
}
2✔
575

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

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

583
         const BigInt base = vars.get_req_bn("Base");
47✔
584
         const BigInt exponent = vars.get_req_bn("Exponent");
47✔
585
         const BigInt modulus = vars.get_req_bn("Modulus");
47✔
586
         const BigInt expected = vars.get_req_bn("Output");
47✔
587

588
         result.test_eq("power_mod", Botan::power_mod(base, exponent, modulus), expected);
94✔
589
         return result;
47✔
590
      }
188✔
591
};
592

593
BOTAN_REGISTER_TEST("math", "bn_powmod", BigInt_Powmod_Test);
594

595
class BigInt_IsPrime_Test final : public Text_Based_Test {
×
596
   public:
597
      BigInt_IsPrime_Test() : Text_Based_Test("bn/isprime.vec", "X") {}
2✔
598

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

604
         const BigInt value = vars.get_req_bn("X");
132✔
605
         const bool is_prime = (header == "Prime");
132✔
606

607
         Test::Result result("BigInt Test " + header);
132✔
608
         result.test_eq("is_prime", Botan::is_prime(value, this->rng()), is_prime);
132✔
609

610
         return result;
132✔
611
      }
132✔
612
};
613

614
BOTAN_REGISTER_TEST("math", "bn_isprime", BigInt_IsPrime_Test);
615

616
class BigInt_IsSquare_Test final : public Text_Based_Test {
×
617
   public:
618
      BigInt_IsSquare_Test() : Text_Based_Test("bn/perfect_square.vec", "X,R") {}
2✔
619

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

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

631
BOTAN_REGISTER_TEST("math", "bn_issquare", BigInt_IsSquare_Test);
632

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

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

640
         const Botan::BigInt a = vars.get_req_bn("Input");
17✔
641
         const Botan::BigInt p = vars.get_req_bn("Modulus");
17✔
642
         const Botan::BigInt exp = vars.get_req_bn("Output");
17✔
643

644
         const Botan::BigInt a_sqrt = Botan::sqrt_modulo_prime(a, p);
17✔
645

646
         result.test_eq("sqrt_modulo_prime", a_sqrt, exp);
17✔
647

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

653
         return result;
17✔
654
      }
68✔
655
};
656

657
BOTAN_REGISTER_TEST("math", "bn_sqrt_modulo_prime", BigInt_Sqrt_Modulo_Prime_Test);
658

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

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

666
         const Botan::BigInt a = vars.get_req_bn("Input");
138✔
667
         const Botan::BigInt mod = vars.get_req_bn("Modulus");
138✔
668
         const Botan::BigInt expected = vars.get_req_bn("Output");
138✔
669

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

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

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

688
         return result;
138✔
689
      }
414✔
690
};
691

692
BOTAN_REGISTER_TEST("math", "bn_invmod", BigInt_InvMod_Test);
693

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

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

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

706
         Fixed_Output_RNG rng(seed);
4✔
707
         Botan::BigInt generated = BigInt::random_integer(rng, min, max);
4✔
708

709
         result.test_eq("random_integer KAT", generated, expected);
4✔
710

711
         return result;
4✔
712
      }
20✔
713
};
714

715
BOTAN_REGISTER_TEST("math", "bn_rand", BigInt_Rand_Test);
716

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

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

730
         Test::Result result("Lucas primality test");
1✔
731

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

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

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

746
         return {result};
2✔
747
      }
2✔
748
};
749

750
BOTAN_REGISTER_TEST("math", "bn_lucas", Lucas_Primality_Test);
751

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

757
         Test::Result result("RSA compute exponent");
1✔
758

759
         const auto e = Botan::BigInt::from_u64(65537);
1✔
760

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

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

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

782
            if(p_idx == q_idx) {
4,000✔
783
               continue;
13✔
784
            }
785

786
            const auto& p = random_primes[p_idx];
3,987✔
787
            const auto& q = random_primes[q_idx];
3,987✔
788

789
            auto phi_n = lcm(p - 1, q - 1);
7,974✔
790

791
            auto d = Botan::compute_rsa_secret_exponent(e, phi_n, p, q);
3,987✔
792

793
            auto one = (e * d) % phi_n;
3,987✔
794

795
            result.test_eq("compute_rsa_secret_exponent returned inverse", (e * d) % phi_n, Botan::BigInt::one());
19,935✔
796
         }
11,961✔
797

798
         return {result};
3✔
799
      }
3✔
800
};
801

802
BOTAN_REGISTER_TEST("math", "rsa_compute_d", RSA_Compute_Exp_Test);
803

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

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

812
         const Botan::BigInt exp_P = vars.get_req_bn("P");
20✔
813
         const Botan::BigInt exp_Q = vars.get_req_bn("Q");
20✔
814

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

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

821
         const size_t p_bits = Botan::to_u32bit(header_parts[1]);
20✔
822
         const size_t q_bits = Botan::to_u32bit(header_parts[0]);
20✔
823

824
         Test::Result result("DSA Parameter Generation");
20✔
825

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

836
         return result;
40✔
837
      }
80✔
838
};
839

840
BOTAN_REGISTER_TEST("math", "dsa_param", DSA_ParamGen_Test);
841

842
std::vector<Test::Result> test_bigint_serialization() {
1✔
843
   auto rng = Test::new_rng("test_bigint_serialization");
1✔
844

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

852
               auto enc10 = a.serialize(10);
1✔
853
               res.test_eq("BigInt::serialize", enc10, Botan::hex_decode("00001234567890ABCDEF"));
2✔
854

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

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

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

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

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

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

880
               Botan::BigInt b(Botan::hex_decode("FEDCBA9876543210BAADC0FFEE"));
1✔
881

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

892
BOTAN_REGISTER_TEST_FN("math", "bignum_auxiliary", test_const_time_left_shift, test_bigint_serialization);
893

894
#endif
895

896
}  // namespace
897

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