diff --git a/Cargo.lock b/Cargo.lock index d4b4af5..b42249a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,7 +53,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bdflib" -version = "0.1.0" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -222,7 +222,7 @@ name = "destools" version = "0.1.0" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bdflib 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bdflib 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "cfb-mode 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "des 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -815,7 +815,7 @@ dependencies = [ "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" -"checksum bdflib 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a42c5a66aa521885c21e5f53735f076d1ad05355fca1e096fa6694182885618c" +"checksum bdflib 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "27f26a69c90d4678e41a177a762b9916f36cc8ec5edb35efc845f1ee73607f73" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" diff --git a/src/lib/hash.rs b/src/lib/hash.rs index d32f6d6..45b3e94 100644 --- a/src/lib/hash.rs +++ b/src/lib/hash.rs @@ -1,4 +1,4 @@ -use sha2::{Digest, Sha256}; +use sha2::{Digest, Sha256, Sha512}; pub type PassKey = (String, Vec); @@ -12,13 +12,22 @@ pub fn create_key(pw: &str) -> Vec { key.to_vec().clone() } -/// Hashes a text to a 32 bytes long key. +/// Hashes a text to a sha256. pub fn sha256(pw: &str) -> Vec { let mut hasher = Sha256::default(); hasher.input(pw); let result = hasher.result(); - result.to_vec().clone() + result.to_vec() +} + +/// Hashes a text to sha512 +pub fn sha512(pw: &str) -> Vec { + let mut hasher = Sha512::default(); + hasher.input(pw); + let result = hasher.result(); + + result.to_vec() } /// Creates a sha256 hashsum from the input data diff --git a/src/lib/mod.rs b/src/lib/mod.rs index b08a062..cbba628 100644 --- a/src/lib/mod.rs +++ b/src/lib/mod.rs @@ -1,2 +1,3 @@ pub mod crypt; pub mod hash; +pub mod timing; diff --git a/src/lib/timing.rs b/src/lib/timing.rs new file mode 100644 index 0000000..3f08d3f --- /dev/null +++ b/src/lib/timing.rs @@ -0,0 +1,32 @@ +use std::collections::HashMap; +use std::time::{Duration, Instant}; + +pub struct TimeTaker { + start_values: HashMap, +} + +impl TimeTaker { + /// Creates a new time taker + pub fn new() -> Self { + Self { + start_values: HashMap::new(), + } + } + + /// Takes the current time for a name + pub fn take(&mut self, name: &str) -> Instant { + let time = Instant::now(); + self.start_values.insert(name.to_string(), time); + + time + } + + /// Returns the elapsed time for a start time name + pub fn since(&self, name: &str) -> Option { + if let Some(start) = self.start_values.get(name) { + return Some(start.elapsed()); + } + + None + } +} diff --git a/src/main.rs b/src/main.rs index ac2a29e..a289fa0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ use crate::lib::crypt::{ decrypt_brute_brute_force, decrypt_data, decrypt_with_dictionary, encrypt_data, }; use crate::lib::hash::{create_key, sha256, sha_checksum}; +use crate::lib::timing::TimeTaker; use bdf::chunks::{DataEntry, HashEntry, HashLookupTable}; use bdf::io::{BDFReader, BDFWriter}; use pbr::ProgressBar; @@ -90,9 +91,14 @@ struct CreateDictionary { #[structopt(short = "o", long = "output", default_value = "dictionary.bdf")] output: String, - /// If the dictionary file should be compressed - #[structopt(short = "c", long = "compress")] - compress: bool, + /// The compression level of the dictionary file from 1 to 9 + /// 0 means no compression + #[structopt(short = "c", long = "compression-level", default_value = "0")] + compress: u32, + + /// The number of password entries per chunk. + #[structopt(long = "entries-per-chunk", default_value = "100000")] + entries_per_chunk: u32, } fn main() { @@ -125,28 +131,41 @@ fn encrypt(_opts: &Opts, args: &Encrypt) { /// Decrypts a des encrypted file. /// Brute forces if the dictionary argument was passed fn decrypt(_opts: &Opts, args: &Decrypt) { + let mut tt = TimeTaker::new(); + tt.take("start"); let input: String = (*args.input).parse().unwrap(); let output: String = (*args.output).parse().unwrap(); let dictionary = args.dictionary.clone(); let data = fs::read(input).expect("Failed to read input file!"); if let Some(input_checksum) = (args.clone()).input_checksum { + tt.take("read-start"); let bin_content = fs::read(input_checksum).expect("Failed to read checksum file!"); let data_checksum = base64::decode(bin_content.as_slice()).unwrap(); + println!( + "Reading took {:.2}s", + tt.since("read-start").unwrap().as_secs_f32() + ); if let Some(dict) = dictionary { + tt.take("decryption-start"); 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!"); + println!( + "Decryption took {:.2}s", + tt.since("decryption-start").unwrap().as_secs_f32() + ); + println!("Finished {:.2}s!", tt.since("start").unwrap().as_secs_f32()); } else { println!("\nNo password found!"); + println!("Finished {:.2}s!", tt.since("start").unwrap().as_secs_f32()); } } else { let sp = spinner("Brute force decrypting file"); if let Some(dec_data) = decrypt_brute_brute_force(&data, &data_checksum) { sp.stop(); fs::write(output, &dec_data).expect("Failed to write output file!"); - println!("\nFinished!"); + println!("Finished {:.2}s!", tt.since("start").unwrap().as_secs_f32()); } else { sp.stop(); println!("\nNo fitting key found. (This should have been impossible)") @@ -161,58 +180,74 @@ fn decrypt(_opts: &Opts, args: &Decrypt) { } const SHA256: &str = "sha256"; + /// Creates a dictionary from an input file and writes it to the output file fn create_dictionary(_opts: &Opts, args: &CreateDictionary) { + let mut tt = TimeTaker::new(); + tt.take("start"); let sp = spinner("Reading input file..."); let input: String = (*args.input).parse().unwrap(); // TODO: Some form of removing duplicates (without itertools) let fout = File::create(args.output.clone()).unwrap(); let writer = BufWriter::new(fout); let handle; - { - let content = fs::read_to_string(input).expect("Failed to read content"); - let lines = content.par_lines(); - let entry_count = lines.clone().count() as u64; - sp.stop(); - let mut pb = ProgressBar::new(entry_count); - pb.set_max_refresh_rate(Some(Duration::from_millis(200))); - let (rx, tx) = sync_channel::(100_000); - let mut bdf_file = BDFWriter::new(writer, entry_count, args.compress); - bdf_file - .add_lookup_entry(HashEntry::new(SHA256.to_string(), 32)) - .expect("Failed to add lookup entry"); - handle = thread::spawn(move || { - for entry in tx { - if let Err(e) = bdf_file.add_data_entry(entry) { - println!("{:?}", e); - } - pb.inc(); + + let content = fs::read_to_string(input).expect("Failed to read content"); + let lines = content.par_lines(); + let entry_count = lines.clone().count() as u64; + sp.stop(); + + let mut pb = ProgressBar::new(entry_count); + pb.set_max_refresh_rate(Some(Duration::from_millis(200))); + let (rx, tx) = sync_channel::(100_00_000); + + let mut bdf_file = BDFWriter::new(writer, entry_count, args.compress != 0); + bdf_file.set_compression_level(args.compress); + bdf_file + .set_entries_per_chunk(args.entries_per_chunk) + .expect("Failed to set the entries per chunk."); + bdf_file + .add_lookup_entry(HashEntry::new(SHA256.to_string(), 32)) + .expect("Failed to add sha256 lookup entry"); + + handle = thread::spawn(move || { + for entry in tx { + if let Err(e) = bdf_file.add_data_entry(entry) { + println!("{:?}", e); } - pb.finish(); - bdf_file.flush().expect("failed to flush the file data"); - bdf_file - .flush_writer() - .expect("failed to flush the file writer"); + pb.inc(); + } + pb.finish(); + bdf_file.flush().expect("failed to flush the file data"); + bdf_file + .flush_writer() + .expect("failed to flush the file writer"); + }); + + tt.take("creation"); + let re = Regex::new("[\\x00\\x08\\x0B\\x0C\\x0E-\\x1F\\t\\r\\a\\n]").unwrap(); + lines + .map(|line| -> String { re.replace_all(line, "").to_string() }) + .map(|pw| -> DataEntry { + let key256 = sha256(&pw); + let mut data_entry = DataEntry::new(pw); + data_entry.add_hash_value(SHA256.to_string(), key256); + + data_entry + }) + .for_each_with(rx, |rx, data_entry| { + rx.send(data_entry) + .expect("Failed to send value to channel."); }); - let re = Regex::new("[\\x00\\x08\\x0B\\x0C\\x0E-\\x1F\\t\\r\\a\\n]").unwrap(); - lines - .map(|line| -> String { re.replace_all(line, "").to_string() }) - .map(|pw| -> DataEntry { - let key = sha256(&pw); - let mut data_entry = DataEntry::new(pw); - data_entry.add_hash_value(SHA256.to_string(), key); - - data_entry - }) - .for_each_with(rx, |rx, data_entry| { - rx.send(data_entry) - .expect("Failed to send value to channel."); - }); - } + if let Err(_err) = handle.join() { println!("Failed to join!"); } - println!("Finished!"); + println!( + "Rainbow table creation took {:.2}s", + tt.since("creation").unwrap().as_secs_f32() + ); + println!("Finished {:.2}s!", tt.since("start").unwrap().as_secs_f32()); } /// Creates a new spinner with a given text