feat: replaced flaky argon2 with better argon2 crate (#37)
* feat: replaced flaky argon2 with better argon2 crate * fix: applied cargo fmt nightly * docs: added comment specifying what the settings for Argon2 mean * fix: made hashing error a bit more descriptive * fix: fixed incorrect value for Kib
This commit is contained in:
parent
6a9f8dfa6f
commit
fdc3e07be6
9 changed files with 84 additions and 62 deletions
57
Cargo.lock
generated
57
Cargo.lock
generated
|
@ -56,16 +56,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
|
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "argon2"
|
||||||
version = "0.3.7"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
|
checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9"
|
||||||
|
dependencies = [
|
||||||
[[package]]
|
"base64ct",
|
||||||
name = "arrayvec"
|
"blake2",
|
||||||
version = "0.7.4"
|
"cpufeatures",
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
"password-hash",
|
||||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "as_variant"
|
name = "as_variant"
|
||||||
|
@ -257,14 +257,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2b_simd"
|
name = "blake2"
|
||||||
version = "1.0.2"
|
version = "0.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780"
|
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"digest",
|
||||||
"arrayvec",
|
|
||||||
"constant_time_eq",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -395,6 +393,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
name = "conduwuit"
|
name = "conduwuit"
|
||||||
version = "0.7.0-alpha+conduwuit-0.1.1"
|
version = "0.7.0-alpha+conduwuit-0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"argon2",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
"axum-server",
|
"axum-server",
|
||||||
|
@ -430,7 +429,6 @@ dependencies = [
|
||||||
"rocksdb",
|
"rocksdb",
|
||||||
"ruma",
|
"ruma",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"rust-argon2",
|
|
||||||
"sd-notify",
|
"sd-notify",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_html_form",
|
"serde_html_form",
|
||||||
|
@ -465,12 +463,6 @@ version = "0.2.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b"
|
checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "constant_time_eq"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
@ -1755,6 +1747,17 @@ dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
|
@ -2335,16 +2338,6 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rust-argon2"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "git+https://github.com/sru-systems/rust-argon2?rev=e6cb5bf99643e565f4f0d103960d655dac9f3097#e6cb5bf99643e565f4f0d103960d655dac9f3097"
|
|
||||||
dependencies = [
|
|
||||||
"base64",
|
|
||||||
"blake2b_simd",
|
|
||||||
"constant_time_eq",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
|
|
|
@ -56,7 +56,7 @@ serde = { version = "1.0.193", features = ["rc"] }
|
||||||
# Used for secure identifiers
|
# Used for secure identifiers
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
# Used to hash passwords
|
# Used to hash passwords
|
||||||
rust-argon2 = { git = "https://github.com/sru-systems/rust-argon2", rev = "e6cb5bf99643e565f4f0d103960d655dac9f3097" }
|
argon2 = "0.5"
|
||||||
reqwest = { version = "0.11.22", default-features = false, features = ["rustls-tls-native-roots", "socks"] }
|
reqwest = { version = "0.11.22", default-features = false, features = ["rustls-tls-native-roots", "socks"] }
|
||||||
# Used for conduit::Error type
|
# Used for conduit::Error type
|
||||||
thiserror = "1.0.51"
|
thiserror = "1.0.51"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||||
use crate::{services, utils, Error, Result, Ruma};
|
use crate::{services, utils, Error, Result, Ruma};
|
||||||
|
use argon2::{PasswordHash, PasswordVerifier};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
|
@ -9,7 +10,7 @@ use ruma::{
|
||||||
UserId,
|
UserId,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tracing::{info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
|
@ -74,9 +75,15 @@ pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Re
|
||||||
"The user has been deactivated",
|
"The user has been deactivated",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
let Ok(parsed_hash) = PasswordHash::new(&hash) else {
|
||||||
let hash_matches = argon2::verify_encoded(&hash, password.as_bytes()).unwrap_or(false);
|
error!("error while hashing user {}", user_id);
|
||||||
|
return Err(Error::BadServerResponse("could not hash"));
|
||||||
|
};
|
||||||
|
let hash_matches = services()
|
||||||
|
.globals
|
||||||
|
.argon
|
||||||
|
.verify_password(password.as_bytes(), &parsed_hash)
|
||||||
|
.is_ok();
|
||||||
if !hash_matches {
|
if !hash_matches {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
|
|
@ -6,8 +6,10 @@ use crate::{
|
||||||
services, utils, Config, Error, PduEvent, Result, Services, SERVICES,
|
services, utils, Config, Error, PduEvent, Result, Services, SERVICES,
|
||||||
};
|
};
|
||||||
use abstraction::{KeyValueDatabaseEngine, KvTree};
|
use abstraction::{KeyValueDatabaseEngine, KvTree};
|
||||||
|
use argon2::{password_hash::SaltString, PasswordHasher, PasswordVerifier};
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use lru_cache::LruCache;
|
use lru_cache::LruCache;
|
||||||
|
use rand::thread_rng;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::{
|
events::{
|
||||||
push_rules::{PushRulesEvent, PushRulesEventContent},
|
push_rules::{PushRulesEvent, PushRulesEventContent},
|
||||||
|
@ -464,11 +466,17 @@ impl KeyValueDatabase {
|
||||||
if services().globals.database_version()? < 2 {
|
if services().globals.database_version()? < 2 {
|
||||||
// We accidentally inserted hashed versions of "" into the db instead of just ""
|
// We accidentally inserted hashed versions of "" into the db instead of just ""
|
||||||
for (userid, password) in db.userid_password.iter() {
|
for (userid, password) in db.userid_password.iter() {
|
||||||
let password = utils::string_from_bytes(&password);
|
let salt = SaltString::generate(thread_rng());
|
||||||
|
let empty_pass = services()
|
||||||
let empty_hashed_password = password.map_or(false, |password| {
|
.globals
|
||||||
argon2::verify_encoded(&password, b"").unwrap_or(false)
|
.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();
|
||||||
|
|
||||||
if empty_hashed_password {
|
if empty_hashed_password {
|
||||||
db.userid_password.insert(&userid, b"")?;
|
db.userid_password.insert(&userid, b"")?;
|
||||||
|
|
|
@ -20,9 +20,9 @@ pub use database::KeyValueDatabase;
|
||||||
pub use service::{pdu::PduEvent, Services};
|
pub use service::{pdu::PduEvent, Services};
|
||||||
pub use utils::error::{Error, Result};
|
pub use utils::error::{Error, Result};
|
||||||
|
|
||||||
pub static SERVICES: RwLock<Option<&'static Services>> = RwLock::new(None);
|
pub static SERVICES: RwLock<Option<&'static Services<'static>>> = RwLock::new(None);
|
||||||
|
|
||||||
pub fn services() -> &'static Services {
|
pub fn services() -> &'static Services<'static> {
|
||||||
SERVICES
|
SERVICES
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
mod data;
|
mod data;
|
||||||
|
use argon2::Argon2;
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
serde::Base64, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedServerName,
|
serde::Base64, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedServerName,
|
||||||
|
@ -51,7 +52,7 @@ type SyncHandle = (
|
||||||
Receiver<Option<Result<sync_events::v3::Response>>>, // rx
|
Receiver<Option<Result<sync_events::v3::Response>>>, // rx
|
||||||
);
|
);
|
||||||
|
|
||||||
pub struct Service {
|
pub struct Service<'a> {
|
||||||
pub db: &'static dyn Data,
|
pub db: &'static dyn Data,
|
||||||
|
|
||||||
pub actual_destination_cache: Arc<RwLock<WellKnownMap>>, // actual_destination, host
|
pub actual_destination_cache: Arc<RwLock<WellKnownMap>>, // actual_destination, host
|
||||||
|
@ -77,6 +78,7 @@ pub struct Service {
|
||||||
pub rotate: RotationHandler,
|
pub rotate: RotationHandler,
|
||||||
|
|
||||||
pub shutdown: AtomicBool,
|
pub shutdown: AtomicBool,
|
||||||
|
pub argon: Argon2<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles "rotation" of long-polling requests. "Rotation" in this context is similar to "rotation" of log files and the like.
|
/// Handles "rotation" of long-polling requests. "Rotation" in this context is similar to "rotation" of log files and the like.
|
||||||
|
@ -140,7 +142,7 @@ impl Resolve for Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Service {
|
impl Service<'_> {
|
||||||
pub fn load(db: &'static dyn Data, config: Config) -> Result<Self> {
|
pub fn load(db: &'static dyn Data, config: Config) -> Result<Self> {
|
||||||
let keypair = db.load_keypair();
|
let keypair = db.load_keypair();
|
||||||
|
|
||||||
|
@ -188,7 +190,12 @@ impl Service {
|
||||||
RoomVersionId::V5,
|
RoomVersionId::V5,
|
||||||
RoomVersionId::V11,
|
RoomVersionId::V11,
|
||||||
];
|
];
|
||||||
|
// 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 s = Self {
|
let mut s = Self {
|
||||||
db,
|
db,
|
||||||
config,
|
config,
|
||||||
|
@ -219,6 +226,7 @@ impl Service {
|
||||||
sync_receivers: RwLock::new(HashMap::new()),
|
sync_receivers: RwLock::new(HashMap::new()),
|
||||||
rotate: RotationHandler::new(),
|
rotate: RotationHandler::new(),
|
||||||
shutdown: AtomicBool::new(false),
|
shutdown: AtomicBool::new(false),
|
||||||
|
argon,
|
||||||
};
|
};
|
||||||
|
|
||||||
fs::create_dir_all(s.get_media_folder())?;
|
fs::create_dir_all(s.get_media_folder())?;
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub mod transaction_ids;
|
||||||
pub mod uiaa;
|
pub mod uiaa;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
|
||||||
pub struct Services {
|
pub struct Services<'a> {
|
||||||
pub appservice: appservice::Service,
|
pub appservice: appservice::Service,
|
||||||
pub pusher: pusher::Service,
|
pub pusher: pusher::Service,
|
||||||
pub rooms: rooms::Service,
|
pub rooms: rooms::Service,
|
||||||
|
@ -30,13 +30,13 @@ pub struct Services {
|
||||||
pub users: users::Service,
|
pub users: users::Service,
|
||||||
pub account_data: account_data::Service,
|
pub account_data: account_data::Service,
|
||||||
pub admin: Arc<admin::Service>,
|
pub admin: Arc<admin::Service>,
|
||||||
pub globals: globals::Service,
|
pub globals: globals::Service<'a>,
|
||||||
pub key_backups: key_backups::Service,
|
pub key_backups: key_backups::Service,
|
||||||
pub media: media::Service,
|
pub media: media::Service,
|
||||||
pub sending: Arc<sending::Service>,
|
pub sending: Arc<sending::Service>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Services {
|
impl Services<'_> {
|
||||||
pub fn build<
|
pub fn build<
|
||||||
D: appservice::Data
|
D: appservice::Data
|
||||||
+ pusher::Data
|
+ pusher::Data
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod data;
|
mod data;
|
||||||
|
|
||||||
|
use argon2::{PasswordHash, PasswordVerifier};
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
|
@ -81,8 +82,14 @@ impl Service {
|
||||||
|
|
||||||
// Check if password is correct
|
// Check if password is correct
|
||||||
if let Some(hash) = services().users.password_hash(&user_id)? {
|
if let Some(hash) = services().users.password_hash(&user_id)? {
|
||||||
let hash_matches =
|
let hash_matches = services()
|
||||||
argon2::verify_encoded(&hash, password.as_bytes()).unwrap_or(false);
|
.globals
|
||||||
|
.argon
|
||||||
|
.verify_password(
|
||||||
|
password.as_bytes(),
|
||||||
|
&PasswordHash::new(&hash).expect("valid hash in database"),
|
||||||
|
)
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
if !hash_matches {
|
if !hash_matches {
|
||||||
uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody {
|
uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
use crate::{Error, Result};
|
use crate::{services, Error, Result};
|
||||||
use argon2::{Config, Variant};
|
use argon2::{password_hash::SaltString, PasswordHasher};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use ring::digest;
|
use ring::digest;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
|
@ -72,14 +72,13 @@ pub fn random_string(length: usize) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a new hash for the given password
|
/// Calculate a new hash for the given password
|
||||||
pub fn calculate_password_hash(password: &str) -> Result<String, argon2::Error> {
|
pub fn calculate_password_hash(password: &str) -> Result<String, argon2::password_hash::Error> {
|
||||||
let hashing_config = Config {
|
let salt = SaltString::generate(thread_rng());
|
||||||
variant: Variant::Argon2id,
|
services()
|
||||||
..Config::owasp2() // m=19456 (19 MiB), t=2, p=1 from https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
|
.globals
|
||||||
};
|
.argon
|
||||||
|
.hash_password(password.as_bytes(), &salt)
|
||||||
let salt = random_string(32);
|
.map(|it| it.to_string())
|
||||||
argon2::hash_encoded(password.as_bytes(), salt.as_bytes(), &hashing_config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(keys))]
|
#[tracing::instrument(skip(keys))]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue