centralize Argon2 setup

This commit is contained in:
2026-02-28 00:23:15 +01:00
parent b6a211bbcf
commit f754e47221
3 changed files with 24 additions and 20 deletions

View File

@@ -1,4 +1,4 @@
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier, password_hash::SaltString};
use argon2::{PasswordHash, PasswordHasher, PasswordVerifier, password_hash::SaltString};
use axum::{
http::{
HeaderMap, StatusCode,
@@ -15,8 +15,9 @@ use crate::{
users::{
User,
auth::{
AuthError, COOKIE_NAME, SessionAuthRequired, SessionAuthenticate, TokenSize,
UserAuthDummyData, UserAuthRequired, UserAuthenticate, UserPasswordHashing,
AuthError, COOKIE_NAME, DUMMY_PASSWORD, DUMMY_PASSWORD_PHC, SHARED_ARGON,
SessionAuthRequired, SessionAuthenticate, TokenSize, UserAuthRequired,
UserAuthenticate, UserPasswordHashing,
},
sessions::Session,
},
@@ -75,29 +76,18 @@ impl SessionAuthRequired for Option<Session> {
impl UserPasswordHashing for User {
fn hash_password(passw: &str) -> Result<String, argon2::password_hash::Error> {
use rand08::rngs::OsRng as ArgonOsRng;
let argon = Argon2::default();
let passw = passw.as_bytes();
let salt = SaltString::generate(&mut ArgonOsRng);
Ok(argon.hash_password(passw, &salt)?.to_string())
Ok(SHARED_ARGON.hash_password(passw, &salt)?.to_string())
}
fn match_hash_password(passw: &str, hash: &str) -> Result<bool, argon2::password_hash::Error> {
let argon = Argon2::default();
let passw = passw.as_bytes();
let hash = PasswordHash::try_from(hash)?;
Ok(argon.verify_password(passw, &hash).is_ok())
Ok(SHARED_ARGON.verify_password(passw, &hash).is_ok())
}
}
// TODO: generate these at startup using predefined Argon2 params if
// these ever change from ::Default - the PHC must have the same factors as real hashes.
impl UserAuthDummyData for User {
/// This PHC generated for b"password"
const DUMMY_PASSWORD_PHC: &str = "$argon2id$v=19$m=19456,t=2,p=1$PXcTKpFhLRB70fVF35XYDQ$QOW2IxdPUvqD38+ScqX5SgO+jwweaMO9DUGqmkTeofQ";
/// Different than the input password of the PHC
const DUMMY_PASSWORD: &str = "different_password";
}
impl From<argon2::password_hash::Error> for AuthError {
fn from(err: argon2::password_hash::Error) -> Self {
AuthError::PassHashError(err)
@@ -213,7 +203,7 @@ pub fn authenticate_via_credentials(
false => Err(AuthError::InvalidCredentials),
},
_ => {
let _ = User::match_hash_password(User::DUMMY_PASSWORD, User::DUMMY_PASSWORD_PHC)?;
let _ = User::match_hash_password(DUMMY_PASSWORD, &*DUMMY_PASSWORD_PHC)?;
Err(AuthError::InvalidCredentials)
}
}

View File

@@ -1,3 +1,6 @@
use std::sync::LazyLock;
use argon2::{Argon2, PasswordHasher, password_hash::SaltString};
use axum::http::HeaderMap;
use rand08::{RngCore, rngs::OsRng};
@@ -29,9 +32,19 @@ pub trait UserPasswordHashing {
/// Returns whether the password matches the hash
fn match_hash_password(passw: &str, hash: &str) -> Result<bool, argon2::password_hash::Error>;
}
pub trait UserAuthDummyData {
const DUMMY_PASSWORD_PHC: &str;
const DUMMY_PASSWORD: &str;
pub static SHARED_ARGON: LazyLock<Argon2> = LazyLock::new(|| Argon2::default());
pub const DUMMY_PASSWORD: &str = "PASSWORD";
pub static DUMMY_PASSWORD_PHC: LazyLock<String> = LazyLock::new(|| {
let salt = SaltString::generate(&mut OsRng);
SHARED_ARGON
.hash_password(DUMMY_PASSWORD.as_bytes(), &salt)
.unwrap()
.to_string()
});
pub fn init_password_dummies() {
let _ = &*DUMMY_PASSWORD_PHC;
println!("Password hashing setup finished");
}
#[derive(thiserror::Error, Debug)]