Skip to content

Commit 875622c

Browse files
authored
Merge pull request #40 from fjarri/boxed-support
`BoxedUint` support
2 parents 752bdee + 2b44a1f commit 875622c

File tree

9 files changed

+163
-87
lines changed

9 files changed

+163
-87
lines changed

CHANGELOG.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
### Changed
1010

11-
- Bumped `crypto-bigint` to 0.6.0-pre.6. ([#38])
12-
- Bumped MSRV to 1.73. (#[38])
13-
- `MillerRabin::new()` takes an `Odd`-wrapped integer. `random_odd_uint()` is renamed to `random_odd_integer()`, takes a `NonZeroU32` for `bit_length`, and returns an `Odd`-wrapped integer. `LucasBase::generate()` takes an `Odd`-wrapped integer. `lucas_test` takes an `Odd`-wrapped integer. (#[38])
14-
- All bit length-type parameters take `u32` instead of `usize`. (#[38])
15-
- All the API is based on the `Integer` trait instead of `Uint` specifically. (#[38])
11+
- Bumped `crypto-bigint` to 0.6.0-pre.7. ([#40])
12+
- Bumped MSRV to 1.73. ([#36])
13+
- `MillerRabin::new()` takes an `Odd`-wrapped integer by value. `random_odd_uint()` returns an `Odd`-wrapped integer. `LucasBase::generate()` takes an `Odd`-wrapped integer. `lucas_test` takes an `Odd`-wrapped integer. ([#36])
14+
- `random_odd_uint()` is renamed to `random_odd_integer()`, takes a `NonZeroU32` for `bit_length`. ([#38])
15+
- All bit length-type parameters take `u32` instead of `usize`. ([#36])
16+
- All the API is based on the `Integer` trait instead of `Uint` specifically. ([#38])
17+
- High-level generation/checking functions take an additional `bits_precision` argument. ([#40])
1618

1719

1820
[#36]: https://github.com/entropyxyz/crypto-primes/pull/36
1921
[#38]: https://github.com/entropyxyz/crypto-primes/pull/38
22+
[#40]: https://github.com/entropyxyz/crypto-primes/pull/40
2023

2124

2225
## [0.5.0] - 2023-08-20

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ categories = ["cryptography", "no-std"]
1010
rust-version = "1.73"
1111

1212
[dependencies]
13-
crypto-bigint = { version = "0.6.0-pre.6", default-features = false, features = ["rand_core"] }
13+
crypto-bigint = { version = "0.6.0-pre.7", default-features = false, features = ["rand_core"] }
1414
rand_core = { version = "0.6.4", default-features = false }
1515
openssl = { version = "0.10.39", optional = true, features = ["vendored"] }
1616
rug = { version = "1.18", default-features = false, features = ["integer"], optional = true }
1717

1818
[dev-dependencies]
19+
# need `crypto-bigint` with `alloc` to test `BoxedUint`
20+
crypto-bigint = { version = "0.6.0-pre.7", default-features = false, features = ["alloc"] }
1921
rand_chacha = "0.3"
2022
criterion = { version = "0.4", features = ["html_reports"] }
2123
num-modular = { version = "0.5", features = ["num-bigint"] }

benches/bench.rs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::num::NonZeroU32;
22

33
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
4-
use crypto_bigint::{nlimbs, Integer, Odd, RandomBits, Uint, U1024, U128, U256};
4+
use crypto_bigint::{nlimbs, BoxedUint, Integer, Odd, RandomBits, Uint, U1024, U128, U256};
55
use rand_chacha::ChaCha8Rng;
66
use rand_core::{CryptoRngCore, OsRng, SeedableRng};
77

@@ -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,50 @@ 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))
239+
});
240+
241+
let mut rng = make_rng();
242+
group.bench_function("(Boxed128) Random safe prime", |b| {
243+
b.iter(|| generate_safe_prime_with_rng::<BoxedUint>(&mut rng, 128, 128))
244+
});
245+
246+
group.sample_size(20);
247+
let mut rng = make_rng();
248+
group.bench_function("(Boxed1024) Random safe prime", |b| {
249+
b.iter(|| generate_safe_prime_with_rng::<BoxedUint>(&mut rng, 1024, 1024))
238250
});
239251

240252
group.finish();
@@ -244,19 +256,19 @@ fn bench_presets(c: &mut Criterion) {
244256

245257
let mut rng = make_rng();
246258
group.bench_function("(U128) Random safe prime", |b| {
247-
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128))
259+
b.iter(|| generate_safe_prime_with_rng::<U128>(&mut rng, 128, 128))
248260
});
249261

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

257269
// The upper bound for the previous test.
258270
group.bench_function("(U256) Random 256 bit safe prime", |b| {
259-
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 256))
271+
b.iter(|| generate_safe_prime_with_rng::<U256>(&mut rng, 256, 256))
260272
});
261273

262274
group.finish();
@@ -267,7 +279,7 @@ fn bench_gmp(c: &mut Criterion) {
267279
let mut group = c.benchmark_group("GMP");
268280

269281
fn random<const L: usize>(rng: &mut impl CryptoRngCore) -> GmpInteger {
270-
let num = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS).get();
282+
let num = random_odd_uint::<Uint<L>>(rng, Uint::<L>::BITS, Uint::<L>::BITS).get();
271283
GmpInteger::from_digits(num.as_words(), Order::Lsf)
272284
}
273285

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

0 commit comments

Comments
 (0)