discrete_logarithm/
index_calculus.rs1use primal::Primes;
2use rug::{rand::RandState, Integer};
3
4use crate::Error;
5
6fn is_smooth(mut n: Integer, factorbase: &[usize]) -> Option<Vec<u32>> {
9 let mut factors = vec![0u32; factorbase.len()];
10
11 for (i, &p) in factorbase.iter().enumerate() {
12 let prime = Integer::from(p);
13 while n.is_divisible(&prime) {
14 factors[i] += 1;
15 n /= ′
16 }
17 }
18
19 if n != 1 {
20 None } else {
22 Some(factors)
23 }
24}
25
26pub fn discrete_log_index_calculus(
51 n: &Integer,
52 a: &Integer,
53 b: &Integer,
54 order: Option<&Integer>,
55) -> Result<Integer, Error> {
56 let a = a.clone() % n;
57 let b = b.clone() % n;
58
59 let order = match order {
60 Some(order) => order.clone(),
61 None => return Err(Error::LogDoesNotExist),
62 };
63
64 let n_f64 = n.to_f64();
67 let log_n = n_f64.ln();
68 let log_log_n = log_n.ln();
69 let b_bound = (0.5 * (log_n * log_log_n).sqrt() * (1.0 + 1.0 / log_log_n)).exp();
70 let b_bound = b_bound as usize;
71
72 let factorbase: Vec<usize> = Primes::all().take_while(|&p| p < b_bound).collect();
74 let lf = factorbase.len();
75
76 if lf == 0 {
77 return Err(Error::LogDoesNotExist);
78 }
79
80 let max_tries = (5 * b_bound * b_bound) as u64;
82
83 let mut relationa: Option<(Vec<Integer>, Integer)> = None;
85 let mut abx = a.clone();
86
87 for x in 0..order.to_u64().unwrap_or(u64::MAX) {
88 if abx == 1 {
89 return Ok((order.clone() - x) % &order);
90 }
91
92 if let Some(factors) = is_smooth(abx.clone(), &factorbase) {
93 let factors_int: Vec<Integer> =
95 factors.iter().map(|&f| Integer::from(f) % &order).collect();
96 relationa = Some((factors_int, Integer::from(x)));
97 break;
98 }
99
100 abx = abx * &b % n;
101 }
102
103 let (mut relationa, relationa_x) = match relationa {
104 Some(r) => r,
105 None => return Err(Error::LogDoesNotExist),
106 };
107 relationa.push(relationa_x);
108
109 let mut relations: Vec<Option<Vec<Integer>>> = vec![None; lf];
111 let mut k = 1; let mut kk = 0; let mut rand_state = RandState::new();
115 let order_minus_1: Integer = order.clone() - 1;
116
117 while k < 3 * lf && kk < max_tries {
118 let x = order_minus_1.clone().random_below(&mut rand_state) + 1;
120
121 let bx = b.clone().pow_mod(&x, n).unwrap();
123
124 let relation = match is_smooth(bx, &factorbase) {
126 Some(factors) => {
127 let mut rel: Vec<Integer> =
128 factors.iter().map(|&f| Integer::from(f) % &order).collect();
129 rel.push(x);
130 rel
131 }
132 None => {
133 kk += 1;
134 continue;
135 }
136 };
137
138 k += 1;
139 kk = 0;
140
141 let mut relation = relation;
143 let mut index = lf; for i in 0..lf {
146 let ri = relation[i].clone() % ℴ
147
148 if ri > 0 && relations[i].is_some() {
149 let existing = relations[i].as_ref().unwrap();
151 for j in 0..=lf {
152 let diff = relation[j].clone() - &ri * &existing[j];
153 relation[j] = (diff % &order + &order) % ℴ
154 }
155 } else {
156 relation[i] = ri.clone();
157 }
158
159 if relation[i] > 0 && index == lf {
160 index = i;
161 }
162 }
163
164 if index == lf || relations[index].is_some() {
165 continue;
167 }
168
169 let rinv = relation[index].clone().invert(&order).unwrap();
171 for item in relation.iter_mut().skip(index) {
172 *item = (rinv.clone() * &*item) % ℴ
173 }
174
175 relations[index] = Some(relation.clone());
176
177 for i in 0..lf {
179 if relationa[i] > 0 && relations[i].is_some() {
180 let rbi = relationa[i].clone();
181 let existing = relations[i].as_ref().unwrap();
182 for j in 0..=lf {
183 let diff = relationa[j].clone() - &rbi * &existing[j];
184 relationa[j] = (diff % &order + &order) % ℴ
185 }
186 }
187 if relationa[i] > 0 {
188 break; }
190 }
191
192 let all_zero = (0..lf).all(|i| relationa[i] == 0);
194 if all_zero {
195 let x = (order.clone() - &relationa[lf]) % ℴ
196
197 if b.clone().pow_mod(&x, n).unwrap() == a {
199 return Ok(x);
200 }
201
202 return Err(Error::LogDoesNotExist);
203 }
204 }
205
206 Err(Error::LogDoesNotExist)
207}
208
209#[cfg(test)]
210mod tests {
211 use std::str::FromStr;
212
213 use rug::ops::Pow;
214
215 use super::*;
216
217 #[test]
218 fn index_calculus() {
219 assert_eq!(
221 discrete_log_index_calculus(
222 &Integer::from_str("24570203447").unwrap(),
223 &Integer::from_str("23859756228").unwrap(),
224 &2.into(),
225 Some(&Integer::from_str("12285101723").unwrap())
226 )
227 .unwrap(),
228 Integer::from_str("4519867240").unwrap()
229 );
230 }
231
232 #[test]
233 fn index_calculus_small() {
234 assert_eq!(
236 discrete_log_index_calculus(
237 &587.into(),
238 &(Integer::from(2).pow(9)),
239 &2.into(),
240 Some(&293.into())
241 )
242 .unwrap(),
243 9
244 );
245 }
246}