diff --git a/Cargo.lock b/Cargo.lock index 4a75294..49a2092 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.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -219,6 +219,15 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "des" version = "0.3.0" @@ -234,10 +243,11 @@ name = "destools" version = "0.1.0" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bdflib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bdflib 0.4.3 (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)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "pbr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -320,6 +330,15 @@ dependencies = [ "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -667,6 +686,11 @@ dependencies = [ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "syn" version = "0.11.11" @@ -827,7 +851,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.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e883dd02966e46e8d8ea6c286d04070343139bfdcb15bbec10af8d50a750e1a2" +"checksum bdflib 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e0c502c6831015e424f0d57e85c50f8010dfec10b3bf29b9a9c930e29d7b0cb" "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" @@ -847,6 +871,7 @@ dependencies = [ "checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" "checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" "checksum des 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74ba5f1b5aee9772379c2670ba81306e65a93c0ee3caade7a1d22b188d88a3af" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" @@ -857,6 +882,7 @@ dependencies = [ "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" "checksum lzma-sys 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "53e48818fd597d46155132bbbb9505d6d1b3d360b4ee25cfa91c406f8a90fe91" @@ -900,6 +926,7 @@ dependencies = [ "checksum structopt-derive 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e79c80e0f4efd86ca960218d4e056249be189ff1c42824dcd9a7f51a56f0bd" "checksum strum 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca6e4730f517e041e547ffe23d29daab8de6b73af4b6ae2a002108169f5e7da" "checksum strum_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3384590878eb0cab3b128e844412e2d010821e7e091211b9d87324173ada7db8" +"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" "checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" diff --git a/Cargo.toml b/Cargo.toml index 686098d..a130928 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,5 @@ pbr = "1.0.2" spinners = "1.2.0" regex = "1.3.4" byteorder = "1.3.4" -bdflib = "0.4.0" \ No newline at end of file +bdflib = "0.4.0" +hmac = "0.7.1" \ No newline at end of file diff --git a/src/lib/crypt.rs b/src/lib/crypt.rs index 8d9af84..9a14245 100644 --- a/src/lib/crypt.rs +++ b/src/lib/crypt.rs @@ -1,4 +1,4 @@ -use crate::lib::hash::sha_checksum; +use crate::lib::hash::create_hmac; use cfb_mode::stream_cipher::{NewStreamCipher, StreamCipher}; use cfb_mode::Cfb; use des::Des; @@ -31,16 +31,14 @@ pub fn decrypt_data(data: &[u8], key: &[u8]) -> Vec { } /// Decrypts data using a dictionary -pub fn decrypt_with_dictionary( - data: &[u8], - dict: Vec<(&String, Vec)>, - checksum: &[u8], -) -> Option> { +pub fn decrypt_with_dictionary(data: &[u8], dict: Vec<(&String, &Vec)>) -> Option> { let decrypted = Mutex::>>::new(None); + let hmac = &data[data.len() - 32..]; + let encrypted_data = &data[..data.len() - 32]; let pass = dict.par_iter().find_first(|(_pw, key)| { - let decrypted_data = decrypt_data(&data, key); - let decr_check = sha_checksum(&decrypted_data); - return if decr_check == checksum { + let decrypted_data = decrypt_data(encrypted_data, &key[0..8]); + let decr_hmac = create_hmac(&key, &decrypted_data).expect("failed to create hmac"); + return if decr_hmac == hmac { let mut decry = decrypted.lock().unwrap(); *decry = Some(decrypted_data); true @@ -57,23 +55,3 @@ pub fn decrypt_with_dictionary( } None } - -/// Decrypts data by generating all possible keys -pub fn decrypt_brute_brute_force(data: &[u8], checksum: &[u8]) -> Option> { - let encryption_key = (0u64..std::u64::MAX) - .into_par_iter() - .find_first(|num: &u64| { - let key: &[u8] = &num.to_le_bytes(); - let decrypted_data = decrypt_data(&data, key); - let decr_check = sha_checksum(&decrypted_data); - - decr_check == checksum - }); - if let Some(num) = encryption_key { - let key: &[u8] = &num.to_le_bytes(); - println!("Key found: {:?}", key); - - return Some(decrypt_data(data, key)); - } - None -} diff --git a/src/lib/hash.rs b/src/lib/hash.rs index 45b3e94..9f49a47 100644 --- a/src/lib/hash.rs +++ b/src/lib/hash.rs @@ -1,6 +1,9 @@ -use sha2::{Digest, Sha256, Sha512}; +use hmac::crypto_mac::InvalidKeyLength; +use hmac::{Hmac, Mac}; +use sha2::{Digest, Sha256}; pub type PassKey = (String, Vec); +type HmacSha256 = Hmac; /// Hashes a text to a 32 bytes long key. pub fn create_key(pw: &str) -> Vec { @@ -21,15 +24,6 @@ pub fn sha256(pw: &str) -> Vec { 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 pub fn sha_checksum(data: &Vec) -> Vec { let mut hasher = Sha256::default(); @@ -38,3 +32,11 @@ pub fn sha_checksum(data: &Vec) -> Vec { result.to_vec() } + +/// Creates a hmac hash to be appended after the encrypted message +pub fn create_hmac(key: &Vec, data: &Vec) -> Result, InvalidKeyLength> { + let mut mac = HmacSha256::new_varkey(key)?; + mac.input(data); + + Ok(mac.result().code().to_vec()) +} diff --git a/src/main.rs b/src/main.rs index 5c51191..ee0a1e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,7 @@ pub mod lib; -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::crypt::{decrypt_with_dictionary, encrypt_data}; +use crate::lib::hash::{create_hmac, sha256}; use crate::lib::timing::TimeTaker; use bdf::chunks::{DataEntry, HashEntry, HashLookupTable}; use bdf::io::{BDFReader, BDFWriter}; @@ -53,10 +51,6 @@ struct Encrypt { /// The output file #[structopt(short = "o", long = "output", default_value = "output.des")] output: String, - - /// The file for the checksum. - #[structopt(long = "checksum-file")] - output_checksum: Option, } #[derive(StructOpt, Clone)] @@ -69,10 +63,6 @@ struct Decrypt { #[structopt(short = "o", long = "output", default_value = "output.txt")] output: String, - /// The file for the checksum. - #[structopt(long = "checksum-file")] - input_checksum: Option, - /// A dictionary file containing a list of passwords /// The file needs to be in a csv format with calculated password hashes. /// The hashes can be calculated with the create-dictionary subcommand from a txt file. @@ -115,15 +105,12 @@ fn encrypt(_opts: &Opts, args: &Encrypt) { let output: String = (*args.output).parse().unwrap(); let data: Vec = fs::read(input).expect("Failed to read input file!"); - if let Some(output_checksum) = (args.clone()).output_checksum { - let checksum = sha_checksum(&data); - let checksum_b64 = base64::encode(checksum.as_slice()); - fs::write(output_checksum, checksum_b64.as_bytes()) - .expect("Failed to write checksum file!"); - } let pass = read_password_from_tty(Some("Password: ")).unwrap(); - let key = create_key(&pass); - let enc_data = encrypt_data(data.as_slice(), key.as_slice()); + let sha256_key = sha256(&pass); + let key = &sha256_key[0..8]; + let mut data_hmac = create_hmac(&sha256_key, &data).expect("failed to create hmac"); + let mut enc_data = encrypt_data(data.as_slice(), &key); + enc_data.append(&mut data_hmac); fs::write(output, enc_data.as_slice()).expect("Failed to write output file!"); } @@ -137,39 +124,21 @@ fn decrypt(_opts: &Opts, args: &Decrypt) { 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 { - let bin_content = fs::read(input_checksum).expect("Failed to read checksum file!"); - let data_checksum = base64::decode(bin_content.as_slice()).unwrap(); - - 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!( - "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()); - } + if let Some(dict) = dictionary { + tt.take("decryption-start"); + if let Some(dec_data) = decrypt_with_dictionary_file(dict, &data) { + fs::write(output, &dec_data).expect("Failed to write output file!"); + 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 { - 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!("Finished {:.2}s!", tt.since("start").unwrap().as_secs_f32()); - } else { - sp.stop(); - println!("\nNo fitting key found. (This should have been impossible)") - } + println!("\nNo password found!"); + println!("Finished {:.2}s!", tt.since("start").unwrap().as_secs_f32()); } } else { - let pass = read_password_from_tty(Some("Password: ")).unwrap(); - let key = create_key(&pass); - let result = decrypt_data(&data, key.as_slice()); - fs::write(output, &result).expect("Failed to write output file!"); + println!("No checksum file given!"); } } @@ -249,11 +218,7 @@ fn spinner(text: &str) -> Spinner { /// Decrypts the file using a bdf dictionary /// The files content is read chunk by chunk to reduce the memory impact since dictionary /// files tend to be several gigabytes in size -fn decrypt_with_dictionary_file( - filename: String, - data: &Vec, - data_checksum: &Vec, -) -> Option> { +fn decrypt_with_dictionary_file(filename: String, data: &Vec) -> Option> { let sp = spinner("Reading dictionary..."); let f = File::open(&filename).expect("Failed to open dictionary file."); let mut bdf_file = BDFReader::new(f); @@ -280,17 +245,17 @@ fn decrypt_with_dictionary_file( sp.stop(); let mut result_data: Option> = None; for entries in tx { - let pw_table: Vec<(&String, Vec)> = entries + let pw_table: Vec<(&String, &Vec)> = entries .par_iter() .map(|entry: &DataEntry| { let pw = &entry.plain; let key: &Vec = entry.get_hash_value(SHA256.to_string()).unwrap(); - (pw, key[0..8].to_vec()) + (pw, key) }) .collect(); pb.inc(); - if let Some(dec_data) = decrypt_with_dictionary(&data, pw_table, &data_checksum) { + if let Some(dec_data) = decrypt_with_dictionary(&data, pw_table) { result_data = Some(dec_data); break; }