1#![doc = include_str!("../README.md")]
2#![deny(rust_2018_idioms)]
3#![warn(missing_docs)]
4
5use std::collections::HashMap;
6
7use n_order::n_order_with_factors;
8use rug::{integer::IsPrime, Integer};
9mod index_calculus;
10mod n_order;
11mod pohlig_hellman;
12mod pollard_rho;
13mod shanks_steps;
14mod trial_mul;
15mod utils;
16
17pub use index_calculus::discrete_log_index_calculus;
18pub use n_order::n_order;
19pub use pohlig_hellman::discrete_log_pohlig_hellman;
20pub use pollard_rho::discrete_log_pollard_rho;
21pub use shanks_steps::discrete_log_shanks_steps;
22pub use trial_mul::discrete_log_trial_mul;
23
24#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
26pub enum Error {
27 #[error("Log does not exist")]
29 LogDoesNotExist,
30 #[error("A and n are not relatively prime")]
32 NotRelativelyPrime,
33}
34
35pub fn discrete_log(n: &Integer, a: &Integer, b: &Integer) -> Result<Integer, Error> {
37 discrete_log_with_order(n, a, b, &n_order(b, n)?)
38}
39
40pub fn discrete_log_with_factors(
44 n: &Integer,
45 a: &Integer,
46 b: &Integer,
47 n_factors: &HashMap<Integer, usize>,
48) -> Result<Integer, Error> {
49 discrete_log_with_order(n, a, b, &n_order_with_factors(b, n, n_factors)?)
50}
51
52pub fn discrete_log_with_order(
56 n: &Integer,
57 a: &Integer,
58 b: &Integer,
59 order: &Integer,
60) -> Result<Integer, Error> {
61 if *n < 1 {
63 return Err(Error::LogDoesNotExist);
64 }
65 if *n == 1 {
67 return Ok(Integer::from(0));
68 }
69
70 if *order < 1000 {
71 discrete_log_trial_mul(n, a, b, Some(order))
72 } else if order.is_probably_prime(100) != IsPrime::No {
73 let n_f64 = n.to_f64();
76 let order_f64 = order.to_f64();
77 let log_n = n_f64.ln();
78 let log_log_n = log_n.ln();
79 let log_order = order_f64.ln();
80
81 if 4.0 * (log_n * log_log_n).sqrt() < log_order - 10.0 {
83 discrete_log_index_calculus(n, a, b, Some(order))
84 } else if *order < shanks_steps::MAX_ORDER {
85 discrete_log_shanks_steps(n, a, b, Some(order))
86 } else {
87 discrete_log_pollard_rho(n, a, b, Some(order))
88 }
89 } else {
90 discrete_log_pohlig_hellman(n, a, b, Some(order))
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use std::str::FromStr;
97
98 use rug::ops::Pow;
99
100 use super::*;
101
102 #[test]
103 fn discrete_log_() {
104 assert_eq!(
105 discrete_log(&587.into(), &(Integer::from(2).pow(9)), &2.into(),).unwrap(),
106 9
107 );
108 assert_eq!(
109 discrete_log(&2456747.into(), &(Integer::from(3).pow(51)), &3.into(),).unwrap(),
110 51
111 );
112 assert_eq!(
113 discrete_log(&32942478.into(), &(Integer::from(11).pow(127)), &11.into(),).unwrap(),
114 127
115 );
116 assert_eq!(
117 discrete_log(
118 &Integer::from_str("432751500361").unwrap(),
119 &(Integer::from(7).pow(324)),
120 &7.into(),
121 )
122 .unwrap(),
123 324
124 );
125 assert_eq!(
126 discrete_log(
127 &Integer::from_str("265390227570863").unwrap(),
128 &Integer::from_str("184500076053622").unwrap(),
129 &2.into(),
130 )
131 .unwrap(),
132 Integer::from_str("17835221372061").unwrap(),
133 );
134 assert_eq!(
135 discrete_log(
136 &Integer::from_str("22708823198678103974314518195029102158525052496759285596453269189798311427475159776411276642277139650833937").unwrap(),
137 &Integer::from_str("17463946429475485293747680247507700244427944625055089103624311227422110546803452417458985046168310373075327").unwrap(),
138 &123456.into(),
139 )
140 .unwrap(),
141 Integer::from_str("2068031853682195777930683306640554533145512201725884603914601918777510185469769997054750835368413389728895").unwrap(),
142 );
143 assert_eq!(
144 discrete_log(&5779.into(), &3528.into(), &6215.into(),).unwrap(),
145 687
146 );
147 }
148
149 #[test]
150 fn big_discrete_log() {
151 let n = Integer::from_str("83408372012221120677052349409462320990177094246143674474872152829440524098582262384066400107950985845255268335597502228206679771838750219696329523257176739436871327238322817403970284015587320158034304282786944710043150568360761457471641695390427267786485448748458445872307883254297662715749746270343116946519").unwrap();
152 let a = Integer::from_str("109770827223661560471527567179288748906402603483328748683689436879660543465776899146036833470531024202351087008847594392666852763100570391337823820240726499421306887565697452868723849092658743267256316770223643723095601213088336064635680075206929620159782416078143076506249031972043819429093074684182845530529249907297736582589125917235222921623698038868900282049587768700860009877737045693722732170123306528145661683416808514556360429554775212088169626620488741903267154641722293484797745665402402381445609873333905772582972140944493849645600529147490903067975300304532955461710562911203871840101407995813072692212").unwrap();
153 let b = Integer::from_str("65537").unwrap();
154
155 assert_eq!(
156 discrete_log(&n, &a, &b).unwrap(),
157 Integer::from_str("495604594360692646132957963901411709").unwrap(),
158 );
159 }
160
161 #[test]
162 fn discrete_log_n_equals_one() {
163 assert_eq!(discrete_log(&1.into(), &0.into(), &0.into()).unwrap(), 0);
165 }
166
167 #[test]
168 fn discrete_log_n_less_than_one() {
169 assert_eq!(
172 discrete_log(&0.into(), &1.into(), &2.into()),
173 Err(Error::NotRelativelyPrime)
174 );
175 assert_eq!(
177 discrete_log_with_order(&0.into(), &1.into(), &2.into(), &10.into()),
178 Err(Error::LogDoesNotExist)
179 );
180 assert_eq!(
181 discrete_log_with_order(&(-1).into(), &1.into(), &2.into(), &10.into()),
182 Err(Error::LogDoesNotExist)
183 );
184 }
185}