Skip to content

Commit 5d511c5

Browse files
committed
Add BoxedUint support
1 parent 8a9218a commit 5d511c5

File tree

7 files changed

+121
-79
lines changed

7 files changed

+121
-79
lines changed

benches/bench.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ fn make_rng() -> ChaCha8Rng {
2727
fn random_odd_uint<T: RandomBits + Integer>(
2828
rng: &mut impl CryptoRngCore,
2929
bit_length: u32,
30+
bits_precision: u32,
3031
) -> Odd<T> {
31-
random_odd_integer::<T>(rng, NonZeroU32::new(bit_length).unwrap())
32+
random_odd_integer::<T>(rng, NonZeroU32::new(bit_length).unwrap(), bits_precision)
3233
}
3334

3435
fn make_sieve<const L: usize>(rng: &mut impl CryptoRngCore) -> Sieve<Uint<L>> {
35-
let start = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS);
36+
let start = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS, Uint::<L>::BITS);
3637
Sieve::new(&start, NonZeroU32::new(Uint::<L>::BITS).unwrap(), false)
3738
}
3839

@@ -45,12 +46,12 @@ fn bench_sieve(c: &mut Criterion) {
4546
let mut group = c.benchmark_group("Sieve");
4647

4748
group.bench_function("(U128) random start", |b| {
48-
b.iter(|| random_odd_uint::<U128>(&mut OsRng, 128))
49+
b.iter(|| random_odd_uint::<U128>(&mut OsRng, 128, 128))
4950
});
5051

5152
group.bench_function("(U128) creation", |b| {
5253
b.iter_batched(
53-
|| random_odd_uint::<U128>(&mut OsRng, 128),
54+
|| random_odd_uint::<U128>(&mut OsRng, 128, 128),
5455
|start| Sieve::new(start.as_ref(), NonZeroU32::new(128).unwrap(), false),
5556
BatchSize::SmallInput,
5657
)
@@ -66,12 +67,12 @@ fn bench_sieve(c: &mut Criterion) {
6667
});
6768

6869
group.bench_function("(U1024) random start", |b| {
69-
b.iter(|| random_odd_uint::<U1024>(&mut OsRng, 1024))
70+
b.iter(|| random_odd_uint::<U1024>(&mut OsRng, 1024, 1024))
7071
});
7172

7273
group.bench_function("(U1024) creation", |b| {
7374
b.iter_batched(
74-
|| random_odd_uint::<U1024>(&mut OsRng, 1024),
75+
|| random_odd_uint::<U1024>(&mut OsRng, 1024, 1024),
7576
|start| Sieve::new(start.as_ref(), NonZeroU32::new(1024).unwrap(), false),
7677
BatchSize::SmallInput,
7778
)
@@ -93,7 +94,7 @@ fn bench_miller_rabin(c: &mut Criterion) {
9394

9495
group.bench_function("(U128) creation", |b| {
9596
b.iter_batched(
96-
|| random_odd_uint::<U128>(&mut OsRng, 128),
97+
|| random_odd_uint::<U128>(&mut OsRng, 128, 128),
9798
|n| MillerRabin::new(&n),
9899
BatchSize::SmallInput,
99100
)
@@ -109,7 +110,7 @@ fn bench_miller_rabin(c: &mut Criterion) {
109110

110111
group.bench_function("(U1024) creation", |b| {
111112
b.iter_batched(
112-
|| random_odd_uint::<U1024>(&mut OsRng, 1024),
113+
|| random_odd_uint::<U1024>(&mut OsRng, 1024, 1024),
113114
|n| MillerRabin::new(&n),
114115
BatchSize::SmallInput,
115116
)
@@ -202,39 +203,39 @@ fn bench_presets(c: &mut Criterion) {
202203

203204
group.bench_function("(U128) Prime test", |b| {
204205
b.iter_batched(
205-
|| random_odd_uint::<U128>(&mut OsRng, 128),
206+
|| random_odd_uint::<U128>(&mut OsRng, 128, 128),
206207
|num| is_prime_with_rng(&mut OsRng, num.as_ref()),
207208
BatchSize::SmallInput,
208209
)
209210
});
210211

211212
group.bench_function("(U128) Safe prime test", |b| {
212213
b.iter_batched(
213-
|| random_odd_uint::<U128>(&mut OsRng, 128),
214+
|| random_odd_uint::<U128>(&mut OsRng, 128, 128),
214215
|num| is_safe_prime_with_rng(&mut OsRng, num.as_ref()),
215216
BatchSize::SmallInput,
216217
)
217218
});
218219

219220
let mut rng = make_rng();
220221
group.bench_function("(U128) Random prime", |b| {
221-
b.iter(|| generate_prime_with_rng::<U128>(&mut rng, 128))
222+
b.iter(|| generate_prime_with_rng::<U128>(&mut rng, 128, 128))
222223
});
223224

224225
let mut rng = make_rng();
225226
group.bench_function("(U1024) Random prime", |b| {
226-
b.iter(|| generate_prime_with_rng::<U1024>(&mut rng, 1024))
227+
b.iter(|| generate_prime_with_rng::<U1024>(&mut rng, 1024, 1024))
227228
});
228229

229230
let mut rng = make_rng();
230231
group.bench_function("(U128) Random safe prime", |b| {
231-
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128))
232+
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128, 128))
232233
});
233234

234235
group.sample_size(20);
235236
let mut rng = make_rng();
236237
group.bench_function("(U1024) Random safe prime", |b| {
237-
b.iter(|| generate_safe_prime_with_rng::<U1024>(&mut rng, 1024))
238+
b.iter(|| generate_safe_prime_with_rng::<U1024>(&mut rng, 1024, 1024))
238239
});
239240

240241
group.finish();
@@ -244,19 +245,19 @@ fn bench_presets(c: &mut Criterion) {
244245

245246
let mut rng = make_rng();
246247
group.bench_function("(U128) Random safe prime", |b| {
247-
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128))
248+
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128, 128))
248249
});
249250

250251
// The performance should scale with the prime size, not with the Uint size.
251252
// So we should strive for this test's result to be as close as possible
252253
// to that of the previous one and as far away as possible from the next one.
253254
group.bench_function("(U256) Random 128 bit safe prime", |b| {
254-
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 128))
255+
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 128, 256))
255256
});
256257

