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

randombit / botan / 20283964805

16 Dec 2025 09:55PM UTC coverage: 90.358% (-0.002%) from 90.36%
20283964805

Pull #5180

github

web-flow
Merge 9310065bb into 3d96b675e
Pull Request #5180: Avoid integer modulo operations when sieving for primes

100966 of 111740 relevant lines covered (90.36%)

12873526.27 hits per line

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

90.08
/src/lib/math/numbertheory/make_prm.cpp
1
/*
2
* Prime Generation
3
* (C) 1999-2007,2018,2019 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/internal/primality.h>
9

10
#include <botan/numthry.h>
11
#include <botan/rng.h>
12
#include <botan/internal/barrett.h>
13
#include <botan/internal/bit_ops.h>
14
#include <botan/internal/ct_utils.h>
15
#include <botan/internal/divide.h>
16
#include <botan/internal/loadstor.h>
17
#include <algorithm>
18

19
namespace Botan {
20

21
namespace {
22

23
class Prime_Sieve final {
×
24
   public:
25
      Prime_Sieve(const BigInt& init_value, size_t sieve_size, word step, bool check_2p1) :
926✔
26
            m_sieve(std::min(sieve_size, PRIME_TABLE_SIZE)), m_step(step), m_check_2p1(check_2p1) {
926✔
27
         for(size_t i = 0; i != m_sieve.size(); ++i) {
508,405✔
28
            m_sieve[i] = ct_mod_word(init_value, PRIMES[i]);
507,479✔
29
         }
30
      }
926✔
31

32
      size_t sieve_size() const { return m_sieve.size(); }
33

34
      bool check_2p1() const { return m_check_2p1; }
1,147,853,322✔
35

36
      bool next() {
1,345,804✔
37
         auto passes = CT::Mask<word>::set();
1,345,804✔
38
         for(size_t i = 0; i != m_sieve.size(); ++i) {
1,149,199,126✔
39
            m_sieve[i] = sieve_step_incr(m_sieve[i], m_step, PRIMES[i]);
1,147,853,322✔
40

41
            // If m_sieve[i] == 0 then val % p == 0 -> not prime
42
            passes &= CT::Mask<word>::expand(m_sieve[i]);
1,147,853,322✔
43

44
            if(this->check_2p1()) {
1,147,853,322✔
45
               /*
46
               If v % p == (p-1)/2 then 2*v+1 == 0 (mod p)
47

48
               So if potentially generating a safe prime, we want to
49
               avoid this value because 2*v+1 will certainly not be prime.
50

51
               See "Safe Prime Generation with a Combined Sieve" M. Wiener
52
               https://eprint.iacr.org/2003/186.pdf
53
               */
54
               passes &= ~CT::Mask<word>::is_equal(m_sieve[i], (PRIMES[i] - 1) / 2);
1,120,514,186✔
55
            }
56
         }
57

58
         return passes.as_bool();
1,345,804✔
59
      }
60

61
   private:
62
      // Return (v + step) % mod
63
      //
64
      // This assumes v is already < mod, which is an invariant of the sieve
65
      static constexpr word sieve_step_incr(word v, word step, word mod) {
1,147,853,322✔
66
         BOTAN_DEBUG_ASSERT(v < mod);
1,147,853,322✔
67
         // The sieve step and primes are public so this modulo is ok
68
         const word stepmod = (step >= mod) ? (step % mod) : step;
1,147,853,322✔
69

70
         // This sum is at most 2*(mod-1)
71
         const word next = (v + stepmod);
1,147,853,322✔
72
         return next - CT::Mask<word>::is_gte(next, mod).if_set_return(mod);
1,147,853,322✔
73
      }
74

75
      std::vector<word> m_sieve;
76
      const word m_step;
77
      const bool m_check_2p1;
78
};
79

80
#if defined(BOTAN_ENABLE_DEBUG_ASSERTS)
81

82
bool no_small_multiples(const BigInt& v, const Prime_Sieve& sieve) {
83
   const size_t sieve_size = sieve.sieve_size();
84
   const bool check_2p1 = sieve.check_2p1();
85

86
   if(v.is_even())
87
      return false;
88

89
   const BigInt v_x2_p1 = 2 * v + 1;
90

91
   for(size_t i = 0; i != sieve_size; ++i) {
92
      if((v % PRIMES[i]) == 0)
93
         return false;
94

95
      if(check_2p1) {
96
         if(v_x2_p1 % PRIMES[i] == 0)
97
            return false;
98
      }
99
   }
100

101
   return true;
102
}
103

104
#endif
105

106
}  // namespace
107

