From 6ed59b895660c1811a75f812686e254d8b52af9e Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 10 Mar 2020 21:09:14 +0100 Subject: [PATCH 1/5] Change to process dictionary file in chunks to reduce memory impact --- src/main.rs | 72 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/src/main.rs b/src/main.rs index a428904..9be350e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use rpassword::read_password_from_tty; use spinners::{Spinner, Spinners}; use std::fs; use std::fs::File; -use std::io::{BufWriter, Write}; +use std::io::{BufRead, BufReader, BufWriter, Write}; use std::sync::mpsc::sync_channel; use std::thread; use std::time::Duration; @@ -128,27 +128,10 @@ fn decrypt(_opts: &Opts, args: &Decrypt) { let data_checksum = base64::decode(bin_content.as_slice()).unwrap(); if let Some(dict) = dictionary { - let sp = spinner("Reading dictionary..."); - let dictionary = fs::read_to_string(dict).expect("Failed to read dictionary file!"); - let lines = dictionary.par_lines(); - - let pw_table: Vec = lines - .map(|line| { - let parts: Vec<&str> = line.split("\t").collect::>(); - let pw = parts[0].parse().unwrap(); - let key_str: String = parts[1].parse().unwrap(); - let key = base64::decode(&key_str).unwrap(); - - (pw, key) - }) - .collect(); - sp.message("Dictionary decrypting file multithreaded".into()); - if let Some(dec_data) = decrypt_with_dictionary(&data, pw_table, &data_checksum) { - sp.stop(); + if let Some(dec_data) = decrypt_with_dictionary_file(dict, &data, &data_checksum) { fs::write(output, &dec_data).expect("Failed to write output file!"); println!("\nFinished!"); } else { - sp.stop(); println!("\nNo password found!"); } } else { @@ -216,3 +199,54 @@ fn create_dictionary(_opts: &Opts, args: &CreateDictionary) { fn spinner(text: &str) -> Spinner { Spinner::new(Spinners::Dots2, text.into()) } + +const LINES_PER_CHUNK: usize = 100000; + +fn decrypt_with_dictionary_file( + filename: String, + data: &Vec, + data_checksum: &Vec, +) -> Option> { + let sp = spinner("Reading dictionary..."); + let f = File::open(filename).expect("Failed to open dictionary file."); + let reader = BufReader::new(f); + let (rx, tx) = sync_channel::>(10); + let handle = thread::spawn(move || { + let mut line_vec: Vec = vec![]; + reader.lines().for_each(|line_result| { + if line_vec.len() > LINES_PER_CHUNK { + if let Err(_) = rx.send(line_vec.clone()) {} + line_vec.clear(); + } + match line_result { + Ok(line) => line_vec.push(line), + Err(err) => eprintln!("Failed with err {}", err), + } + }); + if let Err(_) = rx.send(line_vec.clone()) {} + line_vec.clear(); + }); + sp.message("Dictionary decrypting file multithreaded".into()); + let mut result_data: Option> = None; + for lines in tx { + let pw_table: Vec = lines + .par_iter() + .map(|line| { + let parts: Vec<&str> = line.split("\t").collect::>(); + let pw = parts[0].parse().unwrap(); + let key_str: String = parts[1].parse().unwrap(); + let key = base64::decode(&key_str).unwrap(); + + (pw, key) + }) + .collect(); + if let Some(dec_data) = decrypt_with_dictionary(&data, pw_table, &data_checksum) { + result_data = Some(dec_data); + break; + } + } + handle.join().expect("Failed to wait for thread."); + sp.stop(); + + result_data +} From 96da5e629d35b99789318abb4c46420dda36fa36 Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 10 Mar 2020 21:24:01 +0100 Subject: [PATCH 2/5] Remove timing report from decryption since it only reports the time it took for one chunk --- src/lib/crypt.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lib/crypt.rs b/src/lib/crypt.rs index 129da04..39605ec 100644 --- a/src/lib/crypt.rs +++ b/src/lib/crypt.rs @@ -5,7 +5,6 @@ use des::Des; use rand::Rng; use rayon::prelude::*; use std::sync::Mutex; -use std::time::Instant; type DesCfb = Cfb; @@ -38,7 +37,6 @@ pub fn decrypt_with_dictionary( checksum: &[u8], ) -> Option> { let decrypted = Mutex::>>::new(None); - let start = Instant::now(); let pass = dict.par_iter().find_first(|(_pw, key)| { let decrypted_data = decrypt_data(&data, key); let decr_check = sha_checksum(&decrypted_data); @@ -51,11 +49,7 @@ pub fn decrypt_with_dictionary( }; }); if let Some((pw, _key)) = pass { - println!( - "\nPassword found in {:.2}s: {}", - start.elapsed().as_secs_f32(), - pw - ); + println!("\nPassword found: {}", pw); let decry = decrypted.lock().unwrap(); if let Some(decrypted_data) = (*decry).clone() { return Some(decrypted_data); From 1fb5300a9aa188a1fd3aff021ac4ff7fb3034427 Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 10 Mar 2020 21:41:17 +0100 Subject: [PATCH 3/5] Add progress bar to decryption progress --- src/main.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9be350e..0f71a09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -208,8 +208,10 @@ fn decrypt_with_dictionary_file( data_checksum: &Vec, ) -> Option> { let sp = spinner("Reading dictionary..."); - let f = File::open(filename).expect("Failed to open dictionary file."); + let f = File::open(&filename).expect("Failed to open dictionary file."); let reader = BufReader::new(f); + let mut pb = + ProgressBar::new((get_line_count(&filename) as f64 / LINES_PER_CHUNK as f64).ceil() as u64); let (rx, tx) = sync_channel::>(10); let handle = thread::spawn(move || { let mut line_vec: Vec = vec![]; @@ -226,7 +228,7 @@ fn decrypt_with_dictionary_file( if let Err(_) = rx.send(line_vec.clone()) {} line_vec.clear(); }); - sp.message("Dictionary decrypting file multithreaded".into()); + sp.stop(); let mut result_data: Option> = None; for lines in tx { let pw_table: Vec = lines @@ -240,13 +242,19 @@ fn decrypt_with_dictionary_file( (pw, key) }) .collect(); + pb.inc(); if let Some(dec_data) = decrypt_with_dictionary(&data, pw_table, &data_checksum) { result_data = Some(dec_data); break; } } handle.join().expect("Failed to wait for thread."); - sp.stop(); - + pb.finish(); result_data } + +fn get_line_count(fname: &str) -> usize { + let f = File::open(fname).expect("Failed to open file to get the line count."); + let reader = BufReader::new(f); + return reader.lines().count(); +} From 8f09c68fa0b289f0a9918d2e659ad39d365da314 Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 10 Mar 2020 23:08:13 +0100 Subject: [PATCH 4/5] Remove waiting for thread since the thread will be killed when the program exists anyway --- src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0f71a09..06e5f21 100644 --- a/src/main.rs +++ b/src/main.rs @@ -213,7 +213,7 @@ fn decrypt_with_dictionary_file( let mut pb = ProgressBar::new((get_line_count(&filename) as f64 / LINES_PER_CHUNK as f64).ceil() as u64); let (rx, tx) = sync_channel::>(10); - let handle = thread::spawn(move || { + let _handle = thread::spawn(move || { let mut line_vec: Vec = vec![]; reader.lines().for_each(|line_result| { if line_vec.len() > LINES_PER_CHUNK { @@ -248,7 +248,6 @@ fn decrypt_with_dictionary_file( break; } } - handle.join().expect("Failed to wait for thread."); pb.finish(); result_data } From eb87c1127160ea4b7542adf8c8fe743ac0c4b558 Mon Sep 17 00:00:00 2001 From: trivernis Date: Wed, 11 Mar 2020 10:50:46 +0100 Subject: [PATCH 5/5] Increase lines per chunk constant --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 06e5f21..f5024dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -200,7 +200,7 @@ fn spinner(text: &str) -> Spinner { Spinner::new(Spinners::Dots2, text.into()) } -const LINES_PER_CHUNK: usize = 100000; +const LINES_PER_CHUNK: usize = 10000000; fn decrypt_with_dictionary_file( filename: String,