use alloc::vec::Vec;
use num_bigint::{BigUint, RandPrime};
#[allow(unused_imports)]
use num_traits::Float;
use num_traits::Zero;
use rand_core::CryptoRngCore;
use crate::{
algorithms::rsa::{compute_modulus, compute_private_exponent_euler_totient},
errors::{Error, Result},
};
pub struct RsaPrivateKeyComponents {
pub n: BigUint,
pub e: BigUint,
pub d: BigUint,
pub primes: Vec<BigUint>,
}
pub(crate) fn generate_multi_prime_key_with_exp<R: CryptoRngCore + ?Sized>(
rng: &mut R,
nprimes: usize,
bit_size: usize,
exp: &BigUint,
) -> Result<RsaPrivateKeyComponents> {
if nprimes < 2 {
return Err(Error::NprimesTooSmall);
}
if bit_size < 64 {
let prime_limit = (1u64 << (bit_size / nprimes) as u64) as f64;
let mut pi = prime_limit / (prime_limit.ln() - 1f64);
pi /= 4f64;
pi /= 2f64;
if pi < nprimes as f64 {
return Err(Error::TooFewPrimes);
}
}
let mut primes = vec![BigUint::zero(); nprimes];
let n_final: BigUint;
let d_final: BigUint;
'next: loop {
let mut todo = bit_size;
if nprimes >= 7 {
todo += (nprimes - 2) / 5;
}
for (i, prime) in primes.iter_mut().enumerate() {
*prime = rng.gen_prime(todo / (nprimes - i));
todo -= prime.bits();
}
for (i, prime1) in primes.iter().enumerate() {
for prime2 in primes.iter().take(i) {
if prime1 == prime2 {
continue 'next;
}
}
}
let n = compute_modulus(&primes);
if n.bits() != bit_size {
continue 'next;
}
if let Ok(d) = compute_private_exponent_euler_totient(&primes, exp) {
n_final = n;
d_final = d;
break;
}
}
Ok(RsaPrivateKeyComponents {
n: n_final,
e: exp.clone(),
d: d_final,
primes,
})
}
#[cfg(test)]
mod tests {
use super::*;
use num_bigint::BigUint;
use num_traits::FromPrimitive;
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
const EXP: u64 = 65537;
#[test]
fn test_impossible_keys() {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let exp = BigUint::from_u64(EXP).expect("invalid static exponent");
for i in 0..32 {
let _ = generate_multi_prime_key_with_exp(&mut rng, 2, i, &exp);
let _ = generate_multi_prime_key_with_exp(&mut rng, 3, i, &exp);
let _ = generate_multi_prime_key_with_exp(&mut rng, 4, i, &exp);
let _ = generate_multi_prime_key_with_exp(&mut rng, 5, i, &exp);
}
}
macro_rules! key_generation {
($name:ident, $multi:expr, $size:expr) => {
#[test]
fn $name() {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let exp = BigUint::from_u64(EXP).expect("invalid static exponent");
for _ in 0..10 {
let components =
generate_multi_prime_key_with_exp(&mut rng, $multi, $size, &exp).unwrap();
assert_eq!(components.n.bits(), $size);
assert_eq!(components.primes.len(), $multi);
}
}
};
}
key_generation!(key_generation_128, 2, 128);
key_generation!(key_generation_1024, 2, 1024);
key_generation!(key_generation_multi_3_256, 3, 256);
key_generation!(key_generation_multi_4_64, 4, 64);
key_generation!(key_generation_multi_5_64, 5, 64);
key_generation!(key_generation_multi_8_576, 8, 576);
key_generation!(key_generation_multi_16_1024, 16, 1024);
}