Abstract password hashing into util.
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
282c2feca8
commit
732e8b82aa
13 changed files with 92 additions and 64 deletions
|
@ -35,7 +35,6 @@ sha256_media = [
|
|||
]
|
||||
|
||||
[dependencies]
|
||||
argon2.workspace = true
|
||||
async-trait.workspace = true
|
||||
base64.workspace = true
|
||||
bytes.workspace = true
|
||||
|
|
|
@ -6,10 +6,8 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use argon2::{password_hash::SaltString, PasswordHasher, PasswordVerifier};
|
||||
use database::KeyValueDatabase;
|
||||
use itertools::Itertools;
|
||||
use rand::thread_rng;
|
||||
use ruma::{
|
||||
events::{push_rules::PushRulesEvent, GlobalAccountDataEventType},
|
||||
push::Ruleset,
|
||||
|
@ -67,18 +65,9 @@ pub(crate) async fn migrations(db: &KeyValueDatabase, config: &Config) -> Result
|
|||
if services().globals.database_version()? < 2 {
|
||||
// We accidentally inserted hashed versions of "" into the db instead of just ""
|
||||
for (userid, password) in db.userid_password.iter() {
|
||||
let salt = SaltString::generate(thread_rng());
|
||||
let empty_pass = services()
|
||||
.globals
|
||||
.argon
|
||||
.hash_password(b"", &salt)
|
||||
.expect("our own password to be properly hashed");
|
||||
let empty_hashed_password = services()
|
||||
.globals
|
||||
.argon
|
||||
.verify_password(&password, &empty_pass)
|
||||
.is_ok();
|
||||
|
||||
let empty_pass = utils::hash::password("").expect("our own password to be properly hashed");
|
||||
let password = std::str::from_utf8(&password).expect("password is valid utf-8");
|
||||
let empty_hashed_password = utils::hash::verify_password(password, &empty_pass).is_ok();
|
||||
if empty_hashed_password {
|
||||
db.userid_password.insert(&userid, b"")?;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ use std::{
|
|||
time::Instant,
|
||||
};
|
||||
|
||||
use argon2::Argon2;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use data::Data;
|
||||
use hickory_resolver::TokioAsyncResolver;
|
||||
|
@ -61,7 +60,6 @@ pub struct Service {
|
|||
pub updates_handle: Mutex<Option<JoinHandle<()>>>,
|
||||
pub stateres_mutex: Arc<Mutex<()>>,
|
||||
pub rotate: RotationHandler,
|
||||
pub argon: Argon2<'static>,
|
||||
}
|
||||
|
||||
/// Handles "rotation" of long-polling requests. "Rotation" in this context is
|
||||
|
@ -125,13 +123,6 @@ impl Service {
|
|||
// Experimental, partially supported room versions
|
||||
let unstable_room_versions = vec![RoomVersionId::V2, RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5];
|
||||
|
||||
// 19456 Kib blocks, iterations = 2, parallelism = 1 for more info https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
|
||||
let argon = Argon2::new(
|
||||
argon2::Algorithm::Argon2id,
|
||||
argon2::Version::default(),
|
||||
argon2::Params::new(19456, 2, 1, None).expect("valid parameters"),
|
||||
);
|
||||
|
||||
let mut cidr_range_denylist = Vec::new();
|
||||
for cidr in config.ip_range_denylist.clone() {
|
||||
let cidr = IPAddress::parse(cidr).expect("valid cidr range");
|
||||
|
@ -159,7 +150,6 @@ impl Service {
|
|||
updates_handle: Mutex::new(None),
|
||||
stateres_mutex: Arc::new(Mutex::new(())),
|
||||
rotate: RotationHandler::new(),
|
||||
argon,
|
||||
};
|
||||
|
||||
fs::create_dir_all(s.get_media_folder())?;
|
||||
|
|
|
@ -2,8 +2,7 @@ mod data;
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use argon2::{PasswordHash, PasswordVerifier};
|
||||
use conduit::{utils, Error, Result};
|
||||
use conduit::{utils, utils::hash, Error, Result};
|
||||
use data::Data;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
|
@ -70,15 +69,7 @@ impl Service {
|
|||
|
||||
// Check if password is correct
|
||||
if let Some(hash) = services().users.password_hash(&user_id)? {
|
||||
let hash_matches = services()
|
||||
.globals
|
||||
.argon
|
||||
.verify_password(
|
||||
password.as_bytes(),
|
||||
&PasswordHash::new(&hash).expect("valid hash in database"),
|
||||
)
|
||||
.is_ok();
|
||||
|
||||
let hash_matches = hash::verify_password(password, &hash).is_ok();
|
||||
if !hash_matches {
|
||||
uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody {
|
||||
kind: ErrorKind::forbidden(),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::{collections::BTreeMap, mem::size_of};
|
||||
|
||||
use argon2::{password_hash::SaltString, PasswordHasher};
|
||||
use ruma::{
|
||||
api::client::{device::Device, error::ErrorKind, filter::FilterDefinition},
|
||||
encryption::{CrossSigningKey, DeviceKeys, OneTimeKey},
|
||||
|
@ -227,7 +226,7 @@ impl Data for KeyValueDatabase {
|
|||
/// Hash and set the user's password to the Argon2 hash
|
||||
fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
|
||||
if let Some(password) = password {
|
||||
if let Ok(hash) = calculate_password_hash(password) {
|
||||
if let Ok(hash) = utils::hash::password(password) {
|
||||
self.userid_password
|
||||
.insert(user_id.as_bytes(), hash.as_bytes())?;
|
||||
Ok(())
|
||||
|
@ -1021,13 +1020,3 @@ fn get_username_with_valid_password(username: &[u8], password: &[u8]) -> Option<
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate a new hash for the given password
|
||||
fn calculate_password_hash(password: &str) -> Result<String, argon2::password_hash::Error> {
|
||||
let salt = SaltString::generate(rand::thread_rng());
|
||||
services()
|
||||
.globals
|
||||
.argon
|
||||
.hash_password(password.as_bytes(), &salt)
|
||||
.map(|it| it.to_string())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue