From 52d470058a870e26754071ce58dc989f65ca8bf3 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Wed, 3 Jul 2024 00:44:00 +0000 Subject: [PATCH] split hash utils into directory Signed-off-by: Jason Volk --- src/core/utils/hash.rs | 67 ++++++---------------------------- src/core/utils/hash/argon.rs | 68 +++++++++++++++++++++++++++++++++++ src/core/utils/hash/sha256.rs | 9 +++++ src/core/utils/mod.rs | 9 +---- 4 files changed, 88 insertions(+), 65 deletions(-) create mode 100644 src/core/utils/hash/argon.rs create mode 100644 src/core/utils/hash/sha256.rs diff --git a/src/core/utils/hash.rs b/src/core/utils/hash.rs index d6efaff9..d1219bfe 100644 --- a/src/core/utils/hash.rs +++ b/src/core/utils/hash.rs @@ -1,62 +1,15 @@ -use std::sync::OnceLock; +mod argon; +mod sha256; -use argon2::{ - password_hash, password_hash::SaltString, Algorithm, Argon2, Params, PasswordHash, PasswordHasher, - PasswordVerifier, Version, -}; +use crate::Result; -const M_COST: u32 = Params::DEFAULT_M_COST; // memory size in 1 KiB blocks -const T_COST: u32 = Params::DEFAULT_T_COST; // nr of iterations -const P_COST: u32 = Params::DEFAULT_P_COST; // parallelism +#[inline] +pub fn password(password: &str) -> Result { argon::password(password) } -static ARGON: OnceLock> = OnceLock::new(); - -pub fn password(password: &str) -> Result { - let salt = SaltString::generate(rand::thread_rng()); - ARGON - .get_or_init(init_argon) - .hash_password(password.as_bytes(), &salt) - .map(|it| it.to_string()) +#[inline] +pub fn verify_password(password: &str, password_hash: &str) -> Result<()> { + argon::verify_password(password, password_hash) } -pub fn verify_password(password: &str, password_hash: &str) -> Result<(), password_hash::Error> { - let password_hash = PasswordHash::new(password_hash)?; - ARGON - .get_or_init(init_argon) - .verify_password(password.as_bytes(), &password_hash) -} - -fn init_argon() -> Argon2<'static> { - // 19456 Kib blocks, iterations = 2, parallelism = 1 - // * - debug_assert!(M_COST == 19_456, "M_COST default changed"); - debug_assert!(T_COST == 2, "T_COST default changed"); - debug_assert!(P_COST == 1, "P_COST default changed"); - - let algorithm = Algorithm::Argon2id; - let version = Version::default(); - let out_len: Option = None; - let params = Params::new(M_COST, T_COST, P_COST, out_len).expect("valid parameters"); - Argon2::new(algorithm, version, params) -} - -#[cfg(test)] -mod tests { - #[test] - fn password_hash_and_verify() { - use crate::utils::hash; - let preimage = "temp123"; - let digest = hash::password(preimage).expect("digest"); - hash::verify_password(preimage, &digest).expect("verified"); - } - - #[test] - #[should_panic(expected = "unverified")] - fn password_hash_and_verify_fail() { - use crate::utils::hash; - let preimage = "temp123"; - let fakeimage = "temp321"; - let digest = hash::password(preimage).expect("digest"); - hash::verify_password(fakeimage, &digest).expect("unverified"); - } -} +#[inline] +pub fn calculate_hash(keys: &[&[u8]]) -> Vec { sha256::hash(keys) } diff --git a/src/core/utils/hash/argon.rs b/src/core/utils/hash/argon.rs new file mode 100644 index 00000000..98cef00e --- /dev/null +++ b/src/core/utils/hash/argon.rs @@ -0,0 +1,68 @@ +use std::sync::OnceLock; + +use argon2::{ + password_hash, password_hash::SaltString, Algorithm, Argon2, Params, PasswordHash, PasswordHasher, + PasswordVerifier, Version, +}; + +use crate::{Error, Result}; + +const M_COST: u32 = Params::DEFAULT_M_COST; // memory size in 1 KiB blocks +const T_COST: u32 = Params::DEFAULT_T_COST; // nr of iterations +const P_COST: u32 = Params::DEFAULT_P_COST; // parallelism + +static ARGON: OnceLock> = OnceLock::new(); + +fn init_argon() -> Argon2<'static> { + // 19456 Kib blocks, iterations = 2, parallelism = 1 + // * + debug_assert!(M_COST == 19_456, "M_COST default changed"); + debug_assert!(T_COST == 2, "T_COST default changed"); + debug_assert!(P_COST == 1, "P_COST default changed"); + + let algorithm = Algorithm::Argon2id; + let version = Version::default(); + let out_len: Option = None; + let params = Params::new(M_COST, T_COST, P_COST, out_len).expect("valid parameters"); + Argon2::new(algorithm, version, params) +} + +pub(super) fn password(password: &str) -> Result { + let salt = SaltString::generate(rand::thread_rng()); + ARGON + .get_or_init(init_argon) + .hash_password(password.as_bytes(), &salt) + .map(|it| it.to_string()) + .map_err(map_err) +} + +pub(super) fn verify_password(password: &str, password_hash: &str) -> Result<()> { + let password_hash = PasswordHash::new(password_hash).map_err(map_err)?; + ARGON + .get_or_init(init_argon) + .verify_password(password.as_bytes(), &password_hash) + .map_err(map_err) +} + +fn map_err(e: password_hash::Error) -> Error { Error::Err(e.to_string()) } + +#[cfg(test)] +mod tests { + #[test] + fn password_hash_and_verify() { + use crate::utils::hash; + let preimage = "temp123"; + let digest = hash::password(preimage).expect("digest"); + hash::verify_password(preimage, &digest).expect("verified"); + } + + #[test] + #[should_panic(expected = "unverified")] + fn password_hash_and_verify_fail() { + use crate::utils::hash; + let preimage = "temp123"; + let fakeimage = "temp321"; + let digest = hash::password(preimage).expect("digest"); + hash::verify_password(fakeimage, &digest).expect("unverified"); + } +} diff --git a/src/core/utils/hash/sha256.rs b/src/core/utils/hash/sha256.rs new file mode 100644 index 00000000..6a1f1879 --- /dev/null +++ b/src/core/utils/hash/sha256.rs @@ -0,0 +1,9 @@ +use ring::{digest, digest::SHA256}; + +#[tracing::instrument(skip_all)] +pub(super) fn hash(keys: &[&[u8]]) -> Vec { + // We only hash the pdu's event ids, not the whole pdu + let bytes = keys.join(&0xFF); + let hash = digest::digest(&SHA256, &bytes); + hash.as_ref().to_owned() +} diff --git a/src/core/utils/mod.rs b/src/core/utils/mod.rs index bbaaf2ec..721ed977 100644 --- a/src/core/utils/mod.rs +++ b/src/core/utils/mod.rs @@ -14,11 +14,11 @@ use std::{ }; pub use debug::slice_truncated as debug_slice_truncated; +pub use hash::calculate_hash; pub use html::Escape as HtmlEscape; pub use json::{deserialize_from_str, to_canonical_object}; pub use mutex_map::MutexMap; use rand::prelude::*; -use ring::digest; use ruma::UserId; pub use sys::available_parallelism; @@ -84,13 +84,6 @@ pub fn random_string(length: usize) -> String { .collect() } -#[tracing::instrument(skip(keys))] -pub fn calculate_hash(keys: &[&[u8]]) -> Vec { - // We only hash the pdu's event ids, not the whole pdu - let bytes = keys.join(&0xFF); - let hash = digest::digest(&digest::SHA256, &bytes); - hash.as_ref().to_owned() -} #[allow(clippy::impl_trait_in_params)] pub fn common_elements(