From 856c32e136c89820bb39e1035a1517caeb5352fd Mon Sep 17 00:00:00 2001 From: SymmetricChaos <42520289+SymmetricChaos@users.noreply.github.com> Date: Mon, 30 Dec 2024 00:22:17 -0500 Subject: [PATCH] adler, pbkdf1, pbkdf2 --- hashers/src/adler.rs | 42 +- hashers/src/pbkdf1.rs | 126 ++---- hashers/src/pbkdf2.rs | 170 ++------ hashers/src/scrypt.rs | 606 ++++++++++++++------------- src/hasher_panel/adler32_controls.rs | 20 +- src/hasher_panel/pbkdf1_controls.rs | 105 +++-- src/hasher_panel/pbkdf2_controls.rs | 126 ++++-- 7 files changed, 568 insertions(+), 627 deletions(-) diff --git a/hashers/src/adler.rs b/hashers/src/adler.rs index a72489da..9c12f3b9 100644 --- a/hashers/src/adler.rs +++ b/hashers/src/adler.rs @@ -1,39 +1,37 @@ -use crate::traits::ClassicHasher; -use utils::byte_formatting::ByteFormat; +use crate::traits::StatefulHasher; pub struct Adler32 { - pub input_format: ByteFormat, - pub output_format: ByteFormat, + a: u16, + b: u16, } -impl Default for Adler32 { - fn default() -> Self { - Self { - input_format: ByteFormat::Utf8, - output_format: ByteFormat::Hex, - } +impl Adler32 { + pub fn init() -> Self { + Self { a: 1, b: 0 } } } -impl ClassicHasher for Adler32 { - fn hash(&self, bytes: &[u8]) -> Vec { - let mut a: u16 = 1; - let mut b: u16 = 0; - +impl StatefulHasher for Adler32 { + fn update(&mut self, bytes: &[u8]) { for byte in bytes { - a = a.wrapping_add(*byte as u16); - b = b.wrapping_add(a); + self.a = self.a.wrapping_add(*byte as u16); + self.b = self.b.wrapping_add(self.a); } + } - [b, a].into_iter().flat_map(|w| w.to_be_bytes()).collect() + fn finalize(self) -> Vec { + [self.b, self.a] + .into_iter() + .flat_map(|w| w.to_be_bytes()) + .collect() } - crate::hash_bytes_from_string! {} + crate::stateful_hash_helpers!(); } -crate::basic_hash_tests!( +crate::stateful_hash_tests!( test1, - Adler32::default(), - "Wikipedia", + Adler32::init(), + b"Wikipedia", "11e60398"; ); diff --git a/hashers/src/pbkdf1.rs b/hashers/src/pbkdf1.rs index 637c1ea5..9e9509b4 100644 --- a/hashers/src/pbkdf1.rs +++ b/hashers/src/pbkdf1.rs @@ -1,12 +1,5 @@ -use crate::{ - errors::HasherError, - md2::Md2, - md5::Md5, - sha::Sha1, - traits::{ClassicHasher, StatefulHasher}, -}; +use crate::{md2::Md2, md5::Md5, sha::Sha1, traits::StatefulHasher}; use strum::{Display, EnumIter, VariantNames}; -use utils::byte_formatting::ByteFormat; #[derive(Debug, Clone, Copy, PartialEq, Eq, EnumIter, Display, VariantNames)] #[strum(serialize_all = "UPPERCASE")] @@ -17,90 +10,26 @@ pub enum Pbkdf1Variant { } pub struct Pbkdf1 { - pub input_format: ByteFormat, - pub output_format: ByteFormat, - pub variant: Pbkdf1Variant, - pub salt: [u8; 8], - pub iterations: u32, - pub hash_len: u32, // size of the output in bytes + buffer: Vec, + variant: Pbkdf1Variant, + salt: [u8; 8], + iterations: u32, + hash_len: u32, // size of the output in bytes } -impl Default for Pbkdf1 { - fn default() -> Self { +impl Pbkdf1 { + pub fn init(variant: Pbkdf1Variant, iterations: u32, hash_len: u32, salt: &[u8]) -> Self { + assert!(iterations >= 1); + assert!(hash_len >= 1); + assert!(salt.len() == 8); Self { - input_format: ByteFormat::Utf8, - output_format: ByteFormat::Hex, - salt: [0; 8], - variant: Pbkdf1Variant::Md5, - iterations: 4096, - hash_len: 32, + buffer: Vec::new(), + salt: salt.try_into().unwrap(), + variant, + iterations, + hash_len, } } -} - -impl Pbkdf1 { - pub fn input(mut self, input: ByteFormat) -> Self { - self.input_format = input; - self - } - - pub fn output(mut self, output: ByteFormat) -> Self { - self.output_format = output; - self - } - - pub fn variant(mut self, variant: Pbkdf1Variant) -> Self { - self.variant = variant; - self - } - - pub fn salt(mut self, salt: [u8; 8]) -> Self { - self.salt = salt; - self - } - - pub fn iterations(mut self, iterations: u32) -> Self { - assert!(self.iterations > 0); - self.iterations = iterations; - self - } - - /// For MD2 and MD5 hash_len is limited to 16. For SHA-1 hash_len is limited to 20. - pub fn hash_len(mut self, hash_len: u32) -> Self { - assert!(hash_len > 0); - self.hash_len = hash_len; - self - } - - pub fn salt_from_str(mut self, format: ByteFormat, salt_str: &str) -> Self { - let bytes = format - .text_to_bytes(salt_str) - .expect("byte format error") - .try_into() - .expect("could not convert to [u8; 8]"); - self.salt = bytes; - self - } - - // For changing the key interactively - pub fn set_salt(&mut self, salt: [u8; 8]) { - self.salt = salt; - } - - // Falliable method for changing the salt from a string interactively - pub fn set_salt_from_str( - &mut self, - format: ByteFormat, - salt_str: &str, - ) -> Result<(), HasherError> { - let bytes = format - .text_to_bytes(salt_str) - .map_err(|_| HasherError::general("byte format error"))? - .try_into() - .map_err(|_| HasherError::general("could not convert to [u8; 8]"))?; - self.salt = bytes; - Ok(()) - } pub fn inner_hash(&self, bytes: &[u8]) -> Vec { match self.variant { @@ -111,26 +40,25 @@ impl Pbkdf1 { } } -impl ClassicHasher for Pbkdf1 { - fn hash(&self, bytes: &[u8]) -> Vec { - assert!(self.iterations > 0); - assert!(self.hash_len > 0); +impl StatefulHasher for Pbkdf1 { + fn update(&mut self, bytes: &[u8]) { + self.buffer.extend_from_slice(bytes); + } - let mut working_vector = bytes.to_vec(); - working_vector.extend(self.salt); + fn finalize(mut self) -> Vec { + self.buffer.extend(self.salt); for _ in 0..self.iterations { - working_vector = self.inner_hash(&working_vector); + self.buffer = self.inner_hash(&self.buffer); } - working_vector.truncate(self.hash_len as usize); - working_vector + self.buffer[..self.hash_len as usize].to_vec() } - crate::hash_bytes_from_string! {} + crate::stateful_hash_helpers!(); } // Wasn't able to find any test vectors for PBKDF1 -// crate::basic_hash_tests!( -// Pbkdf1::default().variant(Pbkdf1Variant::Md5).iterations(1).hash_len(16).salt_from_str(ByteFormat::Utf8, "salt"), test1, "password", ""; +// crate::stateful_hash_tests!( +// test1, Pbkdf1::init(Pbkdf1Variant::Md5, 1, 20, b"salt"), b"password", ""; // ); diff --git a/hashers/src/pbkdf2.rs b/hashers/src/pbkdf2.rs index 5657c8f2..54890d29 100644 --- a/hashers/src/pbkdf2.rs +++ b/hashers/src/pbkdf2.rs @@ -1,138 +1,31 @@ -use itertools::Itertools; -use utils::byte_formatting::ByteFormat; - use crate::{ - errors::HasherError, hmac::{Hmac, HmacVariant}, - traits::ClassicHasher, + traits::StatefulHasher, }; +use itertools::Itertools; pub struct Pbkdf2 { - pub input_format: ByteFormat, - pub output_format: ByteFormat, - pub variant: HmacVariant, - pub salt: Vec, - pub iterations: u32, - pub hash_len: u32, // size of the output in bytes -} - -impl Default for Pbkdf2 { - fn default() -> Self { - Self { - input_format: ByteFormat::Utf8, - output_format: ByteFormat::Hex, - salt: Vec::new(), - variant: HmacVariant::Sha256, - iterations: 4096, - hash_len: 32, - } - } + buffer: Vec, + variant: HmacVariant, + salt: Vec, + iterations: u32, + hash_len: u32, // size of the output in bytes } impl Pbkdf2 { - pub fn input(mut self, input: ByteFormat) -> Self { - self.input_format = input; - self - } - - pub fn output(mut self, output: ByteFormat) -> Self { - self.output_format = output; - self - } - - pub fn variant(mut self, variant: HmacVariant) -> Self { - self.variant = variant; - self - } - - pub fn sha0() -> Self { - Self { - variant: HmacVariant::Sha0, - ..Default::default() - } - } - - pub fn sha1() -> Self { - Self { - variant: HmacVariant::Sha1, - ..Default::default() - } - } - - pub fn sha224() -> Self { - Self { - variant: HmacVariant::Sha224, - ..Default::default() - } - } - - pub fn sha256() -> Self { - Self { - variant: HmacVariant::Sha256, - ..Default::default() - } - } - - pub fn sha384() -> Self { + pub fn init(variant: HmacVariant, iterations: u32, hash_len: u32, salt: &[u8]) -> Self { + assert!(iterations >= 1); + assert!(hash_len >= 4); Self { - variant: HmacVariant::Sha384, - ..Default::default() + buffer: Vec::new(), + salt: salt.to_vec(), + variant, + iterations, + hash_len, } } - pub fn sha512() -> Self { - Self { - variant: HmacVariant::Sha512, - ..Default::default() - } - } - - pub fn salt(mut self, salt: Vec) -> Self { - self.salt = salt; - self - } - - pub fn iterations(mut self, iterations: u32) -> Self { - assert!(self.iterations > 0); - self.iterations = iterations; - self - } - - pub fn hash_len(mut self, hash_len: u32) -> Self { - assert!(hash_len > 0); - self.hash_len = hash_len; - self - } - - pub fn salt_from_str(mut self, format: ByteFormat, salt_str: &str) -> Self { - let bytes = format.text_to_bytes(salt_str).expect("invalid key string"); - self.salt = bytes; - self - } - - // For changing the key interactively - pub fn set_salt(&mut self, salt: &[u8]) { - self.salt = salt.to_vec(); - } - - // Falliable method for changing the salt from a string interactively - pub fn set_salt_from_str( - &mut self, - format: ByteFormat, - salt_str: &str, - ) -> Result<(), HasherError> { - let bytes = format - .text_to_bytes(salt_str) - .map_err(|_| HasherError::general("byte format error"))?; - self.salt = bytes; - Ok(()) - } - - pub fn hash_block(&self, hmac: &mut Hmac, block_num: u32) -> Vec { - // The salt followed by the block nunber are the initial input - let mut s = self.salt.clone(); - s.extend(block_num.to_be_bytes()); - + pub fn hash_block(&self, hmac: &mut Hmac, s: &[u8]) -> Vec { // Create the first output block in the chain by hashing the salt (the password has already been set for the hmac) let mut block = hmac.hash_and_reset(&s); let mut temp: Vec = Vec::new(); @@ -157,30 +50,35 @@ impl Pbkdf2 { } } -impl ClassicHasher for Pbkdf2 { - fn hash(&self, bytes: &[u8]) -> Vec { - assert!(self.iterations != 0); +impl StatefulHasher for Pbkdf2 { + fn update(&mut self, bytes: &[u8]) { + self.buffer.extend_from_slice(bytes); + } + fn finalize(mut self) -> Vec { + let mut hmac = Hmac::init(self.variant, &self.buffer); let mut out = Vec::new(); - let mut hmac = Hmac::init(self.variant, bytes); + self.salt.extend([0; 4]); + let l = self.salt.len(); - let mut block_num = 0; + let mut block_num: u32 = 0; while out.len() < self.hash_len as usize { block_num += 1; - out.extend(self.hash_block(&mut hmac, block_num)); + self.salt[l - 4..].copy_from_slice(&block_num.to_be_bytes()); + out.extend(self.hash_block(&mut hmac, &self.salt)); } out.truncate(self.hash_len as usize); out } - crate::hash_bytes_from_string! {} + crate::stateful_hash_helpers!(); } -crate::basic_hash_tests!( - test1, Pbkdf2::sha1().iterations(1).hash_len(20).salt_from_str(ByteFormat::Utf8, "salt"), "password", "0c60c80f961f0e71f3a9b524af6012062fe037a6"; - test2, Pbkdf2::sha1().iterations(2).hash_len(20).salt_from_str(ByteFormat::Utf8, "salt"), "password", "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"; - test3, Pbkdf2::sha1().iterations(4096).hash_len(20).salt_from_str(ByteFormat::Utf8, "salt"), "password", "4b007901b765489abead49d926f721d065a429c1"; - test4, Pbkdf2::sha1().iterations(4096).hash_len(25).salt_from_str(ByteFormat::Utf8, "saltSALTsaltSALTsaltSALTsaltSALTsalt"), "passwordPASSWORDpassword", "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"; - test5, Pbkdf2::sha1().iterations(4096).hash_len(16).salt_from_str(ByteFormat::Utf8, "sa\0lt"), "pass\0word", "56fa6aa75548099dcc37d7f03425e0c3"; +crate::stateful_hash_tests!( + test1, Pbkdf2::init(HmacVariant::Sha1, 1, 20, b"salt"), b"password", "0c60c80f961f0e71f3a9b524af6012062fe037a6"; + test2, Pbkdf2::init(HmacVariant::Sha1, 2, 20, b"salt"), b"password", "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"; + test3, Pbkdf2::init(HmacVariant::Sha1, 4096, 20, b"salt"), b"password", "4b007901b765489abead49d926f721d065a429c1"; + test4, Pbkdf2::init(HmacVariant::Sha1, 4096, 25, b"saltSALTsaltSALTsaltSALTsaltSALTsalt"), b"passwordPASSWORDpassword", "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"; + test5, Pbkdf2::init(HmacVariant::Sha1, 4096, 16, b"sa\0lt"), b"pass\0word", "56fa6aa75548099dcc37d7f03425e0c3"; ); diff --git a/hashers/src/scrypt.rs b/hashers/src/scrypt.rs index f6da8135..e2bb2df7 100644 --- a/hashers/src/scrypt.rs +++ b/hashers/src/scrypt.rs @@ -1,302 +1,304 @@ -// // based on -// // https://datatracker.ietf.org/doc/html/rfc7914.html#section-3 - -// use std::usize; - -// use itertools::Itertools; -// use utils::byte_formatting::{make_u32s_le, u32s_to_bytes_le, ByteFormat}; - -// use crate::{pbkdf2, traits::ClassicHasher}; - -// fn xor_blocks(a: &[u8], b: &[u8]) -> Vec { -// let mut out = Vec::new(); -// for (l, r) in a.iter().zip_eq(b.iter()) { -// out.push(*l ^ *r) -// } -// out -// } - -// macro_rules! salsa { -// ($x: ident, $a: literal, $b: literal, $c: literal, $r: literal) => { -// $x[$a] ^= $x[$b].wrapping_add($x[$c]).rotate_left($r) -// }; -// } - -// fn salsa20_8(a: [u8; 64]) -> [u8; 64] { -// let mut x = make_u32s_le::<16>(&a); -// let orig = x.clone(); -// for _ in 0..4 { -// salsa!(x, 4, 0, 12, 7); -// salsa!(x, 8, 4, 0, 9); -// salsa!(x, 12, 8, 4, 13); -// salsa!(x, 0, 12, 8, 18); -// salsa!(x, 9, 5, 1, 7); -// salsa!(x, 13, 9, 5, 9); -// salsa!(x, 1, 13, 9, 13); -// salsa!(x, 5, 1, 13, 18); -// salsa!(x, 14, 10, 6, 7); -// salsa!(x, 2, 14, 10, 9); -// salsa!(x, 6, 2, 14, 13); -// salsa!(x, 10, 6, 2, 18); -// salsa!(x, 3, 15, 11, 7); -// salsa!(x, 7, 3, 15, 9); -// salsa!(x, 11, 7, 3, 13); -// salsa!(x, 15, 11, 7, 18); -// salsa!(x, 1, 0, 3, 7); -// salsa!(x, 2, 1, 0, 9); -// salsa!(x, 3, 2, 1, 13); -// salsa!(x, 0, 3, 2, 18); -// salsa!(x, 6, 5, 4, 7); -// salsa!(x, 7, 6, 5, 9); -// salsa!(x, 4, 7, 6, 13); -// salsa!(x, 5, 4, 7, 18); -// salsa!(x, 11, 10, 9, 7); -// salsa!(x, 8, 11, 10, 9); -// salsa!(x, 9, 8, 11, 13); -// salsa!(x, 10, 9, 8, 18); -// salsa!(x, 12, 15, 14, 7); -// salsa!(x, 13, 12, 15, 9); -// salsa!(x, 14, 13, 12, 13); -// salsa!(x, 15, 14, 13, 18); -// } -// for i in 0..16 { -// x[i] = x[i].wrapping_add(orig[i]) -// } -// let mut out = [0; 64]; -// u32s_to_bytes_le(&mut out, &x); -// out -// } - -// fn block_mix(block: &mut [u8]) { -// let mut y = Vec::new(); - -// let mut x = [0u8; 64]; -// x.copy_from_slice(&block[block.len() - 64..]); - -// for chunk in block.chunks(64) { -// let t = xor_blocks(&x, chunk); -// x = salsa20_8(t.try_into().unwrap()); -// y.push(x); -// } - -// let mut out = Vec::new(); -// for even in y.iter().step_by(2) { -// out.extend_from_slice(even); -// } -// for odd in y.iter().skip(1).step_by(2) { -// out.extend_from_slice(odd); -// } -// block.copy_from_slice(&out); -// } - -// // weird operation -// // had to copy from here -// // https://github.com/RustCrypto/password-hashes/blob/master/scrypt/src/romix.rs -// fn integerify(x: &[u8], n: usize) -> usize { -// let mask = n - 1; -// let t = u32::from_le_bytes(x[x.len() - 64..x.len() - 60].try_into().unwrap()); -// (t as usize) & mask -// } - -// fn ro_mix(block: &mut [u8], n: usize) { -// let mut x = block.to_vec(); -// let mut v = Vec::with_capacity(n); -// for _ in 0..n { -// v.push(x.clone()); -// block_mix(&mut x); -// } - -// for _ in 0..n { -// let j = integerify(&x, n); -// let mut t = xor_blocks(&x[..], &v[j]); -// block_mix(&mut t); -// x.copy_from_slice(&t); -// } -// block.copy_from_slice(&x); -// } - -// pub struct Scrypt { -// pub input_format: ByteFormat, -// pub output_format: ByteFormat, -// pub salt: Vec, -// pub cost: u32, // N -// pub blocksize_factor: u32, // r -// pub parallelism: u32, // p -// pub key_len: u32, // dkLen -// } - -// impl Default for Scrypt { -// fn default() -> Self { -// Self { -// input_format: ByteFormat::Utf8, -// output_format: ByteFormat::Hex, -// salt: Vec::new(), -// cost: 2, -// blocksize_factor: 1, -// parallelism: 1, -// key_len: 64, -// } -// } -// } - -// impl Scrypt { -// pub fn input(mut self, input: ByteFormat) -> Self { -// self.input_format = input; -// self -// } - -// pub fn output(mut self, output: ByteFormat) -> Self { -// self.output_format = output; -// self -// } - -// pub fn salt(mut self, salt: &[u8]) -> Self { -// self.salt = salt.to_vec(); -// self -// } - -// pub fn cost(mut self, cost: u32) -> Self { -// self.cost = cost; -// self -// } - -// pub fn blocksize_factor(mut self, blocksize_factor: u32) -> Self { -// self.blocksize_factor = blocksize_factor; -// self -// } - -// pub fn parallelism(mut self, parallelism: u32) -> Self { -// self.parallelism = parallelism; -// self -// } - -// pub fn key_len(mut self, key_len: u32) -> Self { -// self.key_len = key_len; -// self -// } - -// pub fn direct(passphrase: &[u8], s: &[u8], n: u32, r: u32, p: u32, dklen: u32) -> Vec { -// Scrypt { -// input_format: ByteFormat::Hex, -// output_format: ByteFormat::Hex, -// salt: s.to_vec(), -// cost: n, -// blocksize_factor: r, -// parallelism: p, -// key_len: dklen, -// } -// .hash(passphrase) -// } -// } - -// impl ClassicHasher for Scrypt { -// fn hash(&self, bytes: &[u8]) -> Vec { -// let blocksize = 128 * self.blocksize_factor; -// let mut p = pbkdf2::Pbkdf2::sha256() -// .salt(self.salt.clone()) -// .iterations(1) -// .hash_len(blocksize * self.parallelism) -// .hash(bytes); - -// for block in p.chunks_mut(blocksize as usize) { -// ro_mix(block, self.cost as usize) -// } - -// pbkdf2::Pbkdf2::sha256() -// .salt(p) -// .iterations(1) -// .hash_len(self.key_len) -// .hash(bytes) -// } - -// crate::hash_bytes_from_string! {} -// } - -// #[cfg(test)] -// mod scrypt_tests { -// use super::*; - -// #[test] -// fn salsa_function() { -// let input = [ -// 0x7e, 0x87, 0x9a, 0x21, 0x4f, 0x3e, 0xc9, 0x86, 0x7c, 0xa9, 0x40, 0xe6, 0x41, 0x71, -// 0x8f, 0x26, 0xba, 0xee, 0x55, 0x5b, 0x8c, 0x61, 0xc1, 0xb5, 0x0d, 0xf8, 0x46, 0x11, -// 0x6d, 0xcd, 0x3b, 0x1d, 0xee, 0x24, 0xf3, 0x19, 0xdf, 0x9b, 0x3d, 0x85, 0x14, 0x12, -// 0x1e, 0x4b, 0x5a, 0xc5, 0xaa, 0x32, 0x76, 0x02, 0x1d, 0x29, 0x09, 0xc7, 0x48, 0x29, -// 0xed, 0xeb, 0xc6, 0x8d, 0xb8, 0xb8, 0xc2, 0x5e, -// ]; -// let output = [ -// 0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99, 0x3b, 0x81, 0xca, 0xcb, 0x02, 0x0c, -// 0xef, 0x05, 0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d, 0xfd, 0x7b, 0x1c, 0x63, -// 0x96, 0x68, 0x2f, 0x29, 0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 0xbc, 0xfe, 0x6b, -// 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba, 0xe4, 0x24, 0xcc, 0x10, 0x2c, 0x91, 0x74, 0x5c, -// 0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81, -// ]; -// assert_eq!(output, salsa20_8(input)) -// } - -// #[test] -// fn blockmix_function() { -// let mut input = [ -// 0xf7, 0xce, 0x0b, 0x65, 0x3d, 0x2d, 0x72, 0xa4, 0x10, 0x8c, 0xf5, 0xab, 0xe9, 0x12, -// 0xff, 0xdd, 0x77, 0x76, 0x16, 0xdb, 0xbb, 0x27, 0xa7, 0x0e, 0x82, 0x04, 0xf3, 0xae, -// 0x2d, 0x0f, 0x6f, 0xad, 0x89, 0xf6, 0x8f, 0x48, 0x11, 0xd1, 0xe8, 0x7b, 0xcc, 0x3b, -// 0xd7, 0x40, 0x0a, 0x9f, 0xfd, 0x29, 0x09, 0x4f, 0x01, 0x84, 0x63, 0x95, 0x74, 0xf3, -// 0x9a, 0xe5, 0xa1, 0x31, 0x52, 0x17, 0xbc, 0xd7, 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, -// 0xbb, 0x22, 0x6c, 0x25, 0xb5, 0x4d, 0xa8, 0x63, 0x70, 0xfb, 0xcd, 0x98, 0x43, 0x80, -// 0x37, 0x46, 0x66, 0xbb, 0x8f, 0xfc, 0xb5, 0xbf, 0x40, 0xc2, 0x54, 0xb0, 0x67, 0xd2, -// 0x7c, 0x51, 0xce, 0x4a, 0xd5, 0xfe, 0xd8, 0x29, 0xc9, 0x0b, 0x50, 0x5a, 0x57, 0x1b, -// 0x7f, 0x4d, 0x1c, 0xad, 0x6a, 0x52, 0x3c, 0xda, 0x77, 0x0e, 0x67, 0xbc, 0xea, 0xaf, -// 0x7e, 0x89, -// ]; -// let output = [ -// 0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99, 0x3b, 0x81, 0xca, 0xcb, 0x02, 0x0c, -// 0xef, 0x05, 0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d, 0xfd, 0x7b, 0x1c, 0x63, -// 0x96, 0x68, 0x2f, 0x29, 0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 0xbc, 0xfe, 0x6b, -// 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba, 0xe4, 0x24, 0xcc, 0x10, 0x2c, 0x91, 0x74, 0x5c, -// 0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81, 0x20, 0xed, 0xc9, 0x75, 0x32, 0x38, -// 0x81, 0xa8, 0x05, 0x40, 0xf6, 0x4c, 0x16, 0x2d, 0xcd, 0x3c, 0x21, 0x07, 0x7c, 0xfe, -// 0x5f, 0x8d, 0x5f, 0xe2, 0xb1, 0xa4, 0x16, 0x8f, 0x95, 0x36, 0x78, 0xb7, 0x7d, 0x3b, -// 0x3d, 0x80, 0x3b, 0x60, 0xe4, 0xab, 0x92, 0x09, 0x96, 0xe5, 0x9b, 0x4d, 0x53, 0xb6, -// 0x5d, 0x2a, 0x22, 0x58, 0x77, 0xd5, 0xed, 0xf5, 0x84, 0x2c, 0xb9, 0xf1, 0x4e, 0xef, -// 0xe4, 0x25, -// ]; -// block_mix(&mut input); -// assert_eq!(input, output); -// } - -// #[test] -// fn romix_function() { -// let mut input: [u8; 128] = [ -// 0xf7, 0xce, 0x0b, 0x65, 0x3d, 0x2d, 0x72, 0xa4, 0x10, 0x8c, 0xf5, 0xab, 0xe9, 0x12, -// 0xff, 0xdd, 0x77, 0x76, 0x16, 0xdb, 0xbb, 0x27, 0xa7, 0x0e, 0x82, 0x04, 0xf3, 0xae, -// 0x2d, 0x0f, 0x6f, 0xad, 0x89, 0xf6, 0x8f, 0x48, 0x11, 0xd1, 0xe8, 0x7b, 0xcc, 0x3b, -// 0xd7, 0x40, 0x0a, 0x9f, 0xfd, 0x29, 0x09, 0x4f, 0x01, 0x84, 0x63, 0x95, 0x74, 0xf3, -// 0x9a, 0xe5, 0xa1, 0x31, 0x52, 0x17, 0xbc, 0xd7, 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, -// 0xbb, 0x22, 0x6c, 0x25, 0xb5, 0x4d, 0xa8, 0x63, 0x70, 0xfb, 0xcd, 0x98, 0x43, 0x80, -// 0x37, 0x46, 0x66, 0xbb, 0x8f, 0xfc, 0xb5, 0xbf, 0x40, 0xc2, 0x54, 0xb0, 0x67, 0xd2, -// 0x7c, 0x51, 0xce, 0x4a, 0xd5, 0xfe, 0xd8, 0x29, 0xc9, 0x0b, 0x50, 0x5a, 0x57, 0x1b, -// 0x7f, 0x4d, 0x1c, 0xad, 0x6a, 0x52, 0x3c, 0xda, 0x77, 0x0e, 0x67, 0xbc, 0xea, 0xaf, -// 0x7e, 0x89, -// ]; -// let output: [u8; 128] = [ -// 0x79, 0xcc, 0xc1, 0x93, 0x62, 0x9d, 0xeb, 0xca, 0x04, 0x7f, 0x0b, 0x70, 0x60, 0x4b, -// 0xf6, 0xb6, 0x2c, 0xe3, 0xdd, 0x4a, 0x96, 0x26, 0xe3, 0x55, 0xfa, 0xfc, 0x61, 0x98, -// 0xe6, 0xea, 0x2b, 0x46, 0xd5, 0x84, 0x13, 0x67, 0x3b, 0x99, 0xb0, 0x29, 0xd6, 0x65, -// 0xc3, 0x57, 0x60, 0x1f, 0xb4, 0x26, 0xa0, 0xb2, 0xf4, 0xbb, 0xa2, 0x00, 0xee, 0x9f, -// 0x0a, 0x43, 0xd1, 0x9b, 0x57, 0x1a, 0x9c, 0x71, 0xef, 0x11, 0x42, 0xe6, 0x5d, 0x5a, -// 0x26, 0x6f, 0xdd, 0xca, 0x83, 0x2c, 0xe5, 0x9f, 0xaa, 0x7c, 0xac, 0x0b, 0x9c, 0xf1, -// 0xbe, 0x2b, 0xff, 0xca, 0x30, 0x0d, 0x01, 0xee, 0x38, 0x76, 0x19, 0xc4, 0xae, 0x12, -// 0xfd, 0x44, 0x38, 0xf2, 0x03, 0xa0, 0xe4, 0xe1, 0xc4, 0x7e, 0xc3, 0x14, 0x86, 0x1f, -// 0x4e, 0x90, 0x87, 0xcb, 0x33, 0x39, 0x6a, 0x68, 0x73, 0xe8, 0xf9, 0xd2, 0x53, 0x9a, -// 0x4b, 0x8e, -// ]; -// ro_mix(&mut input, 16); -// assert_eq!(input, output); -// } -// } - -// crate::basic_hash_tests!( -// test1, Scrypt::default().cost(16), "", "77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906"; -// test2, Scrypt::default().salt(b"NaCl").cost(1024).blocksize_factor(8).parallelism(16), "password", "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640"; -// ); +// based on +// https://datatracker.ietf.org/doc/html/rfc7914.html#section-3 + +use std::usize; + +use itertools::Itertools; +use utils::byte_formatting::{make_u32s_le, u32s_to_bytes_le, ByteFormat}; + +use crate::{ + hmac::HmacVariant, + pbkdf2, + traits::{ClassicHasher, StatefulHasher}, +}; + +fn xor_blocks(a: &[u8], b: &[u8]) -> Vec { + let mut out = Vec::new(); + for (l, r) in a.iter().zip_eq(b.iter()) { + out.push(*l ^ *r) + } + out +} + +macro_rules! salsa { + ($x: ident, $a: literal, $b: literal, $c: literal, $r: literal) => { + $x[$a] ^= $x[$b].wrapping_add($x[$c]).rotate_left($r) + }; +} + +fn salsa20_8(a: [u8; 64]) -> [u8; 64] { + let mut x = make_u32s_le::<16>(&a); + let orig = x.clone(); + for _ in 0..4 { + salsa!(x, 4, 0, 12, 7); + salsa!(x, 8, 4, 0, 9); + salsa!(x, 12, 8, 4, 13); + salsa!(x, 0, 12, 8, 18); + salsa!(x, 9, 5, 1, 7); + salsa!(x, 13, 9, 5, 9); + salsa!(x, 1, 13, 9, 13); + salsa!(x, 5, 1, 13, 18); + salsa!(x, 14, 10, 6, 7); + salsa!(x, 2, 14, 10, 9); + salsa!(x, 6, 2, 14, 13); + salsa!(x, 10, 6, 2, 18); + salsa!(x, 3, 15, 11, 7); + salsa!(x, 7, 3, 15, 9); + salsa!(x, 11, 7, 3, 13); + salsa!(x, 15, 11, 7, 18); + salsa!(x, 1, 0, 3, 7); + salsa!(x, 2, 1, 0, 9); + salsa!(x, 3, 2, 1, 13); + salsa!(x, 0, 3, 2, 18); + salsa!(x, 6, 5, 4, 7); + salsa!(x, 7, 6, 5, 9); + salsa!(x, 4, 7, 6, 13); + salsa!(x, 5, 4, 7, 18); + salsa!(x, 11, 10, 9, 7); + salsa!(x, 8, 11, 10, 9); + salsa!(x, 9, 8, 11, 13); + salsa!(x, 10, 9, 8, 18); + salsa!(x, 12, 15, 14, 7); + salsa!(x, 13, 12, 15, 9); + salsa!(x, 14, 13, 12, 13); + salsa!(x, 15, 14, 13, 18); + } + for i in 0..16 { + x[i] = x[i].wrapping_add(orig[i]) + } + let mut out = [0; 64]; + u32s_to_bytes_le(&mut out, &x); + out +} + +fn block_mix(block: &mut [u8]) { + let mut y = Vec::new(); + + let mut x = [0u8; 64]; + x.copy_from_slice(&block[block.len() - 64..]); + + for chunk in block.chunks(64) { + let t = xor_blocks(&x, chunk); + x = salsa20_8(t.try_into().unwrap()); + y.push(x); + } + + let mut out = Vec::new(); + for even in y.iter().step_by(2) { + out.extend_from_slice(even); + } + for odd in y.iter().skip(1).step_by(2) { + out.extend_from_slice(odd); + } + block.copy_from_slice(&out); +} + +// weird operation +// had to copy from here +// https://github.com/RustCrypto/password-hashes/blob/master/scrypt/src/romix.rs +fn integerify(x: &[u8], n: usize) -> usize { + let mask = n - 1; + let t = u32::from_le_bytes(x[x.len() - 64..x.len() - 60].try_into().unwrap()); + (t as usize) & mask +} + +fn ro_mix(block: &mut [u8], n: usize) { + let mut x = block.to_vec(); + let mut v = Vec::with_capacity(n); + for _ in 0..n { + v.push(x.clone()); + block_mix(&mut x); + } + + for _ in 0..n { + let j = integerify(&x, n); + let mut t = xor_blocks(&x[..], &v[j]); + block_mix(&mut t); + x.copy_from_slice(&t); + } + block.copy_from_slice(&x); +} + +pub struct Scrypt { + pub input_format: ByteFormat, + pub output_format: ByteFormat, + pub salt: Vec, + pub cost: u32, // N + pub blocksize_factor: u32, // r + pub parallelism: u32, // p + pub key_len: u32, // dkLen +} + +impl Default for Scrypt { + fn default() -> Self { + Self { + input_format: ByteFormat::Utf8, + output_format: ByteFormat::Hex, + salt: Vec::new(), + cost: 2, + blocksize_factor: 1, + parallelism: 1, + key_len: 64, + } + } +} + +impl Scrypt { + pub fn input(mut self, input: ByteFormat) -> Self { + self.input_format = input; + self + } + + pub fn output(mut self, output: ByteFormat) -> Self { + self.output_format = output; + self + } + + pub fn salt(mut self, salt: &[u8]) -> Self { + self.salt = salt.to_vec(); + self + } + + pub fn cost(mut self, cost: u32) -> Self { + self.cost = cost; + self + } + + pub fn blocksize_factor(mut self, blocksize_factor: u32) -> Self { + self.blocksize_factor = blocksize_factor; + self + } + + pub fn parallelism(mut self, parallelism: u32) -> Self { + self.parallelism = parallelism; + self + } + + pub fn key_len(mut self, key_len: u32) -> Self { + self.key_len = key_len; + self + } + + pub fn direct(passphrase: &[u8], s: &[u8], n: u32, r: u32, p: u32, dklen: u32) -> Vec { + Scrypt { + input_format: ByteFormat::Hex, + output_format: ByteFormat::Hex, + salt: s.to_vec(), + cost: n, + blocksize_factor: r, + parallelism: p, + key_len: dklen, + } + .hash(passphrase) + } +} + +impl ClassicHasher for Scrypt { + fn hash(&self, bytes: &[u8]) -> Vec { + let blocksize = 128 * self.blocksize_factor; + let mut p = pbkdf2::Pbkdf2::init( + HmacVariant::Sha256, + 1, + blocksize * self.parallelism, + &self.salt, + ) + .hash(bytes); + + for block in p.chunks_mut(blocksize as usize) { + ro_mix(block, self.cost as usize) + } + + pbkdf2::Pbkdf2::init(HmacVariant::Sha256, 1, self.key_len, &p).hash(bytes) + } + + crate::hash_bytes_from_string! {} +} + +#[cfg(test)] +mod scrypt_tests { + use super::*; + + #[test] + fn salsa_function() { + let input = [ + 0x7e, 0x87, 0x9a, 0x21, 0x4f, 0x3e, 0xc9, 0x86, 0x7c, 0xa9, 0x40, 0xe6, 0x41, 0x71, + 0x8f, 0x26, 0xba, 0xee, 0x55, 0x5b, 0x8c, 0x61, 0xc1, 0xb5, 0x0d, 0xf8, 0x46, 0x11, + 0x6d, 0xcd, 0x3b, 0x1d, 0xee, 0x24, 0xf3, 0x19, 0xdf, 0x9b, 0x3d, 0x85, 0x14, 0x12, + 0x1e, 0x4b, 0x5a, 0xc5, 0xaa, 0x32, 0x76, 0x02, 0x1d, 0x29, 0x09, 0xc7, 0x48, 0x29, + 0xed, 0xeb, 0xc6, 0x8d, 0xb8, 0xb8, 0xc2, 0x5e, + ]; + let output = [ + 0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99, 0x3b, 0x81, 0xca, 0xcb, 0x02, 0x0c, + 0xef, 0x05, 0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d, 0xfd, 0x7b, 0x1c, 0x63, + 0x96, 0x68, 0x2f, 0x29, 0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 0xbc, 0xfe, 0x6b, + 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba, 0xe4, 0x24, 0xcc, 0x10, 0x2c, 0x91, 0x74, 0x5c, + 0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81, + ]; + assert_eq!(output, salsa20_8(input)) + } + + #[test] + fn blockmix_function() { + let mut input = [ + 0xf7, 0xce, 0x0b, 0x65, 0x3d, 0x2d, 0x72, 0xa4, 0x10, 0x8c, 0xf5, 0xab, 0xe9, 0x12, + 0xff, 0xdd, 0x77, 0x76, 0x16, 0xdb, 0xbb, 0x27, 0xa7, 0x0e, 0x82, 0x04, 0xf3, 0xae, + 0x2d, 0x0f, 0x6f, 0xad, 0x89, 0xf6, 0x8f, 0x48, 0x11, 0xd1, 0xe8, 0x7b, 0xcc, 0x3b, + 0xd7, 0x40, 0x0a, 0x9f, 0xfd, 0x29, 0x09, 0x4f, 0x01, 0x84, 0x63, 0x95, 0x74, 0xf3, + 0x9a, 0xe5, 0xa1, 0x31, 0x52, 0x17, 0xbc, 0xd7, 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, + 0xbb, 0x22, 0x6c, 0x25, 0xb5, 0x4d, 0xa8, 0x63, 0x70, 0xfb, 0xcd, 0x98, 0x43, 0x80, + 0x37, 0x46, 0x66, 0xbb, 0x8f, 0xfc, 0xb5, 0xbf, 0x40, 0xc2, 0x54, 0xb0, 0x67, 0xd2, + 0x7c, 0x51, 0xce, 0x4a, 0xd5, 0xfe, 0xd8, 0x29, 0xc9, 0x0b, 0x50, 0x5a, 0x57, 0x1b, + 0x7f, 0x4d, 0x1c, 0xad, 0x6a, 0x52, 0x3c, 0xda, 0x77, 0x0e, 0x67, 0xbc, 0xea, 0xaf, + 0x7e, 0x89, + ]; + let output = [ + 0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99, 0x3b, 0x81, 0xca, 0xcb, 0x02, 0x0c, + 0xef, 0x05, 0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d, 0xfd, 0x7b, 0x1c, 0x63, + 0x96, 0x68, 0x2f, 0x29, 0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 0xbc, 0xfe, 0x6b, + 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba, 0xe4, 0x24, 0xcc, 0x10, 0x2c, 0x91, 0x74, 0x5c, + 0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81, 0x20, 0xed, 0xc9, 0x75, 0x32, 0x38, + 0x81, 0xa8, 0x05, 0x40, 0xf6, 0x4c, 0x16, 0x2d, 0xcd, 0x3c, 0x21, 0x07, 0x7c, 0xfe, + 0x5f, 0x8d, 0x5f, 0xe2, 0xb1, 0xa4, 0x16, 0x8f, 0x95, 0x36, 0x78, 0xb7, 0x7d, 0x3b, + 0x3d, 0x80, 0x3b, 0x60, 0xe4, 0xab, 0x92, 0x09, 0x96, 0xe5, 0x9b, 0x4d, 0x53, 0xb6, + 0x5d, 0x2a, 0x22, 0x58, 0x77, 0xd5, 0xed, 0xf5, 0x84, 0x2c, 0xb9, 0xf1, 0x4e, 0xef, + 0xe4, 0x25, + ]; + block_mix(&mut input); + assert_eq!(input, output); + } + + #[test] + fn romix_function() { + let mut input: [u8; 128] = [ + 0xf7, 0xce, 0x0b, 0x65, 0x3d, 0x2d, 0x72, 0xa4, 0x10, 0x8c, 0xf5, 0xab, 0xe9, 0x12, + 0xff, 0xdd, 0x77, 0x76, 0x16, 0xdb, 0xbb, 0x27, 0xa7, 0x0e, 0x82, 0x04, 0xf3, 0xae, + 0x2d, 0x0f, 0x6f, 0xad, 0x89, 0xf6, 0x8f, 0x48, 0x11, 0xd1, 0xe8, 0x7b, 0xcc, 0x3b, + 0xd7, 0x40, 0x0a, 0x9f, 0xfd, 0x29, 0x09, 0x4f, 0x01, 0x84, 0x63, 0x95, 0x74, 0xf3, + 0x9a, 0xe5, 0xa1, 0x31, 0x52, 0x17, 0xbc, 0xd7, 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, + 0xbb, 0x22, 0x6c, 0x25, 0xb5, 0x4d, 0xa8, 0x63, 0x70, 0xfb, 0xcd, 0x98, 0x43, 0x80, + 0x37, 0x46, 0x66, 0xbb, 0x8f, 0xfc, 0xb5, 0xbf, 0x40, 0xc2, 0x54, 0xb0, 0x67, 0xd2, + 0x7c, 0x51, 0xce, 0x4a, 0xd5, 0xfe, 0xd8, 0x29, 0xc9, 0x0b, 0x50, 0x5a, 0x57, 0x1b, + 0x7f, 0x4d, 0x1c, 0xad, 0x6a, 0x52, 0x3c, 0xda, 0x77, 0x0e, 0x67, 0xbc, 0xea, 0xaf, + 0x7e, 0x89, + ]; + let output: [u8; 128] = [ + 0x79, 0xcc, 0xc1, 0x93, 0x62, 0x9d, 0xeb, 0xca, 0x04, 0x7f, 0x0b, 0x70, 0x60, 0x4b, + 0xf6, 0xb6, 0x2c, 0xe3, 0xdd, 0x4a, 0x96, 0x26, 0xe3, 0x55, 0xfa, 0xfc, 0x61, 0x98, + 0xe6, 0xea, 0x2b, 0x46, 0xd5, 0x84, 0x13, 0x67, 0x3b, 0x99, 0xb0, 0x29, 0xd6, 0x65, + 0xc3, 0x57, 0x60, 0x1f, 0xb4, 0x26, 0xa0, 0xb2, 0xf4, 0xbb, 0xa2, 0x00, 0xee, 0x9f, + 0x0a, 0x43, 0xd1, 0x9b, 0x57, 0x1a, 0x9c, 0x71, 0xef, 0x11, 0x42, 0xe6, 0x5d, 0x5a, + 0x26, 0x6f, 0xdd, 0xca, 0x83, 0x2c, 0xe5, 0x9f, 0xaa, 0x7c, 0xac, 0x0b, 0x9c, 0xf1, + 0xbe, 0x2b, 0xff, 0xca, 0x30, 0x0d, 0x01, 0xee, 0x38, 0x76, 0x19, 0xc4, 0xae, 0x12, + 0xfd, 0x44, 0x38, 0xf2, 0x03, 0xa0, 0xe4, 0xe1, 0xc4, 0x7e, 0xc3, 0x14, 0x86, 0x1f, + 0x4e, 0x90, 0x87, 0xcb, 0x33, 0x39, 0x6a, 0x68, 0x73, 0xe8, 0xf9, 0xd2, 0x53, 0x9a, + 0x4b, 0x8e, + ]; + ro_mix(&mut input, 16); + assert_eq!(input, output); + } +} + +crate::basic_hash_tests!( + test1, Scrypt::default().cost(16), "", "77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906"; + test2, Scrypt::default().salt(b"NaCl").cost(1024).blocksize_factor(8).parallelism(16), "password", "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640"; +); diff --git a/src/hasher_panel/adler32_controls.rs b/src/hasher_panel/adler32_controls.rs index 35153bde..e0acb2b3 100644 --- a/src/hasher_panel/adler32_controls.rs +++ b/src/hasher_panel/adler32_controls.rs @@ -1,15 +1,18 @@ -use hashers::adler::Adler32; +use hashers::{adler::Adler32, errors::HasherError, traits::StatefulHasher}; +use utils::byte_formatting::ByteFormat; use super::HasherFrame; pub struct Adler32Frame { - hasher: Adler32, + input_format: ByteFormat, + output_format: ByteFormat, } impl Default for Adler32Frame { fn default() -> Self { Self { - hasher: Default::default(), + input_format: ByteFormat::Utf8, + output_format: ByteFormat::Hex, } } } @@ -29,5 +32,14 @@ impl HasherFrame for Adler32Frame { ui.add_space(16.0); } - crate::hash_string! {} + fn hash_string(&self, text: &str) -> Result { + let bytes = self + .input_format + .text_to_bytes(text) + .map_err(|_| hashers::errors::HasherError::general("byte format error"))?; + + let h = Adler32::init().hash(&bytes); + + Ok(self.output_format.byte_slice_to_text(&h)) + } } diff --git a/src/hasher_panel/pbkdf1_controls.rs b/src/hasher_panel/pbkdf1_controls.rs index 58a25307..f0387322 100644 --- a/src/hasher_panel/pbkdf1_controls.rs +++ b/src/hasher_panel/pbkdf1_controls.rs @@ -1,26 +1,69 @@ use super::HasherFrame; use crate::ui_elements::UiElements; use egui::DragValue; -use hashers::pbkdf1::{Pbkdf1, Pbkdf1Variant}; +use hashers::{ + pbkdf1::{Pbkdf1, Pbkdf1Variant}, + traits::StatefulHasher, +}; +use rand::{thread_rng, RngCore}; use strum::IntoEnumIterator; use utils::byte_formatting::ByteFormat; pub struct Pbkdf1Frame { - hasher: Pbkdf1, - salt: String, - valid_salt: bool, + input_format: ByteFormat, + output_format: ByteFormat, + variant: Pbkdf1Variant, + salt_string: String, + salt: Vec, + iterations: u32, + hash_len: u32, } impl Default for Pbkdf1Frame { fn default() -> Self { Self { - hasher: Default::default(), - salt: String::from("DEADBEEF"), - valid_salt: true, + input_format: ByteFormat::Utf8, + output_format: ByteFormat::Hex, + variant: Pbkdf1Variant::Md5, + salt_string: String::from("DEADBEEF"), + salt: b"DEADBEEF".to_vec(), + iterations: 4096, + hash_len: 64, } } } +impl Pbkdf1Frame { + fn validate_salt(&mut self) { + self.salt_string = self + .salt_string + .chars() + .filter(|c| c.is_ascii_hexdigit()) + .take(16) + .collect(); + if self.salt_string.len() % 2 != 0 { + self.salt_string.insert(0, '0'); + } + self.salt = ByteFormat::Hex + .text_to_bytes(&self.salt_string) + .expect("unable to parse salt input"); + } + + fn salt_control(&mut self, ui: &mut egui::Ui) { + ui.horizontal(|ui| { + if ui.control_string(&mut self.salt_string).lost_focus() { + self.validate_salt(); + }; + if ui.button("🎲").on_hover_text("randomize").clicked() { + let mut rng = thread_rng(); + self.salt = vec![0; 8]; + rng.fill_bytes(&mut self.salt); + self.salt_string = ByteFormat::Hex.byte_slice_to_text(&self.salt) + }; + }); + } +} + impl HasherFrame for Pbkdf1Frame { fn ui(&mut self, ui: &mut egui::Ui, _errors: &mut String) { ui.hyperlink_to( @@ -28,40 +71,40 @@ impl HasherFrame for Pbkdf1Frame { "https://github.com/SymmetricChaos/crypto-gui/blob/master/hashers/src/pbkdf1.rs", ); - ui.byte_io_mode_hasher( - &mut self.hasher.input_format, - &mut self.hasher.output_format, - ); + ui.add_space(8.0); + ui.byte_io_mode_hasher(&mut self.input_format, &mut self.output_format); + ui.add_space(8.0); ui.subheading("Select Inner Hasher"); ui.horizontal(|ui| { for variant in Pbkdf1Variant::iter() { - ui.selectable_value(&mut self.hasher.variant, variant, variant.to_string()); + ui.selectable_value(&mut self.variant, variant, variant.to_string()); } }); - ui.subheading("Select Number of Iterations"); - ui.add(DragValue::new(&mut self.hasher.iterations).range(1..=32768)); + ui.add_space(8.0); + ui.subheading("Number of Iterations"); + ui.add(DragValue::new(&mut self.iterations).range(1..=32768)); + ui.add_space(8.0); + ui.subheading("Output Length (Bytes)"); + ui.add(DragValue::new(&mut self.hash_len).range(4..=512)); + + ui.add_space(8.0); ui.horizontal(|ui| { - ui.subheading("Provide Salt (Hexadecimal)"); - if !self.valid_salt { - ui.error_text("invalid salt"); - } + ui.subheading("Salt (Hexadecimal)"); }); - if ui.control_string(&mut self.salt).changed() { - match self.hasher.set_salt_from_str(ByteFormat::Hex, &self.salt) { - Ok(_) => self.valid_salt = true, - Err(_) => { - self.valid_salt = false; - self.hasher.salt = [0; 8]; - } - } - } - - ui.subheading("Output Length (Bytes)"); - ui.add(DragValue::new(&mut self.hasher.hash_len).range(4..=512)); + self.salt_control(ui); } - crate::hash_string! {} + fn hash_string(&self, text: &str) -> Result { + let bytes = self + .input_format + .text_to_bytes(text) + .map_err(|_| hashers::errors::HasherError::general("byte format error"))?; + + let h = Pbkdf1::init(self.variant, self.iterations, self.hash_len, &self.salt).hash(&bytes); + + Ok(self.output_format.byte_slice_to_text(&h)) + } } diff --git a/src/hasher_panel/pbkdf2_controls.rs b/src/hasher_panel/pbkdf2_controls.rs index c1decfae..9a904c5c 100644 --- a/src/hasher_panel/pbkdf2_controls.rs +++ b/src/hasher_panel/pbkdf2_controls.rs @@ -1,6 +1,6 @@ use egui::DragValue; -use hashers::{hmac::HmacVariant, pbkdf2::Pbkdf2}; -use strum::IntoEnumIterator; +use hashers::{hmac::HmacVariant, pbkdf2::Pbkdf2, traits::StatefulHasher}; +use rand::{thread_rng, RngCore}; use utils::byte_formatting::ByteFormat; use crate::ui_elements::UiElements; @@ -8,21 +8,59 @@ use crate::ui_elements::UiElements; use super::HasherFrame; pub struct Pbkdf2Frame { - hasher: Pbkdf2, - salt: String, - valid_salt: bool, + input_format: ByteFormat, + output_format: ByteFormat, + variant: HmacVariant, + salt_string: String, + salt: Vec, + iterations: u32, + hash_len: u32, } impl Default for Pbkdf2Frame { fn default() -> Self { Self { - hasher: Default::default(), - salt: String::from("BEEF"), - valid_salt: true, + input_format: ByteFormat::Utf8, + output_format: ByteFormat::Hex, + variant: HmacVariant::Sha256, + salt_string: String::from("BEEF"), + salt: b"BEEF".to_vec(), + iterations: 4096, + hash_len: 64, } } } +impl Pbkdf2Frame { + fn validate_salt(&mut self) { + self.salt_string = self + .salt_string + .chars() + .filter(|c| c.is_ascii_hexdigit()) + .collect(); + if self.salt_string.len() % 2 != 0 { + self.salt_string.insert(0, '0'); + } + self.salt = ByteFormat::Hex + .text_to_bytes(&self.salt_string) + .expect("unable to parse salt input"); + } + + fn salt_control(&mut self, ui: &mut egui::Ui) { + ui.horizontal(|ui| { + if ui.control_string(&mut self.salt_string).lost_focus() { + self.validate_salt(); + }; + if ui.button("🎲").on_hover_text("randomize").clicked() { + let mut rng = thread_rng(); + self.salt = vec![0; 32]; + rng.fill_bytes(&mut self.salt); + self.salt_string = ByteFormat::Hex.byte_slice_to_text(&self.salt) + }; + }); + } +} + impl HasherFrame for Pbkdf2Frame { fn ui(&mut self, ui: &mut egui::Ui, _errors: &mut String) { ui.hyperlink_to( @@ -30,39 +68,61 @@ impl HasherFrame for Pbkdf2Frame { "https://github.com/SymmetricChaos/crypto-gui/blob/master/hashers/src/pbkdf2.rs", ); - ui.byte_io_mode_hasher( - &mut self.hasher.input_format, - &mut self.hasher.output_format, - ); + ui.add_space(8.0); + ui.byte_io_mode_hasher(&mut self.input_format, &mut self.output_format); + ui.add_space(8.0); ui.subheading("Select Inner HMAC"); ui.horizontal(|ui| { - for variant in HmacVariant::iter() { - ui.selectable_value(&mut self.hasher.variant, variant, variant.to_string()); - } + ui.selectable_value(&mut self.variant, HmacVariant::Md4, "MD4"); + ui.selectable_value(&mut self.variant, HmacVariant::Md5, "MD5"); + }); + ui.horizontal(|ui| { + ui.selectable_value(&mut self.variant, HmacVariant::Sha0, "SHA0"); + ui.selectable_value(&mut self.variant, HmacVariant::Sha1, "SHA1"); + }); + ui.horizontal(|ui| { + ui.selectable_value(&mut self.variant, HmacVariant::Sha224, "SHA224"); + ui.selectable_value(&mut self.variant, HmacVariant::Sha256, "SHA256"); + ui.selectable_value(&mut self.variant, HmacVariant::Sha384, "SHA384"); + ui.selectable_value(&mut self.variant, HmacVariant::Sha512, "SHA512"); }); + ui.horizontal(|ui| { + ui.selectable_value(&mut self.variant, HmacVariant::Sha512_224, "SHA512-224"); + ui.selectable_value(&mut self.variant, HmacVariant::Sha512_256, "SHA512-256"); + }); + ui.horizontal(|ui| { + ui.selectable_value(&mut self.variant, HmacVariant::Sha3_224, "SHA3-224"); + ui.selectable_value(&mut self.variant, HmacVariant::Sha3_256, "SHA3-256"); + ui.selectable_value(&mut self.variant, HmacVariant::Sha3_384, "SHA3-384"); + ui.selectable_value(&mut self.variant, HmacVariant::Sha3_512, "SHA3-512"); + }); + + ui.add_space(8.0); + ui.subheading("Number of Iterations"); + ui.add(DragValue::new(&mut self.iterations).range(1..=32768)); - ui.subheading("Select Number of Iterations"); - ui.add(DragValue::new(&mut self.hasher.iterations).range(1..=32768)); + ui.add_space(8.0); + ui.subheading("Output Length (Bytes)"); + ui.add(DragValue::new(&mut self.hash_len).range(4..=512)); + ui.add_space(8.0); ui.horizontal(|ui| { - ui.subheading("Provide Salt (Hexadecimal)"); - if !self.valid_salt { - ui.error_text("invalid salt"); - } + ui.subheading("Salt (Hexadecimal)"); }); - if ui.control_string(&mut self.salt).changed() { - if let Ok(bytes) = ByteFormat::Hex.text_to_bytes(&self.salt) { - self.valid_salt = true; - self.hasher.salt = bytes; - } else { - self.valid_salt = false; - self.hasher.salt.clear(); - } - } + self.salt_control(ui); - ui.subheading("Output Length (Bytes)"); - ui.add(DragValue::new(&mut self.hasher.hash_len).range(4..=512)); + ui.add_space(16.0); + } + + fn hash_string(&self, text: &str) -> Result { + let bytes = self + .input_format + .text_to_bytes(text) + .map_err(|_| hashers::errors::HasherError::general("byte format error"))?; + + let h = Pbkdf2::init(self.variant, self.iterations, self.hash_len, &self.salt).hash(&bytes); + + Ok(self.output_format.byte_slice_to_text(&h)) } - crate::hash_string! {} }