From f149374e2c6682ea5b9b1d692b001d6ab5faea4a Mon Sep 17 00:00:00 2001 From: mrw1593 Date: Sun, 19 Mar 2023 12:24:28 -0400 Subject: Implement password hashing --- src/services/crypto.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/services/crypto.rs (limited to 'src/services/crypto.rs') diff --git a/src/services/crypto.rs b/src/services/crypto.rs new file mode 100644 index 0000000..11a5149 --- /dev/null +++ b/src/services/crypto.rs @@ -0,0 +1,80 @@ +use std::hash::Hash; + +use argon2::{hash_raw, verify_raw}; +use exun::RawUnexpected; + +/// A custom pepper used to hide passwords +static PEPPER: [u8; 16] = [ + 0x98, 0x7f, 0x6f, 0xce, 0x20, 0x76, 0x2c, 0x8a, 0xae, 0xf6, 0xee, 0x45, 0xb3, 0x6b, 0x1f, 0x69, +]; + +/// The configuration used for hashing and verifying passwords +static CONFIG: argon2::Config<'_> = argon2::Config { + hash_length: 256, + lanes: 4, + mem_cost: 5333, + time_cost: 4, + secret: &PEPPER, + + ad: &[], + thread_mode: argon2::ThreadMode::Sequential, + variant: argon2::Variant::Argon2i, + version: argon2::Version::Version13, +}; + +/// A password hash and salt for a user +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PasswordHash { + hash: Box<[u8]>, + salt: Box<[u8]>, +} + +impl Hash for PasswordHash { + fn hash(&self, state: &mut H) { + for byte in self.hash.iter() { + state.write_u8(*byte) + } + } +} + +impl PasswordHash { + /// Hash a password using Argon2 + pub fn new(password: &str) -> Result { + let password = password.as_bytes(); + + let salt: [u8; 16] = rand::random(); + let salt = Box::from(salt); + + let hash = hash_raw(password, &salt, &CONFIG)?.into_boxed_slice(); + + Ok(Self { hash, salt }) + } + + /// Create this structure from a given hash and salt + pub fn from_hash_salt(hash: &[u8], salt: &[u8]) -> Self { + Self { + hash: Box::from(hash), + salt: Box::from(salt), + } + } + + /// Get the password hash + pub fn hash(&self) -> &[u8] { + &self.hash + } + + /// Get the salt used for the hash + pub fn salt(&self) -> &[u8] { + &self.salt + } + + /// Check if the given password is the one that was hashed + pub fn check_password(&self, password: &str) -> Result { + Ok(verify_raw( + password.as_bytes(), + &self.salt, + &self.hash, + &CONFIG, + )?) + } +} -- cgit v1.2.3