Skip to content

Commit

Permalink
feat(hsh): decoupling argon2i, bcrypt and scrypt
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastienrousseau committed Sep 10, 2023
1 parent 5db2d05 commit 48f3b99
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 66 deletions.
98 changes: 62 additions & 36 deletions examples/hsh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,83 @@
//! Using the Hash (HSH) library

use std::str::FromStr;
use hsh::{models::{hash_algorithm::HashAlgorithm, hash::Hash}, new_hash};
use hsh::{models::{hash::Hash, hash_algorithm::HashAlgorithm}, new_hash};

fn create_new_hash(password: &str, salt: &str, algorithm: &str) -> Result<Hash, String> {
let hash = Hash::new(password, salt, algorithm)?;
println!("Debug: Salt used for new hash: {:?}", String::from_utf8_lossy(hash.salt()));
Ok(hash)
}

// Creating and verifying hashes
fn create_and_verify_hash() {
// Creates a hash with a password and salt using `Hash::new`.
let mut hash =
Hash::new("password", "salt1234", "argon2i").unwrap();
let hash_argon2i = create_new_hash("password", "salt1234", "argon2i").unwrap();
let hash_bcrypt = create_new_hash("password", "salt1234", "bcrypt").unwrap();
let hash_scrypt = create_new_hash("password", "salt1234", "scrypt").unwrap();

// Verify the newly created hashes
verify_password(&hash_argon2i, "password", "Argon2i");
verify_password(&hash_bcrypt, "password", "BCrypt");
verify_password(&hash_scrypt, "password", "Scrypt");

let mut new_hash_argon2i = hash_argon2i.clone();
new_hash_argon2i.set_password("new_password", "salt1234", "argon2i").unwrap();

// Sets a new password, salt, and algorithm for the hash using `Hash::set_password`.
hash.set_password("new_password", "new_salt1234", "argon2i")
.unwrap();
let mut new_hash_bcrypt = hash_bcrypt.clone();
new_hash_bcrypt.set_password("new_password", "salt1234", "bcrypt").unwrap();

// Verifies a password against the stored hash using `Hash::verify`.
let is_valid = hash.verify("new_password");
let mut new_hash_scrypt = hash_scrypt.clone();
new_hash_scrypt.set_password("new_password", "salt1234", "scrypt").unwrap();

// Verify the updated hashes
verify_password(&new_hash_argon2i, "new_password", "Argon2i");
verify_password(&new_hash_bcrypt, "new_password", "BCrypt");
verify_password(&new_hash_scrypt, "new_password", "Scrypt");
}

fn verify_password(hash: &Hash, password: &str, algorithm: &str) {
let is_valid = hash.verify(password);
match is_valid {
Ok(valid) => {
println!("🦀 Password verification result: ✅ {:>5}", valid)
}
println!("🦀 Password verification result for {}: ✅ {:?}", algorithm, valid);
},
Err(e) => {
eprintln!("🦀 Error during password verification: ❌ {}", e)
eprintln!("🦀 Error during password verification for {}: ❌ {}", algorithm, e);
}
}
}

