1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use indicatif::ProgressBar;
use rug::Integer;

use crate::{key::PrivateKey, Attack, Error, Parameters, Solution};

const MAX_ITERATIONS: u64 = 25_000;
const TICK_SIZE: u64 = MAX_ITERATIONS / 100;

/// Factorial GCD attack (try to find a common factor with Factorial (+ or - 1) numbers)
/// E.g. 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, ...
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FactorialGcdAttack;

impl Attack for FactorialGcdAttack {
    fn name(&self) -> &'static str {
        "factorial_gcd"
    }

    fn run(&self, params: &Parameters, pb: Option<&ProgressBar>) -> Result<Solution, Error> {
        let e = &params.e;
        let n = params.n.as_ref().ok_or(Error::MissingParameters)?;

        if let Some(pb) = pb {
            pb.set_length(MAX_ITERATIONS)
        }

        let mut f = Integer::from(1);
        for i in 2..MAX_ITERATIONS {
            f *= i;

            // Factorial - 1
            let p = Integer::from(&f - 1).gcd(n);
            if 1 < p && &p < n {
                let q = Integer::from(n / &p);
                return Ok(Solution::new_pk(
                    self.name(),
                    PrivateKey::from_p_q(p, q, e.clone())?,
                ));
            }

            // Factorial + 1
            let p = Integer::from(&f + 1).gcd(n);
            if 1 < p && &p < n {
                let q = Integer::from(n / &p);
                return Ok(Solution::new_pk(
                    self.name(),
                    PrivateKey::from_p_q(p, q, e.clone())?,
                ));
            }

            if i % TICK_SIZE == 0 {
                if let Some(pb) = pb {
                    pb.inc(TICK_SIZE);
                }
            }
        }
        Err(Error::NotFound)
    }
}