use primal::Primes;
use rug::{rand::RandState, Integer};
use crate::Error;
fn is_smooth(mut n: Integer, factorbase: &[usize]) -> Option<Vec<u32>> {
let mut factors = vec![0u32; factorbase.len()];
for (i, &p) in factorbase.iter().enumerate() {
let prime = Integer::from(p);
while n.is_divisible(&prime) {
factors[i] += 1;
n /= ′
}
}
if n != 1 {
None } else {
Some(factors)
}
}
pub fn discrete_log_index_calculus(
n: &Integer,
a: &Integer,
b: &Integer,
order: Option<&Integer>,
) -> Result<Integer, Error> {
let a = a.clone() % n;
let b = b.clone() % n;
let order = match order {
Some(order) => order.clone(),
None => return Err(Error::LogDoesNotExist),
};
let n_f64 = n.to_f64();
let log_n = n_f64.ln();
let log_log_n = log_n.ln();
let b_bound = (0.5 * (log_n * log_log_n).sqrt() * (1.0 + 1.0 / log_log_n)).exp();
let b_bound = b_bound as usize;
let factorbase: Vec<usize> = Primes::all().take_while(|&p| p < b_bound).collect();
let lf = factorbase.len();
if lf == 0 {
return Err(Error::LogDoesNotExist);
}
let max_tries = (5 * b_bound * b_bound) as u64;
let mut relationa: Option<(Vec<Integer>, Integer)> = None;
let mut abx = a.clone();
for x in 0..order.to_u64().unwrap_or(u64::MAX) {
if abx == 1 {
return Ok((order.clone() - x) % &order);
}
if let Some(factors) = is_smooth(abx.clone(), &factorbase) {
let factors_int: Vec<Integer> =
factors.iter().map(|&f| Integer::from(f) % &order).collect();
relationa = Some((factors_int, Integer::from(x)));
break;
}
abx = abx * &b % n;
}
let (mut relationa, relationa_x) = match relationa {
Some(r) => r,
None => return Err(Error::LogDoesNotExist),
};
relationa.push(relationa_x);
let mut relations: Vec<Option<Vec<Integer>>> = vec![None; lf];
let mut k = 1; let mut kk = 0;
let mut rand_state = RandState::new();
let order_minus_1: Integer = order.clone() - 1;
while k < 3 * lf && kk < max_tries {
let x = order_minus_1.clone().random_below(&mut rand_state) + 1;
let bx = b.clone().pow_mod(&x, n).unwrap();
let relation = match is_smooth(bx, &factorbase) {
Some(factors) => {
let mut rel: Vec<Integer> =
factors.iter().map(|&f| Integer::from(f) % &order).collect();
rel.push(x);
rel
}
None => {
kk += 1;
continue;
}
};
k += 1;
kk = 0;
let mut relation = relation;
let mut index = lf;
for i in 0..lf {
let ri = relation[i].clone() % ℴ
if ri > 0 && relations[i].is_some() {
let existing = relations[i].as_ref().unwrap();
for j in 0..=lf {
let diff = relation[j].clone() - &ri * &existing[j];
relation[j] = (diff % &order + &order) % ℴ
}
} else {
relation[i] = ri.clone();
}
if relation[i] > 0 && index == lf {
index = i;
}
}
if index == lf || relations[index].is_some() {
continue;
}
let rinv = relation[index].clone().invert(&order).unwrap();
for item in relation.iter_mut().skip(index) {
*item = (rinv.clone() * &*item) % ℴ
}
relations[index] = Some(relation.clone());
for i in 0..lf {
if relationa[i] > 0 && relations[i].is_some() {
let rbi = relationa[i].clone();
let existing = relations[i].as_ref().unwrap();
for j in 0..=lf {
let diff = relationa[j].clone() - &rbi * &existing[j];
relationa[j] = (diff % &order + &order) % ℴ
}
}
if relationa[i] > 0 {
break; }
}
let all_zero = (0..lf).all(|i| relationa[i] == 0);
if all_zero {
let x = (order.clone() - &relationa[lf]) % ℴ
if b.clone().pow_mod(&x, n).unwrap() == a {
return Ok(x);
}
return Err(Error::LogDoesNotExist);
}
}
Err(Error::LogDoesNotExist)
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use rug::ops::Pow;
use super::*;
#[test]
fn index_calculus() {
assert_eq!(
discrete_log_index_calculus(
&Integer::from_str("24570203447").unwrap(),
&Integer::from_str("23859756228").unwrap(),
&2.into(),
Some(&Integer::from_str("12285101723").unwrap())
)
.unwrap(),
Integer::from_str("4519867240").unwrap()
);
}
#[test]
fn index_calculus_small() {
assert_eq!(
discrete_log_index_calculus(
&587.into(),
&(Integer::from(2).pow(9)),
&2.into(),
Some(&293.into())
)
.unwrap(),
9
);
}
}