From 86788c7e8dca964077356ae6a0b0b44699992325 Mon Sep 17 00:00:00 2001 From: izzy Date: Thu, 8 May 2025 12:09:02 +0100 Subject: [PATCH] feat: implement easypwned checks --- Cargo.lock | 32 ++++++++++++++------- Cargo.toml | 3 +- crates/authifier/Cargo.toml | 6 ++-- crates/authifier/src/config/passwords.rs | 36 +++++++++++++++++++++++- crates/rocket_authifier/Cargo.toml | 4 +-- 5 files changed, 65 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72dde93..969cf87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,7 +239,7 @@ checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" [[package]] name = "authifier" -version = "1.0.9" +version = "1.0.9-beta.0" dependencies = [ "async-std", "async-trait", @@ -264,6 +264,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "sha1", "totp-lite", "ulid", "validator", @@ -619,9 +620,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.2", "crypto-common", @@ -985,7 +986,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest 0.10.7", ] [[package]] @@ -1315,7 +1316,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" dependencies = [ - "digest 0.10.3", + "digest 0.10.7", ] [[package]] @@ -1592,7 +1593,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" dependencies = [ - "digest 0.10.3", + "digest 0.10.7", ] [[package]] @@ -1989,7 +1990,7 @@ dependencies = [ [[package]] name = "rocket_authifier" -version = "1.0.9" +version = "1.0.9-beta.0" dependencies = [ "async-std", "authifier", @@ -2328,7 +2329,18 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", ] [[package]] @@ -2339,7 +2351,7 @@ checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.7", ] [[package]] @@ -2661,7 +2673,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc496875d9c8fe9a0ce19e3ee8e8808c60376831a439543f0aac71c9dd129fa" dependencies = [ - "digest 0.10.3", + "digest 0.10.7", "hmac", "sha-1 0.10.0", "sha2", diff --git a/Cargo.toml b/Cargo.toml index 4df366c..971b1d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,3 @@ [workspace] -members = ["crates/*"] \ No newline at end of file +resolver = "1" +members = ["crates/*"] diff --git a/crates/authifier/Cargo.toml b/crates/authifier/Cargo.toml index 26034b8..f128c6e 100644 --- a/crates/authifier/Cargo.toml +++ b/crates/authifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "authifier" -version = "1.0.9" +version = "1.0.9-beta.0" edition = "2021" license = "Apache-2.0" authors = ["Pawel Makles "] @@ -21,7 +21,8 @@ pwned100k = [] have_i_been_pwned = [] hcaptcha = ["reqwest"] shield = ["reqwest"] -default_inbuilts = ["pwned100k", "hcaptcha", "shield"] +easypwned = ["reqwest"] +default_inbuilts = ["pwned100k", "hcaptcha", "shield", "easypwned"] # default_inbuilts = [] default = ["async-std-runtime", "database-mongodb", "default_inbuilts"] @@ -41,6 +42,7 @@ async-trait = "0.1.56" futures = { version = "0.3.21" } # Serde +sha1 = "0.10.6" serde_json = { version = "1.0.81" } iso8601-timestamp = { version = "0.1.10" } serde = { version = "1.0.116", features = ["derive"] } diff --git a/crates/authifier/src/config/passwords.rs b/crates/authifier/src/config/passwords.rs index b8540a2..3be445c 100644 --- a/crates/authifier/src/config/passwords.rs +++ b/crates/authifier/src/config/passwords.rs @@ -1,5 +1,7 @@ use std::collections::HashSet; +use sha1::Digest; + use crate::{Error, Result}; #[derive(Default, Serialize, Deserialize, Clone)] @@ -13,12 +15,14 @@ pub enum PasswordScanning { #[cfg(feature = "pwned100k")] #[default] Top100k, + /// easypwned locally-hosted HIBP database API + #[cfg(feature = "easypwned")] + EasyPwned { endpoint: String }, /// Use the Have I Been Pwned? API #[cfg(feature = "have_i_been_pwned")] HIBP { api_key: String }, } - #[cfg(feature = "pwned100k")] lazy_static! { /// Top 100k compromised passwords @@ -46,6 +50,36 @@ impl PasswordScanning { Ok(()) } } + #[cfg(feature = "easypwned")] + PasswordScanning::EasyPwned { endpoint } => { + let mut hasher = sha1::Sha1::new(); + hasher.update(password); + let pwd_hash = hasher.finalize(); + + #[derive(Deserialize)] + struct EasyPwnedResult { + secure: bool, + } + + if let Ok(response) = reqwest::get(format!("{endpoint}/hash/{pwd_hash:#02x}")).await + { + if let Ok(result) = response.json::().await { + if result.secure { + Ok(()) + } else { + Err(Error::CompromisedPassword) + } + } else if TOP_100K_COMPROMISED.contains(password) { + Err(Error::CompromisedPassword) + } else { + Ok(()) + } + } else if TOP_100K_COMPROMISED.contains(password) { + Err(Error::CompromisedPassword) + } else { + Ok(()) + } + } #[cfg(feature = "pwned100k")] PasswordScanning::Top100k => { if TOP_100K_COMPROMISED.contains(password) { diff --git a/crates/rocket_authifier/Cargo.toml b/crates/rocket_authifier/Cargo.toml index 6c663c8..843da89 100644 --- a/crates/rocket_authifier/Cargo.toml +++ b/crates/rocket_authifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rocket_authifier" -version = "1.0.9" +version = "1.0.9-beta.0" edition = "2021" license = "Apache-2.0" authors = ["Pawel Makles "] @@ -22,7 +22,7 @@ default = [] [dependencies] # Authifier -authifier = { version = "1.0.9", path = "../authifier", features = [ +authifier = { version = "1.0.9-beta.0", path = "../authifier", features = [ "rocket_impl", "okapi_impl", ] }