257258
// The upper bound for the previous test.
258259
group.bench_function("(U256) Random 256 bit safe prime", |b| {
259-
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 256))
260+
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 256, 256))
260261
});
261262

262263
group.finish();
@@ -267,7 +268,7 @@ fn bench_gmp(c: &mut Criterion) {
267268
let mut group = c.benchmark_group("GMP");
268269

269270
fn random<const L: usize>(rng: &mut impl CryptoRngCore) -> GmpInteger {
270-
let num = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS).get();
271+
let num = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS, Uint::<L>::BITS).get();
271272
GmpInteger::from_digits(num.as_words(), Order::Lsf)
272273
}
273274

src/hazmat/jacobi.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub(crate) fn jacobi_symbol_vartime<T: Integer>(
8484
};
8585

8686
// A degenerate case.
87-
if abs_a == 1 || p_long.as_ref() == &T::one() {
87+
if abs_a == 1 || p_long.as_ref() == &T::one_like(p_long) {
8888
return result;
8989
}
9090

src/hazmat/lucas.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Lucas primality test.
2-
use crypto_bigint::{Integer, Monty, Odd, Square, Word};
2+
use crypto_bigint::{Integer, Limb, Monty, Odd, Square, Word};
33

44
use super::{
55
gcd::gcd_vartime,
@@ -161,7 +161,7 @@ impl LucasBase for BruteForceBase {
161161
// Since the loop proceeds in increasing P and starts with P - 2 == 1,
162162
// the shared prime factor must be P + 2.
163163
// If P + 2 == n, then n is prime; otherwise P + 2 is a proper factor of n.
164-
let primality = if n.as_ref() == &T::from(p + 2) {
164+
let primality = if n.as_ref() == &T::from_limb_like(Limb::from(p + 2), n.as_ref()) {
165165
Primality::Prime
166166
} else {
167167
Primality::Composite
@@ -182,6 +182,7 @@ fn decompose<T: Integer>(n: &Odd<T>) -> (u32, Odd<T>) {
182182
// Need to be careful here since `n + 1` can overflow.
183183
// Instead of adding 1 and counting trailing 0s, we count trailing ones on the original `n`.
184184

185+
let one = T::one_like(n);
185186
let s = n.trailing_ones_vartime();
186187
let d = if s < n.bits_precision() {
187188
// The shift won't overflow because of the check above.
@@ -190,10 +191,10 @@ fn decompose<T: Integer>(n: &Odd<T>) -> (u32, Odd<T>) {
190191
n.as_ref()
191192
.overflowing_shr_vartime(s)
192193
.expect("shift should be within range by construction")
193-
.checked_add(&T::one())
194+
.checked_add(&one)
194195
.expect("addition should not overflow by construction")
195196
} else {
196-
T::one()
197+
one
197198
};
198199

199200
(s, Odd::new(d).expect("`d` should be odd by construction"))
@@ -293,6 +294,9 @@ pub fn lucas_test<T: Integer>(
293294
// R. Crandall, C. Pomerance, "Prime numbers: a computational perspective",
294295
// 2nd ed., Springer (2005) (ISBN: 0-387-25282-7, 978-0387-25282-7)
295296

297+
// A word-to-big integer conversion helper
298+
let to_integer = |x: Word| T::from_limb_like(Limb::from(x), candidate.as_ref());
299+
296300
// Find the base for the Lucas sequence.
297301
let (p, abs_q, q_is_negative) = match base.generate(candidate) {
298302
Ok(pq) => pq,
@@ -328,7 +332,7 @@ pub fn lucas_test<T: Integer>(
328332
// it does not noticeably affect the performance.
329333
if abs_q != 1
330334
&& gcd_vartime(candidate.as_ref(), abs_q) != 1
331-
&& candidate.as_ref() > &T::from(abs_q)
335+
&& candidate.as_ref() > &to_integer(abs_q)
332336
{
333337
return Primality::Composite;
334338
}
@@ -351,7 +355,7 @@ pub fn lucas_test<T: Integer>(
351355
let q = if q_is_one {
352356
one.clone()
353357
} else {
354-
let abs_q = <T as Integer>::Monty::new(T::from(abs_q), params.clone());
358+
let abs_q = <T as Integer>::Monty::new(to_integer(abs_q), params.clone());
355359
if q_is_negative {
356360
-abs_q
357361
} else {
@@ -364,7 +368,7 @@ pub fn lucas_test<T: Integer>(
364368
let p = if p_is_one {
365369
one.clone()
366370
} else {
367-
<T as Integer>::Monty::new(T::from(p), params.clone())
371+
<T as Integer>::Monty::new(to_integer(p), params.clone())
368372
};
369373

370374
// Compute d-th element of Lucas sequence (U_d(P, Q), V_d(P, Q)), where:
@@ -387,7 +391,7 @@ pub fn lucas_test<T: Integer>(
387391
let mut qk = one.clone(); // keeps Q^k
388392

389393
// D in Montgomery representation - note that it can be negative.
390-
let abs_d = <T as Integer>::Monty::new(T::from(abs_d), params);
394+
let abs_d = <T as Integer>::Monty::new(to_integer(abs_d), params);
391395
let d_m = if d_is_negative { -abs_d } else { abs_d };
392396

393397
for i in (0..d.bits_vartime()).rev() {

src/hazmat/miller_rabin.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Miller-Rabin primality test.
22
3-
use crypto_bigint::{Integer, Monty, NonZero, Odd, PowBoundedExp, RandomMod, Square};
3+
use crypto_bigint::{Integer, Limb, Monty, NonZero, Odd, PowBoundedExp, RandomMod, Square};
44
use rand_core::CryptoRngCore;
55

66
use super::Primality;
@@ -28,14 +28,16 @@ impl<T: Integer + RandomMod> MillerRabin<T> {
2828
/// Initializes a Miller-Rabin test for `candidate`.
2929
pub fn new(candidate: &Odd<T>) -> Self {
3030
let params = <T as Integer>::Monty::new_params_vartime(candidate.clone());
31-
let one = <T as Integer>::Monty::one(params.clone());
32-
let minus_one = -one.clone();
31+
let m_one = <T as Integer>::Monty::one(params.clone());
32+
let m_minus_one = -m_one.clone();
33+
34+
let one = T::one_like(candidate.as_ref());
3335

3436
// Find `s` and odd `d` such that `candidate - 1 == 2^s * d`.
35-
let (s, d) = if candidate.as_ref() == &T::one() {
36-
(0, T::one())
37+
let (s, d) = if candidate.as_ref() == &one {
38+
(0, one)
3739
} else {
38-
let candidate_minus_one = candidate.wrapping_sub(&T::one());
40+
let candidate_minus_one = candidate.wrapping_sub(&one);
3941
let s = candidate_minus_one.trailing_zeros_vartime();
4042
// Will not overflow because `candidate` is odd and greater than 1.
4143
let d = candidate_minus_one
@@ -48,8 +50,8 @@ impl<T: Integer + RandomMod> MillerRabin<T> {
4850
candidate: candidate.as_ref().clone(),
4951
bit_length: candidate.bits_vartime(),
5052
montgomery_params: params,
51-
one,
52-
minus_one,
53+
one: m_one,
54+
minus_one: m_minus_one,
5355
s,
5456
d,
5557
}
@@ -85,7 +87,7 @@ impl<T: Integer + RandomMod> MillerRabin<T> {
8587

8688
/// Perform a Miller-Rabin check with base 2.
8789
pub fn test_base_two(&self) -> Primality {
88-
self.test(&T::from(2u32))
90+
self.test(&T::from_limb_like(Limb::from(2u32), &self.candidate))
8991
}
9092

9193
/// Perform a Miller-Rabin check with a random base (in the range `[3, candidate-2]`)
@@ -189,7 +191,8 @@ mod tests {
189191
#[test]
190192
fn trivial() {
191193
let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901");
192-
let start = random_odd_integer::<U1024>(&mut rng, NonZeroU32::new(1024).unwrap());
194+
let start =
195+
random_odd_integer::<U1024>(&mut rng, NonZeroU32::new(1024).unwrap(), U1024::BITS);
193196
for num in Sieve::new(start.as_ref(), NonZeroU32::new(1024).unwrap(), false).take(10) {
194197
let mr = MillerRabin::new(&Odd::new(num).unwrap());
195198

src/hazmat/sieve.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ use crate::hazmat::precomputed::{SmallPrime, RECIPROCALS, SMALL_PRIMES};
1414
pub fn random_odd_integer<T: Integer + RandomBits>(
1515
rng: &mut impl CryptoRngCore,
1616
bit_length: NonZeroU32,
17+
bits_precision: u32,
1718
) -> Odd<T> {
1819
let bit_length = bit_length.get();
1920

20-
let mut random = T::random_bits(rng, bit_length);
21-
21+
let mut random = T::random_bits_with_precision(rng, bit_length, bits_precision);
22+
assert!(random.bits_precision() == bits_precision);
2223
// Make it odd
2324
random.set_bit_vartime(0, true);
2425

@@ -99,7 +100,7 @@ impl<T: Integer> Sieve<T> {
99100
base = T::from(3u32);
100101
} else {
101102
// Adjust the base so that we hit odd numbers when incrementing it by 2.
102-
base |= T::one();
103+
base |= T::one_like(start);
103104
}
104105

105106
// Only calculate residues by primes up to and not including `base`,
@@ -157,9 +158,12 @@ impl<T: Integer> Sieve<T> {
157158
}
158159

159160
// Find the increment limit.
160-
let max_value = match T::one().overflowing_shl_vartime(self.max_bit_length).into() {
161+
let max_value = match T::one_like(&self.base)
162+
.overflowing_shl_vartime(self.max_bit_length)
163+
.into()
164+
{
161165
Some(val) => val,
162-
None => T::one(),
166+
None => T::one_like(&self.base),
163167
};
164168
let incr_limit = max_value.wrapping_sub(&self.base);
165169
self.incr_limit = if incr_limit > T::from(INCR_LIMIT) {
@@ -218,7 +222,7 @@ impl<T: Integer> Sieve<T> {
218222
.checked_add(&self.incr.into())
219223
.expect("addition should not overflow by construction");
220224
if self.safe_primes {
221-
num = num.wrapping_shl_vartime(1) | T::one();
225+
num = num.wrapping_shl_vartime(1) | T::one_like(&self.base);
222226
}
223227
Some(num)
224228
};
@@ -279,7 +283,8 @@ mod tests {
279283
let max_prime = SMALL_PRIMES[SMALL_PRIMES.len() - 1];
280284

281285
let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901");
282-
let start = random_odd_integer::<U64>(&mut rng, NonZeroU32::new(32).unwrap()).get();
286+
let start =
287+
random_odd_integer::<U64>(&mut rng, NonZeroU32::new(32).unwrap(), U64::BITS).get();
283288
for num in Sieve::new(&start, NonZeroU32::new(32).unwrap(), false).take(100) {
284289
let num_u64 = u64::from(num);
285290
assert!(num_u64.leading_zeros() == 32);
@@ -360,17 +365,18 @@ mod tests {
360365
#[test]
361366
fn random_below_max_length() {
362367
for _ in 0..10 {
363-
let r = random_odd_integer::<U64>(&mut OsRng, NonZeroU32::new(50).unwrap()).get();
368+
let r = random_odd_integer::<U64>(&mut OsRng, NonZeroU32::new(50).unwrap(), U64::BITS)
369+
.get();
364370
assert_eq!(r.bits(), 50);
365371
}
366372
}
367373

368374
#[test]
369375
#[should_panic(
370-
expected = "try_random_bits() failed: BitLengthTooLarge { bit_length: 65, bits_precision: 64 }"
376+
expected = "try_random_bits_with_precision() failed: BitLengthTooLarge { bit_length: 65, bits_precision: 64 }"
371377
)]
372378
fn random_odd_uint_too_many_bits() {
373-
let _p = random_odd_integer::<U64>(&mut OsRng, NonZeroU32::new(65).unwrap());
379+
let _p = random_odd_integer::<U64>(&mut OsRng, NonZeroU32::new(65).unwrap(), U64::BITS);
374380
}
375381

376382
#[test]

0 commit comments

Comments
 (0)