1
2extern crate hamming;
7
8#[macro_use]
9extern crate log;
10
11use std::io::{Read, BufReader};
12use std::fs::File;
13use hamming::distance;
14use std::collections::HashMap;
15use std::ascii::AsciiExt;
16
17pub trait Xor {
18 fn xor(&mut self, key_bytes : &Vec<u8>) -> Vec<u8>;
20}
21
22fn xor(reader: &mut Read, key_bytes : &Vec<u8>) -> Vec<u8> {
23 let mut key_idx = 0;
24 let mut warning_shown = false;
25 let mut encoded_bytes: Vec<u8> = Vec::new();
26
27 loop {
29 let mut data = [0; 1024];
30 let num_read = reader.read(&mut data[..]).unwrap();
31
32 if num_read == 0 {
33 break;
34 }
35
36 let data_bytes = &data[0 .. num_read];
37
38 for b in data_bytes {
39 let k = key_bytes[key_idx];
40 let e = b ^ k;
41
42 encoded_bytes.push(e);
43
44 key_idx += 1;
45
46 if key_idx >= key_bytes.len() {
47 key_idx = key_idx % key_bytes.len();
48
49 if !warning_shown {
50 warning_shown = true;
51 warn!("Key wasn't long enough and had to be re-used to fully encode data, use a longer key to be secure.");
52 }
53 }
54 }
55 }
56
57 encoded_bytes
58}
59
60impl<'a, R: Read> Xor for &'a mut R {
61 fn xor(&mut self, key_bytes : &Vec<u8>) -> Vec<u8> {
62 xor(self, key_bytes)
63 }
64}
65
66impl Xor for Read {
67 fn xor(&mut self, key_bytes : &Vec<u8>) -> Vec<u8> {
68 xor(self, key_bytes)
69 }
70}
71
72pub trait Score {
73 fn score(&self) -> f32;
78}
79
80pub trait ScoreAgainstDictionary {
81 fn score_with_words(&self, words_list : Vec<String>) -> f32;
87}
88
89impl Score for char {
90 fn score(&self) -> f32 {
91 score_character(*self)
92 }
93}
94
95impl Score for String {
96 fn score(&self) -> f32 {
97
98 let expected_char_frequency = get_char_score_map();
99
100 let ascii_only_vector : Vec<u8> = self.chars()
103 .filter(|c| c.is_ascii())
104 .map(|c| c.to_ascii_lowercase())
105 .filter(|c| expected_char_frequency.get(&c).is_some())
106 .map(|c| c as u8)
107 .collect();
108
109 let ascii_only = String::from_utf8(ascii_only_vector).unwrap();
111 debug!("Ascii only is: {}", ascii_only);
112
113 let mut actual_char_frequency = HashMap::new();
114
115 for c in ascii_only.chars() {
117 let count = actual_char_frequency.entry(c).or_insert(0.0);
118 *count += 1.0;
119 }
120 for count in actual_char_frequency.values_mut() {
121 *count = *count / ascii_only.len() as f32;
122 }
123
124 let mut sum = 0.0f32;
125
126 for (c, freq) in actual_char_frequency {
127 let expected = expected_char_frequency.get(&c).unwrap();
128 let diff = (*expected - freq).abs() * 10.0;
129
130 debug!("Diff for char '{}' is {}", c, diff);
131 sum += diff;
132 }
133
134 let proportion_of_ascii = ascii_only.len() as f32 / self.len() as f32;
135
136 sum = sum * proportion_of_ascii;
137
138 sum
139 }
140}
141
142impl ScoreAgainstDictionary for String {
143 fn score_with_words(&self, words_list : Vec<String>) -> f32 {
144 let mut sum = 0.0f32;
145
146 sum += self.score();
148
149 sum += score_words(self, words_list);
151
152 sum
153 }
154}
155
156pub fn load_words_list(path : &str) -> Vec<String> {
160
161 let mut dictionary_lines : Vec<String> = Vec::new();
163
164 match File::open(path) {
165 Ok(file) => {
166 let mut reader = BufReader::new(file);
168 let mut dictionary_data = String::new();
169 let _ = reader.read_to_string(&mut dictionary_data);
170
171 for line in dictionary_data.lines() {
172 let word = line.to_lowercase();
173 dictionary_lines.push(word);
174 }
175
176 dictionary_lines.sort_by(|a, b| {
178 let x = a.len();
179 let y = b.len();
180
181 y.cmp(&x)
182 });
183 },
184 Err(err) => {
185 println!("Failed to open dictionary file '{}' because: {:?}", path, err);
186 }
187 }
188
189 dictionary_lines
190}
191
192fn recursive_add_keys(length: u32, prefix : Vec<u8>, keys : &mut Vec<String>) {
193 if prefix.len() == (length as usize) {
194 let key = String::from_utf8(prefix).unwrap();
196 keys.push(key);
197 } else {
198 for idx in 0..128 {
199 let mut new_prefix = prefix.clone();
200 new_prefix.push(idx);
201
202 recursive_add_keys(length, new_prefix, keys);
203 }
204 }
205}
206
207
208pub fn gen_ascii_keys(length : u32) -> Vec<String> {
212 let mut keys : Vec<String> = Vec::new();
213 let prefix : Vec<u8> = Vec::new();
214
215 recursive_add_keys(length, prefix, &mut keys);
216
217 keys
218}
219
220pub fn avg_normalized_hamming_distance(input : &Vec<u8>, max_keysize : usize) -> HashMap<usize, f32> {
233
234 let mut keysize_to_avg_hamming_dist = HashMap::new();
235
236 for keysize in 1..(max_keysize+1) {
237
238 let mut chunks = input.chunks(keysize);
239 let mut num_chunks_compared = 0;
240 let mut average_hamming_dist = 0.0_f32;
241
242 for _ in 1..3 {
245
246 let left_chunk = chunks.next();
247 let right_chunk = chunks.next();
248
249 if left_chunk.is_none() {
250 break;
251 }
252 if right_chunk.is_none() {
253 break;
254 }
255
256 let left = left_chunk.unwrap();
257 let right = right_chunk.unwrap();
258
259 if left.len() != right.len() {
260 break;
261 }
262
263 let hamming_dist = distance(left, right);
264 let normalized_hamming = hamming_dist as f32 / keysize as f32;
265 average_hamming_dist += normalized_hamming;
266
267 debug!("{:4.3} is the normalized hamming distance for keysize {} and block {}", normalized_hamming, keysize, num_chunks_compared);
268
269 num_chunks_compared += 1;
270 }
271
272 if num_chunks_compared != 0 {
273 average_hamming_dist = average_hamming_dist / num_chunks_compared as f32;
274 keysize_to_avg_hamming_dist.insert(keysize, average_hamming_dist);
275 } else {
276 debug!("Not enough data in input file to check a keysize of '{}'", keysize);
277 }
278 }
279
280 keysize_to_avg_hamming_dist
281}
282
283fn score_words(words : &String, dictionary : Vec<String>) -> f32 {
284 let mut score : f32 = 0.0;
285
286 let mut cloned_input = words.clone();
293 for word in dictionary {
294 if cloned_input.contains(word.as_str()) {
295 let adjustment = 3.0 * (word.len() as f32).exp();
296 score = score + adjustment;
297
298 cloned_input = cloned_input.replacen(word.as_str(), "", 1);
299 }
300 }
301
302 score
303}
304
305
306fn score_character(c : char) -> f32 {
307 let character_scores = get_char_score_map();
308
309 if character_scores.contains_key(&c) {
310 let value = character_scores.get(&c).unwrap();
311 *value
312 } else {
313 0.00
314 }
315}
316
317fn get_char_score_map() -> HashMap<char, f32> {
321 let mut character_scores = HashMap::new();
322
323 character_scores.insert(' ', 15.000); character_scores.insert('e', 12.702);
325 character_scores.insert('t', 9.056);
326 character_scores.insert('a', 8.167);
327 character_scores.insert('o', 7.507);
328 character_scores.insert('i', 6.966);
329 character_scores.insert('n', 6.749);
330 character_scores.insert('s', 6.327);
331 character_scores.insert('h', 6.094);
332 character_scores.insert('r', 5.987);
333 character_scores.insert('d', 4.253);
334 character_scores.insert('l', 4.025);
335 character_scores.insert('c', 2.782);
336 character_scores.insert('u', 2.758);
337 character_scores.insert('m', 2.406);
338 character_scores.insert('w', 2.360);
339 character_scores.insert('f', 2.228);
340 character_scores.insert('g', 2.015);
341 character_scores.insert('y', 1.974);
342 character_scores.insert('p', 1.929);
343 character_scores.insert('b', 1.492);
344 character_scores.insert('v', 0.978);
345 character_scores.insert('k', 0.772);
346 character_scores.insert('j', 0.153);
347 character_scores.insert('x', 0.150);
348 character_scores.insert('q', 0.095);
349 character_scores.insert('z', 0.074);
350
351 character_scores
352}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357 use std::io::Cursor;
358
359 #[test]
360 fn xor_works() {
361
362 let data : Vec<u8> = vec![0b11111111u8, 0b11111111u8, 0b00001111u8, 0b10101010u8, 0b11111111u8, 0b11111111u8, 0b00001111u8, 0b10101010u8];
364 let key : Vec<u8> = vec![0b11111111u8, 0b00000000u8, 0b11110000u8, 0b01010101u8];
365
366 let reader : &mut Read = &mut Cursor::new(data);
367
368 let cipher = reader.xor(key);
369
370 assert_eq!(0b00000000u8, cipher[0]);
371 assert_eq!(0b11111111u8, cipher[1]);
372 assert_eq!(0b11111111u8, cipher[2]);
373 assert_eq!(0b11111111u8, cipher[3]);
374 assert_eq!(0b00000000u8, cipher[4]);
375 assert_eq!(0b11111111u8, cipher[5]);
376 assert_eq!(0b11111111u8, cipher[6]);
377 assert_eq!(0b11111111u8, cipher[7]);
378 }
379
380 #[test]
381 fn scoring_strings_works() {
382 let a = String::from("hello world");
383 let b = String::from("9[;,1.23,45");
384 let c = String::from("$*(&^$@!as3");
385 let d = String::from("kj12asd89hh");
386
387 let score_a = a.score();
388 let score_b = b.score();
389 let score_c = c.score();
390 let score_d = d.score();
391
392 assert!(score_a > score_b);
393 assert!(score_a > score_c);
394 assert!(score_a > score_d);
395 }
396
397}