1extern crate rand;
2extern crate regex;
3
4use regex::Regex;
6use rand::Rng;
7
8#[derive(Debug, PartialEq)]
9pub enum Status {
10 Good,
11 Invalid,
12 Blacklisted,
13 Phony
14}
15
16fn get_key_byte(seed: &i64, a: i16, b: i16, c: i16) -> String {
17 let a_shift = a % 25;
18 let b_shift = b % 3;
19 let mut result;
20
21 if a_shift % 2 == 0 {
22 result = ((seed >> a_shift) & 0x000000FF) ^ ((seed >> b_shift) | c as i64);
23 } else {
24 result = ((seed >> a_shift) & 0x000000FF) ^ ((seed >> b_shift) & c as i64);
25 }
26
27 result = result & 0xFF;
28
29 format!("{:01$X}", result, 2)
32}
33
34fn get_checksum(s: &str) -> String {
35 let mut left = 0x0056; let mut right: u16 = 0x00AF; for ch in s.chars() {
39 right += ch as u16;
40 if right > 0x00FF {
41 right -= 0x00FF;
42 }
43
44 left += right;
45 if left > 0x00FF {
46 left -= 0x00FF;
47 }
48 }
49
50 let sum = (left << 8) + right;
51
52 format!("{:01$X}", sum, 4)
54}
55
56pub fn make_key(seed: &i64, num_bytes: &i8, byte_shifts: &Vec<(i16, i16, i16)>) -> String {
57 let mut key_bytes: Vec<String> = vec![];
58
59 for i in 0..*num_bytes {
60 let index = i as usize;
61 let shift = byte_shifts[index];
62 key_bytes.push(get_key_byte(&seed, shift.0, shift.1, shift.2));
63 }
64
65 let mut result = format!("{:01$X}", seed, 8);
66 for byte in key_bytes {
67 result = format!("{}{}", result, byte);
68 }
69
70 result = format!("{}{}", result, get_checksum(&result[..]));
71
72 let subs: Vec<&str> = result.split("").filter(|s| s.len() > 0).collect();
74 let mut key: Vec<String> = vec![];
75 for chunk in subs.chunks(4) {
76 key.push(chunk.join(""));
77 }
78
79 key.join("-")
80}
81
82pub fn check_key_checksum(key: &str, num_bytes: &i8) -> bool {
83 let s = key.replace("-", "").to_uppercase();
84 let length = s.len();
85
86 if length != (12 + (2 * num_bytes)) as usize {
88 return false;
89 }
90
91 let checksum = &s[length - 4..length];
92 let slice = &s[..length - 4];
93
94 checksum == get_checksum(&slice)
95}
96
97pub fn check_key(s: &str, blacklist: &Vec<String>, num_bytes: &i8, byte_shifts: &Vec<(i16, i16, i16)>, positions: Option<Vec<i16>>) -> Status {
98 if !check_key_checksum(s, num_bytes) {
99 return Status::Invalid;
100 }
101
102 if *num_bytes < 3 {
103 return Status::Invalid;
104 }
105
106 let key = s.replace("-", "").to_uppercase();
107 let seed: String = key.chars().take(8).collect();
108
109 for item in blacklist {
110 if seed == item.to_uppercase() {
111 return Status::Blacklisted;
112 }
113 }
114
115 let re = Regex::new(r"[A-F0-9]{8}").unwrap();
116 if !re.is_match(&seed) {
117 return Status::Phony;
118 }
119
120 let seed_num = match i64::from_str_radix(&seed[..], 16) {
121 Err(_) => return Status::Invalid,
122 Ok(s) => s
123 };
124
125 match positions {
126 Some(pos) => check_bytes(key, seed_num, byte_shifts, pos),
127 None => check_random_bytes(key, seed_num, num_bytes, byte_shifts)
128 }
129}
130
131fn check_bytes(key: String, seed_num: i64, byte_shifts: &Vec<(i16, i16, i16)>, positions: Vec<i16>) -> Status {
138 if positions.len() < 2 {
139 println!("Must check at least 2 bytes");
140 return Status::Invalid;
141 }
142
143 for (i, pos) in positions.into_iter().enumerate() {
144 let start = ((pos * 2) + 8) as usize;
145 let end = start + 2;
146
147 if end > key.len() {
148 return Status::Invalid;
149 }
150
151 let key_byte = &key[start..end];
152 let shifts = &byte_shifts[i as usize];
153
154 let byte = get_key_byte(&seed_num, shifts.0, shifts.1, shifts.2);
155 if key_byte != byte {
156 return Status::Phony;
157 }
158 }
159
160 Status::Good
161}
162
163fn check_random_bytes(key: String, seed_num: i64, num_bytes: &i8, byte_shifts: &Vec<(i16, i16, i16)>) -> Status {
167 let mut bytes_to_check = 3;
168 if *num_bytes > 5 {
169 bytes_to_check = num_bytes / 2;
170 }
171
172 let mut checked: Vec<i8> = Vec::new();
174
175 for _ in 0..bytes_to_check {
176 let mut byte_to_check = rand::thread_rng().gen_range(0, num_bytes - 1);
177 while checked.contains(&byte_to_check) {
178 byte_to_check = rand::thread_rng().gen_range(0, num_bytes - 1);
179 }
180 checked.push(byte_to_check);
181
182 let start = ((byte_to_check * 2) + 8) as usize;
183 let end = start + 2;
184
185 if end > key.len() {
186 return Status::Invalid;
187 }
188
189
190 let key_byte = &key[start..end];
191 println!("num {:?}, shifts {:?}", &byte_to_check, &byte_shifts);
192
193 let shifts = &byte_shifts[byte_to_check as usize];
194
195 let byte = get_key_byte(&seed_num, shifts.0, shifts.1, shifts.2);
196 if key_byte != byte {
197 return Status::Phony;
198 }
199 }
200
201 Status::Good
202}
203
204#[cfg(test)]
205mod test {
206 #[test]
207 fn test_bytes() {
208 let seed = 0xA2791717;
209 assert_eq!(super::get_key_byte(&seed, 24, 3, 200), "7D");
210 assert_eq!(super::get_key_byte(&seed, 10, 0, 56), "7A");
211 assert_eq!(super::get_key_byte(&seed, 1, 2, 91), "CA");
212 assert_eq!(super::get_key_byte(&seed, 7, 1, 100), "2E");
213 }
214
215 #[test]
216 fn test_get_checksum() {
217 let key = "A279-1717-7D7A-CA2E-7154";
218 assert_eq!(super::get_checksum(key), "49DA");
219
220 let second_key = "3ABC-9099-E39D-4E65-E060";
221 assert_eq!(super::get_checksum(second_key), "82F0");
222 }
223
224 #[test]
225 fn test_make_key() {
226 let seed = 0x3abc9099;
227 let num_bytes = 4;
228 let byte_shifts = vec![(24, 3, 200), (10, 0, 56), (1, 2, 91), (7, 1, 100)];
229 assert_eq!("3ABC-9099-E39D-4E65-E060", super::make_key(&seed, &num_bytes, &byte_shifts));
230 }
231
232 #[test]
233 fn test_check_key() {
234 let key = "3ABC-9099-E39D-4E65-E060";
235 let blacklist = vec![];
236 let num_bytes = 4;
237 let byte_shifts = vec![(24, 3, 200), (10, 0, 56), (1, 2, 91), (7, 1, 100)];
238 assert_eq!(super::check_key(&key, &blacklist, &num_bytes, &byte_shifts, Option::None), super::Status::Good);
239
240 let byte_shifts = vec![(24, 3, 200), (1, 2, 91)];
241 let positions = Option::Some(vec![0, 2]);
242 assert_eq!(super::check_key(&key, &blacklist, &num_bytes, &byte_shifts, positions), super::Status::Good);
243
244 let byte_shifts = vec![(1, 2, 91), (10, 0, 56)];
245 let positions = Option::Some(vec![2, 1]);
246 assert_eq!(super::check_key(&key, &blacklist, &num_bytes, &byte_shifts, positions), super::Status::Good);
247
248 let inconsistent_key = "3abC-9099-e39D-4E65-E060";
249 let byte_shifts = vec![(24, 3, 200), (10, 0, 56), (1, 2, 91), (7, 1, 100)];
250 assert_eq!(super::check_key(&inconsistent_key, &blacklist, &num_bytes, &byte_shifts, Option::None), super::Status::Good);
251
252 let wrong_checksum = "3ABC-9099-E39D-4E65-E061";
253 assert_eq!(super::check_key(&wrong_checksum, &blacklist, &num_bytes, &byte_shifts, Option::None), super::Status::Invalid);
254
255 let second_fake_key = "3ABC-9099-E49D-4E65-E761";
256 assert_eq!(super::check_key(&second_fake_key, &blacklist, &num_bytes, &byte_shifts, Option::None), super::Status::Phony);
257
258 let third_fake_key = "3ABC-9199-E39D-4E65-EB61";
259 assert_eq!(super::check_key(&third_fake_key, &blacklist, &num_bytes, &byte_shifts, Option::None), super::Status::Phony);
260
261 let invalid_key = "BC-9099-E39D-4E65-E061";
262 assert_eq!(super::check_key(&invalid_key, &blacklist, &num_bytes, &byte_shifts, Option::None), super::Status::Invalid);
263
264 let second_invalid_key = "3AXC-9099-E39D-4E65-E061";
265 assert_eq!(super::check_key(&second_invalid_key, &blacklist, &num_bytes, &byte_shifts, Option::None), super::Status::Invalid);
266 }
267
268 #[test]
269 fn test_blacklist() {
270 let key = "3ABC-9099-E39D-4E65-E060";
271 let blacklist = vec!["3abc9099".to_string()];
272 let num_bytes = 4;
273 let byte_shifts = vec![(24, 3, 200), (10, 0, 56), (1, 2, 91), (7, 1, 100)];
274
275 assert_eq!(super::check_key(&key, &blacklist, &num_bytes, &byte_shifts, Option::None), super::Status::Blacklisted);
276
277 let byte_shifts = vec![(24, 3, 200), (1, 2, 91)];
278 let positions = Option::Some(vec![0, 2]);
279 assert_eq!(super::check_key(&key, &blacklist, &num_bytes, &byte_shifts, positions), super::Status::Blacklisted);
280 }
281}