diff --git a/Cargo.toml b/Cargo.toml index 96fcd7b..48a89ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,13 +10,13 @@ categories = ["cryptography", "no-std"] rust-version = "1.65" [dependencies] -crypto-bigint = { version = "0.5.4", default-features = false, features = ["rand_core"] } +crypto-bigint = { git = "https://github.com/xuganyu96/crypto-bigint.git", default-features = false, features = ["rand_core", "alloc"], branch = "boxed-uintlike-coverage" } 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 } +rand_chacha = "0.3" [dev-dependencies] -rand_chacha = "0.3" criterion = { version = "0.4", features = ["html_reports"] } num-modular = { version = "0.5", features = ["num-bigint"] } num-bigint = "0.4" diff --git a/benches/bench.rs b/benches/bench.rs index 7c469ed..082c3df 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -1,5 +1,6 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; -use crypto_bigint::{nlimbs, Uint, U1024}; +use crypto_bigint::{U1024, U128, U256}; +use crypto_primes::UintLike; use rand_chacha::ChaCha8Rng; use rand_core::{CryptoRngCore, OsRng, SeedableRng}; @@ -22,27 +23,35 @@ fn make_rng() -> ChaCha8Rng { ChaCha8Rng::from_seed(*b"01234567890123456789012345678901") } -fn make_sieve(rng: &mut impl CryptoRngCore) -> Sieve { - let start: Uint = random_odd_uint(rng, Uint::::BITS); - Sieve::new(&start, Uint::::BITS, false) +fn make_sieve( + rng: &mut impl CryptoRngCore, + bit_length: u32, + bits_precision: u32, +) -> Sieve { + let start: T = random_odd_uint(rng, bit_length, bits_precision); + return Sieve::new(&start, bit_length, false, bits_precision); } -fn make_presieved_num(rng: &mut impl CryptoRngCore) -> Uint { - let mut sieve = make_sieve(rng); - sieve.next().unwrap() +fn make_presieved_num( + rng: &mut impl CryptoRngCore, + bit_length: u32, + bits_precision: u32, +) -> T { + let mut sieve = make_sieve::(rng, bit_length, bits_precision); + return sieve.next().unwrap(); } 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, U128::BITS)) }); 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, U128::BITS), + |start| Sieve::::new(&start, 128, false, U128::BITS), BatchSize::SmallInput, ) }); @@ -50,27 +59,27 @@ fn bench_sieve(c: &mut Criterion) { // 5 is the average number of pre-sieved samples we need to take before we encounter a prime group.bench_function("(U128) 5 samples", |b| { b.iter_batched( - || make_sieve::<{ nlimbs!(128) }>(&mut OsRng), + || make_sieve::(&mut OsRng, 128, U128::BITS), |sieve| sieve.take(5).for_each(drop), BatchSize::SmallInput, ) }); 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, U1024::BITS)) }); 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, U1024::BITS), + |start| Sieve::::new(&start, 1024, false, U1024::BITS), BatchSize::SmallInput, ) }); group.bench_function("(U1024) 5 samples", |b| { b.iter_batched( - || make_sieve::<{ nlimbs!(1024) }>(&mut OsRng), + || make_sieve::(&mut OsRng, 1024, U1024::BITS), |sieve| sieve.take(5).for_each(drop), BatchSize::SmallInput, ) @@ -84,7 +93,7 @@ fn bench_miller_rabin(c: &mut Criterion) { group.bench_function("(U128) creation", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128), + || random_odd_uint::(&mut OsRng, 128, U128::BITS), |start| MillerRabin::new(&start), BatchSize::SmallInput, ) @@ -92,7 +101,7 @@ fn bench_miller_rabin(c: &mut Criterion) { 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::(&mut OsRng, 128, U128::BITS)), |mr| mr.test_random_base(&mut OsRng), BatchSize::SmallInput, ) @@ -100,7 +109,7 @@ fn bench_miller_rabin(c: &mut Criterion) { group.bench_function("(U1024) creation", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(1024) }>(&mut OsRng, 1024), + || random_odd_uint::(&mut OsRng, 1024, U1024::BITS), |start| MillerRabin::new(&start), BatchSize::SmallInput, ) @@ -108,7 +117,7 @@ fn bench_miller_rabin(c: &mut Criterion) { 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::(&mut OsRng, 1024, U1024::BITS)), |mr| mr.test_random_base(&mut OsRng), BatchSize::SmallInput, ) @@ -121,7 +130,7 @@ fn bench_lucas(c: &mut Criterion) { let mut rng = make_rng(); group.bench_function("(U128) Selfridge base, strong check (pre-sieved)", |b| { b.iter_batched( - || make_presieved_num::<{ nlimbs!(128) }>(&mut rng), + || make_presieved_num::(&mut rng, 128, U128::BITS), |n| lucas_test(&n, SelfridgeBase, LucasCheck::Strong), BatchSize::SmallInput, ) @@ -130,7 +139,7 @@ fn bench_lucas(c: &mut Criterion) { let mut rng = make_rng(); group.bench_function("(U1024) Selfridge base, strong check (pre-sieved)", |b| { b.iter_batched( - || make_presieved_num::<{ nlimbs!(1024) }>(&mut rng), + || make_presieved_num::(&mut rng, 1024, U1024::BITS), |n| lucas_test(&n, SelfridgeBase, LucasCheck::Strong), BatchSize::SmallInput, ) @@ -139,7 +148,7 @@ fn bench_lucas(c: &mut Criterion) { let mut rng = make_rng(); group.bench_function("(U1024) A* base, Lucas-V check (pre-sieved)", |b| { b.iter_batched( - || make_presieved_num::<{ nlimbs!(1024) }>(&mut rng), + || make_presieved_num::(&mut rng, 1024, U1024::BITS), |n| lucas_test(&n, AStarBase, LucasCheck::LucasV), BatchSize::SmallInput, ) @@ -150,7 +159,7 @@ fn bench_lucas(c: &mut Criterion) { "(U1024) brute force base, almost extra strong (pre-sieved)", |b| { b.iter_batched( - || make_presieved_num::<{ nlimbs!(1024) }>(&mut rng), + || make_presieved_num::(&mut rng, 1024, U1024::BITS), |n| lucas_test(&n, BruteForceBase, LucasCheck::AlmostExtraStrong), BatchSize::SmallInput, ) @@ -160,7 +169,7 @@ fn bench_lucas(c: &mut Criterion) { let mut rng = make_rng(); group.bench_function("(U1024) brute force base, extra strong (pre-sieved)", |b| { b.iter_batched( - || make_presieved_num::<{ nlimbs!(1024) }>(&mut rng), + || make_presieved_num::(&mut rng, 1024, U1024::BITS), |n| lucas_test(&n, BruteForceBase, LucasCheck::ExtraStrong), BatchSize::SmallInput, ) @@ -192,7 +201,7 @@ fn bench_presets(c: &mut Criterion) { group.bench_function("(U128) Prime test", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128), + || random_odd_uint::(&mut OsRng, 128, U128::BITS), |num| is_prime_with_rng(&mut OsRng, &num), BatchSize::SmallInput, ) @@ -200,7 +209,7 @@ fn bench_presets(c: &mut Criterion) { group.bench_function("(U128) Safe prime test", |b| { b.iter_batched( - || random_odd_uint::<{ nlimbs!(128) }>(&mut OsRng, 128), + || random_odd_uint::(&mut OsRng, 128, U128::BITS), |num| is_safe_prime_with_rng(&mut OsRng, &num), BatchSize::SmallInput, ) @@ -208,23 +217,23 @@ fn bench_presets(c: &mut Criterion) { 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, U128::BITS)) }); 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, U1024::BITS)) }); 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, U128::BITS)) }); 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, U1024::BITS)) }); group.finish(); @@ -234,19 +243,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, U128::BITS)) }); // 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, U256::BITS)) }); // 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, U256::BITS)) }); group.finish(); @@ -263,7 +272,7 @@ fn bench_gmp(c: &mut Criterion) { group.bench_function("(U128) Random prime", |b| { b.iter_batched( - || random::<{ nlimbs!(128) }>(&mut OsRng), + || random::(&mut OsRng), |n| n.next_prime(), BatchSize::SmallInput, ) @@ -271,7 +280,7 @@ fn bench_gmp(c: &mut Criterion) { group.bench_function("(U1024) Random prime", |b| { b.iter_batched( - || random::<{ nlimbs!(1024) }>(&mut OsRng), + || random::(&mut OsRng), |n| n.next_prime(), BatchSize::SmallInput, ) diff --git a/src/hazmat.rs b/src/hazmat.rs index f07e5b8..e4d6bbf 100644 --- a/src/hazmat.rs +++ b/src/hazmat.rs @@ -1,8 +1,8 @@ //! Components to build your own primality test. //! Handle with care. -mod gcd; -mod jacobi; +pub(crate) mod gcd; +pub(crate) mod jacobi; mod lucas; mod miller_rabin; mod precomputed; @@ -12,6 +12,7 @@ pub(crate) mod primes; pub(crate) mod pseudoprimes; mod sieve; +pub use jacobi::JacobiSymbol; pub use lucas::{lucas_test, AStarBase, BruteForceBase, LucasBase, LucasCheck, SelfridgeBase}; pub use miller_rabin::MillerRabin; pub use sieve::{random_odd_uint, Sieve}; diff --git a/src/hazmat/gcd.rs b/src/hazmat/gcd.rs index 0b3d6bf..2a793fe 100644 --- a/src/hazmat/gcd.rs +++ b/src/hazmat/gcd.rs @@ -1,9 +1,11 @@ -use crypto_bigint::{Limb, NonZero, Uint}; +use crypto_bigint::{Limb, NonZero}; + +use crate::UintLike; /// Calculates the greatest common divisor of `n` and `m`. /// By definition, `gcd(0, m) == m`. /// `n` must be non-zero. -pub(crate) fn gcd(n: &Uint, m: u32) -> u32 { +pub(crate) fn gcd(n: &T, m: u32) -> u32 { // 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,12 +13,12 @@ pub(crate) fn gcd(n: &Uint, m: u32) -> u32 { // 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; } // Normalize input: the resulting (a, b) are both small, a >= b, and b != 0. - let (mut a, mut b): (u32, u32) = if n.bits() > (u32::BITS as usize) { + let (mut a, mut b): (u32, u32) = if n.bits() > u32::BITS { // `m` is non-zero, so we can unwrap. let (_quo, n) = n.div_rem_limb(NonZero::new(Limb::from(m)).unwrap()); // `n` is a remainder of a division by `u32`, so it can be safely cast to `u32`. @@ -47,7 +49,7 @@ pub(crate) fn gcd(n: &Uint, m: u32) -> u32 { #[cfg(test)] mod tests { - use crypto_bigint::{Encoding, U128}; + use crypto_bigint::U128; use num_bigint::BigUint; use num_integer::Integer; use proptest::prelude::*; diff --git a/src/hazmat/jacobi.rs b/src/hazmat/jacobi.rs index 8f2bc04..2f4d197 100644 --- a/src/hazmat/jacobi.rs +++ b/src/hazmat/jacobi.rs @@ -1,9 +1,14 @@ //! Jacobi symbol calculation. -use crypto_bigint::{Integer, Limb, NonZero, Uint, Word}; +use core::fmt::Display; +use crypto_bigint::{BoxedUint, Limb, NonZero, Uint, Word}; + +use crate::UintLike; + +#[allow(missing_docs)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum JacobiSymbol { +pub enum JacobiSymbol { Zero, One, MinusOne, @@ -21,7 +26,7 @@ impl core::ops::Neg for JacobiSymbol { } // A helper trait to generalize some functions over Word and Uint. -trait SmallMod { +pub(crate) trait SmallMod { fn mod8(&self) -> Word; fn mod4(&self) -> Word; } @@ -44,6 +49,16 @@ impl SmallMod for Uint { } } +impl SmallMod for BoxedUint { + fn mod8(&self) -> Word { + return self.as_limbs().get(0).unwrap().0 % 7; + } + + fn mod4(&self) -> Word { + return self.as_limbs().get(0).expect("Missing limbs").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. @@ -73,7 +88,7 @@ fn swap(j: JacobiSymbol, a: T, p: V) -> (JacobiSymbol, } /// Returns the Jacobi symbol `(a/p)` given an odd `p`. Panics on even `p`. -pub(crate) fn jacobi_symbol(a: i32, p_long: &Uint) -> JacobiSymbol { +pub(crate) fn jacobi_symbol(a: i32, p_long: &T) -> JacobiSymbol { if p_long.is_even().into() { panic!("`p_long` must be an odd integer, but got {}", p_long); } @@ -94,7 +109,7 @@ pub(crate) fn jacobi_symbol(a: i32, p_long: &Uint) -> JacobiS }; // A degenerate case. - if a_pos == 1 || p_long == &Uint::::ONE { + if a_pos == 1 || p_long == &T::one_with_precision(p_long.bits_precision()) { return result; } @@ -110,7 +125,10 @@ pub(crate) fn jacobi_symbol(a: i32, p_long: &Uint) -> JacobiS if a == 1 { return result; } - let (result, a_long, p) = swap(result, a, *p_long); + // NOTE: prior to the UintLike generics it was *p_long, which works because Uint implements + // Copy. However, BoxedUint does not implement Copy (which it should not anyways since it + // is heap-allocated), so explicit cloning is the next best thing + let (result, a_long, p) = swap(result, a, p_long.clone()); // Can unwrap here, since `p` is swapped with `a`, // and `a` would be odd after `reduce_numerator()`. let (_, a) = a_long.div_rem_limb(NonZero::new(Limb::from(p)).unwrap()); @@ -151,7 +169,7 @@ mod tests { use alloc::format; - use crypto_bigint::{Encoding, U128}; + use crypto_bigint::U128; use num_bigint::{BigInt, Sign}; use num_modular::ModularSymbols; use proptest::prelude::*; diff --git a/src/hazmat/lucas.rs b/src/hazmat/lucas.rs index 69f0039..42c0bdb 100644 --- a/src/hazmat/lucas.rs +++ b/src/hazmat/lucas.rs @@ -1,14 +1,9 @@ //! Lucas primality test. -use crypto_bigint::{ - modular::runtime_mod::{DynResidue, DynResidueParams}, - CheckedAdd, Integer, Limb, Uint, Word, -}; -use super::{ - gcd::gcd, - jacobi::{jacobi_symbol, JacobiSymbol}, - Primality, -}; +use crate::hazmat::jacobi::JacobiSymbol; +use crate::{UintLike, UintModLike}; + +use super::Primality; /// The maximum number of attempts to find `D` such that `(D/n) == -1`. // This is widely believed to be impossible. @@ -30,7 +25,7 @@ pub trait LucasBase { /// Given an odd integer, returns `Ok((P, 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: &Uint) -> Result<(u32, i32), Primality>; + fn generate(&self, n: &T) -> Result<(u32, i32), Primality>; } /// "Method A" for selecting the base given in Baillie & Wagstaff[^Baillie1980], @@ -48,11 +43,8 @@ pub trait LucasBase { pub struct SelfridgeBase; impl LucasBase for SelfridgeBase { - fn generate(&self, n: &Uint) -> Result<(u32, i32), Primality> { + fn generate(&self, n: &T) -> Result<(u32, i32), Primality> { let mut d = 5_i32; - let n_is_small = n.bits_vartime() < (Limb::BITS - 1); - // Can unwrap here since it won't overflow after `&` - let small_n: u32 = (n.as_words()[0] & Word::from(u32::MAX)).try_into().unwrap(); let mut attempts = 0; loop { if attempts >= MAX_ATTEMPTS { @@ -61,12 +53,12 @@ impl LucasBase for SelfridgeBase { if attempts >= ATTEMPTS_BEFORE_SQRT { let sqrt_n = n.sqrt_vartime(); - if &sqrt_n.wrapping_mul(&sqrt_n) == n { + if &::wrapping_mul(&sqrt_n, &sqrt_n) == n { return Err(Primality::Composite); } } - let j = jacobi_symbol(d, n); + let j = T::jacobi_symbol_small(d, n); if j == JacobiSymbol::MinusOne { break; @@ -78,7 +70,7 @@ impl LucasBase for SelfridgeBase { // this small modification of Selfridge's method A // enables 5 and 11 to be classified as Lucas probable primes. // Otherwise GCD(D, n) > 1, and therefore n is not prime. - if !(n_is_small && small_n == d.abs_diff(0)) { + if n != &T::from(d.abs_diff(0)).widen(n.bits_precision()) { return Err(Primality::Composite); } } @@ -108,7 +100,7 @@ impl LucasBase for SelfridgeBase { pub struct AStarBase; impl LucasBase for AStarBase { - fn generate(&self, n: &Uint) -> Result<(u32, i32), Primality> { + fn generate(&self, n: &T) -> Result<(u32, i32), Primality> { SelfridgeBase .generate(n) .map(|(p, q)| if q == -1 { (5, 5) } else { (p, q) }) @@ -127,7 +119,7 @@ impl LucasBase for AStarBase { pub struct BruteForceBase; impl LucasBase for BruteForceBase { - fn generate(&self, n: &Uint) -> Result<(u32, i32), Primality> { + fn generate(&self, n: &T) -> Result<(u32, i32), Primality> { let mut p = 3_u32; let mut attempts = 0; @@ -138,13 +130,13 @@ impl LucasBase for BruteForceBase { if attempts >= ATTEMPTS_BEFORE_SQRT { let sqrt_n = n.sqrt_vartime(); - if &sqrt_n.wrapping_mul(&sqrt_n) == n { + if &::wrapping_mul(&sqrt_n, &sqrt_n) == n { return Err(Primality::Composite); } } // Can unwrap here since `p` is always small (see the condition above). - let j = jacobi_symbol((p * p - 4).try_into().unwrap(), n); + let j = T::jacobi_symbol_small((p * p - 4).try_into().unwrap(), n); if j == JacobiSymbol::MinusOne { break; @@ -155,7 +147,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 == &Uint::::from(p + 2) { + let primality = if n == &T::from(p + 2).widen(n.bits_precision()) { Primality::Prime } else { Primality::Composite @@ -172,7 +164,7 @@ impl LucasBase for BruteForceBase { } /// For the given odd `n`, finds `s` and odd `d` such that `n + 1 == 2^s * d`. -fn decompose(n: &Uint) -> (usize, Uint) { +fn decompose(n: &T) -> (u32, T) { debug_assert!(bool::from(n.is_odd())); // Need to be careful here since `n + 1` can overflow. @@ -180,7 +172,10 @@ fn decompose(n: &Uint) -> (usize, Uint) { let s = n.trailing_ones(); // This won't overflow since the original `n` was odd, so we right-shifted at least once. - let d = Option::from((n >> s).checked_add(&Uint::::ONE)).expect("Integer overflow"); + let d = Option::from( + (n.clone().shr_vartime(s).0).checked_add(&T::one_with_precision(n.bits_precision())), + ) + .expect("Integer overflow"); (s, d) } @@ -268,8 +263,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: &Uint, +pub fn lucas_test( + candidate: &T, base: impl LucasBase, check: LucasCheck, ) -> Primality { @@ -305,7 +300,10 @@ pub fn lucas_test( // we check that gcd(n, Q) = 1 anyway - again, since `Q` is small, // it does not noticeably affect the performance. let abs_q = q.abs_diff(0); - if abs_q != 1 && gcd(candidate, abs_q) != 1 && candidate > &Uint::::from(abs_q) { + if abs_q != 1 + && candidate.gcd_small(abs_q) != 1 + && candidate > &T::from(abs_q).widen(candidate.bits_precision()) + { return Primality::Composite; } @@ -315,19 +313,22 @@ pub fn lucas_test( // Some constants in Montgomery form - let params = DynResidueParams::::new(candidate); + // Can unwrap here since we already checked that `candidate` is odd + let params = + ::Modular::new_params(candidate).expect("Montgomery modulus must be odd"); - let zero = DynResidue::::zero(params); - let one = DynResidue::::one(params); - let two = one + one; - let minus_two = -two; + let zero = ::Modular::zero(¶ms); + let one = ::Modular::one(¶ms); + 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 = DynResidue::::new(&Uint::::from(q.abs_diff(0)), params); + // No need to widen; widen happens at T::Modular::new + let abs_q = ::Modular::new(&T::from(q.abs_diff(0)), ¶ms); if q < 0 { -abs_q } else { @@ -338,9 +339,9 @@ pub fn lucas_test( // Convert P to Montgomery form let p = if p_is_one { - one + one.clone() } else { - DynResidue::::new(&Uint::::from(p), params) + ::Modular::new(&T::from(p), ¶ms) }; // Compute d-th element of Lucas sequence (U_d(P, Q), V_d(P, Q)), where: @@ -358,20 +359,21 @@ 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 = DynResidue::::zero(params); // keeps U_k - let mut qk = one; // keeps Q^k + let mut vk = two.clone(); // keeps V_k + let mut uk = ::Modular::zero(¶ms); // keeps U_k + let mut qk = one.clone(); // keeps Q^k // D in Montgomery representation - note that it can be negative. - let abs_d = DynResidue::::new(&Uint::::from(discriminant.abs_diff(0)), params); + let abs_d = ::Modular::new(&T::from(discriminant.abs_diff(0)), ¶ms); let d_m = if discriminant < 0 { -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 q_2k = qk.square(); + let u_2k = uk * &vk; + + let v_2k = vk.square() - &(qk.clone() + &qk); uk = u_2k; vk = v_2k; @@ -380,11 +382,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; @@ -442,7 +448,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; @@ -456,10 +462,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; } } @@ -472,7 +478,7 @@ mod tests { use alloc::format; - use crypto_bigint::{Uint, U128, U64}; + use crypto_bigint::{BoxedUint, Uint, U128, U64}; #[cfg(feature = "tests-exhaustive")] use num_prime::nt_funcs::is_prime64; @@ -480,7 +486,10 @@ mod tests { use super::{ decompose, lucas_test, AStarBase, BruteForceBase, LucasBase, LucasCheck, SelfridgeBase, }; - use crate::hazmat::{primes, pseudoprimes, Primality}; + use crate::{ + hazmat::{primes, pseudoprimes, Primality}, + UintLike, + }; #[test] fn bases_derived_traits() { @@ -503,6 +512,12 @@ mod tests { assert_eq!(SelfridgeBase.generate(&num), Err(Primality::Composite)); assert_eq!(AStarBase.generate(&num), Err(Primality::Composite)); assert_eq!(BruteForceBase.generate(&num), Err(Primality::Composite)); + + // The same test, but with BoxedUint + let num = BoxedUint::from(131u32).widen(64).square(); + assert_eq!(SelfridgeBase.generate(&num), Err(Primality::Composite)); + assert_eq!(AStarBase.generate(&num), Err(Primality::Composite)); + assert_eq!(BruteForceBase.generate(&num), Err(Primality::Composite)); } #[test] @@ -511,7 +526,11 @@ mod tests { assert_eq!( BruteForceBase.generate(&U64::from(5u32)), Err(Primality::Prime) - ) + ); + assert_eq!( + BruteForceBase.generate(&BoxedUint::from(5u32).widen(64)), + Err(Primality::Prime) + ); } #[test] @@ -521,6 +540,10 @@ mod tests { lucas_test(&U64::from(6u32), SelfridgeBase, LucasCheck::Strong), Primality::Composite ); + assert_eq!( + lucas_test(&BoxedUint::from(6u32), SelfridgeBase, LucasCheck::Strong), + Primality::Composite + ); } #[test] @@ -534,7 +557,7 @@ mod tests { struct TestBase; impl LucasBase for TestBase { - fn generate(&self, _n: &Uint) -> Result<(u32, i32), Primality> { + fn generate(&self, _n: &T) -> Result<(u32, i32), Primality> { Ok((5, 5)) } } @@ -543,6 +566,14 @@ mod tests { lucas_test(&U64::from(15u32), TestBase, LucasCheck::Strong), Primality::Composite ); + assert_eq!( + lucas_test( + &BoxedUint::from(15u32).widen(64), + TestBase, + LucasCheck::Strong + ), + Primality::Composite + ); } #[test] @@ -550,6 +581,19 @@ mod tests { assert_eq!(decompose(&U128::MAX), (128, U128::ONE)); assert_eq!(decompose(&U128::ONE), (1, U128::ONE)); assert_eq!(decompose(&U128::from(7766015u32)), (15, U128::from(237u32))); + + assert_eq!( + decompose(&BoxedUint::from(u128::MAX)), + (128, BoxedUint::one_with_precision(128)) + ); + assert_eq!( + decompose(&BoxedUint::one_with_precision(128)), + (1, BoxedUint::one_with_precision(128)) + ); + assert_eq!( + decompose(&BoxedUint::from(7766015u32)), + (15, BoxedUint::from(237u32)) + ); } fn is_slpsp(num: u32) -> bool { diff --git a/src/hazmat/miller_rabin.rs b/src/hazmat/miller_rabin.rs index 29c401a..64a16fd 100644 --- a/src/hazmat/miller_rabin.rs +++ b/src/hazmat/miller_rabin.rs @@ -1,13 +1,10 @@ //! Miller-Rabin primality test. +use crypto_bigint::{NonZero, PowBoundedExp}; use rand_core::CryptoRngCore; -use crypto_bigint::{ - modular::runtime_mod::{DynResidue, DynResidueParams}, - CheckedAdd, Integer, NonZero, RandomMod, Uint, -}; - use super::Primality; +use crate::{UintLike, UintModLike}; /// Precomputed data used to perform Miller-Rabin primality test[^Pomerance1980]. /// The numbers that pass it are commonly called "strong probable primes" @@ -17,37 +14,42 @@ 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, - bit_length: usize, - montgomery_params: DynResidueParams, - one: DynResidue, - minus_one: DynResidue, - s: usize, - d: Uint, +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MillerRabin { + candidate: T, + bit_length: u32, + montgomery_params: ::Params, + one: T::Modular, + minus_one: T::Modular, + s: u32, + d: T, } -impl MillerRabin { +impl MillerRabin { /// Initializes a Miller-Rabin test for `candidate`. /// /// Panics if `candidate` is even. - pub fn new(candidate: &Uint) -> Self { + pub fn new(candidate: &T) -> Self { if candidate.is_even().into() { panic!("`candidate` must be odd."); } - let params = DynResidueParams::::new(candidate); - let one = DynResidue::::one(params); - let minus_one = -one; + // Can unwrap here since we already checked that `candidate` is odd + let params = ::new_params(candidate) + .expect("Montgomery modulus must be odd"); + let one = ::one(¶ms); + let minus_one = -one.clone(); // Find `s` and odd `d` such that `candidate - 1 == 2^s * d`. - let candidate_minus_one = candidate.wrapping_sub(&Uint::::ONE); + let candidate_minus_one = ::wrapping_sub( + candidate, + &T::one_with_precision(candidate.bits_precision()), + ); let s = candidate_minus_one.trailing_zeros(); - let d = candidate_minus_one >> s; + let d = candidate_minus_one.shr_vartime(s).0; Self { - candidate: *candidate, + candidate: candidate.clone(), bit_length: candidate.bits_vartime(), montgomery_params: params, one, @@ -58,11 +60,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 = DynResidue::::new(base, self.montgomery_params); + let base = ::new(base, &self.montgomery_params); // Implementation detail: bounded exp gets faster every time we decrease the bound // by the window length it uses, which is currently 4 bits. @@ -87,7 +89,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).widen(self.candidate.bits_precision())) } /// Perform a Miller-Rabin check with a random base (in the range `[3, candidate-2]`) @@ -102,12 +104,16 @@ 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 = ::wrapping_sub( + &self.candidate, + &T::from(4u32).widen(self.candidate.bits_precision()), + ); let range_nonzero = NonZero::new(range).unwrap(); // 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 = Option::from( - Uint::::random_mod(rng, &range_nonzero).checked_add(&Uint::::from(3u32)), + T::random_mod(rng, &range_nonzero) + .checked_add(&T::from(3u32).widen(self.candidate.bits_precision())), ) .expect("Integer overflow"); self.test(&random) @@ -127,7 +133,10 @@ 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_uint, Sieve}, + UintLike, + }; #[test] fn miller_rabin_derived_traits() { @@ -155,9 +164,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) @@ -195,8 +204,8 @@ mod tests { #[test] fn trivial() { let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); - let start: U1024 = random_odd_uint(&mut rng, 1024); - for num in Sieve::new(&start, 1024, false).take(10) { + let start: U1024 = random_odd_uint(&mut rng, 1024, U1024::BITS); + for num in Sieve::new(&start, 1024, false, U1024::BITS).take(10) { let mr = MillerRabin::new(&num); // Trivial tests, must always be true. diff --git a/src/hazmat/precomputed.rs b/src/hazmat/precomputed.rs index bdd363e..055dd26 100644 --- a/src/hazmat/precomputed.rs +++ b/src/hazmat/precomputed.rs @@ -1,4 +1,4 @@ -use crypto_bigint::{Limb, Reciprocal, Word}; +use crypto_bigint::{Limb, NonZero, Reciprocal, Word}; /// The type that fits any small prime from the table. pub(crate) type SmallPrime = u16; @@ -151,7 +151,9 @@ const fn create_reciprocals() -> [Reciprocal; SMALL_PRIMES_SIZE] { let mut arr = [Reciprocal::default(); SMALL_PRIMES_SIZE]; let mut i = 0; while i < SMALL_PRIMES_SIZE { - arr[i] = Reciprocal::ct_new(Limb(SMALL_PRIMES[i] as Word)).0; + let limb = + NonZero::::const_new(Limb(SMALL_PRIMES[i] as Word)).expect("Prime is zero"); + arr[i] = Reciprocal::new(limb); i += 1; } arr diff --git a/src/hazmat/sieve.rs b/src/hazmat/sieve.rs index 7b29625..bc3d73d 100644 --- a/src/hazmat/sieve.rs +++ b/src/hazmat/sieve.rs @@ -3,21 +3,31 @@ use alloc::{vec, vec::Vec}; -use crypto_bigint::{CheckedAdd, Random, Uint}; use rand_core::CryptoRngCore; -use crate::hazmat::precomputed::{SmallPrime, RECIPROCALS, SMALL_PRIMES}; +use crate::{ + hazmat::precomputed::{SmallPrime, RECIPROCALS, SMALL_PRIMES}, + UintLike, +}; /// 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(rng: &mut impl CryptoRngCore, bit_length: usize) -> Uint { +/// Where T is stack allocated (Uint), `bits_precision` must be exactly `T::BITS`. Where T is +/// heap-allocated (BoxedUint), `bits_precision` will be passed to the appropriate type. +/// +/// If `bit_length` is greater than `bits_precision`, or if `bit_length` is 0, this function will +/// panic +pub fn random_odd_uint( + rng: &mut impl CryptoRngCore, + bit_length: u32, + bits_precision: u32, +) -> T { if bit_length == 0 { panic!("Bit length must be non-zero"); } - if bit_length > Uint::::BITS { + if bit_length > bits_precision { panic!( "The requested bit length ({}) is larger than the chosen Uint size", bit_length @@ -25,16 +35,16 @@ pub fn random_odd_uint(rng: &mut impl CryptoRngCore, bit_length: } // 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 random = T::random_bits(rng, bit_length, bits_precision); // Make it odd - random |= Uint::::ONE; + let random = random | T::one_with_precision(bits_precision); // Make sure it's the correct bit size - random |= Uint::::ONE << (bit_length - 1); + let random = random + | T::one_with_precision(bits_precision) + .shl_vartime(bit_length - 1) + .0; random } @@ -50,22 +60,22 @@ 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, residues: Vec, - max_bit_length: usize, + max_bit_length: u32, produces_nothing: bool, starts_from_exception: bool, 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 @@ -75,15 +85,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` is zero or greater than `bits_precision` /// /// If `safe_primes` is `true`, both the returned `n` and `n/2` are sieved. - pub fn new(start: &Uint, max_bit_length: usize, safe_primes: bool) -> Self { + pub fn new(start: &T, max_bit_length: u32, safe_primes: bool, bits_precision: u32) -> Self { if max_bit_length == 0 { panic!("The requested bit length cannot be zero"); } - if max_bit_length > Uint::::BITS { + if max_bit_length > bits_precision { panic!( "The requested bit length ({}) is larger than the chosen Uint size", max_bit_length @@ -93,41 +103,43 @@ impl Sieve { // If we are targeting safe primes, iterate over the corresponding // possible Germain primes (`n/2`), reducing the task to that with `safe_primes = false`. let (max_bit_length, base) = if safe_primes { - (max_bit_length - 1, start >> 1) + (max_bit_length - 1, start.shr_vartime(1).0) } else { - (max_bit_length, *start) + (max_bit_length, start.clone()) }; let mut base = base; + let base_bits_precision = base.bits_precision(); // 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).widen(base.bits_precision()) { starts_from_exception = true; - base = Uint::::from(3u32); + base = T::from(3u32).widen(base.bits_precision()); } else { // Adjust the base so that we hit odd numbers when incrementing it by 2. - base |= Uint::::ONE; + base = base | T::one_with_precision(base_bits_precision); } // 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 { - SMALL_PRIMES - .iter() - .enumerate() - .find(|(_i, p)| Uint::::from(**p) >= base) - .map(|(i, _p)| i) - .unwrap_or(SMALL_PRIMES.len()) - } else { - // This will be the majority of use cases - SMALL_PRIMES.len() - }; + let residues_len = + if T::from(SMALL_PRIMES[SMALL_PRIMES.len() - 1]).widen(base.bits_precision()) >= base { + SMALL_PRIMES + .iter() + .enumerate() + .find(|(_i, p)| T::from(**p).widen(base.bits_precision()) >= base) + .map(|(i, _p)| i) + .unwrap_or(SMALL_PRIMES.len()) + } else { + // This will be the majority of use cases + SMALL_PRIMES.len() + }; Self { base, @@ -167,8 +179,13 @@ impl Sieve { } // Find the increment limit. - let max_value = (Uint::::ONE << self.max_bit_length).wrapping_sub(&Uint::::ONE); - let incr_limit = max_value.wrapping_sub(&self.base); + let max_value = ::wrapping_sub( + &T::one_with_precision(self.base.bits_precision()) + .shl_vartime(self.max_bit_length) + .0, + &T::one_with_precision(self.base.bits_precision()), + ); + let incr_limit = ::wrapping_sub(&max_value, &self.base); self.incr_limit = if incr_limit > INCR_LIMIT.into() { INCR_LIMIT } else { @@ -177,7 +194,7 @@ 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].try_into().unwrap(); + let incr_limit_small: Residue = incr_limit.try_into_u32().unwrap(); incr_limit_small }; @@ -210,17 +227,17 @@ 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 = + let mut num: T = Option::from(self.base.checked_add(&self.incr.into())).expect("Integer overflow"); if self.safe_primes { - num = (num << 1) | Uint::::ONE; + num = num.shl_vartime(1).0 | T::one_with_precision(self.base.bits_precision()); } Some(num) }; @@ -229,7 +246,7 @@ impl Sieve { result } - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option { // Corner cases handled here if self.produces_nothing { @@ -238,7 +255,10 @@ 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 }) + .widen(self.base.bits_precision()), + ); } // Main loop @@ -253,8 +273,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) @@ -280,8 +300,8 @@ mod tests { let max_prime = SMALL_PRIMES[SMALL_PRIMES.len() - 1]; let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); - let start: U64 = random_odd_uint(&mut rng, 32); - for num in Sieve::new(&start, 32, false).take(100) { + let start: U64 = random_odd_uint(&mut rng, 32, U64::BITS); + for num in Sieve::new(&start, 32, false, U64::BITS).take(100) { let num_u64: u64 = num.into(); assert!(num_u64.leading_zeros() == 32); @@ -292,8 +312,9 @@ mod tests { } } - fn check_sieve(start: u32, bit_length: usize, safe_prime: bool, reference: &[u32]) { - let test = Sieve::new(&U64::from(start), bit_length, safe_prime).collect::>(); + fn check_sieve(start: u32, bit_length: u32, safe_prime: bool, reference: &[u32]) { + let test = + Sieve::new(&U64::from(start), bit_length, safe_prime, U64::BITS).collect::>(); assert_eq!(test.len(), reference.len()); for (x, y) in test.iter().zip(reference.iter()) { assert_eq!(x, &U64::from(*y)); @@ -348,19 +369,19 @@ 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); + let _sieve = Sieve::new(&U64::ONE, 0, false, U64::BITS); } #[test] #[should_panic(expected = "The requested bit length (65) is larger than the chosen Uint size")] fn sieve_too_many_bits() { - let _sieve = Sieve::new(&U64::ONE, 65, false); + let _sieve = Sieve::new(&U64::ONE, 65, false, U64::BITS); } #[test] fn random_below_max_length() { for _ in 0..10 { - let r: U64 = random_odd_uint(&mut OsRng, 50); + let r: U64 = random_odd_uint(&mut OsRng, 50, U64::BITS); assert_eq!(r.bits(), 50); } } @@ -368,18 +389,18 @@ mod tests { #[test] #[should_panic(expected = "Bit length must be non-zero")] fn random_odd_uint_0bits() { - let _p: U64 = random_odd_uint(&mut OsRng, 0); + let _p: U64 = random_odd_uint(&mut OsRng, 0, U64::BITS); } #[test] #[should_panic(expected = "The requested bit length (65) is larger than the chosen Uint size")] fn random_odd_uint_too_many_bits() { - let _p: U64 = random_odd_uint(&mut OsRng, 65); + let _p: U64 = random_odd_uint(&mut OsRng, 65, U64::BITS); } #[test] fn sieve_derived_traits() { - let s = Sieve::new(&U64::ONE, 10, false); + let s = Sieve::new(&U64::ONE, 10, false, U64::BITS); assert!(format!("{s:?}").starts_with("Sieve")); assert_eq!(s.clone(), s); } diff --git a/src/lib.rs b/src/lib.rs index f708159..8fec413 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +// #![no_std] // TODO: uncomment this after finishing debugging #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![doc = include_str!("../README.md")] #![deny(unsafe_code)] @@ -18,6 +18,7 @@ extern crate alloc; pub mod hazmat; mod presets; mod traits; +mod uint_traits; pub use presets::{ generate_prime_with_rng, generate_safe_prime_with_rng, is_prime_with_rng, @@ -27,3 +28,5 @@ pub use traits::RandomPrimeWithRng; #[cfg(feature = "default-rng")] pub use presets::{generate_prime, generate_safe_prime, is_prime, is_safe_prime}; + +pub use uint_traits::{UintLike, UintModLike}; diff --git a/src/presets.rs b/src/presets.rs index 50eada1..117e1b8 100644 --- a/src/presets.rs +++ b/src/presets.rs @@ -1,4 +1,3 @@ -use crypto_bigint::{Integer, Uint}; use rand_core::CryptoRngCore; #[cfg(feature = "default-rng")] @@ -7,31 +6,38 @@ use rand_core::OsRng; use crate::hazmat::{ lucas_test, random_odd_uint, AStarBase, LucasCheck, MillerRabin, Primality, Sieve, }; +use crate::UintLike; /// Returns a random prime of size `bit_length` using [`OsRng`] as the RNG. /// If `bit_length` is `None`, the full size of `Uint` is used. /// +/// Where T is stack allocated (Uint), `bits_precision` must be exactly `T::BITS`. Where T is +/// heap-allocated (BoxedUint), `bits_precision` will be passed to the appropriate type. +/// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn generate_prime(bit_length: Option) -> Uint { - generate_prime_with_rng(&mut OsRng, bit_length) +pub fn generate_prime(bit_length: u32, bits_precision: u32) -> T { + generate_prime_with_rng(&mut OsRng, bit_length, bits_precision) } /// Returns a random safe prime (that is, such that `(n - 1) / 2` is also prime) /// of size `bit_length` using [`OsRng`] as the RNG. /// If `bit_length` is `None`, the full size of `Uint` is used. /// +/// Where T is stack allocated (Uint), `bits_precision` must be exactly `T::BITS`. Where T is +/// heap-allocated (BoxedUint), `bits_precision` will be passed to the appropriate type. +/// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn generate_safe_prime(bit_length: Option) -> Uint { - generate_safe_prime_with_rng(&mut OsRng, bit_length) +pub fn generate_safe_prime(bit_length: u32, bits_precision: u32) -> T { + generate_safe_prime_with_rng(&mut OsRng, bit_length, bits_precision) } /// Checks probabilistically if the given number is prime using [`OsRng`] as the RNG. /// /// 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,27 +47,30 @@ 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) } /// Returns a random prime of size `bit_length` using the provided RNG. /// If `bit_length` is `None`, the full size of `Uint` is used. /// +/// Where T is stack allocated (Uint), `bits_precision` must be exactly `T::BITS`. Where T is +/// heap-allocated (BoxedUint), `bits_precision` will be passed to the appropriate type. +/// /// 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, + bits_precision: u32, +) -> T { if bit_length < 2 { panic!("`bit_length` must be 2 or greater."); } loop { - let start: Uint = random_odd_uint(rng, bit_length); - let sieve = Sieve::new(&start, bit_length, false); + let start = random_odd_uint::(rng, bit_length, bits_precision); + let sieve = Sieve::new(&start, bit_length, false, bits_precision); for num in sieve { if is_prime_with_rng(rng, &num) { return num; @@ -77,17 +86,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, + bits_precision: u32, +) -> T { if bit_length < 3 { panic!("`bit_length` must be 3 or greater."); } loop { - let start: Uint = random_odd_uint(rng, bit_length); - let sieve = Sieve::new(&start, bit_length, true); + let start = random_odd_uint::(rng, bit_length, bits_precision); + let sieve = Sieve::new(&start, bit_length, true, bits_precision); for num in sieve { if is_safe_prime_with_rng(rng, &num) { return num; @@ -121,8 +130,8 @@ 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).widen(num.bits_precision()) { return true; } if num.is_even().into() { @@ -135,22 +144,24 @@ 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).widen(num.bits_precision()) { return true; } - if num.as_words()[0] & 3 != 3 { + if T::from(3u32).widen(num.bits_precision()) & num.clone() + != T::from(3u32).widen(num.bits_precision()) + { return false; } - _is_prime_with_rng(rng, num) && _is_prime_with_rng(rng, &(num >> 1)) + _is_prime_with_rng(rng, num) && _is_prime_with_rng(rng, &num.shr_vartime(1).0) } /// Checks for primality assuming that `num` is odd. -fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Uint) -> bool { +fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &T) -> bool { debug_assert!(bool::from(num.is_odd())); let mr = MillerRabin::new(num); @@ -174,7 +185,7 @@ fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Uint(length: usize, num: &Uint) { - let mut next = *num; + let mut next: Uint = *num; for i in 0..length { assert!(is_prime(&next)); @@ -232,7 +243,7 @@ mod tests { assert!(is_safe_prime(&next)); } - next = (next << 1).checked_add(&Uint::::ONE).unwrap(); + next = (next.shl(1)).checked_add(&Uint::::ONE).unwrap(); } // The chain ended. @@ -251,8 +262,17 @@ mod tests { #[test] fn prime_generation() { - for bit_length in (28..=128).step_by(10) { - let p: U128 = generate_prime(Some(bit_length)); + for bit_length in (28u32..=128).step_by(10) { + let p: U128 = generate_prime(bit_length, U128::BITS); + assert!(p.bits_vartime() == bit_length); + assert!(is_prime(&p)); + } + } + + #[test] + fn boxed_prime_generation() { + for bit_length in (28u32..=128).step_by(10) { + let p: BoxedUint = generate_prime(bit_length, 128); assert!(p.bits_vartime() == bit_length); assert!(is_prime(&p)); } @@ -261,7 +281,16 @@ 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, U128::BITS); + assert!(p.bits_vartime() == bit_length); + assert!(is_safe_prime(&p)); + } + } + + #[test] + fn safe_boxed_prime_generation() { + for bit_length in (28u32..=128).step_by(10) { + let p: BoxedUint = generate_safe_prime(bit_length, 128); assert!(p.bits_vartime() == bit_length); assert!(is_safe_prime(&p)); } @@ -294,25 +323,25 @@ 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, U64::BITS); } #[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, U64::BITS); } #[test] #[should_panic(expected = "The requested bit length (65) is larger than the chosen Uint size")] 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, U64::BITS); } #[test] #[should_panic(expected = "The requested bit length (65) is larger than the chosen Uint size")] 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, U64::BITS); } fn is_prime_ref(num: Word) -> bool { @@ -321,9 +350,9 @@ mod tests { #[test] fn corner_cases_generate_prime() { - for bits in 2usize..5 { + for bits in 2u32..5 { for _ in 0..100 { - let p: U64 = generate_prime(Some(bits)); + let p: U64 = generate_prime(bits, U64::BITS); let p_word = p.as_words()[0]; assert!(is_prime_ref(p_word)); } @@ -332,9 +361,9 @@ mod tests { #[test] fn corner_cases_generate_safe_prime() { - for bits in 3usize..5 { + for bits in 3u32..5 { for _ in 0..100 { - let p: U64 = generate_safe_prime(Some(bits)); + let p: U64 = generate_safe_prime(bits, U64::BITS); let p_word = p.as_words()[0]; assert!(is_prime_ref(p_word) && is_prime_ref(p_word / 2)); } diff --git a/src/traits.rs b/src/traits.rs index c36c11d..bed5970 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -15,7 +15,11 @@ 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, + bits_precision: 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,7 +30,8 @@ pub trait RandomPrimeWithRng { /// See [`is_prime_with_rng`] for details about the performed checks. fn generate_safe_prime_with_rng( rng: &mut impl CryptoRngCore, - bit_length: Option, + bit_length: u32, + bits_precision: u32, ) -> Self; /// Checks probabilistically if the given number is prime using the provided RNG. @@ -41,14 +46,19 @@ pub trait RandomPrimeWithRng { } impl RandomPrimeWithRng for Uint { - fn generate_prime_with_rng(rng: &mut impl CryptoRngCore, bit_length: Option) -> Self { - generate_prime_with_rng(rng, bit_length) + 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_safe_prime_with_rng( rng: &mut impl CryptoRngCore, - bit_length: Option, + bit_length: u32, + bits_precision: u32, ) -> Self { - generate_safe_prime_with_rng(rng, bit_length) + generate_safe_prime_with_rng(rng, bit_length, bits_precision) } fn is_prime_with_rng(&self, rng: &mut impl CryptoRngCore) -> bool { is_prime_with_rng(rng, self) @@ -73,8 +83,10 @@ 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)) + assert!( + U64::generate_prime_with_rng(&mut OsRng, 10, U64::BITS).is_prime_with_rng(&mut OsRng) + ); + assert!(U64::generate_safe_prime_with_rng(&mut OsRng, 10, U64::BITS) .is_safe_prime_with_rng(&mut OsRng)); } } diff --git a/src/uint_traits.rs b/src/uint_traits.rs new file mode 100644 index 0000000..90dd8db --- /dev/null +++ b/src/uint_traits.rs @@ -0,0 +1,364 @@ +use crate::hazmat::{ + gcd, + jacobi::{self, JacobiSymbol}, +}; +use core::ops::{Add, Mul, Neg, Sub}; + +use crypto_bigint::{ + modular::{BoxedResidue, BoxedResidueParams, DynResidue, DynResidueParams}, + subtle::CtOption, + BoxedUint, ConstChoice, Integer, Limb, NonZero, PowBoundedExp, Random, RandomMod, Reciprocal, + Uint, Word, +}; +use rand_core::CryptoRngCore; + +// would be nice to have: *Assign traits; arithmetic traits for &self (BitAnd and Shr in particular); +#[allow(missing_docs)] +pub trait UintLike: Integer + RandomMod { + type Modular: UintModLike; + + // We can get by with non-small versions of jacobi_symbol and gcd, they don't have a big impact + // on the performance. + fn jacobi_symbol_small(lhs: i32, rhs: &Self) -> JacobiSymbol; + fn gcd_small(&self, rhs: u32) -> u32; + fn bit_vartime(&self, index: u32) -> bool; + fn trailing_zeros(&self) -> u32; + fn trailing_ones(&self) -> u32; + fn wrapping_sub(&self, rhs: &Self) -> Self; + fn wrapping_mul(&self, rhs: &Self) -> Self; + fn sqrt_vartime(&self) -> Self; + fn shr_vartime(&self, shift: u32) -> (Self, ConstChoice); + fn shl_vartime(&self, shift: u32) -> (Self, ConstChoice); + fn random_bits(rng: &mut impl CryptoRngCore, bit_length: u32, bits_precision: u32) -> Self; + fn ct_div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb); + fn try_into_u32(&self) -> Option; // Will have to be implemented at Uint level if we want to use TryFrom trait + + fn as_limbs(&self) -> &[Limb]; + fn as_words(&self) -> &[Word]; + fn div_rem_limb(&self, rhs: NonZero) -> (Self, Limb); + + // This is necessary for making sure that every BoxedUint has the same bits_precision + // TODO: remove them after BoxedUint can work with different bits_precision + fn one_with_precision(bits_precision: u32) -> Self; + fn zero_with_precision(bits_precision: u32) -> Self; + fn widen(&self, bits_precision: u32) -> Self; +} + +#[allow(missing_docs)] +pub trait UintModLike: + core::fmt::Debug + + Clone + + Eq + + Sized + + for<'a> Add<&'a Self, Output = Self> + + for<'a> Sub<&'a Self, Output = Self> + + for<'a> Mul<&'a Self, Output = Self> + + Neg + + PowBoundedExp +{ + type Raw: UintLike; + type Params: Clone + Eq + core::fmt::Debug; + + fn new_params(modulus: &Self::Raw) -> CtOption; + fn new(raw: &Self::Raw, params: &Self::Params) -> Self; + + fn zero(params: &Self::Params) -> Self; + fn one(params: &Self::Params) -> Self; + fn square(&self) -> Self; + fn div_by_2(&self) -> Self; +} + +/// Uint impls +impl UintLike for Uint { + type Modular = DynResidue; + + fn as_words(&self) -> &[Word] { + self.as_words() + } + + fn jacobi_symbol_small(lhs: i32, rhs: &Self) -> JacobiSymbol { + jacobi::jacobi_symbol(lhs, rhs) + } + + fn gcd_small(&self, rhs: u32) -> u32 { + gcd::gcd(self, rhs) + } + + fn trailing_zeros(&self) -> u32 { + Self::trailing_zeros(self) + } + + fn trailing_ones(&self) -> u32 { + Self::trailing_ones(self) + } + + fn wrapping_sub(&self, rhs: &Self) -> Self { + Self::wrapping_sub(self, rhs) + } + + fn wrapping_mul(&self, rhs: &Self) -> Self { + Self::wrapping_mul(self, rhs) + } + + fn bit_vartime(&self, index: u32) -> bool { + Self::bit_vartime(self, index) + } + + fn sqrt_vartime(&self) -> Self { + Self::sqrt_vartime(self) + } + + fn shr_vartime(&self, shift: u32) -> (Self, ConstChoice) { + let opt = self.overflowing_shr_vartime(shift); + if opt.is_none().into() { + // opt.is_none() indicates overflow + (Self::ZERO, ConstChoice::TRUE) + } else { + ( + opt.expect("attempt to right shift with overflow"), + ConstChoice::FALSE, + ) + } + } + + fn shl_vartime(&self, shift: u32) -> (Self, ConstChoice) { + let opt = self.overflowing_shl_vartime(shift); + if opt.is_none().into() { + // opt.is_none() indicates overflow + (Self::ZERO, ConstChoice::TRUE) + } else { + ( + opt.expect("attempt to left shift with overflow"), + ConstChoice::FALSE, + ) + } + } + + fn as_limbs(&self) -> &[Limb] { + self.as_limbs() + } + + fn random_bits(rng: &mut impl CryptoRngCore, bit_length: u32, _bits_precision: u32) -> Self { + let random = Self::random(rng) & Self::MAX >> (Self::BITS - bit_length); + let random = random | Self::ONE << (bit_length - 1); + return random; + } + + fn ct_div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb) { + self.div_rem_limb_with_reciprocal(reciprocal) + } + + fn try_into_u32(&self) -> Option { + self.as_words()[0].try_into().ok() + } + + fn div_rem_limb(&self, rhs: NonZero) -> (Self, Limb) { + self.div_rem_limb(rhs) + } + + fn one_with_precision(_bits_precision: u32) -> Self { + Self::ONE + } + + fn zero_with_precision(_bits_precision: u32) -> Self { + Self::ZERO + } + + fn widen(&self, _bits_precision: u32) -> Self { + *self + } +} + +impl UintModLike for DynResidue { + type Raw = Uint; + type Params = DynResidueParams; + + fn new_params(modulus: &Self::Raw) -> CtOption { + Self::Params::new(modulus) + } + + fn new(value: &Self::Raw, params: &Self::Params) -> Self { + Self::new(value, *params) + } + + fn zero(params: &Self::Params) -> Self { + Self::zero(*params) + } + + fn one(precomputed: &Self::Params) -> Self { + Self::one(*precomputed) + } + + fn square(&self) -> Self { + Self::square(self) + } + + fn div_by_2(&self) -> Self { + Self::div_by_2(self) + } +} + +impl UintLike for BoxedUint { + type Modular = BoxedResidue; + + fn jacobi_symbol_small(lhs: i32, rhs: &Self) -> JacobiSymbol { + jacobi::jacobi_symbol(lhs, rhs) + } + + fn gcd_small(&self, rhs: u32) -> u32 { + gcd::gcd(self, rhs) + } + + /// TODO: BoxedUint does not implement bit_vartime + /// TODO: this needs to be tested + fn bit_vartime(&self, index: u32) -> bool { + if index >= self.bits_precision() { + return false; + } + (self.as_limbs()[(index / Limb::BITS) as usize].0 >> (index % Limb::BITS)) & 1 == 1 + } + + fn trailing_zeros(&self) -> u32 { + self.trailing_zeros() + } + + /// TODO: BoxedUint does not implement trailing_ones, but it should be doable + /// TODO: this needs to be testesd, but this is not the implementation I would go with in + /// crypto-bigint + fn trailing_ones(&self) -> u32 { + let limbs = self.as_limbs(); + + let mut count = 0; + let mut i = 0; + let mut nonmax_limb_not_encountered = ConstChoice::TRUE; + while i < limbs.len() { + let l = limbs[i]; + let z = l.trailing_ones(); + let should_count: bool = nonmax_limb_not_encountered.into(); + if should_count { + count += z; + } + let is_max = l.0 == Limb::MAX.0; + if should_count && is_max { + nonmax_limb_not_encountered = ConstChoice::TRUE; + } else { + nonmax_limb_not_encountered = ConstChoice::FALSE; + } + i += 1; + } + + count + } + + fn wrapping_sub(&self, rhs: &Self) -> Self { + self.wrapping_sub(rhs) + } + + fn wrapping_mul(&self, rhs: &Self) -> Self { + self.wrapping_mul(rhs) + } + + /// BoxedUint does not implement sqrt_vartime + fn sqrt_vartime(&self) -> Self { + self.sqrt_vartime() + } + + /// TODO: BoxedUint::shr_vartime should behave similarly as Uint::shr_vartime; instead of + /// returning Option, return (val, choice) + fn shr_vartime(&self, shift: u32) -> (Self, ConstChoice) { + let (val, overflow) = self.overflowing_shr(shift); + if overflow.into() { + (val, ConstChoice::TRUE) + } else { + (val, ConstChoice::FALSE) + } + } + + /// TODO: BoxedUint::shl_vartime should behave similarly as Uint::shr_vartime; instead of + /// returning Option, return (val, choice) + fn shl_vartime(&self, shift: u32) -> (Self, ConstChoice) { + let (val, overflow) = self.overflowing_shl(shift); + if overflow.into() { + (val, ConstChoice::TRUE) + } else { + (val, ConstChoice::FALSE) + } + } + + fn random_bits(rng: &mut impl CryptoRngCore, bit_length: u32, bits_precision: u32) -> Self { + let random = Self::random(rng, bits_precision) + & Self::max(bits_precision) + .overflowing_shr(bits_precision - bit_length) + .0; + let random = random + | Self::one_with_precision(bits_precision) + .overflowing_shl(bit_length - 1) + .0; + return random; + } + + /// TODO: BoxedUint does not implement div_rem_limb_with_reciprocal + /// TODO: BoxedUint does not implement shl_limb + fn ct_div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb) { + self.div_rem_limb_with_reciprocal(reciprocal) + } + + fn try_into_u32(&self) -> Option { + self.as_words()[0].try_into().ok() + } + + fn as_limbs(&self) -> &[Limb] { + self.as_limbs() + } + + fn as_words(&self) -> &[Word] { + self.as_words() + } + + /// TODO: BoxedUint does not implement div_rem_limb + fn div_rem_limb(&self, rhs: NonZero) -> (Self, Limb) { + self.div_rem_limb(rhs) + } + + fn one_with_precision(bits_precision: u32) -> Self { + Self::one_with_precision(bits_precision) + } + + fn zero_with_precision(bits_precision: u32) -> Self { + Self::zero_with_precision(bits_precision) + } + + fn widen(&self, bits_precision: u32) -> Self { + self.widen(bits_precision) + } +} + +impl UintModLike for BoxedResidue { + type Raw = BoxedUint; + type Params = BoxedResidueParams; + + fn new_params(modulus: &Self::Raw) -> CtOption { + Self::Params::new(modulus.clone()) + } + + fn new(raw: &Self::Raw, params: &Self::Params) -> Self { + Self::new(raw.widen(params.bits_precision()).clone(), params.clone()) + } + + fn zero(params: &Self::Params) -> Self { + Self::zero(params.clone()) + } + + fn one(params: &Self::Params) -> Self { + Self::one(params.clone()) + } + + fn square(&self) -> Self { + self.square() + } + + /// TODO: BoxedUint does not implement div_by_2 + fn div_by_2(&self) -> Self { + self.div_by_2() + } +}