Created
December 15, 2021 16:43
-
-
Save Gabriel-Paulucci/47d4b5b1ff8f3975939fb91f1acc3c3c to your computer and use it in GitHub Desktop.
Hash Password and Salt in Rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::fmt::Display; | |
use chrono::Utc; | |
use sha3::Sha3_512; | |
use uuid::Uuid; | |
use crate::errors::HashError; | |
struct HashedElement(Box<[u8]>); | |
impl HashedElement { | |
fn hash(element: &str) -> Self { | |
let element_to_bytes = element.as_bytes(); | |
Self::hash_raw(element_to_bytes) | |
} | |
fn hash_raw(element: &[u8]) -> Self { | |
let mut hasher = Sha3_512::new(); | |
hasher.update(element); | |
let hashed_content = hasher.finalize()[..].into(); | |
Self(hashed_content) | |
} | |
fn hash_from_base(base64: &str) -> Result<Self, HashError> { | |
let data = base64::decode(base64).map_err(|_| HashError::InvalidBase64)?; | |
Ok(Self(data.into())) | |
} | |
fn append_hash(&self, more_hash: &Self) -> Self { | |
let arrs = [&*self.0, &*more_hash.0].concat(); | |
Self::hash_raw(&*arrs) | |
} | |
} | |
impl Display for HashedElement { | |
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
write!(f, "{}", base64::encode(&*self.0)) | |
} | |
} | |
#[derive(PartialEq, Eq, Debug)] | |
pub struct PasswordHasher { | |
salt: String, | |
password: String, | |
} | |
impl PasswordHasher { | |
pub fn new(extra_data: &str, password: &str) -> Self { | |
let extra_data_hash = HashedElement::hash(extra_data); | |
let password_hash = HashedElement::hash(password); | |
let content_hash = extra_data_hash.append_hash(&password_hash); | |
let uuid = HashedElement::hash(&Uuid::new_v4().to_string()); | |
let now = HashedElement::hash(&Utc::now().timestamp().to_string()); | |
let salt_hash = content_hash.append_hash(&uuid).append_hash(&now); | |
let password_hash = content_hash.append_hash(&salt_hash); | |
Self { | |
salt: salt_hash.to_string(), | |
password: password_hash.to_string(), | |
} | |
} | |
pub fn new_from_created_salt( | |
password: &str, | |
extra_data: &str, | |
salt: &str, | |
) -> Result<Self, HashError> { | |
let salt = HashedElement::hash_from_base(salt)?; | |
let password = HashedElement::hash(password); | |
let extra_data = HashedElement::hash(extra_data); | |
let password = extra_data.append_hash(&password); | |
let password = password.append_hash(&salt); | |
Ok(Self { | |
salt: salt.to_string(), | |
password: password.to_string(), | |
}) | |
} | |
pub fn load_password_hasher_info(pass_hashed: String, salt: String) -> Self { | |
Self { | |
password: pass_hashed, | |
salt, | |
} | |
} | |
pub fn get_password_hash(&self) -> &str { | |
&self.password | |
} | |
pub fn get_salt(&self) -> &str { | |
&self.salt | |
} | |
} | |
#[test] | |
fn check_password_creator_and_login() { | |
let username = "username"; | |
let password = "superpassword"; | |
let password_generated = PasswordHasher::new(username, password); | |
let generated_salt = password_generated.get_salt(); | |
let login_password_generated = | |
PasswordHasher::new_from_created_salt(password, username, generated_salt).unwrap(); | |
assert_eq!(password_generated, login_password_generated); | |
} | |
#[test] | |
fn check_wrong_password_creator_and_login() { | |
let username = "username"; | |
let password = "superpassword"; | |
let wrong_password = "notsuperpassword"; | |
let sing_up_password = PasswordHasher::new(username, password); | |
let pass_salt = sing_up_password.get_salt(); | |
let sing_in_password = | |
PasswordHasher::new_from_created_salt(wrong_password, username, pass_salt).unwrap(); | |
assert!(sing_up_password != sing_in_password); | |
} | |
#[test] | |
fn check_from_load() { | |
let username = "username"; | |
let password = "superpassword"; | |
let sing_up = PasswordHasher::new(username, password); | |
let hash_pass = sing_up.get_password_hash(); | |
let salt = sing_up.get_salt(); | |
let reconstructed = PasswordHasher::load_password_hasher_info(hash_pass.into(), salt.into()); | |
assert_eq!(sing_up, reconstructed); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Really helped me :3
Thanks!!