// Parsing and displaying hashes
fn parse_and_display_hash() {
// Parses a hash algorithm from a string using `HashAlgorithm::from_str`.
let parsed_hash_algorithm =
HashAlgorithm::from_str("argon2i").unwrap();
println!(
"🦀 Parsed hash algorithm: ✅ {}",
parsed_hash_algorithm
);

// Creates a new hash using the `new_hash!` macro.
let hash = new_hash!("password", "salt12345", "argon2i");

// Converts the hash to a string manually.
let hash_string = match hash {
Ok(hash) => format!(
"🦀 Hash to a string: ✅ {}",
hash.to_string_representation()
),
Err(err) => format!(
"🦀 Hash to a string: ❌ Error: {}",
err
),
let parsed_argon2i = HashAlgorithm::from_str("argon2i").unwrap();
let parsed_bcrypt = HashAlgorithm::from_str("bcrypt").unwrap();
let parsed_scrypt = HashAlgorithm::from_str("scrypt").unwrap();

println!("🦀 Parsed Argon2i hash algorithm: {}", parsed_argon2i);
println!("🦀 Parsed Bcrypt hash algorithm: {}", parsed_bcrypt);
println!("🦀 Parsed Scrypt hash algorithm: {}", parsed_scrypt);

let argon2i_hash = new_hash!("password", "salt12345", "argon2i");
let bcrypt_hash = new_hash!("password", "salt12345", "bcrypt");
let scrypt_hash = new_hash!("password", "salt12345", "scrypt");

let argon2i_hash_string = match argon2i_hash {
Ok(hash) => hash.to_string_representation(),
Err(e) => format!("Error: {}", e),
};
let bcrypt_hash_string = match bcrypt_hash {
Ok(hash) => hash.to_string_representation(),
Err(e) => format!("Error: {}", e),
};
println!("{}", hash_string);
let scrypt_hash_string = match scrypt_hash {
Ok(hash) => hash.to_string_representation(),
Err(e) => format!("Error: {}", e),
};

println!("🦀 Argon2i Hash to a string: {}", argon2i_hash_string);
println!("🦀 Bcrypt Hash to a string: {}", bcrypt_hash_string);
println!("🦀 Scrypt Hash to a string: {}", scrypt_hash_string);
}


fn main() {
create_and_verify_hash();
parse_and_display_hash();
Expand Down
37 changes: 37 additions & 0 deletions src/algorithms/argon2i.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright © 2023 Hash (HSH) library. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::models::hash_algorithm::HashingAlgorithm;
use argon2rs::argon2i_simple;
use serde::{Serialize, Deserialize};

/// Implementation of the Argon2i hashing algorithm.
///
/// `Argon2i` is a struct that represents the Argon2i hashing algorithm,
/// which is a memory-hard algorithm resistant to GPU-based attacks and side-channel attacks.
/// It is one of the multiple hashing algorithms that can be used for password hashing in this library.
///
/// This struct implements the `HashingAlgorithm` trait, providing a concrete implementation
/// for hashing passwords using the Argon2i algorithm.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Argon2i;

impl HashingAlgorithm for Argon2i {
/// Hashes a given password using Argon2i algorithm.
///
/// Given a plaintext `password` and a `salt`, this method returns a hashed representation
/// of the password using Argon2i algorithm.
///
/// # Parameters
///
/// - `password`: The plaintext password to be hashed.
/// - `salt`: A cryptographic salt to prevent rainbow table attacks.
///
/// # Returns
///
/// Returns a `Result` containing the hashed password as a vector of bytes.
/// If hashing fails for some reason, returns a `String` detailing the error.
fn hash_password(password: &str, salt: &str) -> Result<Vec<u8>, String> {
Ok(argon2i_simple(password, salt).into_iter().collect())
}
}
55 changes: 55 additions & 0 deletions src/algorithms/bcrypt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright © 2023 Hash (HSH) library. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::models::hash_algorithm::HashingAlgorithm;
use bcrypt::{hash, DEFAULT_COST};
use serde::{Serialize, Deserialize};

/// Implementation of the Bcrypt hashing algorithm.
///
/// `Bcrypt` is a struct that represents the Bcrypt hashing algorithm,
/// which is based on the Blowfish cipher and is particularly effective against brute-force attacks.
///
/// This struct implements the `HashingAlgorithm` trait, providing a concrete implementation
/// for hashing passwords using the Bcrypt algorithm.
///
/// # Features
///
/// - Computationally intensive, making brute-force attacks more difficult.
/// - Uses key stretching to make pre-computed attacks (like rainbow tables) less effective.
///
/// # Examples
///
/// ```
/// use hsh::models::hash_algorithm::HashingAlgorithm;
/// use hsh::algorithms::bcrypt::Bcrypt;
///
/// let password = "supersecret";
/// let salt = "randomsalt";
///
/// let hashed_password = Bcrypt::hash_password(password, salt).unwrap();
/// ```
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Bcrypt;

impl HashingAlgorithm for Bcrypt {
/// Hashes a given password using the Bcrypt algorithm.
///
/// Given a plaintext `password` and a `salt`, this method returns a hashed representation
/// of the password using the Bcrypt algorithm.
///
/// # Parameters
///
/// - `password`: The plaintext password to be hashed.
/// - `salt`: A cryptographic salt to prevent rainbow table attacks.
///
/// # Returns
///
/// Returns a `Result` containing the hashed password as a vector of bytes.
/// If hashing fails for some reason, returns a `String` detailing the error.
fn hash_password(password: &str, _salt: &str) -> Result<Vec<u8>, String> {
hash(password, DEFAULT_COST)
.map_err(|e| e.to_string())
.map(|hash_parts| hash_parts.into_bytes())
}
}
11 changes: 11 additions & 0 deletions src/algorithms/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright © 2023 Hash (HSH) library. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT

/// The `argon2i` module contains the Argon2i password hashing algorithm.
pub mod argon2i;

/// The `bcrypt` module contains the Bcrypt password hashing algorithm.
pub mod bcrypt;

/// The `scrypt` module contains the Scrypt password hashing algorithm.
pub mod scrypt;
47 changes: 47 additions & 0 deletions src/algorithms/scrypt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright © 2023 Hash (HSH) library. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::models::hash_algorithm::HashingAlgorithm;
use scrypt::scrypt;
use scrypt::Params;
use serde::{Serialize, Deserialize};

/// Implementation of the Scrypt hashing algorithm.
///
/// `Scrypt` is a struct that represents the Scrypt hashing algorithm,
/// which is a memory-hard algorithm designed to be computationally intensive,
/// thereby making it difficult to perform large-scale custom hardware attacks.
///
/// This struct implements the `HashingAlgorithm` trait, providing a concrete implementation
/// for hashing passwords using the Scrypt algorithm.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Scrypt;

impl HashingAlgorithm for Scrypt {
/// Hashes a given password using the Scrypt algorithm.
///
/// Given a plaintext `password` and a `salt`, this method returns a hashed representation
/// of the password using the Scrypt algorithm.
///
/// # Parameters
///
/// - `password`: The plaintext password to be hashed.
/// - `salt`: A cryptographic salt to prevent rainbow table attacks.
///
/// # Returns
///
/// Returns a `Result` containing the hashed password as a vector of bytes.
/// If hashing fails for some reason, it returns a `String` detailing the error.
fn hash_password(password: &str, salt: &str) -> Result<Vec<u8>, String> {
let params = Params::new(14, 8, 1, 64).map_err(|e| e.to_string())?;
let mut output = [0u8; 64];
scrypt(
password.as_bytes(),
salt.as_bytes(),
&params,
&mut output,
)
.map_err(|e| e.to_string())
.map(|_| output.to_vec())
}
}
Loading

0 comments on commit 48f3b99

Please sign in to comment.