1#![forbid(unsafe_code)]
6#![deny(
7missing_debug_implementations,
8missing_docs,
9trivial_casts,
10trivial_numeric_casts,
11unused_extern_crates,
12unused_import_braces,
13unused_qualifications,
14warnings
15)]
16
17use itertools::izip;
18use num::Integer;
19use num_bigint::{BigInt, ToBigInt};
20
21fn modulo(a: &BigInt, m: &BigInt) -> BigInt {
23 ((a % m) + m) % m
24}
25
26fn modinv(a: &BigInt, m: &BigInt) -> Option<BigInt> {
27 let egcd = std::cmp::max(a, m).extended_gcd(&std::cmp::min(a.clone(), m.clone()));
28 if egcd.gcd != num::one() {
29 None
30 } else {
31 Some(modulo(&egcd.y, m))
32 }
33}
34
35#[derive(Debug, Eq, PartialEq)]
37pub struct LCG {
38 pub state: BigInt,
40 pub a: BigInt,
42 pub c: BigInt,
44 pub m: BigInt,
46}
47
48pub fn crack_lcg(values: &[isize]) -> Option<LCG> {
54 if values.len() < 3 {
59 return None;
60 }
61 let diffs = izip!(values, values.iter().skip(1))
62 .map(|(a, b)| b - a)
63 .collect::<Vec<isize>>();
64 let zeroes = izip!(&diffs, (&diffs).iter().skip(1), (&diffs).iter().skip(2))
65 .map(|(a, b, c)| c * a - b * b)
66 .collect::<Vec<_>>();
67 let modulus = zeroes
68 .iter()
69 .fold(0isize, |sum, val| sum.gcd(val))
70 .to_bigint()?;
71
72 let multiplier = modulo(
73 &((values[2] - values[1]).to_bigint()?
74 * modinv(
75 &(&values[1].to_bigint()? - &values[0].to_bigint()?),
76 &modulus,
77 )?),
78 &modulus,
79 );
80
81 let increment = modulo(&(values[1] - values[0] * &multiplier), &modulus);
82 Some(LCG {
83 state: values.last()?.to_bigint()?,
84 m: modulus,
85 a: multiplier,
86 c: increment,
87 })
88}
89
90impl Iterator for LCG {
91 type Item = BigInt;
92
93 fn next(&mut self) -> Option<BigInt> {
94 Some(self.rand())
95 }
96}
97
98impl LCG {
99 pub fn rand(&mut self) -> BigInt {
103 self.state = modulo(&(&self.state * (&self.a) + (&self.c)), &self.m);
104 self.state.clone()
105 }
106
107 pub fn prev(&mut self) -> Option<BigInt> {
113 self.state = modulo(
114 &(modinv(&self.a, &self.m)? * (&self.state - (&self.c))),
115 &self.m,
116 );
117 Some(self.state.clone())
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use crate::{crack_lcg, LCG};
124 use num::ToPrimitive;
125 use num_bigint::ToBigInt;
126
127 #[test]
128 fn it_generates_numbers_correctly_forward_and_backwards() {
129 let mut rand = LCG {
130 state: 32760.to_bigint().unwrap(),
131 a: 5039.to_bigint().unwrap(),
132 c: 76581.to_bigint().unwrap(),
133 m: 479001599.to_bigint().unwrap(),
134 };
135
136 let mut forward = (&mut rand).take(10).collect::<Vec<_>>();
137
138 assert_eq!(
139 forward,
140 vec![
141 165154221.to_bigint().unwrap(),
142 186418737.to_bigint().unwrap(),
143 41956685.to_bigint().unwrap(),
144 180107137.to_bigint().unwrap(),
145 330911418.to_bigint().unwrap(),
146 58145764.to_bigint().unwrap(),
147 326604388.to_bigint().unwrap(),
148 389095148.to_bigint().unwrap(),
149 96982646.to_bigint().unwrap(),
150 113998795.to_bigint().unwrap()
151 ]
152 );
153 forward.reverse();
154 rand.rand();
155 assert_eq!(
156 (0..10).filter_map(|_| rand.prev()).collect::<Vec<_>>(),
157 forward
158 );
159 }
160
161 #[test]
162 fn it_cracks_lcg_correctly() {
163 let mut rand = LCG {
164 state: 32760.to_bigint().unwrap(),
165 a: 5039.to_bigint().unwrap(),
166 c: 0.to_bigint().unwrap(),
167 m: 479001599.to_bigint().unwrap(),
168 };
169
170 let cracked_lcg = crack_lcg(
171 &(&mut rand)
172 .take(10)
173 .map(|x| x.to_isize().unwrap())
174 .collect::<Vec<_>>(),
175 )
176 .unwrap();
177 assert_eq!(rand, cracked_lcg);
178 }
179}