diff --git a/CHANGELOG.md b/CHANGELOG.md index c34723d..2692ff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Bumped `crypto-bigint` to 0.6.0-pre.5. ([#38]) +- Bumped `crypto-bigint` to 0.6.0-pre.6. ([#38]) - Bumped MSRV to 1.73. (#[38]) -- `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. (#[38]) +- `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]) - All bit length-type parameters take `u32` instead of `usize`. (#[38]) +- All the API is based on the `Integer` trait instead of `Uint` specifically. (#[38]) -[#35]: https://github.com/entropyxyz/crypto-primes/pull/38 +[#36]: https://github.com/entropyxyz/crypto-primes/pull/36 +[#38]: https://github.com/entropyxyz/crypto-primes/pull/38 ## [0.5.0] - 2023-08-20 diff --git a/Cargo.toml b/Cargo.toml index 54b05f6..f0741d4 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.5", default-features = false, features = ["rand_core"] } +crypto-bigint = { version = "0.6.0-pre.6", default-features = false, features = ["rand_core"] } rand_core = { version = "0.6.4", default-features = false } openssl = { version = "0.10.39", optional = true, features = ["vendored"] } rug = { version = "1.18", default-features = false, features = ["integer"], optional = true } diff --git a/benches/bench.rs b/benches/bench.rs index 48eb504..3923f46 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -1,10 +1,12 @@ +use core::num::NonZeroU32; + use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; -use crypto_bigint::{nlimbs, Odd, Uint, U1024}; +use crypto_bigint::{nlimbs, Integer, Odd, RandomBits, Uint, U1024, U128, U256}; use rand_chacha::ChaCha8Rng; use rand_core::{CryptoRngCore, OsRng, SeedableRng}; #[cfg(feature = "tests-gmp")] -use rug::{integer::Order, Integer}; +use rug::{integer::Order, Integer as GmpInteger}; #[cfg(feature = "tests-openssl")] use openssl::bn::BigNum; @@ -12,7 +14,7 @@ use openssl::bn::BigNum; use crypto_primes::{ generate_prime_with_rng, generate_safe_prime_with_rng, hazmat::{ - lucas_test, random_odd_uint, AStarBase, BruteForceBase, LucasCheck, MillerRabin, + lucas_test, random_odd_integer, AStarBase, BruteForceBase, LucasCheck, MillerRabin, SelfridgeBase, Sieve, }, is_prime_with_rng, is_safe_prime_with_rng, @@ -22,9 +24,16 @@ fn make_rng() -> ChaCha8Rng { ChaCha8Rng::from_seed(*b"01234567890123456789012345678901") } -fn make_sieve(rng: &mut impl CryptoRngCore) -> Sieve { - let start = random_odd_uint::(rng, Uint::::BITS); - Sieve::new(&start, Uint::::BITS, false) +fn random_odd_uint( + rng: &mut impl CryptoRngCore, + bit_length: u32, +) -> Odd { + 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); + Sieve::new(&start, NonZeroU32::new(Uint::::BITS).unwrap(), false) } fn make_presieved_num(rng: &mut impl CryptoRngCore) -> Odd> { @@ -36,13 +45,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::<{ nlimbs!(128) }>(&mut OsRng, 128)) + b.iter(|| random_odd_uint::(&mut OsRng, 128)) }); group.bench_function("(U128) creation", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128), - |start| Sieve::new(&start, 128, false), + || random_odd_uint::(&mut OsRng, 128), + |start| Sieve::new(start.as_ref(), NonZeroU32::new(128).unwrap(), false), BatchSize::SmallInput, ) }); @@ -57,13 +66,13 @@ fn bench_sieve(c: &mut Criterion) { }); group.bench_function("(U1024) random start", |b| { - b.iter(|| random_odd_uint::<{ nlimbs!(1024) }>(&mut OsRng, 1024)) + b.iter(|| random_odd_uint::(&mut OsRng, 1024)) }); group.bench_function("(U1024) creation", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(1024) }>(&mut OsRng, 1024), - |start| Sieve::new(&start, 1024, false), + || random_odd_uint::(&mut OsRng, 1024), + |start| Sieve::new(start.as_ref(), NonZeroU32::new(1024).unwrap(), false), BatchSize::SmallInput, ) }); @@ -84,15 +93,15 @@ fn bench_miller_rabin(c: &mut Criterion) { group.bench_function("(U128) creation", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128), - MillerRabin::new, + || random_odd_uint::(&mut OsRng, 128), + |n| MillerRabin::new(&n), 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, ) @@ -100,15 +109,15 @@ fn bench_miller_rabin(c: &mut Criterion) { group.bench_function("(U1024) creation", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(1024) }>(&mut OsRng, 1024), - MillerRabin::new, + || random_odd_uint::(&mut OsRng, 1024), + |n| MillerRabin::new(&n), 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, ) @@ -193,39 +202,39 @@ fn bench_presets(c: &mut Criterion) { group.bench_function("(U128) Prime test", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128), - |num| is_prime_with_rng(&mut OsRng, &num), + || random_odd_uint::(&mut OsRng, 128), + |num| is_prime_with_rng(&mut OsRng, num.as_ref()), BatchSize::SmallInput, ) }); group.bench_function("(U128) Safe prime test", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128), - |num| is_safe_prime_with_rng(&mut OsRng, &num), + || random_odd_uint::(&mut OsRng, 128), + |num| is_safe_prime_with_rng(&mut OsRng, num.as_ref()), BatchSize::SmallInput, ) }); let mut rng = make_rng(); group.bench_function("(U128) Random prime", |b| { - b.iter(|| generate_prime_with_rng::<{ nlimbs!(128) }>(&mut rng, None)) + 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::<{ nlimbs!(1024) }>(&mut rng, None)) + 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::<{ nlimbs!(128) }>(&mut rng, None)) + 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::<{ nlimbs!(1024) }>(&mut rng, None)) + b.iter(|| generate_safe_prime_with_rng::(&mut rng, 1024)) }); group.finish(); @@ -235,19 +244,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::<{ nlimbs!(128) }>(&mut rng, None)) + 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::<{ nlimbs!(256) }>(&mut rng, Some(128))) + 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::<{ nlimbs!(256) }>(&mut rng, None)) + b.iter(|| generate_safe_prime_with_rng::(&mut rng, 256)) }); group.finish(); @@ -257,9 +266,9 @@ fn bench_presets(c: &mut Criterion) { fn bench_gmp(c: &mut Criterion) { let mut group = c.benchmark_group("GMP"); - fn random(rng: &mut impl CryptoRngCore) -> Integer { - let num = random_odd_uint::(rng, Uint::::BITS); - Integer::from_digits(num.as_words(), Order::Lsf) + fn random(rng: &mut impl CryptoRngCore) -> GmpInteger { + let num = random_odd_uint::>(rng, Uint::::BITS).get(); + GmpInteger::from_digits(num.as_words(), Order::Lsf) } group.bench_function("(U128) Random prime", |b| { diff --git a/src/hazmat.rs b/src/hazmat.rs index f07e5b8..456b0d1 100644 --- a/src/hazmat.rs +++ b/src/hazmat.rs @@ -14,7 +14,7 @@ mod sieve; pub use lucas::{lucas_test, AStarBase, BruteForceBase, LucasBase, LucasCheck, SelfridgeBase}; pub use miller_rabin::MillerRabin; -pub use sieve::{random_odd_uint, Sieve}; +pub use sieve::{random_odd_integer, Sieve}; /// Possible results of various primality tests. #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/src/hazmat/gcd.rs b/src/hazmat/gcd.rs index c51e4a2..57a0c85 100644 --- a/src/hazmat/gcd.rs +++ b/src/hazmat/gcd.rs @@ -1,9 +1,9 @@ -use crypto_bigint::{Limb, NonZero, Uint, Word}; +use crypto_bigint::{Integer, Limb, NonZero, Word}; /// Calculates the greatest common divisor of `n` and `m`. /// By definition, `gcd(0, m) == m`. /// `n` must be non-zero. -pub(crate) fn gcd_vartime(n: &Uint, m: Word) -> Word { +pub(crate) fn gcd_vartime(n: &T, m: Word) -> Word { // This is an internal function, and it will never be called with `m = 0`. // Allowing `m = 0` would require us to have the return type of `Uint` // (since `gcd(n, 0) = n`). @@ -11,7 +11,7 @@ pub(crate) fn gcd_vartime(n: &Uint, m: Word) -> Word { // This we can check since it doesn't affect the return type, // even though `n` will not be 0 either in the application. - if n == &Uint::::ZERO { + if n.is_zero().into() { return m; } @@ -23,7 +23,7 @@ pub(crate) fn gcd_vartime(n: &Uint, m: Word) -> Word { } else { // In this branch `n` is `Word::BITS` bits or shorter, // so we can safely take the first limb. - let n = n.as_words()[0]; + let n = n.as_ref()[0].0; if n > m { (n, m) } else { diff --git a/src/hazmat/jacobi.rs b/src/hazmat/jacobi.rs index f45434f..c435872 100644 --- a/src/hazmat/jacobi.rs +++ b/src/hazmat/jacobi.rs @@ -1,6 +1,6 @@ //! Jacobi symbol calculation. -use crypto_bigint::{Limb, NonZero, Odd, Uint, Word}; +use crypto_bigint::{Integer, Limb, NonZero, Odd, Word}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum JacobiSymbol { @@ -20,37 +20,13 @@ impl core::ops::Neg for JacobiSymbol { } } -// A helper trait to generalize some functions over Word and Uint. -trait SmallMod { - fn mod8(&self) -> Word; - fn mod4(&self) -> Word; -} - -impl SmallMod for Word { - fn mod8(&self) -> Word { - self & 7 - } - fn mod4(&self) -> Word { - self & 3 - } -} - -impl SmallMod for Uint { - fn mod8(&self) -> Word { - self.as_limbs()[0].0 & 7 - } - fn mod4(&self) -> Word { - self.as_limbs()[0].0 & 3 - } -} - /// Transforms `(a/p)` -> `(r/p)` for odd `p`, where the resulting `r` is odd, and `a = r * 2^s`. /// Takes a Jacobi symbol value, and returns `r` and the new Jacobi symbol, /// negated if the transformation changes parity. /// /// Note that the returned `r` is odd. -fn reduce_numerator(j: JacobiSymbol, a: Word, p: &V) -> (JacobiSymbol, Word) { - let p_mod_8 = p.mod8(); +fn apply_reduce_numerator(j: JacobiSymbol, a: Word, p: Word) -> (JacobiSymbol, Word) { + let p_mod_8 = p & 7; let s = a.trailing_zeros(); let j = if (s & 1) == 1 && (p_mod_8 == 3 || p_mod_8 == 5) { -j @@ -60,23 +36,40 @@ fn reduce_numerator(j: JacobiSymbol, a: Word, p: &V) -> (JacobiSymb (j, a >> s) } +fn reduce_numerator_long(j: JacobiSymbol, a: Word, p: &T) -> (JacobiSymbol, Word) { + apply_reduce_numerator(j, a, p.as_ref()[0].0) +} + +fn reduce_numerator_short(j: JacobiSymbol, a: Word, p: Word) -> (JacobiSymbol, Word) { + apply_reduce_numerator(j, a, p) +} + /// Transforms `(a/p)` -> `(p/a)` for odd and coprime `a` and `p`. /// Takes a Jacobi symbol value, and returns the swapped pair and the new Jacobi symbol, /// negated if the transformation changes parity. -fn swap(j: JacobiSymbol, a: T, p: V) -> (JacobiSymbol, V, T) { - let j = if a.mod4() == 1 || p.mod4() == 1 { +fn apply_swap(j: JacobiSymbol, a: Word, p: Word) -> JacobiSymbol { + if a & 3 == 1 || p & 3 == 1 { j } else { -j - }; + } +} + +fn swap_long(j: JacobiSymbol, a: Word, p: &Odd) -> (JacobiSymbol, &Odd, Word) { + let j = apply_swap(j, a, p.as_ref().as_ref()[0].0); + (j, p, a) +} + +fn swap_short(j: JacobiSymbol, a: Word, p: Word) -> (JacobiSymbol, Word, Word) { + let j = apply_swap(j, a, p); (j, p, a) } /// Returns the Jacobi symbol `(a/p)` given an odd `p`. -pub(crate) fn jacobi_symbol_vartime( +pub(crate) fn jacobi_symbol_vartime( abs_a: Word, a_is_negative: bool, - p_long: &Odd>, + p_long: &Odd, ) -> JacobiSymbol { let result = JacobiSymbol::One; // Keep track of all the sign flips here. @@ -84,14 +77,14 @@ pub(crate) fn jacobi_symbol_vartime( // (-a/n) = (-1/n) * (a/n) // = (-1)^((n-1)/2) * (a/n) // = (-1 if n = 3 mod 4 else 1) * (a/n) - let result = if a_is_negative && p_long.mod4() == 3 { + let result = if a_is_negative && p_long.as_ref().as_ref()[0].0 & 3 == 3 { -result } else { result }; // A degenerate case. - if abs_a == 1 || p_long.as_ref() == &Uint::::ONE { + if abs_a == 1 || p_long.as_ref() == &T::one() { return result; } @@ -100,14 +93,14 @@ pub(crate) fn jacobi_symbol_vartime( // Normalize input: at the end we want `a < p`, `p` odd, and both fitting into a `Word`. let (result, a, p): (JacobiSymbol, Word, Word) = if p_long.bits_vartime() <= Limb::BITS { let a = a_limb.0; - let p = p_long.as_limbs()[0].0; + let p = p_long.as_ref().as_ref()[0].0; (result, a % p, p) } else { - let (result, a) = reduce_numerator(result, a_limb.0, p_long.as_ref()); + let (result, a) = reduce_numerator_long(result, a_limb.0, p_long.as_ref()); if a == 1 { return result; } - let (result, a_long, p) = swap(result, a, p_long.get()); + let (result, a_long, p) = swap_long(result, a, p_long); // Can unwrap here, since `p` is swapped with `a`, // and `a` would be odd after `reduce_numerator()`. let a = @@ -127,7 +120,7 @@ pub(crate) fn jacobi_symbol_vartime( // At this point `p` is odd (either coming from outside of the `loop`, // or from the previous iteration, where a previously reduced `a` // was swapped into its place), so we can call this. - (result, a) = reduce_numerator(result, a, &p); + (result, a) = reduce_numerator_short(result, a, p); if a == 1 { return result; @@ -138,7 +131,7 @@ pub(crate) fn jacobi_symbol_vartime( // Note that technically `swap()` only returns a valid `result` if `a` and `p` are coprime. // But if they are not, we will return `Zero` eventually, // which is not affected by any sign changes. - (result, a, p) = swap(result, a, p); + (result, a, p) = swap_short(result, a, p); a %= p; } diff --git a/src/hazmat/lucas.rs b/src/hazmat/lucas.rs index d96676a..42ddbde 100644 --- a/src/hazmat/lucas.rs +++ b/src/hazmat/lucas.rs @@ -1,8 +1,5 @@ //! Lucas primality test. -use crypto_bigint::{ - modular::{MontyForm, MontyParams}, - CheckedAdd, Integer, Odd, Uint, Word, -}; +use crypto_bigint::{Integer, Monty, Odd, Square, Word}; use super::{ gcd::gcd_vartime, @@ -30,7 +27,7 @@ pub trait LucasBase { /// Given an odd integer, returns `Ok((P, abs(Q), is_negative(Q)))` on success, /// or `Err(Primality)` if the primality for the given integer was discovered /// during the search for a base. - fn generate(&self, n: &Odd>) -> Result<(Word, Word, bool), Primality>; + fn generate(&self, n: &Odd) -> Result<(Word, Word, bool), Primality>; } /// "Method A" for selecting the base given in Baillie & Wagstaff[^Baillie1980], @@ -48,11 +45,11 @@ pub trait LucasBase { pub struct SelfridgeBase; impl LucasBase for SelfridgeBase { - fn generate(&self, n: &Odd>) -> Result<(Word, Word, bool), Primality> { + fn generate(&self, n: &Odd) -> Result<(Word, Word, bool), Primality> { let mut abs_d = 5; let mut d_is_negative = false; let n_is_small = n.bits_vartime() < Word::BITS; // if true, `n` fits into one `Word` - let small_n = n.as_words()[0]; + let small_n = n.as_ref().as_ref()[0].0; let mut attempts = 0; loop { if attempts >= MAX_ATTEMPTS { @@ -61,7 +58,7 @@ impl LucasBase for SelfridgeBase { if attempts >= ATTEMPTS_BEFORE_SQRT { let sqrt_n = n.sqrt_vartime(); - if &sqrt_n.wrapping_mul(&sqrt_n) == n { + if &sqrt_n.wrapping_mul(&sqrt_n) == n.as_ref() { return Err(Primality::Composite); } } @@ -113,7 +110,7 @@ impl LucasBase for SelfridgeBase { pub struct AStarBase; impl LucasBase for AStarBase { - fn generate(&self, n: &Odd>) -> Result<(Word, Word, bool), Primality> { + fn generate(&self, n: &Odd) -> Result<(Word, Word, bool), Primality> { SelfridgeBase.generate(n).map(|(p, abs_q, q_is_negative)| { if abs_q == 1 && q_is_negative { (5, 5, false) @@ -136,7 +133,7 @@ impl LucasBase for AStarBase { pub struct BruteForceBase; impl LucasBase for BruteForceBase { - fn generate(&self, n: &Odd>) -> Result<(Word, Word, bool), Primality> { + fn generate(&self, n: &Odd) -> Result<(Word, Word, bool), Primality> { let mut p = 3; let mut attempts = 0; @@ -147,7 +144,7 @@ impl LucasBase for BruteForceBase { if attempts >= ATTEMPTS_BEFORE_SQRT { let sqrt_n = n.sqrt_vartime(); - if &sqrt_n.wrapping_mul(&sqrt_n) == n { + if &sqrt_n.wrapping_mul(&sqrt_n) == n.as_ref() { return Err(Primality::Composite); } } @@ -164,7 +161,7 @@ impl LucasBase for BruteForceBase { // Since the loop proceeds in increasing P and starts with P - 2 == 1, // the shared prime factor must be P + 2. // If P + 2 == n, then n is prime; otherwise P + 2 is a proper factor of n. - let primality = if n.as_ref() == &Uint::::from(p + 2) { + let primality = if n.as_ref() == &T::from(p + 2) { Primality::Prime } else { Primality::Composite @@ -181,22 +178,22 @@ impl LucasBase for BruteForceBase { } /// For the given odd `n`, finds `s` and odd `d` such that `n + 1 == 2^s * d`. -fn decompose(n: &Odd>) -> (u32, Odd>) { +fn decompose(n: &Odd) -> (u32, Odd) { // Need to be careful here since `n + 1` can overflow. // Instead of adding 1 and counting trailing 0s, we count trailing ones on the original `n`. - let s = n.trailing_ones(); + let s = n.trailing_ones_vartime(); let d = if s < n.bits_precision() { // The shift won't overflow because of the check above. // The addition won't overflow since the original `n` was odd, // so we right-shifted at least once. n.as_ref() - .overflowing_shr(s) + .overflowing_shr_vartime(s) .expect("shift should be within range by construction") - .checked_add(&Uint::ONE) + .checked_add(&T::one()) .expect("addition should not overflow by construction") } else { - Uint::ONE + T::one() }; (s, Odd::new(d).expect("`d` should be odd by construction")) @@ -285,8 +282,8 @@ pub enum LucasCheck { /// Performs the primality test based on Lucas sequence. /// See [`LucasCheck`] for possible checks, and the implementors of [`LucasBase`] /// for the corresponding bases. -pub fn lucas_test( - candidate: &Odd>, +pub fn lucas_test( + candidate: &Odd, base: impl LucasBase, check: LucasCheck, ) -> Primality { @@ -330,8 +327,8 @@ pub fn lucas_test( // we check that gcd(n, Q) = 1 anyway - again, since `Q` is small, // it does not noticeably affect the performance. if abs_q != 1 - && gcd_vartime(candidate, abs_q) != 1 - && candidate.as_ref() > &Uint::::from(abs_q) + && gcd_vartime(candidate.as_ref(), abs_q) != 1 + && candidate.as_ref() > &T::from(abs_q) { return Primality::Composite; } @@ -342,19 +339,19 @@ pub fn lucas_test( // Some constants in Montgomery form - let params = MontyParams::::new(*candidate); + let params = ::Monty::new_params_vartime(candidate.clone()); - let zero = MontyForm::::zero(params); - let one = MontyForm::::one(params); - let two = one + one; - let minus_two = -two; + let zero = ::Monty::zero(params.clone()); + let one = ::Monty::one(params.clone()); + let two = one.clone() + &one; + let minus_two = -two.clone(); // Convert Q to Montgomery form let q = if q_is_one { - one + one.clone() } else { - let abs_q = MontyForm::::new(&Uint::::from(abs_q), params); + let abs_q = ::Monty::new(T::from(abs_q), params.clone()); if q_is_negative { -abs_q } else { @@ -365,9 +362,9 @@ pub fn lucas_test( // Convert P to Montgomery form let p = if p_is_one { - one + one.clone() } else { - MontyForm::::new(&Uint::::from(p), params) + ::Monty::new(T::from(p), params.clone()) }; // Compute d-th element of Lucas sequence (U_d(P, Q), V_d(P, Q)), where: @@ -385,19 +382,19 @@ pub fn lucas_test( // We can therefore start with k=0 and build up to k=d in log2(d) steps. // Starting with k = 0 - let mut vk = two; // keeps V_k - let mut uk = MontyForm::::zero(params); // keeps U_k - let mut qk = one; // keeps Q^k + let mut vk = two.clone(); // keeps V_k + let mut uk = ::Monty::zero(params.clone()); // keeps U_k + let mut qk = one.clone(); // keeps Q^k // D in Montgomery representation - note that it can be negative. - let abs_d = MontyForm::::new(&Uint::::from(abs_d), params); + let abs_d = ::Monty::new(T::from(abs_d), params); let d_m = if d_is_negative { -abs_d } else { abs_d }; for i in (0..d.bits_vartime()).rev() { // k' = k * 2 - let u_2k = uk * vk; - let v_2k = vk.square() - (qk + qk); + let u_2k = uk * &vk; + let v_2k = vk.square() - &(qk.clone() + &qk); let q_2k = qk.square(); uk = u_2k; @@ -407,11 +404,15 @@ pub fn lucas_test( if d.bit_vartime(i) { // k' = k + 1 - let (p_uk, p_vk) = if p_is_one { (uk, vk) } else { (p * uk, p * vk) }; + let (p_uk, p_vk) = if p_is_one { + (uk.clone(), vk.clone()) + } else { + (p.clone() * &uk, p.clone() * &vk) + }; - let u_k1 = (p_uk + vk).div_by_2(); - let v_k1 = (d_m * uk + p_vk).div_by_2(); - let q_k1 = qk * q; + let u_k1 = (p_uk + &vk).div_by_2(); + let v_k1 = (d_m.clone() * &uk + &p_vk).div_by_2(); + let q_k1 = qk * &q; uk = u_k1; vk = v_k1; @@ -469,7 +470,7 @@ pub fn lucas_test( // k' = 2k // V_{k'} = V_k^2 - 2 Q^k - vk = vk * vk - qk - qk; + vk = vk.square() - &qk - &qk; if check != LucasCheck::LucasV && vk == zero { return Primality::ProbablyPrime; @@ -483,10 +484,10 @@ pub fn lucas_test( if check == LucasCheck::LucasV { // At this point vk = V_{d * 2^(s-1)}. // Double the index again: - vk = vk * vk - qk - qk; // now vk = V_{d * 2^s} = V_{n+1} + vk = vk.square() - &qk - &qk; // now vk = V_{d * 2^s} = V_{n+1} // Lucas-V check[^Baillie2021]: if V_{n+1} == 2 Q, report `n` as prime. - if vk == q + q { + if vk == q.clone() + &q { return Primality::ProbablyPrime; } } @@ -499,7 +500,7 @@ mod tests { use alloc::format; - use crypto_bigint::{Odd, Uint, Word, U128, U64}; + use crypto_bigint::{Integer, Odd, Uint, Word, U128, U64}; #[cfg(feature = "tests-exhaustive")] use num_prime::nt_funcs::is_prime64; @@ -552,10 +553,7 @@ mod tests { struct TestBase; impl LucasBase for TestBase { - fn generate( - &self, - _n: &Odd>, - ) -> Result<(Word, Word, bool), Primality> { + fn generate(&self, _n: &Odd) -> Result<(Word, Word, bool), Primality> { Ok((5, 5, false)) } } diff --git a/src/hazmat/miller_rabin.rs b/src/hazmat/miller_rabin.rs index faf1156..31e2439 100644 --- a/src/hazmat/miller_rabin.rs +++ b/src/hazmat/miller_rabin.rs @@ -1,12 +1,8 @@ //! Miller-Rabin primality test. +use crypto_bigint::{Integer, Monty, NonZero, Odd, PowBoundedExp, RandomMod, Square}; use rand_core::CryptoRngCore; -use crypto_bigint::{ - modular::{MontyForm, MontyParams}, - CheckedAdd, NonZero, Odd, RandomMod, Uint, -}; - use super::Primality; /// Precomputed data used to perform Miller-Rabin primality test[^Pomerance1980]. @@ -17,31 +13,29 @@ use super::Primality; /// C. Pomerance, J. L. Selfridge, S. S. Wagstaff "The Pseudoprimes to 25*10^9", /// Math. Comp. 35 1003-1026 (1980), /// DOI: [10.2307/2006210](https://dx.doi.org/10.2307/2006210) -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct MillerRabin { - candidate: Uint, +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MillerRabin { + candidate: T, bit_length: u32, - montgomery_params: MontyParams, - one: MontyForm, - minus_one: MontyForm, + montgomery_params: <::Monty as Monty>::Params, + one: ::Monty, + minus_one: ::Monty, s: u32, - d: Uint, + d: T, } -impl MillerRabin { +impl MillerRabin { /// Initializes a Miller-Rabin test for `candidate`. - /// - /// Panics if `candidate` is even. - pub fn new(candidate: Odd>) -> Self { - let params = MontyParams::::new(candidate); - let one = MontyForm::::one(params); - let minus_one = -one; + pub fn new(candidate: &Odd) -> Self { + let params = ::Monty::new_params_vartime(candidate.clone()); + let one = ::Monty::one(params.clone()); + let minus_one = -one.clone(); // Find `s` and odd `d` such that `candidate - 1 == 2^s * d`. - let (s, d) = if candidate.as_ref() == &Uint::ONE { - (0, Uint::ONE) + let (s, d) = if candidate.as_ref() == &T::one() { + (0, T::one()) } else { - let candidate_minus_one = candidate.wrapping_sub(&Uint::ONE); + let candidate_minus_one = candidate.wrapping_sub(&T::one()); let s = candidate_minus_one.trailing_zeros_vartime(); // Will not overflow because `candidate` is odd and greater than 1. let d = candidate_minus_one @@ -51,7 +45,7 @@ impl MillerRabin { }; Self { - candidate: *candidate, + candidate: candidate.as_ref().clone(), bit_length: candidate.bits_vartime(), montgomery_params: params, one, @@ -62,11 +56,11 @@ impl MillerRabin { } /// Perform a Miller-Rabin check with a given base. - pub fn test(&self, base: &Uint) -> 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. - let base = MontyForm::::new(base, self.montgomery_params); + let base = ::Monty::new(base.clone(), self.montgomery_params.clone()); // Implementation detail: bounded exp gets faster every time we decrease the bound // by the window length it uses, which is currently 4 bits. @@ -91,7 +85,7 @@ impl MillerRabin { /// Perform a Miller-Rabin check with base 2. pub fn test_base_two(&self) -> Primality { - self.test(&Uint::::from(2u32)) + self.test(&T::from(2u32)) } /// Perform a Miller-Rabin check with a random base (in the range `[3, candidate-2]`) @@ -106,14 +100,14 @@ impl MillerRabin { panic!("No suitable random base possible when `candidate == 3`; use the base 2 test.") } - let range = self.candidate.wrapping_sub(&Uint::::from(4u32)); + let range = self.candidate.wrapping_sub(&T::from(4u32)); // Can unwrap here since `candidate` is odd, and `candidate >= 4` (as checked above) let range_nonzero = NonZero::new(range).expect("the range should be non-zero by construction"); // This should not overflow as long as `random_mod()` behaves according to the contract // (that is, returns a number within the given range). - let random = Uint::::random_mod(rng, &range_nonzero) - .checked_add(&Uint::::from(3u32)) + let random = T::random_mod(rng, &range_nonzero) + .checked_add(&T::from(3u32)) .expect("addition should not overflow by construction"); self.test(&random) } @@ -123,8 +117,9 @@ impl MillerRabin { mod tests { use alloc::format; + use core::num::NonZeroU32; - use crypto_bigint::{Odd, Uint, U1024, U128, U1536, U64}; + use crypto_bigint::{Integer, Odd, RandomMod, Uint, U1024, U128, U1536, U64}; use rand_chacha::ChaCha8Rng; use rand_core::{CryptoRngCore, OsRng, SeedableRng}; @@ -132,11 +127,11 @@ mod tests { use num_prime::nt_funcs::is_prime64; use super::MillerRabin; - use crate::hazmat::{primes, pseudoprimes, random_odd_uint, Sieve}; + use crate::hazmat::{primes, pseudoprimes, random_odd_integer, Sieve}; #[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); } @@ -146,7 +141,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); } @@ -154,9 +149,9 @@ mod tests { pseudoprimes::STRONG_BASE_2.iter().any(|x| *x == num) } - fn random_checks( + fn random_checks( rng: &mut impl CryptoRngCore, - mr: &MillerRabin, + mr: &MillerRabin, count: usize, ) -> usize { (0..count) @@ -178,7 +173,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 @@ -194,9 +189,9 @@ mod tests { #[test] fn trivial() { let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); - let start: Odd = random_odd_uint(&mut rng, 1024); - for num in Sieve::new(&start, 1024, 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.as_ref(), 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()); @@ -209,9 +204,9 @@ mod tests { let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); // Mersenne prime 2^127-1 - let num = U128::from_be_hex("7fffffffffffffffffffffffffffffff"); + let num = Odd::new(U128::from_be_hex("7fffffffffffffffffffffffffffffff")).unwrap(); - let mr = MillerRabin::new(Odd::new(num).unwrap()); + 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()); @@ -223,7 +218,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()); @@ -251,7 +246,7 @@ 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()); @@ -264,7 +259,7 @@ mod tests { 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()); @@ -291,7 +286,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 8b10edd..57f183c 100644 --- a/src/hazmat/sieve.rs +++ b/src/hazmat/sieve.rs @@ -2,45 +2,29 @@ //! before proceeding with slower tests. use alloc::{vec, vec::Vec}; +use core::num::NonZeroU32; -use crypto_bigint::{CheckedAdd, Odd, Random, Uint}; +use crypto_bigint::{Integer, Odd, RandomBits}; 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). -/// -/// Panics if `bit_length` is 0 or is greater than the bit size of the target `Uint`. -pub fn random_odd_uint( +pub fn random_odd_integer( rng: &mut impl CryptoRngCore, - bit_length: u32, -) -> Odd> { - if bit_length == 0 { - panic!("Bit length must be non-zero"); - } - - if bit_length > Uint::::BITS { - panic!( - "The requested bit length ({}) is larger than the chosen Uint size", - bit_length - ); - } + bit_length: NonZeroU32, +) -> Odd { + let bit_length = bit_length.get(); - // TODO: not particularly efficient, can be improved by zeroing high bits instead of shifting - let mut random = Uint::::random(rng); - if bit_length != Uint::::BITS { - random >>= Uint::::BITS - bit_length; - } + let mut random = T::random_bits(rng, bit_length); // Make it odd - random |= Uint::::ONE; + random.set_bit_vartime(0, true); // Make sure it's the correct bit size // Will not overflow since `bit_length` is ensured to be within the size of the integer. - random |= Uint::::ONE - .overflowing_shl_vartime(bit_length - 1) - .expect("shift should be within range by construction"); + random.set_bit_vartime(bit_length - 1, true); Odd::new(random).expect("the number should be odd by construction") } @@ -56,11 +40,11 @@ const INCR_LIMIT: Residue = Residue::MAX - SMALL_PRIMES[SMALL_PRIMES.len() - 1] /// An iterator returning numbers with up to and including given bit length, /// starting from a given number, that are not multiples of the first 2048 small primes. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Sieve { - // Instead of dividing `Uint` by small primes every time (which is slow), +pub struct Sieve { + // Instead of dividing a big integer by small primes every time (which is slow), // we keep a "base" and a small increment separately, // so that we can only calculate the residues of the increment. - base: Uint, + base: T, incr: Residue, incr_limit: Residue, safe_primes: bool, @@ -71,7 +55,7 @@ pub struct Sieve { last_round: bool, } -impl 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 @@ -81,17 +65,15 @@ impl Sieve { /// 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`). /// - /// Panics if `max_bit_length` is zero or greater than the size of the target `Uint`. + /// 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: &Uint, max_bit_length: u32, safe_primes: bool) -> Self { - if max_bit_length == 0 { - panic!("The requested bit length cannot be zero"); - } + 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 > Uint::::BITS { + if max_bit_length > start.bits_precision() { panic!( - "The requested bit length ({}) is larger than the chosen Uint size", + "The requested bit length ({}) is larger than the precision of `start`", max_bit_length ); } @@ -101,33 +83,33 @@ impl Sieve { let (max_bit_length, base) = if safe_primes { (max_bit_length - 1, start.wrapping_shr_vartime(1)) } else { - (max_bit_length, *start) + (max_bit_length, start.clone()) }; let mut base = base; // This is easier than making all the methods generic enough to handle these corner cases. - let produces_nothing = max_bit_length < base.bits() || max_bit_length < 2; + let produces_nothing = max_bit_length < base.bits_vartime() || max_bit_length < 2; // Add the exception to the produced candidates - the only one that doesn't fit // the general pattern of incrementing the base by 2. let mut starts_from_exception = false; - if base <= Uint::::from(2u32) { + if base <= T::from(2u32) { starts_from_exception = true; - base = Uint::::from(3u32); + base = T::from(3u32); } else { // Adjust the base so that we hit odd numbers when incrementing it by 2. - base |= Uint::::ONE; + base |= T::one(); } // Only calculate residues by primes up to and not including `base`, // because when we only have the resiude, // we cannot distinguish between a prime itself and a multiple of that prime. - let residues_len = if Uint::::from(SMALL_PRIMES[SMALL_PRIMES.len() - 1]) >= base { + let residues_len = if T::from(SMALL_PRIMES[SMALL_PRIMES.len() - 1]) >= base { SMALL_PRIMES .iter() .enumerate() - .find(|(_i, p)| Uint::::from(**p) >= base) + .find(|(_i, p)| T::from(**p) >= base) .map(|(i, _p)| i) .unwrap_or(SMALL_PRIMES.len()) } else { @@ -175,12 +157,12 @@ impl Sieve { } // Find the increment limit. - let max_value = Uint::::ONE - .overflowing_shl(self.max_bit_length) - .unwrap_or(Uint::ZERO) - .wrapping_sub(&Uint::::ONE); + let max_value = match T::one().overflowing_shl_vartime(self.max_bit_length).into() { + Some(val) => val, + None => T::one(), + }; let incr_limit = max_value.wrapping_sub(&self.base); - self.incr_limit = if incr_limit > Uint::::from(INCR_LIMIT) { + self.incr_limit = if incr_limit > T::from(INCR_LIMIT) { INCR_LIMIT } else { // We are close to `2^max_bit_length - 1`. @@ -188,7 +170,8 @@ impl Sieve { self.last_round = true; // Can unwrap here since we just checked above that `incr_limit <= INCR_LIMIT`, // and `INCR_LIMIT` fits into `Residue`. - let incr_limit_small: Residue = incr_limit.as_words()[0] + let incr_limit_small: Residue = incr_limit.as_ref()[0] + .0 .try_into() .expect("the increment limit should fit within `Residue`"); incr_limit_small @@ -223,19 +206,19 @@ impl Sieve { // Returns the restored `base + incr` if it is not composite (wrt the small primes), // and bumps the increment unconditionally. - fn maybe_next(&mut self) -> Option> { + fn maybe_next(&mut self) -> Option { let result = if self.current_is_composite() { None } else { // The overflow should never happen here since `incr` // is never greater than `incr_limit`, and the latter is chosen such that // it does not overflow when added to `base` (see `update_residues()`). - let mut num: Uint = self + let mut num = self .base .checked_add(&self.incr.into()) .expect("addition should not overflow by construction"); if self.safe_primes { - num = num.wrapping_shl_vartime(1) | Uint::::ONE; + num = num.wrapping_shl_vartime(1) | T::one(); } Some(num) }; @@ -244,7 +227,7 @@ impl Sieve { result } - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option { // Corner cases handled here if self.produces_nothing { @@ -253,7 +236,7 @@ impl Sieve { if self.starts_from_exception { self.starts_from_exception = false; - return Some(Uint::::from(if self.safe_primes { 5u32 } else { 2u32 })); + return Some(T::from(if self.safe_primes { 5u32 } else { 2u32 })); } // Main loop @@ -268,8 +251,8 @@ impl Sieve { } } -impl Iterator for Sieve { - type Item = Uint; +impl Iterator for Sieve { + type Item = T; fn next(&mut self) -> Option { Self::next(self) @@ -281,13 +264,14 @@ mod tests { use alloc::format; use alloc::vec::Vec; + use core::num::NonZeroU32; - use crypto_bigint::{Odd, U64}; + use crypto_bigint::U64; use num_prime::nt_funcs::factorize64; use rand_chacha::ChaCha8Rng; use rand_core::{OsRng, SeedableRng}; - use super::{random_odd_uint, Sieve}; + use super::{random_odd_integer, Sieve}; use crate::hazmat::precomputed::SMALL_PRIMES; #[test] @@ -295,9 +279,9 @@ mod tests { let max_prime = SMALL_PRIMES[SMALL_PRIMES.len() - 1]; let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); - let start: Odd = random_odd_uint(&mut rng, 32); - for num in Sieve::new(&start, 32, false).take(100) { - let num_u64: u64 = num.into(); + 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); let factors_and_powers = factorize64(num_u64); @@ -308,7 +292,12 @@ mod tests { } fn check_sieve(start: u32, bit_length: u32, safe_prime: bool, reference: &[u32]) { - let test = Sieve::new(&U64::from(start), bit_length, safe_prime).collect::>(); + let test = Sieve::new( + &U64::from(start), + NonZeroU32::new(bit_length).unwrap(), + safe_prime, + ) + .collect::>(); assert_eq!(test.len(), reference.len()); for (x, y) in test.iter().zip(reference.iter()) { assert_eq!(x, &U64::from(*y)); @@ -361,40 +350,32 @@ mod tests { } #[test] - #[should_panic(expected = "The requested bit length cannot be zero")] - fn sieve_zero_bits() { - let _sieve = Sieve::new(&U64::ONE, 0, false); - } - - #[test] - #[should_panic(expected = "The requested bit length (65) is larger than the chosen Uint size")] + #[should_panic( + expected = "The requested bit length (65) is larger than the precision of `start`" + )] fn sieve_too_many_bits() { - let _sieve = Sieve::new(&U64::ONE, 65, false); + let _sieve = Sieve::new(&U64::ONE, NonZeroU32::new(65).unwrap(), false); } #[test] fn random_below_max_length() { for _ in 0..10 { - let r: Odd = random_odd_uint(&mut OsRng, 50); + let r = random_odd_integer::(&mut OsRng, NonZeroU32::new(50).unwrap()).get(); assert_eq!(r.bits(), 50); } } #[test] - #[should_panic(expected = "Bit length must be non-zero")] - fn random_odd_uint_0bits() { - let _p: Odd = random_odd_uint(&mut OsRng, 0); - } - - #[test] - #[should_panic(expected = "The requested bit length (65) is larger than the chosen Uint size")] + #[should_panic( + expected = "try_random_bits() failed: BitLengthTooLarge { bit_length: 65, bits_precision: 64 }" + )] fn random_odd_uint_too_many_bits() { - let _p: Odd = random_odd_uint(&mut OsRng, 65); + let _p = random_odd_integer::(&mut OsRng, NonZeroU32::new(65).unwrap()); } #[test] fn sieve_derived_traits() { - let s = Sieve::new(&U64::ONE, 10, false); + let s = Sieve::new(&U64::ONE, NonZeroU32::new(10).unwrap(), false); assert!(format!("{s:?}").starts_with("Sieve")); assert_eq!(s.clone(), s); } diff --git a/src/presets.rs b/src/presets.rs index 5b880ab..ba80c98 100644 --- a/src/presets.rs +++ b/src/presets.rs @@ -1,11 +1,13 @@ -use crypto_bigint::{Odd, Uint}; +use core::num::NonZeroU32; + +use crypto_bigint::{Integer, Odd, RandomBits, RandomMod}; use rand_core::CryptoRngCore; #[cfg(feature = "default-rng")] use rand_core::OsRng; use crate::hazmat::{ - lucas_test, random_odd_uint, AStarBase, LucasCheck, MillerRabin, Primality, Sieve, + lucas_test, random_odd_integer, AStarBase, LucasCheck, MillerRabin, Primality, Sieve, }; /// Returns a random prime of size `bit_length` using [`OsRng`] as the RNG. @@ -13,7 +15,7 @@ use crate::hazmat::{ /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn generate_prime(bit_length: Option) -> Uint { +pub fn generate_prime(bit_length: u32) -> T { generate_prime_with_rng(&mut OsRng, bit_length) } @@ -23,7 +25,7 @@ pub fn generate_prime(bit_length: Option) -> Uint { /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn generate_safe_prime(bit_length: Option) -> Uint { +pub fn generate_safe_prime(bit_length: u32) -> T { generate_safe_prime_with_rng(&mut OsRng, bit_length) } @@ -31,7 +33,7 @@ pub fn generate_safe_prime(bit_length: Option) -> Uint { /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn is_prime(num: &Uint) -> bool { +pub fn is_prime(num: &T) -> bool { is_prime_with_rng(&mut OsRng, num) } @@ -41,7 +43,7 @@ pub fn is_prime(num: &Uint) -> bool { /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn is_safe_prime(num: &Uint) -> bool { +pub fn is_safe_prime(num: &T) -> bool { is_safe_prime_with_rng(&mut OsRng, num) } @@ -51,17 +53,17 @@ pub fn is_safe_prime(num: &Uint) -> bool { /// 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. -pub fn generate_prime_with_rng( +pub fn generate_prime_with_rng( rng: &mut impl CryptoRngCore, - bit_length: Option, -) -> Uint { - let bit_length = bit_length.unwrap_or(Uint::::BITS); + bit_length: 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"); loop { - let start = random_odd_uint::(rng, bit_length); - let sieve = Sieve::new(&start, bit_length, false); + let start = random_odd_integer::(rng, bit_length); + let sieve = Sieve::new(start.as_ref(), bit_length, false); for num in sieve { if is_prime_with_rng(rng, &num) { return num; @@ -77,17 +79,17 @@ pub fn generate_prime_with_rng( /// Panics if `bit_length` is less than 3, or is greater than the bit size of the target `Uint`. /// /// See [`is_prime_with_rng`] for details about the performed checks. -pub fn generate_safe_prime_with_rng( +pub fn generate_safe_prime_with_rng( rng: &mut impl CryptoRngCore, - bit_length: Option, -) -> Uint { - let bit_length = bit_length.unwrap_or(Uint::::BITS); + bit_length: 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_uint::(rng, bit_length); - let sieve = Sieve::new(&start, bit_length, true); + let start = random_odd_integer::(rng, bit_length); + let sieve = Sieve::new(start.as_ref(), bit_length, true); for num in sieve { if is_safe_prime_with_rng(rng, &num) { return num; @@ -121,12 +123,12 @@ pub fn generate_safe_prime_with_rng( /// "Strengthening the Baillie-PSW primality test", /// Math. Comp. 90 1931-1955 (2021), /// DOI: [10.1090/mcom/3616](https://doi.org/10.1090/mcom/3616) -pub fn is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Uint) -> bool { - if num == &Uint::::from(2u32) { +pub fn is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &T) -> bool { + if num == &T::from(2u32) { return true; } - let odd_num = match Odd::new(*num).into() { + let odd_num = match Odd::new(num.clone()).into() { Some(x) => x, None => return false, }; @@ -137,27 +139,30 @@ pub fn is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Uin /// Checks probabilistically if the given number is a safe prime using the provided RNG. /// /// See [`is_prime_with_rng`] for details about the performed checks. -pub fn is_safe_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Uint) -> bool { +pub fn is_safe_prime_with_rng( + rng: &mut impl CryptoRngCore, + num: &T, +) -> bool { // Since, by the definition of safe prime, `(num - 1) / 2` must also be prime, // and therefore odd, `num` has to be equal to 3 modulo 4. // 5 is the only exception, so we check for it. - if num == &Uint::::from(5u32) { + if num == &T::from(5u32) { return true; } - if num.as_words()[0] & 3 != 3 { + if num.as_ref()[0].0 & 3 != 3 { return false; } // These are ensured to be odd by the check above. - let odd_num = Odd::new(*num).expect("`num` should be odd here"); + 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"); _is_prime_with_rng(rng, &odd_num) && _is_prime_with_rng(rng, &odd_half_num) } /// Checks for primality assuming that `num` is odd. -fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Odd>) -> bool { - let mr = MillerRabin::new(*num); +fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Odd) -> bool { + let mr = MillerRabin::new(num); if !mr.test_base_two().is_probably_prime() { return false; @@ -238,8 +243,7 @@ mod tests { } next = next - .overflowing_shl_vartime(1) - .unwrap() + .wrapping_shl_vartime(1) .checked_add(&Uint::::ONE) .unwrap(); } @@ -261,7 +265,7 @@ mod tests { #[test] fn prime_generation() { for bit_length in (28..=128).step_by(10) { - let p: U128 = generate_prime(Some(bit_length)); + let p: U128 = generate_prime(bit_length); assert!(p.bits_vartime() == bit_length); assert!(is_prime(&p)); } @@ -270,7 +274,7 @@ mod tests { #[test] fn safe_prime_generation() { for bit_length in (28..=128).step_by(10) { - let p: U128 = generate_safe_prime(Some(bit_length)); + let p: U128 = generate_safe_prime(bit_length); assert!(p.bits_vartime() == bit_length); assert!(is_safe_prime(&p)); } @@ -303,25 +307,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, Some(1)); + 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, Some(2)); + let _p: U64 = generate_safe_prime_with_rng(&mut OsRng, 2); } #[test] - #[should_panic(expected = "The requested bit length (65) is larger than the chosen Uint size")] + #[should_panic( + 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, Some(65)); + let _p: U64 = generate_prime_with_rng(&mut OsRng, 65); } #[test] - #[should_panic(expected = "The requested bit length (65) is larger than the chosen Uint size")] + #[should_panic( + 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, Some(65)); + let _p: U64 = generate_safe_prime_with_rng(&mut OsRng, 65); } fn is_prime_ref(num: Word) -> bool { @@ -332,7 +340,7 @@ mod tests { fn corner_cases_generate_prime() { for bits in 2..5 { for _ in 0..100 { - let p: U64 = generate_prime(Some(bits)); + let p: U64 = generate_prime(bits); let p_word = p.as_words()[0]; assert!(is_prime_ref(p_word)); } @@ -343,7 +351,7 @@ mod tests { fn corner_cases_generate_safe_prime() { for bits in 3..5 { for _ in 0..100 { - let p: U64 = generate_safe_prime(Some(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)); } @@ -355,13 +363,14 @@ mod tests { #[cfg(feature = "tests-openssl")] mod tests_openssl { use alloc::format; + use core::num::NonZeroU32; - use crypto_bigint::{Odd, U128}; + use crypto_bigint::U128; use openssl::bn::{BigNum, BigNumContext}; use rand_core::OsRng; use super::{generate_prime, is_prime}; - use crate::hazmat::random_odd_uint; + use crate::hazmat::random_odd_integer; fn openssl_is_prime(num: &BigNum, ctx: &mut BigNumContext) -> bool { num.is_prime(64, ctx).unwrap() @@ -381,7 +390,7 @@ mod tests_openssl { // Generate primes, let OpenSSL check them for _ in 0..100 { - let p: U128 = generate_prime(Some(128)); + let p: U128 = generate_prime(128); let p_bn = to_openssl(&p); assert!( openssl_is_prime(&p_bn, &mut ctx), @@ -399,8 +408,8 @@ mod tests_openssl { // Generate random numbers, check if our test agrees with OpenSSL for _ in 0..100 { - let p: Odd = random_odd_uint(&mut OsRng, 128); - let actual = is_prime(&p); + 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); assert_eq!( @@ -414,7 +423,9 @@ mod tests_openssl { #[cfg(test)] #[cfg(feature = "tests-gmp")] mod tests_gmp { - use crypto_bigint::{Odd, U128}; + use core::num::NonZeroU32; + + use crypto_bigint::U128; use rand_core::OsRng; use rug::{ integer::{IsPrime, Order}, @@ -422,7 +433,7 @@ mod tests_gmp { }; use super::{generate_prime, is_prime}; - use crate::hazmat::random_odd_uint; + use crate::hazmat::random_odd_integer; fn gmp_is_prime(num: &Integer) -> bool { matches!(num.is_probably_prime(25), IsPrime::Yes | IsPrime::Probably) @@ -440,14 +451,14 @@ mod tests_gmp { fn gmp_cross_check() { // Generate primes, let GMP check them for _ in 0..100 { - let p: U128 = generate_prime(Some(128)); + 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: Odd = random_odd_uint(&mut OsRng, 128); + 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); @@ -456,8 +467,8 @@ mod tests_gmp { // Generate random numbers, check if our test agrees with GMP for _ in 0..100 { - let p: Odd = random_odd_uint(&mut OsRng, 128); - let actual = is_prime(&p); + 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); assert_eq!( diff --git a/src/traits.rs b/src/traits.rs index bb72242..46e7c99 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,4 +1,4 @@ -use crypto_bigint::Uint; +use crypto_bigint::{Integer, RandomBits, RandomMod}; use rand_core::CryptoRngCore; use crate::{ @@ -15,7 +15,7 @@ pub trait RandomPrimeWithRng { /// 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: Option) -> 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. @@ -24,7 +24,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: Option) -> Self; + fn generate_safe_prime_with_rng(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self; /// Checks probabilistically if the given number is prime using the provided RNG. /// @@ -37,11 +37,11 @@ pub trait RandomPrimeWithRng { fn is_safe_prime_with_rng(&self, rng: &mut impl CryptoRngCore) -> bool; } -impl RandomPrimeWithRng for Uint { - fn generate_prime_with_rng(rng: &mut impl CryptoRngCore, bit_length: Option) -> Self { +impl RandomPrimeWithRng for T { + 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: Option) -> Self { + 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 { @@ -67,8 +67,9 @@ 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, Some(10)).is_prime_with_rng(&mut OsRng)); - assert!(U64::generate_safe_prime_with_rng(&mut OsRng, Some(10)) - .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_safe_prime_with_rng(&mut OsRng, 10).is_safe_prime_with_rng(&mut OsRng) + ); } }