From 7fe888191b3a5ac67cf3d15ce5cb22cc4c5a18e3 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Wed, 27 Dec 2023 14:45:11 -0800 Subject: [PATCH] Dissolve UintLike, Integer supports all we need now --- Cargo.toml | 2 +- benches/bench.rs | 25 +++++--- src/hazmat.rs | 2 +- src/hazmat/lucas.rs | 24 ++++---- src/hazmat/miller_rabin.rs | 23 ++++--- src/hazmat/sieve.rs | 87 +++++++++++---------------- src/lib.rs | 3 - src/presets.rs | 58 +++++++++++------- src/traits.rs | 4 +- src/uint_traits.rs | 120 ------------------------------------- 10 files changed, 113 insertions(+), 235 deletions(-) delete mode 100644 src/uint_traits.rs diff --git a/Cargo.toml b/Cargo.toml index d1fcea9..f1c7207 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 = ["alloc", "rand_core"] } +crypto-bigint = { git = "https://github.com/RustCrypto/crypto-bigint", default-features = false, features = ["alloc", "rand_core"], commit = "42ea8e6965573e8039294adfaa6a59909859eadd" } 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 edd5637..e5c602a 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}; 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 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, Uint::::BITS, false) + Sieve::new(&start, NonZeroU32::new(Uint::::BITS).unwrap(), false) } fn make_presieved_num(rng: &mut impl CryptoRngCore) -> Odd> { @@ -42,7 +51,7 @@ fn bench_sieve(c: &mut Criterion) { group.bench_function("(U128) creation", |b| { b.iter_batched( || random_odd_uint::>(&mut OsRng, 128), - |start| Sieve::new(start.as_ref(), 128, false), + |start| Sieve::new(start.as_ref(), NonZeroU32::new(128).unwrap(), false), BatchSize::SmallInput, ) }); @@ -63,7 +72,7 @@ fn bench_sieve(c: &mut Criterion) { group.bench_function("(U1024) creation", |b| { b.iter_batched( || random_odd_uint::>(&mut OsRng, 1024), - |start| Sieve::new(start.as_ref(), 1024, false), + |start| Sieve::new(start.as_ref(), NonZeroU32::new(1024).unwrap(), false), BatchSize::SmallInput, ) }); @@ -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 { + fn random(rng: &mut impl CryptoRngCore) -> GmpInteger { let num = random_odd_uint::>(rng, Uint::::BITS).get(); - Integer::from_digits(num.as_words(), Order::Lsf) + 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/lucas.rs b/src/hazmat/lucas.rs index bbe89e3..42ddbde 100644 --- a/src/hazmat/lucas.rs +++ b/src/hazmat/lucas.rs @@ -6,7 +6,6 @@ use super::{ jacobi::{jacobi_symbol_vartime, JacobiSymbol}, Primality, }; -use crate::UintLike; /// The maximum number of attempts to find `D` such that `(D/n) == -1`. // This is widely believed to be impossible. @@ -28,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], @@ -46,7 +45,7 @@ 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` @@ -111,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) @@ -134,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; @@ -179,7 +178,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: &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`. @@ -283,7 +282,7 @@ 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( +pub fn lucas_test( candidate: &Odd, base: impl LucasBase, check: LucasCheck, @@ -340,7 +339,7 @@ pub fn lucas_test( // Some constants in Montgomery form - let params = ::Monty::new_params(candidate.clone()); + let params = ::Monty::new_params_vartime(candidate.clone()); let zero = ::Monty::zero(params.clone()); let one = ::Monty::one(params.clone()); @@ -501,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; @@ -509,10 +508,7 @@ mod tests { use super::{ decompose, lucas_test, AStarBase, BruteForceBase, LucasBase, LucasCheck, SelfridgeBase, }; - use crate::{ - hazmat::{primes, pseudoprimes, Primality}, - UintLike, - }; + use crate::hazmat::{primes, pseudoprimes, Primality}; #[test] fn bases_derived_traits() { @@ -557,7 +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 bc0ca07..fe3241c 100644 --- a/src/hazmat/miller_rabin.rs +++ b/src/hazmat/miller_rabin.rs @@ -1,10 +1,9 @@ //! Miller-Rabin primality test. -use crypto_bigint::{Integer, Monty, NonZero, Odd, PowBoundedExp, Square}; +use crypto_bigint::{Integer, Monty, NonZero, Odd, PowBoundedExp, RandomMod, Square}; use rand_core::CryptoRngCore; use super::Primality; -use crate::UintLike; /// Precomputed data used to perform Miller-Rabin primality test[^Pomerance1980]. /// The numbers that pass it are commonly called "strong probable primes" @@ -15,7 +14,7 @@ use crate::UintLike; /// Math. Comp. 35 1003-1026 (1980), /// DOI: [10.2307/2006210](https://dx.doi.org/10.2307/2006210) #[derive(Clone, Debug, PartialEq, Eq)] -pub struct MillerRabin { +pub struct MillerRabin { candidate: T, bit_length: u32, montgomery_params: <::Monty as Monty>::Params, @@ -25,10 +24,10 @@ pub struct MillerRabin { d: T, } -impl MillerRabin { +impl MillerRabin { /// Initializes a Miller-Rabin test for `candidate`. pub fn new(candidate: Odd) -> Self { - let params = ::Monty::new_params(candidate.clone()); + let params = ::Monty::new_params_vartime(candidate.clone()); let one = ::Monty::one(params.clone()); let minus_one = -one.clone(); @@ -118,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}; @@ -127,10 +127,7 @@ mod tests { use num_prime::nt_funcs::is_prime64; use super::MillerRabin; - use crate::{ - hazmat::{primes, pseudoprimes, random_odd_uint, Sieve}, - UintLike, - }; + use crate::hazmat::{primes, pseudoprimes, random_odd_integer, Sieve}; #[test] fn miller_rabin_derived_traits() { @@ -152,7 +149,7 @@ mod tests { pseudoprimes::STRONG_BASE_2.iter().any(|x| *x == num) } - fn random_checks( + fn random_checks( rng: &mut impl CryptoRngCore, mr: &MillerRabin, count: usize, @@ -192,8 +189,8 @@ mod tests { #[test] fn trivial() { let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); - let start = random_odd_uint::(&mut rng, 1024); - for num in Sieve::new(start.as_ref(), 1024, false).take(10) { + 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. diff --git a/src/hazmat/sieve.rs b/src/hazmat/sieve.rs index f624d1f..57f183c 100644 --- a/src/hazmat/sieve.rs +++ b/src/hazmat/sieve.rs @@ -2,34 +2,21 @@ //! before proceeding with slower tests. use alloc::{vec, vec::Vec}; +use core::num::NonZeroU32; -use crypto_bigint::Odd; +use crypto_bigint::{Integer, Odd, RandomBits}; use rand_core::CryptoRngCore; -use crate::{ - hazmat::precomputed::{SmallPrime, RECIPROCALS, SMALL_PRIMES}, - UintLike, -}; +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(rng: &mut impl CryptoRngCore, bit_length: u32) -> Odd { - if bit_length == 0 { - panic!("Bit length must be non-zero"); - } - - // TODO: what do we do here if `bit_length` is greater than Uint::BITS? - // assume that the user knows what he's doing since it is a hazmat function? - /*if bit_length > Uint::::BITS { - panic!( - "The requested bit length ({}) is larger than the chosen Uint size", - bit_length - ); - }*/ +pub fn random_odd_integer( + rng: &mut impl CryptoRngCore, + 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 = T::random_bits(rng, bit_length); // Make it odd @@ -53,7 +40,7 @@ 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 { +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. @@ -68,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 @@ -78,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: &T, 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 > 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 ); } @@ -266,7 +251,7 @@ impl Sieve { } } -impl Iterator for Sieve { +impl Iterator for Sieve { type Item = T; fn next(&mut self) -> Option { @@ -279,13 +264,14 @@ mod tests { use alloc::format; use alloc::vec::Vec; + use core::num::NonZeroU32; 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] @@ -293,8 +279,8 @@ mod tests { let max_prime = SMALL_PRIMES[SMALL_PRIMES.len() - 1]; let mut rng = ChaCha8Rng::from_seed(*b"01234567890123456789012345678901"); - let start = random_odd_uint::(&mut rng, 32).get(); - for num in Sieve::new(&start, 32, false).take(100) { + let start = random_odd_integer::(&mut rng, NonZeroU32::new(32).unwrap()).get(); + for num in Sieve::new(&start, NonZeroU32::new(32).unwrap(), false).take(100) { let num_u64 = u64::from(num); assert!(num_u64.leading_zeros() == 32); @@ -306,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)); @@ -359,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 = random_odd_uint::(&mut OsRng, 50).get(); + 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 = 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 = 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/lib.rs b/src/lib.rs index 03a4640..f708159 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,6 @@ 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, @@ -28,5 +27,3 @@ 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; diff --git a/src/presets.rs b/src/presets.rs index 7232dbe..5d39a41 100644 --- a/src/presets.rs +++ b/src/presets.rs @@ -1,20 +1,21 @@ -use crypto_bigint::Odd; +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, }; -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. /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn generate_prime(bit_length: u32) -> T { +pub fn generate_prime(bit_length: u32) -> T { generate_prime_with_rng(&mut OsRng, bit_length) } @@ -24,7 +25,7 @@ pub fn generate_prime(bit_length: u32) -> T { /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn generate_safe_prime(bit_length: u32) -> T { +pub fn generate_safe_prime(bit_length: u32) -> T { generate_safe_prime_with_rng(&mut OsRng, bit_length) } @@ -32,7 +33,7 @@ pub fn generate_safe_prime(bit_length: u32) -> T { /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn is_prime(num: &T) -> bool { +pub fn is_prime(num: &T) -> bool { is_prime_with_rng(&mut OsRng, num) } @@ -42,7 +43,7 @@ pub fn is_prime(num: &T) -> bool { /// /// See [`is_prime_with_rng`] for details about the performed checks. #[cfg(feature = "default-rng")] -pub fn is_safe_prime(num: &T) -> bool { +pub fn is_safe_prime(num: &T) -> bool { is_safe_prime_with_rng(&mut OsRng, num) } @@ -52,12 +53,16 @@ pub fn is_safe_prime(num: &T) -> 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(rng: &mut impl CryptoRngCore, bit_length: u32) -> T { +pub fn generate_prime_with_rng( + rng: &mut impl CryptoRngCore, + 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 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) { @@ -74,15 +79,16 @@ pub fn generate_prime_with_rng(rng: &mut impl CryptoRngCore, bit_le /// 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: 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 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) { @@ -117,7 +123,7 @@ 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: &T) -> bool { +pub fn is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &T) -> bool { if num == &T::from(2u32) { return true; } @@ -133,7 +139,10 @@ pub fn is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &T) -> /// 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: &T) -> 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. @@ -152,7 +161,7 @@ pub fn is_safe_prime_with_rng(rng: &mut impl CryptoRngCore, num: &T } /// Checks for primality assuming that `num` is odd. -fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Odd) -> bool { +fn _is_prime_with_rng(rng: &mut impl CryptoRngCore, num: &Odd) -> bool { let mr = MillerRabin::new(num.clone()); if !mr.test_base_two().is_probably_prime() { @@ -308,13 +317,17 @@ mod tests { } #[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, 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, 65); } @@ -350,13 +363,14 @@ mod tests { #[cfg(feature = "tests-openssl")] mod tests_openssl { use alloc::format; + use core::num::NonZeroU32; 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() @@ -394,7 +408,7 @@ mod tests_openssl { // Generate random numbers, check if our test agrees with OpenSSL for _ in 0..100 { - let p = random_odd_uint::(&mut OsRng, 128); + 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); @@ -409,6 +423,8 @@ mod tests_openssl { #[cfg(test)] #[cfg(feature = "tests-gmp")] mod tests_gmp { + use core::num::NonZeroU32; + use crypto_bigint::U128; use rand_core::OsRng; use rug::{ @@ -417,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) @@ -442,7 +458,7 @@ mod tests_gmp { // Generate primes with GMP, check them for _ in 0..100 { - let start = 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); @@ -451,7 +467,7 @@ mod tests_gmp { // Generate random numbers, check if our test agrees with GMP for _ in 0..100 { - let p = random_odd_uint::(&mut OsRng, 128); + let p = random_odd_integer::(&mut OsRng, NonZeroU32::new(128).unwrap()); let actual = is_prime(p.as_ref()); let p_bn = to_gmp(&p); let expected = gmp_is_prime(&p_bn); diff --git a/src/traits.rs b/src/traits.rs index 458d85c..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::{ @@ -37,7 +37,7 @@ pub trait RandomPrimeWithRng { fn is_safe_prime_with_rng(&self, rng: &mut impl CryptoRngCore) -> bool; } -impl RandomPrimeWithRng for Uint { +impl RandomPrimeWithRng for T { fn generate_prime_with_rng(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self { generate_prime_with_rng(rng, bit_length) } diff --git a/src/uint_traits.rs b/src/uint_traits.rs deleted file mode 100644 index 015ba4e..0000000 --- a/src/uint_traits.rs +++ /dev/null @@ -1,120 +0,0 @@ -#![allow(missing_docs)] - -use crypto_bigint::{subtle::CtOption, BoxedUint, Integer, Random, RandomMod, Uint}; -use rand_core::CryptoRngCore; - -// would be nice to have: *Assign traits; arithmetic traits for &self (BitAnd and Shr in particular); -pub trait UintLike: Integer + RandomMod { - fn bit_vartime(&self, index: u32) -> bool; - fn set_bit_vartime(&mut self, index: u32, value: bool); - fn trailing_zeros_vartime(&self) -> u32; - fn trailing_ones_vartime(&self) -> u32; - fn sqrt_vartime(&self) -> Self; - fn overflowing_shl_vartime(&self, shift: u32) -> CtOption; - fn overflowing_shr_vartime(&self, shift: u32) -> CtOption; - fn wrapping_shl_vartime(&self, shift: u32) -> Self; - fn wrapping_shr_vartime(&self, shift: u32) -> Self; - fn random_bits(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self; -} - -// Uint impls - -impl UintLike for Uint { - fn set_bit_vartime(&mut self, index: u32, value: bool) { - if value { - *self |= Uint::ONE << index - } else { - *self &= (Uint::ONE << index).not() - } - } - - fn trailing_zeros_vartime(&self) -> u32 { - Self::trailing_zeros_vartime(self) - } - - fn trailing_ones_vartime(&self) -> u32 { - Self::trailing_ones_vartime(self) - } - - fn bit_vartime(&self, index: u32) -> bool { - Self::bit_vartime(self, index) - } - - fn sqrt_vartime(&self) -> Self { - Self::sqrt_vartime(self) - } - - fn overflowing_shl_vartime(&self, shift: u32) -> CtOption { - Self::overflowing_shl_vartime(self, shift).into() - } - - fn overflowing_shr_vartime(&self, shift: u32) -> CtOption { - Self::overflowing_shr_vartime(self, shift).into() - } - - fn wrapping_shl_vartime(&self, shift: u32) -> Self { - Self::wrapping_shl_vartime(self, shift) - } - - fn wrapping_shr_vartime(&self, shift: u32) -> Self { - Self::wrapping_shr_vartime(self, shift) - } - - fn random_bits(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self { - if bit_length > Self::BITS { - panic!("The requested bit length ({bit_length}) is larger than the chosen Uint size"); - } - let random = Self::random(rng); - random >> (Self::BITS - bit_length) - } -} - -impl UintLike for BoxedUint { - fn set_bit_vartime(&mut self, index: u32, value: bool) { - if value { - *self |= Self::one() << index - } else { - *self &= (Self::one() << index).not() - } - } - - fn trailing_zeros_vartime(&self) -> u32 { - Self::trailing_zeros(self) - } - - fn trailing_ones_vartime(&self) -> u32 { - Self::trailing_ones_vartime(self) - } - - fn bit_vartime(&self, index: u32) -> bool { - Self::bit_vartime(self, index) - } - - fn sqrt_vartime(&self) -> Self { - Self::sqrt_vartime(self) - } - - fn overflowing_shl_vartime(&self, shift: u32) -> CtOption { - let (res, is_some) = Self::overflowing_shl(self, shift); - CtOption::new(res, is_some) - } - - fn overflowing_shr_vartime(&self, shift: u32) -> CtOption { - let (res, is_some) = Self::overflowing_shr(self, shift); - CtOption::new(res, is_some) - } - - fn wrapping_shl_vartime(&self, shift: u32) -> Self { - Self::wrapping_shl_vartime(self, shift) - } - - fn wrapping_shr_vartime(&self, shift: u32) -> Self { - Self::wrapping_shr_vartime(self, shift) - } - - fn random_bits(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self { - let random = Self::random(rng, bit_length); - let bits_precision = random.bits_precision(); - random >> (bits_precision - bit_length) - } -}