use std::collections::HashMap;
use indicatif::ProgressBar;
use rug::{integer::IsPrime, Integer};
use crate::{key::PrivateKey, Attack, AttackSpeed, Error, Parameters, Solution};
const MAX_DEEP: usize = 4;
const OPTIMAL_B1: [usize; 12] = [
2000, 11000, 50000, 250000, 1000000, 3000000, 11000000, 44000000, 110000000, 260000000, 850000000, 2900000000, ];
fn ecm(
n: &Integer,
pb: Option<&ProgressBar>,
seed: usize,
deep: usize,
) -> Result<HashMap<Integer, usize>, Error> {
if deep == MAX_DEEP {
return Ok(HashMap::from([(n.clone(), 1)]));
}
if let Some(pb) = pb {
pb.set_prefix(format!("ecm ({}/{})", deep + 1, MAX_DEEP));
}
let mut factors = HashMap::new();
for (factor, count) in ecm::ecm_with_params(n, OPTIMAL_B1[deep], 100_000, 100, seed, pb)
.or(Err(Error::NotFound))?
{
if factor.is_probably_prime(100) != IsPrime::No {
factors.insert(factor, count);
} else {
let sub_factors = ecm(&factor, pb, seed * 17, deep + 1)?;
for (sub_factor, sub_count) in sub_factors {
*factors.entry(sub_factor).or_insert(0) += sub_count * count;
}
}
}
Ok(factors)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EcmAttack;
impl Attack for EcmAttack {
fn name(&self) -> &'static str {
"ecm"
}
fn speed(&self) -> AttackSpeed {
AttackSpeed::Slow
}
fn run(&self, params: &Parameters, pb: Option<&ProgressBar>) -> Result<Solution, Error> {
let e = ¶ms.e;
let n = params.n.as_ref().ok_or(Error::MissingParameters)?;
let factors = ecm(n, pb, 1234, 0)
.or(Err(Error::NotFound))?
.iter()
.flat_map(|(p, e)| std::iter::repeat_n(p, *e))
.cloned()
.collect::<Vec<_>>();
if factors.len() < 2 {
return Err(Error::NotFound);
}
Ok(Solution::new_pk(
self.name(),
PrivateKey::from_factors(factors, e)?,
))
}
}
#[cfg(test)]
mod tests {
use crate::{Attack, Parameters};
use super::*;
#[test]
fn attack() {
let p = Integer::from(1779681653);
let q = Integer::from(1903643191);
let params = Parameters {
n: Some(p.clone() * &q),
..Default::default()
};
let solution = EcmAttack.run(¶ms, None).unwrap();
let pk = solution.pk.unwrap();
assert_eq!(pk.p(), p);
assert_eq!(pk.q(), q);
}
}