diff --git a/Cargo.toml b/Cargo.toml index bf7d69f..dfc273a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["cryptography", "no-std"] rust-version = "1.73" [dependencies] -crypto-bigint = { version = "0.6.0-pre.7", default-features = false, features = ["rand_core"] } +crypto-bigint = { version = "0.6.0-pre.7", default-features = false, features = ["rand_core", "zeroize"] } rand_core = { version = "0.6.4", default-features = false } openssl = { version = "0.10.39", optional = true, features = ["vendored"] } rug = { version = "1.26", default-features = false, features = ["integer"], optional = true } diff --git a/benches/bench.rs b/benches/bench.rs index d4f22ed..a90d08f 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -27,14 +27,17 @@ fn make_rng() -> ChaCha8Rng { fn random_odd_uint( rng: &mut impl CryptoRngCore, bit_length: u32, - bits_precision: u32, ) -> Odd { - random_odd_integer::(rng, NonZeroU32::new(bit_length).unwrap(), bits_precision) + random_odd_integer::(rng, NonZeroU32::new(bit_length).unwrap()) } fn make_sieve(rng: &mut impl CryptoRngCore) -> Sieve> { - let start = random_odd_uint::>(rng, Uint::::BITS, Uint::::BITS); - Sieve::new(&start, NonZeroU32::new(Uint::::BITS).unwrap(), false) + let start = random_odd_uint::>(rng, Uint::::BITS); + Sieve::new( + start.get(), + NonZeroU32::new(Uint::::BITS).unwrap(), + false, + ) } fn make_presieved_num(rng: &mut impl CryptoRngCore) -> Odd> { @@ -46,13 +49,13 @@ fn bench_sieve(c: &mut Criterion) { let mut group = c.benchmark_group("Sieve"); group.bench_function("(U128) random start", |b| { - b.iter(|| random_odd_uint::(&mut OsRng, 128, 128)) + b.iter(|| random_odd_uint::(&mut OsRng, 128)) }); group.bench_function("(U128) creation", |b| { b.iter_batched( - || random_odd_uint::(&mut OsRng, 128, 128), - |start| Sieve::new(start.as_ref(), NonZeroU32::new(128).unwrap(), false), + || random_odd_uint::(&mut OsRng, 128), + |start| Sieve::new(start.get(), NonZeroU32::new(128).unwrap(), false), BatchSize::SmallInput, ) }); @@ -67,13 +70,13 @@ fn bench_sieve(c: &mut Criterion) { }); group.bench_function("(U1024) random start", |b| { - b.iter(|| random_odd_uint::(&mut OsRng, 1024, 1024)) + b.iter(|| random_odd_uint::(&mut OsRng, 1024)) }); group.bench_function("(U1024) creation", |b| { b.iter_batched( - || random_odd_uint::(&mut OsRng, 1024, 1024), - |start| Sieve::new(start.as_ref(), NonZeroU32::new(1024).unwrap(), false), + || random_odd_uint::(&mut OsRng, 1024), + |start| Sieve::new(start.get(), NonZeroU32::new(1024).unwrap(), false), BatchSize::SmallInput, ) }); @@ -94,15 +97,15 @@ fn bench_miller_rabin(c: &mut Criterion) { group.bench_function("(U128) creation", |b| { b.iter_batched( - || random_odd_uint::(&mut OsRng, 128, 128), - |n| MillerRabin::new(&n), + || random_odd_uint::(&mut OsRng, 128), + MillerRabin::::new, BatchSize::SmallInput, ) }); group.bench_function("(U128) random base test (pre-sieved)", |b| { b.iter_batched( - || MillerRabin::new(&make_presieved_num::<{ nlimbs!(128) }>(&mut OsRng)), + || MillerRabin::new(make_presieved_num::<{ nlimbs!(128) }>(&mut OsRng)), |mr| mr.test_random_base(&mut OsRng), BatchSize::SmallInput, ) @@ -110,15 +113,15 @@ fn bench_miller_rabin(c: &mut Criterion) { group.bench_function("(U1024) creation", |b| { b.iter_batched( - || random_odd_uint::(&mut OsRng, 1024, 1024), - |n| MillerRabin::new(&n), + || random_odd_uint::(&mut OsRng, 1024), + MillerRabin::::new, BatchSize::SmallInput, ) }); group.bench_function("(U1024) random base test (pre-sieved)", |b| { b.iter_batched( - || MillerRabin::new(&make_presieved_num::<{ nlimbs!(1024) }>(&mut OsRng)), + || MillerRabin::new(make_presieved_num::<{ nlimbs!(1024) }>(&mut OsRng)), |mr| mr.test_random_base(&mut OsRng), BatchSize::SmallInput, ) @@ -132,7 +135,7 @@ fn bench_lucas(c: &mut Criterion) { group.bench_function("(U128) Selfridge base, strong check (pre-sieved)", |b| { b.iter_batched( || make_presieved_num::<{ nlimbs!(128) }>(&mut rng), - |n| lucas_test(&n, SelfridgeBase, LucasCheck::Strong), + |n| lucas_test(n, SelfridgeBase, LucasCheck::Strong), BatchSize::SmallInput, ) }); @@ -141,7 +144,7 @@ fn bench_lucas(c: &mut Criterion) { group.bench_function("(U1024) Selfridge base, strong check (pre-sieved)", |b| { b.iter_batched( || make_presieved_num::<{ nlimbs!(1024) }>(&mut rng), - |n| lucas_test(&n, SelfridgeBase, LucasCheck::Strong), + |n| lucas_test(n, SelfridgeBase, LucasCheck::Strong), BatchSize::SmallInput, ) }); @@ -150,7 +153,7 @@ fn bench_lucas(c: &mut Criterion) { group.bench_function("(U1024) A* base, Lucas-V check (pre-sieved)", |b| { b.iter_batched( || make_presieved_num::<{ nlimbs!(1024) }>(&mut rng), - |n| lucas_test(&n, AStarBase, LucasCheck::LucasV), + |n| lucas_test(n, AStarBase, LucasCheck::LucasV), BatchSize::SmallInput, ) }); @@ -161,7 +164,7 @@ fn bench_lucas(c: &mut Criterion) { |b| { b.iter_batched( || make_presieved_num::<{ nlimbs!(1024) }>(&mut rng), - |n| lucas_test(&n, BruteForceBase, LucasCheck::AlmostExtraStrong), + |n| lucas_test(n, BruteForceBase, LucasCheck::AlmostExtraStrong), BatchSize::SmallInput, ) }, @@ -171,7 +174,7 @@ fn bench_lucas(c: &mut Criterion) { group.bench_function("(U1024) brute force base, extra strong (pre-sieved)", |b| { b.iter_batched( || make_presieved_num::<{ nlimbs!(1024) }>(&mut rng), - |n| lucas_test(&n, BruteForceBase, LucasCheck::ExtraStrong), + |n| lucas_test(n, BruteForceBase, LucasCheck::ExtraStrong), BatchSize::SmallInput, ) }); @@ -191,7 +194,7 @@ fn bench_lucas(c: &mut Criterion) { group.bench_function("(U1024) Selfridge base, strong check, slow path", |b| { b.iter(|| { - lucas_test(&slow_path, SelfridgeBase, LucasCheck::Strong); + lucas_test(slow_path, SelfridgeBase, LucasCheck::Strong); }) }); @@ -203,7 +206,7 @@ fn bench_presets(c: &mut Criterion) { group.bench_function("(U128) Prime test", |b| { b.iter_batched( - || random_odd_uint::(&mut OsRng, 128, 128), + || random_odd_uint::(&mut OsRng, 128), |num| is_prime_with_rng(&mut OsRng, num.as_ref()), BatchSize::SmallInput, ) @@ -211,7 +214,7 @@ fn bench_presets(c: &mut Criterion) { group.bench_function("(U128) Safe prime test", |b| { b.iter_batched( - || random_odd_uint::(&mut OsRng, 128, 128), + || random_odd_uint::(&mut OsRng, 128), |num| is_safe_prime_with_rng(&mut OsRng, num.as_ref()), BatchSize::SmallInput, ) @@ -219,34 +222,34 @@ fn bench_presets(c: &mut Criterion) { let mut rng = make_rng(); group.bench_function("(U128) Random prime", |b| { - b.iter(|| generate_prime_with_rng::(&mut rng, 128, 128)) + b.iter(|| generate_prime_with_rng::(&mut rng, 128)) }); let mut rng = make_rng(); group.bench_function("(U1024) Random prime", |b| { - b.iter(|| generate_prime_with_rng::(&mut rng, 1024, 1024)) + b.iter(|| generate_prime_with_rng::(&mut rng, 1024)) }); let mut rng = make_rng(); group.bench_function("(U128) Random safe prime", |b| { - b.iter(|| generate_safe_prime_with_rng::(&mut rng, 128, 128)) + b.iter(|| generate_safe_prime_with_rng::(&mut rng, 128)) }); group.sample_size(20); let mut rng = make_rng(); group.bench_function("(U1024) Random safe prime", |b| { - b.iter(|| generate_safe_prime_with_rng::(&mut rng, 1024, 1024)) + b.iter(|| generate_safe_prime_with_rng::(&mut rng, 1024)) }); let mut rng = make_rng(); group.bench_function("(Boxed128) Random safe prime", |b| { - b.iter(|| generate_safe_prime_with_rng::(&mut rng, 128, 128)) + b.iter(|| generate_safe_prime_with_rng::(&mut rng, 128)) }); group.sample_size(20); let mut rng = make_rng(); group.bench_function("(Boxed1024) Random safe prime", |b| { - b.iter(|| generate_safe_prime_with_rng::(&mut rng, 1024, 1024)) + b.iter(|| generate_safe_prime_with_rng::(&mut rng, 1024)) }); group.finish(); @@ -256,19 +259,19 @@ fn bench_presets(c: &mut Criterion) { let mut rng = make_rng(); group.bench_function("(U128) Random safe prime", |b| { - b.iter(|| generate_safe_prime_with_rng::(&mut rng, 128, 128)) + b.iter(|| generate_safe_prime_with_rng::(&mut rng, 128)) }); // The performance should scale with the prime size, not with the Uint size. // So we should strive for this test's result to be as close as possible // to that of the previous one and as far away as possible from the next one. group.bench_function("(U256) Random 128 bit safe prime", |b| { - b.iter(|| generate_safe_prime_with_rng::(&mut rng, 128, 256)) + b.iter(|| generate_safe_prime_with_rng::(&mut rng, 128)) }); // The upper bound for the previous test. group.bench_function("(U256) Random 256 bit safe prime", |b| { - b.iter(|| generate_safe_prime_with_rng::(&mut rng, 256, 256)) + b.iter(|| generate_safe_prime_with_rng::(&mut rng, 256)) }); group.finish(); @@ -279,7 +282,7 @@ fn bench_gmp(c: &mut Criterion) { let mut group = c.benchmark_group("GMP"); fn random(rng: &mut impl CryptoRngCore) -> GmpInteger { - let num = random_odd_uint::>(rng, Uint::::BITS, Uint::::BITS).get(); + let num = random_odd_uint::>(rng, Uint::::BITS).get(); GmpInteger::from_digits(num.as_words(), Order::Lsf) } diff --git a/src/hazmat/lucas.rs b/src/hazmat/lucas.rs index f4ad47b..3faeae5 100644 --- a/src/hazmat/lucas.rs +++ b/src/hazmat/lucas.rs @@ -10,7 +10,7 @@ use super::{ /// The maximum number of attempts to find `D` such that `(D/n) == -1`. // This is widely believed to be impossible. // So if we exceed it, we will panic reporting the value of `n`. -const MAX_ATTEMPTS: usize = 10000; +const MAX_ATTEMPTS: usize = 10_000; /// The number of attempts to find `D` such that `(D/n) == -1` /// before checking that `n` is a square (in which case such `D` does not exist). @@ -287,7 +287,7 @@ pub enum LucasCheck { /// See [`LucasCheck`] for possible checks, and the implementors of [`LucasBase`] /// for the corresponding bases. pub fn lucas_test( - candidate: &Odd, + candidate: Odd, base: impl LucasBase, check: LucasCheck, ) -> Primality { @@ -301,7 +301,7 @@ pub fn lucas_test( let to_integer = |x: Word| T::from_limb_like(Limb::from(x), candidate.as_ref()); // Find the base for the Lucas sequence. - let (p, abs_q, q_is_negative) = match base.generate(candidate) { + let (p, abs_q, q_is_negative) = match base.generate(&candidate) { Ok(pq) => pq, Err(primality) => return primality, }; @@ -342,10 +342,11 @@ pub fn lucas_test( // Find `d` and `s`, such that `d` is odd and `d * 2^s = n - (D/n)`. // Since `(D/n) == -1` by construction, we're looking for `d * 2^s = n + 1`. - let (s, d) = decompose(candidate); + let (s, d) = decompose(&candidate); // Some constants in Montgomery form - + // TODO(dp): Cloning `candidate` should not be necessary. It's only needed because the + // `to_integer` closure captures it, but all we really need is the `BITS` let params = ::Monty::new_params_vartime(candidate.clone()); let zero = ::Monty::zero(params.clone()); @@ -567,7 +568,7 @@ mod tests { assert_eq!( lucas_test( - &Odd::new(U64::from(15u32)).unwrap(), + Odd::new(U64::from(15u32)).unwrap(), TestBase, LucasCheck::Strong ), @@ -621,7 +622,7 @@ mod tests { // Test both single-limb and multi-limb, just in case. assert_eq!( lucas_test( - &Odd::new(Uint::<1>::from(*num)).unwrap(), + Odd::new(Uint::<1>::from(*num)).unwrap(), SelfridgeBase, LucasCheck::Strong ) @@ -630,7 +631,7 @@ mod tests { ); assert_eq!( lucas_test( - &Odd::new(Uint::<2>::from(*num)).unwrap(), + Odd::new(Uint::<2>::from(*num)).unwrap(), SelfridgeBase, LucasCheck::Strong ) @@ -652,7 +653,7 @@ mod tests { // Test both single-limb and multi-limb, just in case. assert_eq!( lucas_test( - &Odd::new(Uint::<1>::from(*num)).unwrap(), + Odd::new(Uint::<1>::from(*num)).unwrap(), AStarBase, LucasCheck::LucasV ) @@ -661,7 +662,7 @@ mod tests { ); assert_eq!( lucas_test( - &Odd::new(Uint::<2>::from(*num)).unwrap(), + Odd::new(Uint::<2>::from(*num)).unwrap(), AStarBase, LucasCheck::LucasV ) @@ -692,7 +693,7 @@ mod tests { // Test both single-limb and multi-limb, just in case. assert_eq!( lucas_test( - &Odd::new(Uint::<1>::from(*num)).unwrap(), + Odd::new(Uint::<1>::from(*num)).unwrap(), BruteForceBase, check ) @@ -702,7 +703,7 @@ mod tests { ); assert_eq!( lucas_test( - &Odd::new(Uint::<2>::from(*num)).unwrap(), + Odd::new(Uint::<2>::from(*num)).unwrap(), BruteForceBase, check ) @@ -719,11 +720,11 @@ mod tests { // with `EXTRA_STRONG_LUCAS` or `STRONG_LUCAS` - there's none. for num in pseudoprimes::STRONG_FIBONACCI.iter() { assert!( - !lucas_test(&Odd::new(*num).unwrap(), SelfridgeBase, LucasCheck::Strong) + !lucas_test(Odd::new(*num).unwrap(), SelfridgeBase, LucasCheck::Strong) .is_probably_prime() ); assert!(!lucas_test( - &Odd::new(*num).unwrap(), + Odd::new(*num).unwrap(), BruteForceBase, LucasCheck::ExtraStrong ) @@ -799,21 +800,21 @@ mod tests { #[test] fn large_carmichael_number() { let p = Odd::new(pseudoprimes::LARGE_CARMICHAEL_NUMBER).unwrap(); - assert!(!lucas_test(&p, SelfridgeBase, LucasCheck::Strong).is_probably_prime()); - assert!(!lucas_test(&p, AStarBase, LucasCheck::LucasV).is_probably_prime()); - assert!(!lucas_test(&p, BruteForceBase, LucasCheck::AlmostExtraStrong).is_probably_prime()); - assert!(!lucas_test(&p, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime()); + assert!(!lucas_test(p, SelfridgeBase, LucasCheck::Strong).is_probably_prime()); + assert!(!lucas_test(p, AStarBase, LucasCheck::LucasV).is_probably_prime()); + assert!(!lucas_test(p, BruteForceBase, LucasCheck::AlmostExtraStrong).is_probably_prime()); + assert!(!lucas_test(p, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime()); } fn test_large_primes(nums: &[Uint]) { for num in nums { let num = Odd::new(*num).unwrap(); - assert!(lucas_test(&num, SelfridgeBase, LucasCheck::Strong).is_probably_prime()); - assert!(lucas_test(&num, AStarBase, LucasCheck::LucasV).is_probably_prime()); + assert!(lucas_test(num, SelfridgeBase, LucasCheck::Strong).is_probably_prime()); + assert!(lucas_test(num, AStarBase, LucasCheck::LucasV).is_probably_prime()); assert!( - lucas_test(&num, BruteForceBase, LucasCheck::AlmostExtraStrong).is_probably_prime() + lucas_test(num, BruteForceBase, LucasCheck::AlmostExtraStrong).is_probably_prime() ); - assert!(lucas_test(&num, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime()); + assert!(lucas_test(num, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime()); } } @@ -831,15 +832,14 @@ mod tests { for num in pseudoprimes::LARGE_LUCAS_V { let num = Odd::new(*num).unwrap(); // These are false positives for Lucas-V test - assert!(lucas_test(&num, AStarBase, LucasCheck::LucasV).is_probably_prime()); + assert!(lucas_test(num, AStarBase, LucasCheck::LucasV).is_probably_prime()); // These tests should work correctly - assert!(!lucas_test(&num, SelfridgeBase, LucasCheck::Strong).is_probably_prime()); + assert!(!lucas_test(num, SelfridgeBase, LucasCheck::Strong).is_probably_prime()); assert!( - !lucas_test(&num, BruteForceBase, LucasCheck::AlmostExtraStrong) - .is_probably_prime() + !lucas_test(num, BruteForceBase, LucasCheck::AlmostExtraStrong).is_probably_prime() ); - assert!(!lucas_test(&num, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime()); + assert!(!lucas_test(num, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime()); } } @@ -847,7 +847,7 @@ mod tests { fn corner_cases() { // By convention, 1 is composite. That's what `num-prime` returns. let res = lucas_test( - &Odd::new(U64::ONE).unwrap(), + Odd::new(U64::ONE).unwrap(), BruteForceBase, LucasCheck::AlmostExtraStrong, ); @@ -869,7 +869,7 @@ mod tests { let odd_num = Odd::new(Uint::<1>::from(num)).unwrap(); - let res = lucas_test(&odd_num, BruteForceBase, LucasCheck::AlmostExtraStrong) + let res = lucas_test(odd_num, BruteForceBase, LucasCheck::AlmostExtraStrong) .is_probably_prime(); let expected = aeslpsp || res_ref; assert_eq!( @@ -878,21 +878,21 @@ mod tests { ); let res = - lucas_test(&odd_num, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime(); + lucas_test(odd_num, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime(); let expected = eslpsp || res_ref; assert_eq!( res, expected, "Brute force base: n={num}, expected={expected}, actual={res}", ); - let res = lucas_test(&odd_num, SelfridgeBase, LucasCheck::Strong).is_probably_prime(); + let res = lucas_test(odd_num, SelfridgeBase, LucasCheck::Strong).is_probably_prime(); let expected = slpsp || res_ref; assert_eq!( res, expected, "Selfridge base: n={num}, expected={expected}, actual={res}", ); - let res = lucas_test(&odd_num, AStarBase, LucasCheck::LucasV).is_probably_prime(); + let res = lucas_test(odd_num, AStarBase, LucasCheck::LucasV).is_probably_prime(); let expected = vpsp || res_ref; assert_eq!( diff --git a/src/hazmat/miller_rabin.rs b/src/hazmat/miller_rabin.rs index 50d7ae6..f7b8c99 100644 --- a/src/hazmat/miller_rabin.rs +++ b/src/hazmat/miller_rabin.rs @@ -16,18 +16,28 @@ use super::Primality; /// DOI: [10.2307/2006210](https://dx.doi.org/10.2307/2006210) #[derive(Clone, Debug, PartialEq, Eq)] pub struct MillerRabin { + // The odd number that may or may not be a prime. candidate: T, - bit_length: u32, + /// The number of bits necessesary to represent the candidate. Note: this is not the number of + /// bits used by a `T` in memory. + bits: u32, + /// Pre-computed parameters for the Montgomery form of `T`. montgomery_params: <::Monty as Monty>::Params, + /// The number 1 in Montgomery form. one: ::Monty, + /// The number -1 in Montgomery form. minus_one: ::Monty, + /// The `s` exponent in the Miller-Rabin test, that finds `s` and `d` odd s.t. `candidate - 1 == + /// 2^s * d` (the pair `s` and `d` is unique). s: u32, + /// The `d` factor in the Miller-Rabin test, that finds `s` and `d` odd s.t. `candidate - + /// 1 == 2^s * d` (the pair `s` and `d` is unique). d: T, } impl MillerRabin { /// Initializes a Miller-Rabin test for `candidate`. - pub fn new(candidate: &Odd) -> Self { + pub fn new(candidate: Odd) -> Self { let params = ::Monty::new_params_vartime(candidate.clone()); let m_one = ::Monty::one(params.clone()); let m_minus_one = -m_one.clone(); @@ -43,13 +53,13 @@ impl MillerRabin { // Will not overflow because `candidate` is odd and greater than 1. let d = candidate_minus_one .overflowing_shr_vartime(s) - .expect("shift should be within range by construction"); + .expect("shifting by `s` is within range by construction: `candidate` is odd and greater than 1"); (s, d) }; Self { - candidate: candidate.as_ref().clone(), - bit_length: candidate.bits_vartime(), + bits: candidate.bits_vartime(), + candidate: candidate.get(), montgomery_params: params, one: m_one, minus_one: m_minus_one, @@ -59,7 +69,7 @@ impl MillerRabin { } /// Perform a Miller-Rabin check with a given base. - pub fn test(&self, base: &T) -> Primality { + pub fn test(&self, base: T) -> Primality { // TODO: it may be faster to first check that gcd(base, candidate) == 1, // otherwise we can return `Composite` right away. @@ -70,7 +80,7 @@ impl MillerRabin { // So even when the bound isn't low enough that the number can fit // in a smaller number of limbs, there is still a performance gain // from specifying the bound. - let mut test = base.pow_bounded_exp(&self.d, self.bit_length); + let mut test = base.pow_bounded_exp(&self.d, self.bits); if test == self.one || test == self.minus_one { return Primality::ProbablyPrime; @@ -88,7 +98,7 @@ impl MillerRabin { /// Perform a Miller-Rabin check with base 2. pub fn test_base_two(&self) -> Primality { - self.test(&T::from_limb_like(Limb::from(2u32), &self.candidate)) + self.test(T::from_limb_like(Limb::from(2u32), &self.candidate)) } /// Perform a Miller-Rabin check with a random base (in the range `[3, candidate-2]`) @@ -112,7 +122,16 @@ impl MillerRabin { let random = T::random_mod(rng, &range_nonzero) .checked_add(&T::from(3u32)) .expect("addition should not overflow by construction"); - self.test(&random) + self.test(random) + } + + /// Returns the number of bits necessary to represent the candidate. + /// NOTE: This is different than the number of bits of *storage* the integer takes up. + /// + /// For example, a U512 type occupies 8 64-bit words, but the number `7` contained in such a type + /// has a bit length of 3 because 7 is `b111`. + pub fn bits(&self) -> u32 { + self.bits } } @@ -134,7 +153,7 @@ mod tests { #[test] fn miller_rabin_derived_traits() { - let mr = MillerRabin::new(&Odd::new(U64::ONE).unwrap()); + let mr = MillerRabin::new(Odd::new(U64::ONE).unwrap()); assert!(format!("{mr:?}").starts_with("MillerRabin")); assert_eq!(mr.clone(), mr); } @@ -144,7 +163,7 @@ mod tests { expected = "No suitable random base possible when `candidate == 3`; use the base 2 test." )] fn random_base_range_check() { - let mr = MillerRabin::new(&Odd::new(U64::from(3u32)).unwrap()); + let mr = MillerRabin::new(Odd::new(U64::from(3u32)).unwrap()); mr.test_random_base(&mut OsRng); } @@ -176,7 +195,7 @@ mod tests { // with about 1/4 probability. So we're expecting less than // 35 out of 100 false positives, seems to work. - let mr = MillerRabin::new(&Odd::new(U64::from(*num)).unwrap()); + let mr = MillerRabin::new(Odd::new(U64::from(*num)).unwrap()); assert_eq!( mr.test_base_two().is_probably_prime(), actual_expected_result @@ -192,14 +211,13 @@ mod tests { #[test] fn trivial() { let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); - let start = - random_odd_integer::(&mut rng, NonZeroU32::new(1024).unwrap(), U1024::BITS); - for num in Sieve::new(start.as_ref(), NonZeroU32::new(1024).unwrap(), false).take(10) { - let mr = MillerRabin::new(&Odd::new(num).unwrap()); + let start = random_odd_integer::(&mut rng, NonZeroU32::new(1024).unwrap()); + for num in Sieve::new(start.get(), NonZeroU32::new(1024).unwrap(), false).take(10) { + let mr = MillerRabin::new(Odd::new(num).unwrap()); // Trivial tests, must always be true. - assert!(mr.test(&1u32.into()).is_probably_prime()); - assert!(mr.test(&num.wrapping_sub(&1u32.into())).is_probably_prime()); + assert!(mr.test(1u32.into()).is_probably_prime()); + assert!(mr.test(num.wrapping_sub(&1u32.into())).is_probably_prime()); } } @@ -210,7 +228,7 @@ mod tests { // Mersenne prime 2^127-1 let num = Odd::new(U128::from_be_hex("7fffffffffffffffffffffffffffffff")).unwrap(); - let mr = MillerRabin::new(&num); + let mr = MillerRabin::new(num); assert!(mr.test_base_two().is_probably_prime()); for _ in 0..10 { assert!(mr.test_random_base(&mut rng).is_probably_prime()); @@ -222,7 +240,7 @@ mod tests { let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); for num in pseudoprimes::STRONG_FIBONACCI.iter() { - let mr = MillerRabin::new(&Odd::new(*num).unwrap()); + let mr = MillerRabin::new(Odd::new(*num).unwrap()); assert!(!mr.test_base_two().is_probably_prime()); for _ in 0..1000 { assert!(!mr.test_random_base(&mut rng).is_probably_prime()); @@ -250,20 +268,20 @@ mod tests { #[test] fn large_carmichael_number() { - let mr = MillerRabin::new(&Odd::new(pseudoprimes::LARGE_CARMICHAEL_NUMBER).unwrap()); + let mr = MillerRabin::new(Odd::new(pseudoprimes::LARGE_CARMICHAEL_NUMBER).unwrap()); // It is known to pass MR tests for all prime bases <307 assert!(mr.test_base_two().is_probably_prime()); - assert!(mr.test(&U1536::from(293u64)).is_probably_prime()); + assert!(mr.test(U1536::from(293u64)).is_probably_prime()); // A test with base 307 correctly reports the number as composite. - assert!(!mr.test(&U1536::from(307u64)).is_probably_prime()); + assert!(!mr.test(U1536::from(307u64)).is_probably_prime()); } fn test_large_primes(nums: &[Uint]) { let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); for num in nums { - let mr = MillerRabin::new(&Odd::new(*num).unwrap()); + let mr = MillerRabin::new(Odd::new(*num).unwrap()); assert!(mr.test_base_two().is_probably_prime()); for _ in 0..10 { assert!(mr.test_random_base(&mut rng).is_probably_prime()); @@ -290,7 +308,7 @@ mod tests { let spsp = is_spsp(num); - let mr = MillerRabin::new(&Odd::new(U64::from(num)).unwrap()); + let mr = MillerRabin::new(Odd::new(U64::from(num)).unwrap()); let res = mr.test_base_two().is_probably_prime(); let expected = spsp || res_ref; assert_eq!( diff --git a/src/hazmat/sieve.rs b/src/hazmat/sieve.rs index 31baf42..155ebb0 100644 --- a/src/hazmat/sieve.rs +++ b/src/hazmat/sieve.rs @@ -9,17 +9,18 @@ use rand_core::CryptoRngCore; use crate::hazmat::precomputed::{SmallPrime, RECIPROCALS, SMALL_PRIMES}; -/// Returns a random odd integer with given bit length -/// (that is, with both `0` and `bit_length-1` bits set). +/// Returns a random odd integer with given bit length (that is, with both `0` and `bit_length-1` +/// bits set). +/// +/// *Panics*: if the `bit_length` is bigger than the bits available in the `Integer`, e.g. 37 for a +/// `U32`. pub fn random_odd_integer( rng: &mut impl CryptoRngCore, bit_length: NonZeroU32, - bits_precision: u32, ) -> Odd { let bit_length = bit_length.get(); - let mut random = T::random_bits_with_precision(rng, bit_length, bits_precision); - assert!(random.bits_precision() == bits_precision); + let mut random = T::random_bits(rng, bit_length); // Make it odd random.set_bit_vartime(0, true); @@ -27,7 +28,7 @@ pub fn random_odd_integer( // Will not overflow since `bit_length` is ensured to be within the size of the integer. random.set_bit_vartime(bit_length - 1, true); - Odd::new(random).expect("the number should be odd by construction") + Odd::new(random).expect("the number is odd by construction") } // The type we use to calculate incremental residues. @@ -57,11 +58,9 @@ pub struct Sieve { } impl Sieve { - /// Creates a new sieve, iterating from `start` and - /// until the last number with `max_bit_length` bits, - /// producing numbers that are not non-trivial multiples - /// of a list of small primes in the range `[2, start)` (`safe_primes = false`) - /// or `[2, start/2)` (`safe_primes = true`). + /// Creates a new sieve, iterating from `start` and until the last number with `max_bit_length` + /// bits, producing numbers that are not non-trivial multiples of a list of small primes in the + /// range `[2, start)` (`safe_primes = false`) or `[2, start/2)` (`safe_primes = true`). /// /// Note that `start` is adjusted to `2`, or the next `1 mod 2` number (`safe_primes = false`); /// and `5`, or `3 mod 4` number (`safe_primes = true`). @@ -69,7 +68,7 @@ impl Sieve { /// Panics if `max_bit_length` greater than the precision of `start`. /// /// If `safe_primes` is `true`, both the returned `n` and `n/2` are sieved. - pub fn new(start: &T, max_bit_length: NonZeroU32, safe_primes: bool) -> Self { + pub fn new(start: T, max_bit_length: NonZeroU32, safe_primes: bool) -> Self { let max_bit_length = max_bit_length.get(); if max_bit_length > start.bits_precision() { @@ -84,7 +83,7 @@ impl Sieve { let (max_bit_length, base) = if safe_primes { (max_bit_length - 1, start.wrapping_shr_vartime(1)) } else { - (max_bit_length, start.clone()) + (max_bit_length, start) }; let mut base = base; @@ -100,7 +99,7 @@ impl Sieve { base = T::from(3u32); } else { // Adjust the base so that we hit odd numbers when incrementing it by 2. - base |= T::one_like(start); + base |= T::one(); } // Only calculate residues by primes up to and not including `base`, @@ -186,26 +185,18 @@ impl Sieve { // Returns `true` if the current `base + incr` is divisible by any of the small primes. fn current_is_composite(&self) -> bool { - for (i, m) in self.residues.iter().enumerate() { + self.residues.iter().enumerate().any(|(i, m)| { let d = SMALL_PRIMES[i] as Residue; let r = (*m as Residue + self.incr) % d; - if r == 0 { - return true; - } - // A trick from "Safe Prime Generation with a Combined Sieve" by Michael J. Wiener // (https://eprint.iacr.org/2003/186). // Remember that the check above was for the `(n - 1)/2`; // If `(n - 1)/2 mod d == (d - 1)/2`, it means that `n mod d == 0`. // In other words, we are checking the remainder of `n mod d` // for virtually no additional cost. - if self.safe_primes && r == (d - 1) >> 1 { - return true; - } - } - - false + r == 0 || (self.safe_primes && r == (d - 1) >> 1) + }) } // Returns the restored `base + incr` if it is not composite (wrt the small primes), @@ -283,9 +274,8 @@ mod tests { let max_prime = SMALL_PRIMES[SMALL_PRIMES.len() - 1]; let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); - let start = - random_odd_integer::(&mut rng, NonZeroU32::new(32).unwrap(), U64::BITS).get(); - for num in Sieve::new(&start, NonZeroU32::new(32).unwrap(), false).take(100) { + let start = random_odd_integer::(&mut rng, NonZeroU32::new(32).unwrap()).get(); + for num in Sieve::new(start, NonZeroU32::new(32).unwrap(), false).take(100) { let num_u64 = u64::from(num); assert!(num_u64.leading_zeros() == 32); @@ -295,10 +285,31 @@ mod tests { assert!(factors[0] > max_prime as u64); } } + #[test] + fn random_boxed() { + let max_prime = SMALL_PRIMES[SMALL_PRIMES.len() - 1]; + + let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); + let start = + random_odd_integer::(&mut rng, NonZeroU32::new(32).unwrap()) + .get(); + + for num in Sieve::new(start, NonZeroU32::new(32).unwrap(), false).take(100) { + // For 32-bit targets + #[allow(clippy::useless_conversion)] + let num_u64: u64 = num.as_words()[0].into(); + assert!(num_u64.leading_zeros() == 32); + + let factors_and_powers = factorize64(num_u64); + let factors = factors_and_powers.into_keys().collect::>(); + + assert!(factors[0] > max_prime as u64); + } + } fn check_sieve(start: u32, bit_length: u32, safe_prime: bool, reference: &[u32]) { let test = Sieve::new( - &U64::from(start), + U64::from(start), NonZeroU32::new(bit_length).unwrap(), safe_prime, ) @@ -359,30 +370,37 @@ mod tests { expected = "The requested bit length (65) is larger than the precision of `start`" )] fn sieve_too_many_bits() { - let _sieve = Sieve::new(&U64::ONE, NonZeroU32::new(65).unwrap(), false); + let _sieve = Sieve::new(U64::ONE, NonZeroU32::new(65).unwrap(), false); } #[test] fn random_below_max_length() { for _ in 0..10 { - let r = random_odd_integer::(&mut OsRng, NonZeroU32::new(50).unwrap(), U64::BITS) - .get(); + let r = random_odd_integer::(&mut OsRng, NonZeroU32::new(50).unwrap()).get(); assert_eq!(r.bits(), 50); } } #[test] #[should_panic( - expected = "try_random_bits_with_precision() failed: BitLengthTooLarge { bit_length: 65, bits_precision: 64 }" + expected = "try_random_bits() failed: BitLengthTooLarge { bit_length: 65, bits_precision: 64 }" )] fn random_odd_uint_too_many_bits() { - let _p = random_odd_integer::(&mut OsRng, NonZeroU32::new(65).unwrap(), U64::BITS); + let _p = random_odd_integer::(&mut OsRng, NonZeroU32::new(65).unwrap()); } #[test] fn sieve_derived_traits() { - let s = Sieve::new(&U64::ONE, NonZeroU32::new(10).unwrap(), false); + let s = Sieve::new(U64::ONE, NonZeroU32::new(10).unwrap(), false); + // Debug assert!(format!("{s:?}").starts_with("Sieve")); + // Clone assert_eq!(s.clone(), s); + + // PartialEq + let s2 = Sieve::new(U64::ONE, NonZeroU32::new(10).unwrap(), false); + assert_eq!(s, s2); + let s3 = Sieve::new(U64::ONE, NonZeroU32::new(12).unwrap(), false); + assert_ne!(s, s3); } } diff --git a/src/presets.rs b/src/presets.rs index 2e2a2b5..7a98732 100644 --- a/src/presets.rs +++ b/src/presets.rs @@ -14,11 +14,8 @@ use crate::hazmat::{ /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn generate_prime( - bit_length: u32, - bits_precision: u32, -) -> T { - generate_prime_with_rng(&mut OsRng, bit_length, bits_precision) +pub fn generate_prime(bit_length: u32) -> T { + generate_prime_with_rng(&mut OsRng, bit_length) } /// Returns a random safe prime (that is, such that `(n - 1) / 2` is also prime) of size @@ -26,11 +23,8 @@ pub fn generate_prime( /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn generate_safe_prime( - bit_length: u32, - bits_precision: u32, -) -> T { - generate_safe_prime_with_rng(&mut OsRng, bit_length, bits_precision) +pub fn generate_safe_prime(bit_length: u32) -> T { + generate_safe_prime_with_rng(&mut OsRng, bit_length) } /// Probabilistically checks if the given number is prime using [`OsRng`] as the RNG. @@ -58,19 +52,17 @@ pub fn is_safe_prime(num: &T) -> bool { pub fn generate_prime_with_rng( rng: &mut impl CryptoRngCore, bit_length: u32, - bits_precision: u32, ) -> T { if bit_length < 2 { panic!("`bit_length` must be 2 or greater."); } let bit_length = NonZeroU32::new(bit_length).expect("`bit_length` should be non-zero"); + // Empirically, this loop is traversed 1 time. loop { - let start = random_odd_integer::(rng, bit_length, bits_precision); - let sieve = Sieve::new(start.as_ref(), bit_length, false); - for num in sieve { - if is_prime_with_rng(rng, &num) { - return num; - } + let start = random_odd_integer::(rng, bit_length); + let mut sieve = Sieve::new(start.get(), bit_length, false); + if let Some(prime) = sieve.find(|num| is_prime_with_rng(rng, num)) { + return prime; } } } @@ -84,15 +76,14 @@ pub fn generate_prime_with_rng( pub fn generate_safe_prime_with_rng( rng: &mut impl CryptoRngCore, bit_length: u32, - bits_precision: u32, ) -> T { if bit_length < 3 { panic!("`bit_length` must be 3 or greater."); } let bit_length = NonZeroU32::new(bit_length).expect("`bit_length` should be non-zero"); loop { - let start = random_odd_integer::(rng, bit_length, bits_precision); - let sieve = Sieve::new(start.as_ref(), bit_length, true); + let start = random_odd_integer::(rng, bit_length); + let sieve = Sieve::new(start.get(), bit_length, true); for num in sieve { if is_safe_prime_with_rng(rng, &num) { return num; @@ -132,7 +123,7 @@ pub fn is_prime_with_rng(rng: &mut impl CryptoRngCore, n } match Odd::new(num.clone()).into() { - Some(x) => _is_prime_with_rng(rng, &x), + Some(x) => _is_prime_with_rng(rng, x), None => false, } } @@ -159,28 +150,33 @@ pub fn is_safe_prime_with_rng( } // These are ensured to be odd by the check above. - let odd_num = Odd::new(num.clone()).expect("`num` should be odd here"); - let odd_half_num = Odd::new(num.wrapping_shr_vartime(1)).expect("`num/2` should be odd here"); + let odd_num = Odd::new(num.clone()).expect("`num` is odd here given the checks above"); + let odd_half_num = Odd::new(num.wrapping_shr_vartime(1)).expect("The binary rep of `num` ends in `11`, so shifting right by one is guaranteed leave a `1` at the end, so it's odd"); - _is_prime_with_rng(rng, &odd_num) && _is_prime_with_rng(rng, &odd_half_num) + _is_prime_with_rng(rng, odd_num) && _is_prime_with_rng(rng, odd_half_num) } /// Checks for primality. -fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Odd) -> bool { +/// First run a Miller-Rabin test with base 2 +/// If the outcome of M-R is "probably prime", then run a Lucas test +/// If the Lucas test is inconclusive, run a Miller-Rabin with random base and unless this second +/// M-R test finds it's composite, then conclude that it's prime. +fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: Odd) -> bool { + let candidate = num.clone(); let mr = MillerRabin::new(num); if !mr.test_base_two().is_probably_prime() { return false; } - match lucas_test(num, AStarBase, LucasCheck::Strong) { + match lucas_test(candidate, AStarBase, LucasCheck::Strong) { Primality::Composite => return false, Primality::Prime => return true, _ => {} } // The random base test only makes sense when `num > 3`. - if num.bits() > 2 && !mr.test_random_base(rng).is_probably_prime() { + if mr.bits() > 2 && !mr.test_random_base(rng).is_probably_prime() { return false; } @@ -189,7 +185,7 @@ fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: #[cfg(test)] mod tests { - use crypto_bigint::{BoxedUint, CheckedAdd, Uint, Word, U128, U64}; + use crypto_bigint::{nlimbs, BoxedUint, CheckedAdd, Uint, Word, U128, U64}; use num_prime::nt_funcs::is_prime64; use rand_core::OsRng; @@ -270,7 +266,7 @@ mod tests { #[test] fn prime_generation() { for bit_length in (28..=128).step_by(10) { - let p: U128 = generate_prime(bit_length, U128::BITS); + let p: U128 = generate_prime(bit_length); assert!(p.bits_vartime() == bit_length); assert!(is_prime(&p)); } @@ -279,8 +275,9 @@ mod tests { #[test] fn prime_generation_boxed() { for bit_length in (28..=128).step_by(10) { - let p: BoxedUint = generate_prime(bit_length, 128); + let p: BoxedUint = generate_prime(bit_length); assert!(p.bits_vartime() == bit_length); + assert!(p.to_words().len() == nlimbs!(bit_length)); assert!(is_prime(&p)); } } @@ -288,7 +285,7 @@ mod tests { #[test] fn safe_prime_generation() { for bit_length in (28..=128).step_by(10) { - let p: U128 = generate_safe_prime(bit_length, U128::BITS); + let p: U128 = generate_safe_prime(bit_length); assert!(p.bits_vartime() == bit_length); assert!(is_safe_prime(&p)); } @@ -296,9 +293,10 @@ mod tests { #[test] fn safe_prime_generation_boxed() { - for bit_length in (28..=128).step_by(10) { - let p: BoxedUint = generate_safe_prime(bit_length, 128); + for bit_length in (28..=189).step_by(10) { + let p: BoxedUint = generate_safe_prime(bit_length); assert!(p.bits_vartime() == bit_length); + assert!(p.to_words().len() == nlimbs!(bit_length)); assert!(is_safe_prime(&p)); } } @@ -330,29 +328,29 @@ mod tests { #[test] #[should_panic(expected = "`bit_length` must be 2 or greater")] fn generate_prime_too_few_bits() { - let _p: U64 = generate_prime_with_rng(&mut OsRng, 1, U64::BITS); + let _p: U64 = generate_prime_with_rng(&mut OsRng, 1); } #[test] #[should_panic(expected = "`bit_length` must be 3 or greater")] fn generate_safe_prime_too_few_bits() { - let _p: U64 = generate_safe_prime_with_rng(&mut OsRng, 2, U64::BITS); + let _p: U64 = generate_safe_prime_with_rng(&mut OsRng, 2); } #[test] #[should_panic( - expected = "try_random_bits_with_precision() failed: BitLengthTooLarge { bit_length: 65, bits_precision: 64 }" + expected = "try_random_bits() failed: BitLengthTooLarge { bit_length: 65, bits_precision: 64 }" )] fn generate_prime_too_many_bits() { - let _p: U64 = generate_prime_with_rng(&mut OsRng, 65, U64::BITS); + let _p: U64 = generate_prime_with_rng(&mut OsRng, 65); } #[test] #[should_panic( - expected = "try_random_bits_with_precision() failed: BitLengthTooLarge { bit_length: 65, bits_precision: 64 }" + expected = "try_random_bits() failed: BitLengthTooLarge { bit_length: 65, bits_precision: 64 }" )] fn generate_safe_prime_too_many_bits() { - let _p: U64 = generate_safe_prime_with_rng(&mut OsRng, 65, U64::BITS); + let _p: U64 = generate_safe_prime_with_rng(&mut OsRng, 65); } fn is_prime_ref(num: Word) -> bool { @@ -363,7 +361,7 @@ mod tests { fn corner_cases_generate_prime() { for bits in 2..5 { for _ in 0..100 { - let p: U64 = generate_prime(bits, U64::BITS); + let p: U64 = generate_prime(bits); let p_word = p.as_words()[0]; assert!(is_prime_ref(p_word)); } @@ -374,7 +372,7 @@ mod tests { fn corner_cases_generate_safe_prime() { for bits in 3..5 { for _ in 0..100 { - let p: U64 = generate_safe_prime(bits, U64::BITS); + let p: U64 = generate_safe_prime(bits); let p_word = p.as_words()[0]; assert!(is_prime_ref(p_word) && is_prime_ref(p_word / 2)); } @@ -413,7 +411,7 @@ mod tests_openssl { // Generate primes, let OpenSSL check them for _ in 0..100 { - let p: U128 = generate_prime(128, U128::BITS); + let p: U128 = generate_prime(128); let p_bn = to_openssl(&p); assert!( openssl_is_prime(&p_bn, &mut ctx), @@ -431,8 +429,7 @@ mod tests_openssl { // Generate random numbers, check if our test agrees with OpenSSL for _ in 0..100 { - let p = - random_odd_integer::(&mut OsRng, NonZeroU32::new(128).unwrap(), U128::BITS); + let p = random_odd_integer::(&mut OsRng, NonZeroU32::new(128).unwrap()); let actual = is_prime(p.as_ref()); let p_bn = to_openssl(&p); let expected = openssl_is_prime(&p_bn, &mut ctx); @@ -475,15 +472,14 @@ mod tests_gmp { fn gmp_cross_check() { // Generate primes, let GMP check them for _ in 0..100 { - let p: U128 = generate_prime(128, U128::BITS); + let p: U128 = generate_prime(128); let p_bn = to_gmp(&p); assert!(gmp_is_prime(&p_bn), "GMP reports {p} as composite"); } // Generate primes with GMP, check them for _ in 0..100 { - let start = - random_odd_integer::(&mut OsRng, NonZeroU32::new(128).unwrap(), U128::BITS); + let start = random_odd_integer::(&mut OsRng, NonZeroU32::new(128).unwrap()); let start_bn = to_gmp(&start); let p_bn = start_bn.next_prime(); let p = from_gmp(&p_bn); @@ -492,8 +488,7 @@ mod tests_gmp { // Generate random numbers, check if our test agrees with GMP for _ in 0..100 { - let p = - random_odd_integer::(&mut OsRng, NonZeroU32::new(128).unwrap(), U128::BITS); + let p = random_odd_integer::(&mut OsRng, NonZeroU32::new(128).unwrap()); let actual = is_prime(p.as_ref()); let p_bn = to_gmp(&p); let expected = gmp_is_prime(&p_bn); diff --git a/src/traits.rs b/src/traits.rs index e8af4d6..e657b3e 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,4 +1,4 @@ -use crypto_bigint::{Integer, RandomBits, RandomMod}; +use crypto_bigint::{zeroize::Zeroize, Integer, RandomBits, RandomMod}; use rand_core::CryptoRngCore; use crate::{ @@ -8,17 +8,13 @@ use crate::{ /// Provides a generic way to access methods for random prime number generation /// and primality checking, wrapping the standalone functions ([`is_prime_with_rng`] etc). -pub trait RandomPrimeWithRng { +pub trait RandomPrimeWithRng: Zeroize { /// Returns a random prime of size `bit_length` using the provided RNG. /// /// Panics if `bit_length` is less than 2, or greater than the bit size of the target `Uint`. /// /// See [`is_prime_with_rng`] for details about the performed checks. - fn generate_prime_with_rng( - rng: &mut impl CryptoRngCore, - bit_length: u32, - bits_precision: u32, - ) -> Self; + fn generate_prime_with_rng(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self; /// Returns a random safe prime (that is, such that `(n - 1) / 2` is also prime) /// of size `bit_length` using the provided RNG. @@ -26,11 +22,7 @@ pub trait RandomPrimeWithRng { /// Panics if `bit_length` is less than 3, or greater than the bit size of the target `Uint`. /// /// See [`is_prime_with_rng`] for details about the performed checks. - fn generate_safe_prime_with_rng( - rng: &mut impl CryptoRngCore, - bit_length: u32, - bits_precision: u32, - ) -> Self; + fn generate_safe_prime_with_rng(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self; /// Probabilistically checks if the given number is prime using the provided RNG. /// @@ -45,21 +37,13 @@ pub trait RandomPrimeWithRng { impl RandomPrimeWithRng for T where - T: Integer + RandomBits + RandomMod, + T: Integer + RandomBits + RandomMod + Zeroize, { - fn generate_prime_with_rng( - rng: &mut impl CryptoRngCore, - bit_length: u32, - bits_precision: u32, - ) -> Self { - generate_prime_with_rng(rng, bit_length, bits_precision) + fn generate_prime_with_rng(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self { + generate_prime_with_rng(rng, bit_length) } - fn generate_safe_prime_with_rng( - rng: &mut impl CryptoRngCore, - bit_length: u32, - bits_precision: u32, - ) -> Self { - generate_safe_prime_with_rng(rng, bit_length, bits_precision) + fn generate_safe_prime_with_rng(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self { + generate_safe_prime_with_rng(rng, bit_length) } fn is_prime_with_rng(&self, rng: &mut impl CryptoRngCore) -> bool { is_prime_with_rng(rng, self) @@ -71,7 +55,7 @@ where #[cfg(test)] mod tests { - use crypto_bigint::U64; + use crypto_bigint::{BoxedUint, U64}; use rand_core::OsRng; use super::RandomPrimeWithRng; @@ -84,10 +68,22 @@ mod tests { assert!(!U64::from(13u32).is_safe_prime_with_rng(&mut OsRng)); assert!(U64::from(11u32).is_safe_prime_with_rng(&mut OsRng)); + assert!(U64::generate_prime_with_rng(&mut OsRng, 10).is_prime_with_rng(&mut OsRng)); assert!( - U64::generate_prime_with_rng(&mut OsRng, 10, U64::BITS).is_prime_with_rng(&mut OsRng) + U64::generate_safe_prime_with_rng(&mut OsRng, 10).is_safe_prime_with_rng(&mut OsRng) ); - assert!(U64::generate_safe_prime_with_rng(&mut OsRng, 10, U64::BITS) + } + + #[test] + fn boxed_uint_impl() { + assert!(!BoxedUint::from(15u32).is_prime_with_rng(&mut OsRng)); + assert!(BoxedUint::from(19u32).is_prime_with_rng(&mut OsRng)); + + assert!(!BoxedUint::from(13u32).is_safe_prime_with_rng(&mut OsRng)); + assert!(BoxedUint::from(11u32).is_safe_prime_with_rng(&mut OsRng)); + + assert!(BoxedUint::generate_prime_with_rng(&mut OsRng, 10).is_prime_with_rng(&mut OsRng)); + assert!(BoxedUint::generate_safe_prime_with_rng(&mut OsRng, 10) .is_safe_prime_with_rng(&mut OsRng)); } }