108
/*
109
* Generate a random prime
110
*/
111
BigInt random_prime(
800✔
112
   RandomNumberGenerator& rng, size_t bits, const BigInt& coprime, size_t equiv, size_t modulo, size_t prob) {
113
   if(bits <= 1) {
800✔
114
      throw Invalid_Argument("random_prime: Can't make a prime of " + std::to_string(bits) + " bits");
8✔
115
   }
116
   if(coprime.is_negative() || (!coprime.is_zero() && coprime.is_even()) || coprime.bits() >= bits) {
1,603✔
117
      throw Invalid_Argument("random_prime: invalid coprime");
×
118
   }
119
   // TODO(Botan4) reduce this to ~1000
120
   if(modulo == 0 || modulo >= 100000) {
798✔
121
      throw Invalid_Argument("random_prime: Invalid modulo value");
×
122
   }
123

124
   equiv %= modulo;
798✔
125

126
   if(equiv == 0) {
798✔
127
      throw Invalid_Argument("random_prime Invalid value for equiv/modulo");
1✔
128
   }
129

130
   // Handle small values:
131

132
   if(bits <= 16) {
797✔
133
      if(equiv != 1 || modulo != 2 || coprime != 0) {
38✔
134
         throw Not_Implemented("random_prime equiv/modulo/coprime options not usable for small primes");
×
135
      }
136

137
      if(bits == 2) {
19✔
138
         return BigInt::from_word(((rng.next_byte() % 2) == 0 ? 2 : 3));
2✔
139
      } else if(bits == 3) {
18✔
140
         return BigInt::from_word(((rng.next_byte() % 2) == 0 ? 5 : 7));
2✔
141
      } else if(bits == 4) {
17✔
142
         return BigInt::from_word(((rng.next_byte() % 2) == 0 ? 11 : 13));
1✔
143
      } else {
144
         for(;;) {
9,390✔
145
            // This is slightly biased, but for small primes it does not seem to matter
146
            uint8_t b[4] = {0};
4,703✔
147
            rng.randomize(b, 4);
4,703✔
148
            const size_t idx = load_le<uint32_t>(b, 0) % PRIME_TABLE_SIZE;
4,703✔
149
            const uint16_t small_prime = PRIMES[idx];
4,703✔
150

151
            if(high_bit(small_prime) == bits) {
9,406✔
152
               return BigInt::from_word(small_prime);
16✔
153
            }
154
         }
4,687✔
155
      }
156
   }
157

158
   const size_t MAX_ATTEMPTS = 32 * 1024;
778✔
159

160
   const size_t mr_trials = miller_rabin_test_iterations(bits, prob, true);
778✔
161

162
   while(true) {
×
163
      BigInt p(rng, bits);
778✔
164

165
      // Force lowest and two top bits on
166
      p.set_bit(bits - 1);
778✔
167
      p.set_bit(bits - 2);
778✔
168
      p.set_bit(0);
778✔
169

170
      // Force p to be equal to equiv mod modulo
171
      p += (modulo - (p % modulo)) + equiv;
778✔
172

173
      Prime_Sieve sieve(p, bits, modulo, true);
778✔
174

175
      for(size_t attempt = 0; attempt <= MAX_ATTEMPTS; ++attempt) {
1,312,223✔
176
         p += modulo;
1,312,223✔
177

178
         if(!sieve.next()) {
1,312,223✔
179
            continue;
1,311,445✔
180
         }
181

182
         // here p can be even if modulo is odd, continue on in that case
183
         if(p.is_even()) {
61,162✔
184
            continue;
11,317✔
185
         }
186

187
         BOTAN_DEBUG_ASSERT(no_small_multiples(p, sieve));
19,264✔
188

189
         auto mod_p = Barrett_Reduction::for_secret_modulus(p);
19,264✔
190

191
         if(coprime > 1) {
19,264✔
192
            /*
193
            First do a single M-R iteration to quickly eliminate most non-primes,
194
            before doing the coprimality check which is expensive
195
            */
196
            if(!is_miller_rabin_probable_prime(p, mod_p, rng, 1)) {
99✔
197
               continue;
93✔
198
            }
199

200
            /*
201
            * Check if p - 1 and coprime are relatively prime, using gcd.
202
            * The gcd computation is const-time
203
            */
204
            if(gcd(p - 1, coprime) > 1) {
12✔
205
               continue;
×
206
            }
207
         }
208

209
         if(p.bits() > bits) {
19,171✔
210
            break;
211
         }
212

213
         if(!is_miller_rabin_probable_prime(p, mod_p, rng, mr_trials)) {
19,171✔
214
            continue;
18,393✔
215
         }
216

217
         if(prob > 32 && !is_lucas_probable_prime(p, mod_p)) {
778✔
218
            continue;
×
219
         }
220

221
         return p;
778✔
222
      }
19,264✔
223
   }
778✔
224
}
225

