use crate::bignum::{MontModulus, Uint};
use crate::rng::RngCore;
const SMALL_PRIMES: [u64; 24] = [
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
];
fn mod_small<const LIMBS: usize>(n: &Uint<LIMBS>, p: u64) -> u64 {
let limbs = n.as_limbs();
let mut rem: u128 = 0;
let mut i = LIMBS;
while i > 0 {
i -= 1;
rem = ((rem << 64) | limbs[i] as u128) % p as u128;
}
rem as u64
}
fn random_uint<const LIMBS: usize, R: RngCore>(rng: &mut R) -> Uint<LIMBS> {
let mut limbs = [0u64; LIMBS];
for limb in &mut limbs {
*limb = rng.next_u64();
}
Uint::from_limbs(limbs)
}
pub fn is_prime<const LIMBS: usize, R: RngCore>(
n: &Uint<LIMBS>,
rng: &mut R,
rounds: usize,
) -> bool {
let one = Uint::ONE;
let two = Uint::from_u64(2);
if n == &Uint::ZERO || n == &one {
return false;
}
if n == &two {
return true;
}
if !bool::from(n.is_odd()) {
return false; }
for &p in &SMALL_PRIMES {
if mod_small(n, p) == 0 {
return n == &Uint::from_u64(p);
}
}
let n_minus_1 = n.wrapping_sub(&one);
let mut d = n_minus_1;
let mut s = 0u32;
while !bool::from(d.is_odd()) {
d = d.shr1();
s += 1;
}
let modulus = MontModulus::new(*n);
'rounds: for _ in 0..rounds {
let mut a = random_uint::<LIMBS, R>(rng).reduce(n);
if a == Uint::ZERO || a == one || a == n_minus_1 {
a = two;
}
let mut x = modulus.pow(&a, &d);
if x == one || x == n_minus_1 {
continue 'rounds;
}
for _ in 0..s.saturating_sub(1) {
x = modulus.mul_mod(&x, &x);
if x == n_minus_1 {
continue 'rounds;
}
}
return false; }
true
}
fn mask_to_bits(limbs: &mut [u64], bits: usize) {
for (i, limb) in limbs.iter_mut().enumerate() {
let low = i * 64;
if low >= bits {
*limb = 0;
} else if low + 64 > bits {
let keep = bits - low; *limb &= (1u64 << keep) - 1;
}
}
}
pub fn random_prime<const LIMBS: usize, R: RngCore>(
rng: &mut R,
bits: usize,
rounds: usize,
) -> Uint<LIMBS> {
assert!(bits >= 2 && bits <= LIMBS * 64, "bits out of range");
loop {
let mut limbs = [0u64; LIMBS];
for limb in &mut limbs {
*limb = rng.next_u64();
}
mask_to_bits(&mut limbs, bits);
limbs[(bits - 1) / 64] |= 1 << ((bits - 1) % 64); limbs[(bits - 2) / 64] |= 1 << ((bits - 2) % 64); limbs[0] |= 1; let candidate = Uint::from_limbs(limbs);
if is_prime(&candidate, rng, rounds) {
return candidate;
}
}
}
#[cfg(feature = "alloc")]
pub(crate) use crate::bignum::prime::is_prime_boxed;
#[cfg(feature = "alloc")]
pub(crate) fn random_prime_boxed<R: RngCore>(
rng: &mut R,
bits: usize,
rounds: usize,
) -> crate::bignum::BoxedUint {
use crate::bignum::BoxedUint;
let nlimbs = bits.div_ceil(64);
loop {
let mut limbs = alloc::vec![0u64; nlimbs];
for limb in &mut limbs {
*limb = rng.next_u64();
}
mask_to_bits(&mut limbs, bits);
limbs[(bits - 1) / 64] |= 1 << ((bits - 1) % 64);
limbs[(bits - 2) / 64] |= 1 << ((bits - 2) % 64);
limbs[0] |= 1;
let candidate = BoxedUint::from_limbs(limbs);
if is_prime_boxed(&candidate, rng, rounds) {
return candidate;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::Sha256;
use crate::rng::HmacDrbg;
fn rng() -> HmacDrbg<Sha256> {
HmacDrbg::new(b"prime-test-seed", b"nonce", &[])
}
#[test]
fn known_primes_and_composites() {
let mut r = rng();
assert!(is_prime(&Uint::<1>::from_u64(2), &mut r, 20));
assert!(is_prime(&Uint::<1>::from_u64(7919), &mut r, 20));
assert!(is_prime(
&Uint::<1>::from_u64(2_305_843_009_213_693_951),
&mut r,
20
));
assert!(!is_prime(&Uint::<1>::from_u64(0), &mut r, 20));
assert!(!is_prime(&Uint::<1>::from_u64(1), &mut r, 20));
assert!(!is_prime(&Uint::<1>::from_u64(7917), &mut r, 20)); assert!(!is_prime(&Uint::<1>::from_u64(70747), &mut r, 20));
assert!(!is_prime(&Uint::<1>::from_u64(7918), &mut r, 20));
}
#[test]
fn generated_primes_are_prime() {
let mut r = rng();
for _ in 0..3 {
let p = random_prime::<1, _>(&mut r, 64, 20);
assert!(bool::from(p.is_odd()));
assert!(p.as_limbs()[0] >> 63 == 1, "top bit should be set");
assert!(is_prime(&p, &mut r, 25));
}
let p = random_prime::<2, _>(&mut r, 96, 20);
assert!(is_prime(&p, &mut r, 25));
assert_eq!(p.as_limbs()[1] >> 31, 1, "bit 95 set, above cleared");
}
}