Skip to content

Commit

Permalink
Simplify preset structure
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Feb 13, 2025
1 parent e18ad64 commit 4e8ed4f
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 49 deletions.
9 changes: 0 additions & 9 deletions src/hazmat/miller_rabin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,6 @@ impl<T: Integer + RandomMod> MillerRabin<T> {
.expect("addition should not overflow by construction");
self.test(&random)
}

/// Returns the number of bits necessary to represent the candidate.
/// NOTE: This is different than the number of bits of *storage* the integer takes up.
///
/// For example, a U512 type occupies 8 64-bit words, but the number `7` contained in such a type
/// has a bit length of 3 because 7 is `b111`.
pub(crate) fn bits(&self) -> u32 {
self.bits
}
}

/**
Expand Down
77 changes: 37 additions & 40 deletions src/presets.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crypto_bigint::{Integer, Limb, Odd, RandomBits, RandomMod};
use crypto_bigint::{Integer, Odd, RandomBits, RandomMod, Word};
use rand_core::CryptoRngCore;

#[cfg(feature = "default-rng")]
Expand Down Expand Up @@ -162,6 +162,10 @@ where
.expect("will produce a result eventually")
}

fn equals_primitive<T: Integer>(num: &T, primitive: Word) -> bool {
num.bits_vartime() <= u16::BITS && num.as_ref()[0].0 == primitive
}

/// Probabilistically checks if the given number is prime using the provided RNG.
///
/// Performed checks:
Expand All @@ -187,68 +191,61 @@ where
/// "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<T: Integer + RandomMod>(rng: &mut impl CryptoRngCore, num: &T) -> bool {
if num == &T::from_limb_like(Limb::from(2u32), num) {
return true;
}

match Odd::new(num.clone()).into() {
Some(x) => _is_prime_with_rng(rng, x),
None => false,
pub fn is_prime_with_rng<T: Integer + RandomMod>(rng: &mut impl CryptoRngCore, candidate: &T) -> bool {
if equals_primitive(candidate, 1) {
return false;
}
}

/// Probabilistically checks 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<T: Integer + RandomMod>(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 == &T::from_limb_like(Limb::from(5u32), num) {
if equals_primitive(candidate, 2) {
return true;
}

// Safe primes are always of the form 4k + 3 (i.e. n ≡ 3 mod 4)
// The last two digits of a binary number give you its value modulo 4.
// Primes p=4n+3 will always end in 11​ in binary because p ≡ 3 mod 4.
if num.as_ref()[0].0 & 3 != 3 {
return false;
}

// These are ensured to be odd by the check above.
let odd_num = Odd::new(num.clone()).expect("`num` is odd here given the checks above");
let odd_half_num = Odd::new(num.wrapping_shr_vartime(1)).expect("The binary rep of `num` ends in `11`, so shifting right by one is guaranteed leave a `1` at the end, so it's odd");

_is_prime_with_rng(rng, odd_num) && _is_prime_with_rng(rng, odd_half_num)
}
let odd_candidate: Odd<T> = match Odd::new(candidate.clone()).into() {
Some(x) => x,
None => return false,
};

/// Checks for primality.
/// First run a Miller-Rabin test with base 2
/// If the outcome of M-R is "probably prime", then run a Lucas test
/// If the Lucas test is inconclusive, run a Miller-Rabin with random base and unless this second
/// M-R test finds it's composite, then conclude that it's prime.
fn _is_prime_with_rng<T: Integer + RandomMod>(rng: &mut impl CryptoRngCore, candidate: Odd<T>) -> bool {
let mr = MillerRabin::new(candidate.clone());
let mr = MillerRabin::new(odd_candidate.clone());

if !mr.test_base_two().is_probably_prime() {
return false;
}

match lucas_test(candidate, AStarBase, LucasCheck::Strong) {
match lucas_test(odd_candidate, AStarBase, LucasCheck::Strong) {
Primality::Composite => return false,
Primality::Prime => return true,
_ => {}
}

// The random base test only makes sense when `num > 3`.
if mr.bits() > 2 && !mr.test_random_base(rng).is_probably_prime() {
if !equals_primitive(candidate, 3) && !mr.test_random_base(rng).is_probably_prime() {
return false;
}

true
}

/// Probabilistically checks 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<T: Integer + RandomMod>(rng: &mut impl CryptoRngCore, candidate: &T) -> bool {
// Since, by the definition of safe prime, `(candidate - 1) / 2` must also be prime,
// and therefore odd, `candidate` has to be equal to 3 modulo 4.
// 5 is the only exception, so we check for it.
if equals_primitive(candidate, 5) {
return true;
}

// Safe primes are always of the form 4k + 3 (i.e. n ≡ 3 mod 4)
// The last two digits of a binary number give you its value modulo 4.
// Primes p=4n+3 will always end in 11 in binary because p ≡ 3 mod 4.
if candidate.as_ref()[0].0 & 3 != 3 {
return false;
}

is_prime_with_rng(rng, candidate) && is_prime_with_rng(rng, &candidate.wrapping_shr_vartime(1))
}

#[cfg(test)]
mod tests {
use crypto_bigint::{nlimbs, BoxedUint, CheckedAdd, Uint, Word, U128, U64};
Expand Down

0 comments on commit 4e8ed4f

Please sign in to comment.