From cef4c2e1033607fe688c369ae2a12a2cc579c113 Mon Sep 17 00:00:00 2001 From: MiloTheFox Date: Sat, 7 Sep 2024 11:44:38 +0000 Subject: [PATCH] =?UTF-8?q?Dateien=20nach=20=E2=80=9Esrc=E2=80=9C=20hochla?= =?UTF-8?q?den?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/errors.rs | 24 +++++++++ src/main.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 src/errors.rs create mode 100644 src/main.rs diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..e3eebc4 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,24 @@ +use argon2::password_hash::{Error as PasswordHashError, SaltString}; +use thiserror::Error; + +#[derive(Debug)] +pub struct ArgonError(pub PasswordHashError); + +impl std::fmt::Display for ArgonError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for ArgonError {} + +#[derive(Error, Debug)] +pub enum MyError { + #[error("Error hashing password with salt {salt}: {source}")] + HashingError { + source: ArgonError, + salt: SaltString, + }, + #[error("Failed to generate password")] + PasswordGenerationError, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e1a8a04 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,136 @@ +use argon2::{password_hash::SaltString, Algorithm, Argon2, Params, PasswordHasher, Version}; +use colored::Colorize; +use passwords::{analyzer, scorer, PasswordGenerator}; +use rand_core::OsRng; +use rayon::prelude::*; +use zeroize::Zeroize; + +mod errors; +use errors::{ArgonError, MyError}; + +// Configuration Constants +const MEMORY_COST: u32 = 50; +const TIME_COST: u32 = 2; +const PARALLELISM: u32 = 2; +const OUTPUT_LEN: usize = 32; +const NUMBER_OF_PASSWORDS: u32 = 50; + +type PasswordWithScore = (String, f64); + +fn main() -> Result<(), MyError> { + let argon2 = create_argon2(); + + // Generate and hash passwords in parallel + let results: Result, _> = (0..NUMBER_OF_PASSWORDS) + .into_par_iter() + .map(|_| { + let password_gen = create_password_generator(); + let (mut password, _) = generate_password(&password_gen)?; + let salt = SaltString::generate(&mut OsRng); + let hash_result = hash_password(&argon2, &password, &salt); + password.zeroize(); // Zeroize the password to prevent memory-based attacks + hash_result + }) + .collect(); + + match results { + Ok(hashes) => { + hashes + .into_par_iter() + .for_each(|hash| println!("Hash output: {}", hash)); + println!( + "{}", + "[LOG] All passwords have been hashed successfully".green() + ); + Ok(()) + } + Err(e) => Err(e), + } +} + +fn create_argon2() -> Argon2<'static> { + let params = Params::new(MEMORY_COST, TIME_COST, PARALLELISM, Some(OUTPUT_LEN)) + .expect("Failed to set Argon2 parameters"); + Argon2::new(Algorithm::Argon2id, Version::V0x13, params) +} + +fn hash_password( + argon2: &Argon2<'_>, + password: &str, + salt: &SaltString, +) -> Result { + argon2 + .hash_password(password.as_bytes(), salt) + .map_err(|source| MyError::HashingError { + source: ArgonError::from(errors::ArgonError(source)), + salt: salt.clone(), + }) + .map(|hash| hash.to_string()) +} + +fn create_password_generator() -> PasswordGenerator { + PasswordGenerator { + length: 16, + numbers: true, + lowercase_letters: true, + uppercase_letters: true, + symbols: true, + spaces: false, + exclude_similar_characters: true, + strict: true, + } +} + +fn generate_password(password_gen: &PasswordGenerator) -> Result { + let password = password_gen + .generate_one() + .map_err(|_| MyError::PasswordGenerationError)?; + let analyzed = analyzer::analyze(&password); + let score = scorer::score(&analyzed); + Ok((password, score)) +} + +#[cfg(test)] +mod tests { + use super::*; + use lazy_static::lazy_static; + + lazy_static! { + static ref PASSWORDGENERATOR: PasswordGenerator = PasswordGenerator { + length: 16, + numbers: true, + lowercase_letters: true, + uppercase_letters: true, + symbols: true, + spaces: false, + exclude_similar_characters: true, + strict: true, + }; + } + + #[test] + fn test_generate_password() { + match generate_password(&PASSWORDGENERATOR) { + Ok((password, _score)) => { + assert!(password.len() == 16 && password.chars().all(|c| c.is_ascii_graphic())); + } + Err(e) => { + panic!("Password generation failed with error: {}", e); + } + } + } + + #[test] + fn test_hash_password() { + let (mut password, _score) = match generate_password(&PASSWORDGENERATOR) { + Ok(result) => result, + Err(e) => panic!("Password generation failed with error: {}", e), + }; + + let argon2 = create_argon2(); + let salt = SaltString::generate(&mut OsRng); + let result = argon2.hash_password(&password.as_bytes(), &salt); + assert!(result.is_ok()); + password.zeroize(); + } +}