226
BigInt generate_rsa_prime(RandomNumberGenerator& keygen_rng,
148✔
227
                          RandomNumberGenerator& prime_test_rng,
228
                          size_t bits,
229
                          const BigInt& coprime,
230
                          size_t prob) {
231
   if(bits < 512) {
148✔
232
      throw Invalid_Argument("generate_rsa_prime bits too small");
×
233
   }
234

235
   /*
236
   * The restriction on coprime <= 64 bits is arbitrary but generally speaking
237
   * very large RSA public exponents are a bad idea both for performance and due
238
   * to attacks on small d.
239
   */
240
   if(coprime <= 1 || coprime.is_even() || coprime.bits() > 64) {
296✔
241
      throw Invalid_Argument("generate_rsa_prime coprime must be small odd positive integer");
×
242
   }
243

244
   const size_t MAX_ATTEMPTS = 32 * 1024;
148✔
245

246
   const size_t mr_trials = miller_rabin_test_iterations(bits, prob, true);
148✔
247

248
   while(true) {
×
249
      BigInt p(keygen_rng, bits);
148✔
250

251
      CT::poison(p);
148✔
252

253
      /*
254
      Force high two bits so multiplication always results in expected n bit integer
255

256
      Force the two low bits, and step by 4, so the generated prime is always == 3 (mod 4).
257
      This way when we perform the inversion modulo phi(n) it is always of the form 2*o
258
      with o odd, which allows a fastpath and avoids leaking any information about the
259
      structure of the prime.
260
      */
261
      p.set_bit(bits - 1);
148✔
262
      p.set_bit(bits - 2);
148✔
263
      p.set_bit(1);
148✔
264
      p.set_bit(0);
148✔
265

266
      const word step = 4;
148✔
267

268
      Prime_Sieve sieve(p, bits, step, false);
148✔
269

270
      for(size_t attempt = 0; attempt <= MAX_ATTEMPTS; ++attempt) {
33,581✔
271
         p += step;
33,581✔
272

273
         if(!sieve.next()) {
33,581✔
274
            continue;
33,433✔
275
         }
276

277
         BOTAN_DEBUG_ASSERT(no_small_multiples(p, sieve));
4,478✔
278

279
         auto mod_p = Barrett_Reduction::for_secret_modulus(p);
4,478✔
280

281
         /*
282
         * Do a single primality test first before checking coprimality, since
283
         * currently a single Miller-Rabin test is faster than computing gcd,
284
         * and this eliminates almost all wasted gcd computations.
285
         */
286
         if(!is_miller_rabin_probable_prime(p, mod_p, prime_test_rng, 1)) {
4,478✔
287
            continue;
4,330✔
288
         }
289

290
         /*
291
         * Check if p - 1 and coprime are relatively prime.
292
         */
293
         if(gcd(p - 1, coprime) > 1) {
296✔
294
            continue;
×
295
         }
296

297
         if(p.bits() > bits) {
148✔
298
            break;
299
         }
300

301
         if(is_miller_rabin_probable_prime(p, mod_p, prime_test_rng, mr_trials)) {
148✔
302
            CT::unpoison(p);
148✔
303
            return p;
296✔
304
         }
305
      }
4,478✔
306
   }
148✔
307
}
308

309
/*
310
* Generate a random safe prime
311
*/
312
BigInt random_safe_prime(RandomNumberGenerator& rng, size_t bits) {
8✔
313
   if(bits <= 64) {
8✔
314
      throw Invalid_Argument("random_safe_prime: Can't make a prime of " + std::to_string(bits) + " bits");
×
315
   }
316

317
   const size_t error_bound = 128;
8✔
318

319
   BigInt q;
8✔
320
   BigInt p;
8✔
321
   for(;;) {
248✔
322
      /*
323
      Generate q == 2 (mod 3), since otherwise [in the case of q == 1 (mod 3)],
324
      2*q+1 == 3 (mod 3) and so certainly not prime.
325
      */
326
      q = random_prime(rng, bits - 1, BigInt::zero(), 2, 3, error_bound);
248✔
327
      p = (q << 1) + 1;
496✔
328

329
      if(is_prime(p, rng, error_bound, true)) {
248✔
330
         return p;
8✔
331
      }
332
   }
333
}
8✔
334

335
}  // namespace Botan
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