diff --git a/Cargo.lock b/Cargo.lock index e72fd491..550e2e78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,16 +57,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] -name = "arrayref" -version = "0.3.7" +name = "argon2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] [[package]] name = "as_variant" @@ -345,14 +345,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] -name = "blake2b_simd" -version = "1.0.2" +name = "blake2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", + "digest", ] [[package]] @@ -498,12 +496,6 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - [[package]] name = "core-foundation" version = "0.9.4" @@ -852,6 +844,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" name = "grapevine" version = "0.1.0" dependencies = [ + "argon2", "async-trait", "axum 0.7.5", "axum-extra", @@ -887,7 +880,6 @@ dependencies = [ "ring", "ruma", "rusqlite", - "rust-argon2", "rust-rocksdb", "sd-notify", "serde", @@ -1750,6 +1742,17 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "pear" version = "0.2.9" @@ -2373,17 +2376,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "rust-argon2" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d9848531d60c9cbbcf9d166c885316c24bc0e2a9d3eba0956bb6cbbd79bc6e8" -dependencies = [ - "base64 0.21.7", - "blake2b_simd", - "constant_time_eq", -] - [[package]] name = "rust-librocksdb-sys" version = "0.22.0+9.2.1" diff --git a/Cargo.toml b/Cargo.toml index 24d7dbb1..3ab71e30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,7 @@ workspace = true # Keep sorted [dependencies] +argon2 = "0.5.3" async-trait = "0.1.80" axum = { version = "0.7.5", default-features = false, features = ["form", "http1", "http2", "json", "matched-path", "tracing"] } axum-extra = { version = "0.9.3", features = ["typed-header"] } @@ -122,7 +123,6 @@ ring = "0.17.8" rocksdb = { package = "rust-rocksdb", version = "0.26.0", features = ["lz4", "multi-threaded-cf", "zstd"], optional = true } ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "server-util", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] } rusqlite = { version = "0.31.0", optional = true, features = ["bundled"] } -rust-argon2 = "2.1.0" sd-notify = { version = "0.4.1", optional = true } serde = { version = "1.0.202", features = ["rc"] } serde_html_form = "0.2.6" diff --git a/src/api/client_server/session.rs b/src/api/client_server/session.rs index f18dd3b2..69e4cd9b 100644 --- a/src/api/client_server/session.rs +++ b/src/api/client_server/session.rs @@ -113,11 +113,7 @@ pub(crate) async fn login_route( )); } - let hash_matches = - argon2::verify_encoded(&hash, password.as_bytes()) - .unwrap_or(false); - - if !hash_matches { + if !utils::verify_password_hash(hash, password) { return Err(Error::BadRequest( ErrorKind::forbidden(), "Wrong username or password.", diff --git a/src/database.rs b/src/database.rs index 44d7b1c4..b53ab8a1 100644 --- a/src/database.rs +++ b/src/database.rs @@ -584,10 +584,9 @@ impl KeyValueDatabase { for (userid, password) in db.userid_password.iter() { let password = utils::string_from_bytes(&password); - let empty_hashed_password = - password.map_or(false, |password| { - argon2::verify_encoded(&password, b"") - .unwrap_or(false) + let empty_hashed_password = password + .map_or(false, |password| { + utils::verify_password_hash("", password) }); if empty_hashed_password { diff --git a/src/service/uiaa.rs b/src/service/uiaa.rs index 25c2bbe7..2a130896 100644 --- a/src/service/uiaa.rs +++ b/src/service/uiaa.rs @@ -87,11 +87,7 @@ impl Service { // Check if password is correct if let Some(hash) = services().users.password_hash(&user_id)? { - let hash_matches = - argon2::verify_encoded(&hash, password.as_bytes()) - .unwrap_or(false); - - if !hash_matches { + if !utils::verify_password_hash(hash, password) { uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { kind: ErrorKind::forbidden(), diff --git a/src/utils.rs b/src/utils.rs index 0c8418a8..9694595f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -7,9 +7,9 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; -use argon2::{Config, Variant}; +use argon2::{password_hash, Argon2, PasswordHasher, PasswordVerifier}; use cmp::Ordering; -use rand::prelude::*; +use rand::{prelude::*, rngs::OsRng}; use ring::digest; use ruma::{ canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject, @@ -72,16 +72,33 @@ pub(crate) fn random_string(length: usize) -> String { } /// Calculate a new hash for the given password -pub(crate) fn calculate_password_hash( - password: &str, -) -> Result { - let hashing_config = Config { - variant: Variant::Argon2id, - ..Default::default() +pub(crate) fn calculate_password_hash( + password: B, +) -> Result +where + B: AsRef<[u8]>, +{ + Argon2::default() + .hash_password( + password.as_ref(), + &password_hash::SaltString::generate(&mut OsRng), + ) + .map(|x| x.serialize()) +} + +/// Compare a password to a hash +/// +/// Returns `true` if the password matches the hash, `false` otherwise. +pub(crate) fn verify_password_hash(hash: S, password: B) -> bool +where + S: AsRef, + B: AsRef<[u8]>, +{ + let Ok(hash) = password_hash::PasswordHash::new(hash.as_ref()) else { + return false; }; - let salt = random_string(32); - argon2::hash_encoded(password.as_bytes(), salt.as_bytes(), &hashing_config) + Argon2::default().verify_password(password.as_ref(), &hash).is_ok() } #[tracing::instrument(skip(keys))]