diff --git a/Cargo.lock b/Cargo.lock index 3efde9f..6d61333 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -391,6 +391,45 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" +[[package]] +name = "asn1-rs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.13", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-recursion" version = "1.0.0" @@ -456,6 +495,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" +[[package]] +name = "base64urlsafedata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d02340c3f25c8422ba85d481123406dd7506505485bac1c694b26eb538da8daf" +dependencies = [ + "base64 0.13.0", + "serde", + "serde_json", +] + [[package]] name = "bcrypt" version = "0.13.0" @@ -740,6 +790,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "compact_jwt" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9417bb4f581b7a5e08fabb4398b910064363bbfd7b75a10d1da3bfff3ef9b36" +dependencies = [ + "base64 0.13.0", + "base64urlsafedata", + "openssl", + "serde", + "serde_json", + "tracing", + "url", + "uuid 1.1.2", +] + [[package]] name = "comunic_server" version = "0.1.0" @@ -961,6 +1027,12 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + [[package]] name = "deflate" version = "0.9.1" @@ -979,6 +1051,20 @@ dependencies = [ "adler32", ] +[[package]] +name = "der-parser" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint 0.4.3", + "num-traits", + "rusticata-macros", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -1023,6 +1109,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -1911,9 +2008,9 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "mailchecker" -version = "4.1.19" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf79ae4aa0804bf975b72d7e8330a810739ae8f717c55e747de1f09c6f9ae8" +checksum = "9188ee74b3b27e3a35bdc2764e806445de607a0d0324b0281bd0e5c5b8bad4a3" dependencies = [ "fast_chemail", "once_cell", @@ -2101,7 +2198,7 @@ dependencies = [ "subprocess", "thiserror", "time 0.3.13", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -2229,6 +2326,15 @@ dependencies = [ "libc", ] +[[package]] +name = "oid-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.13.1" @@ -2782,6 +2888,15 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustls" version = "0.18.1" @@ -3141,6 +3256,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "tap" version = "1.0.1" @@ -3408,6 +3535,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + [[package]] name = "untrusted" version = "0.7.1" @@ -3439,6 +3572,16 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +[[package]] +name = "uuid" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +dependencies = [ + "getrandom 0.2.7", + "serde", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -3557,21 +3700,52 @@ dependencies = [ [[package]] name = "webauthn-rs" -version = "0.3.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b266eccb4b32595876f5c73ea443b0516da0b1df72ca07bc08ed9ba7f96ec1" +checksum = "08ea37595bdb8d731f39949249e41fb4c66cdad5487693575cc1078c8a67abac" +dependencies = [ + "base64urlsafedata", + "serde", + "tracing", + "url", + "uuid 1.1.2", + "webauthn-rs-core", +] + +[[package]] +name = "webauthn-rs-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a12633855c4094f700cad8e2aad4c7d07d762745c4e26a1684a4351bcdf0d4" dependencies = [ "base64 0.13.0", + "base64urlsafedata", + "compact_jwt", + "der-parser", "nom", "openssl", "rand 0.8.5", "serde", "serde_cbor", - "serde_derive", "serde_json", "thiserror", "tracing", "url", + "uuid 1.1.2", + "webauthn-rs-proto", + "x509-parser", +] + +[[package]] +name = "webauthn-rs-proto" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a72f222f4496b34acfc7809ac8440984665105785f3556b541ab14c515cf357" +dependencies = [ + "base64urlsafedata", + "serde", + "serde_json", + "url", ] [[package]] @@ -3710,6 +3884,24 @@ dependencies = [ "tap", ] +[[package]] +name = "x509-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +dependencies = [ + "asn1-rs", + "base64 0.13.0", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time 0.3.13", +] + [[package]] name = "xml5ever" version = "0.16.2" diff --git a/Cargo.toml b/Cargo.toml index b9d0ca4..24a60e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ serde_json = "1.0.82" futures = "0.3.21" encoding_rs = "0.8.31" percent-encoding = "2.1.0" -mailchecker = "4.1.19" +mailchecker = "5.0.0" sha1 = "0.10.1" rand = "0.8.5" chrono = "0.4.19" @@ -40,7 +40,7 @@ mp4 = "0.12.0" zip = "0.6.2" webpage = "1.4.0" gouth = "0.2.1" -webauthn-rs = "0.3.2" +webauthn-rs = "0.4.3" url = "2.2.2" async-recursion = "1.0.0" tokio = { version = "1.20.1", features = ["rt-multi-thread"] } \ No newline at end of file diff --git a/src/api_data/admin/admin_auth_options.rs b/src/api_data/admin/admin_auth_options.rs index 287d531..559a1f4 100644 --- a/src/api_data/admin/admin_auth_options.rs +++ b/src/api_data/admin/admin_auth_options.rs @@ -20,7 +20,7 @@ pub struct AdminAuthOptions { } impl AdminAuthOptions { - pub fn new(admin: &Admin, keys: &Vec) -> Self { + pub fn new(admin: &Admin, keys: &[AdminKey]) -> Self { Self { reset_token: admin.reset_token.is_some(), keys: keys.iter().map(|k| AuthKey { id: k.id, name: k.name.to_string(), password: k.password.is_some() }).collect(), diff --git a/src/controllers/admin/admin_keys_controller.rs b/src/controllers/admin/admin_keys_controller.rs index 3109edf..be7cd37 100644 --- a/src/controllers/admin/admin_keys_controller.rs +++ b/src/controllers/admin/admin_keys_controller.rs @@ -4,6 +4,7 @@ use bcrypt::verify; +use webauthn_rs::prelude::Uuid; use crate::api_data::admin::admin_auth_success::AdminAuthSuccess; use crate::api_data::admin::admin_keys_api::AdminKeyAPI; @@ -27,8 +28,7 @@ impl HttpRequestHandler { let keys = admin_account_key_helper::get_admin_keys(admin.id)?; let key = keys.into_iter() - .filter(|k| k.id == key_id) - .next(); + .find(|k| k.id == key_id); match key { Some(key) => Ok(key), @@ -55,7 +55,13 @@ pub async fn get_keys_list(r: &mut HttpRequestHandler) -> RequestResult { pub async fn challenge_register_key(r: &mut HttpRequestHandler) -> RequestResult { let wan = get_wan(); - let (res, state) = wan.generate_challenge_register(&r.admin_id()?.id_str(), false)?; + let user_info = admin_account_helper::find_admin_by_id(r.admin_id()?)?; + + let (res, state) = wan.start_passkey_registration( + Uuid::new_v4(), + &user_info.name, + &user_info.name, + None)?; admin_key_registration_challenges_helper::set(r.admin_id()?, state)?; @@ -74,9 +80,9 @@ pub async fn register_key(r: &mut HttpRequestHandler) -> RequestResult { )?; let wan = get_wan(); - let key = wan.register_credential(&creds, &state, |_| Ok(false))?; + let key = wan.finish_passkey_registration(&creds, &state)?; - let key_id = admin_account_key_helper::add_key(r.admin_id()?, &key_name, key.0, key_password)?; + let key_id = admin_account_key_helper::add_key(r.admin_id()?, &key_name, key, key_password)?; log_admin_action(r.admin_id()?, &r.remote_ip(), AdminAction::RegisteredAdminKey { @@ -120,7 +126,7 @@ pub async fn challenge_auth_with_key(r: &mut HttpRequestHandler) -> RequestResul let key = r.post_admin_auth_key("mail", "key_id")?; let (challenge_response, auth_state) = - get_wan().generate_challenge_authenticate(vec![key.key])?; + get_wan().start_passkey_authentication(&[key.key])?; admin_key_authentication_challenges_helper::set(key.id, auth_state)?; @@ -138,9 +144,9 @@ pub async fn auth_with_key(r: &mut HttpRequestHandler) -> RequestResult { )?; // Perform authentication - let state = get_wan().authenticate_credential(&credentials, &state)?; - if !state.1.user_present { - r.forbidden("Invalid key!".to_string())?; + let state = get_wan().finish_passkey_authentication(&credentials, &state)?; + if !state.user_verified() { + r.forbidden("Invalid key! (user_verified = false)".to_string())?; } // Check key password (if any) diff --git a/src/data/admin.rs b/src/data/admin.rs index f3a3151..3fd0208 100644 --- a/src/data/admin.rs +++ b/src/data/admin.rs @@ -4,7 +4,7 @@ use mysql::serde::{Deserializer, Serializer}; use serde::{Deserialize, Serialize}; -use webauthn_rs::proto::Credential; +use webauthn_rs::prelude::Passkey; use crate::constants::admin::{ADMIN_ROLES_LIST, AdminRole}; use crate::data::u64_visitor::U64Visitor; @@ -50,7 +50,7 @@ pub struct AdminKey { pub admin_id: AdminID, pub name: String, pub time_add: u64, - pub key: Credential, + pub key: Passkey, pub password: Option, } @@ -98,6 +98,6 @@ impl Serialize for AdminID { impl<'de> Deserialize<'de> for AdminID { fn deserialize(deserializer: D) -> Result>::Error> where D: Deserializer<'de> { - deserializer.deserialize_u64(U64Visitor {}).map(|id| AdminID::new(id)) + deserializer.deserialize_u64(U64Visitor {}).map(AdminID::new) } } \ No newline at end of file diff --git a/src/data/base_request_handler.rs b/src/data/base_request_handler.rs index 9b95c10..84e4858 100644 --- a/src/data/base_request_handler.rs +++ b/src/data/base_request_handler.rs @@ -565,14 +565,14 @@ pub trait BaseRequestHandler { } /// Get the response to a key register credential included in the request - fn post_register_public_key_credential(&mut self, name: &str) -> Res { + fn post_register_public_key_credential(&mut self, name: &str) -> Res { let str = self.post_string(name)?; Ok(serde_json::from_str(&str)?) } /// Get the response to a key authentication included in the request - fn post_auth_public_key_credential(&mut self, name: &str) -> Res { + fn post_auth_public_key_credential(&mut self, name: &str) -> Res { let str = self.post_string(name)?; Ok(serde_json::from_str(&str)?) diff --git a/src/data/webauthn_config.rs b/src/data/webauthn_config.rs index 840b9fc..f907c41 100644 --- a/src/data/webauthn_config.rs +++ b/src/data/webauthn_config.rs @@ -2,41 +2,25 @@ //! //! @author Pierre Hubert -use webauthn_rs::{Webauthn, WebauthnConfig}; +use webauthn_rs::{Webauthn, WebauthnBuilder}; use crate::data::config::conf; -pub struct ComunicAdminWebauthnConfig { - origin: url::Url, - relying_party_id: String, -} - -impl WebauthnConfig for ComunicAdminWebauthnConfig { - fn get_relying_party_name(&self) -> &str { - "ComunicAdmin" - } - - fn get_origin(&self) -> &url::Url { - &self.origin - } - - fn get_relying_party_id(&self) -> &str { - &self.relying_party_id - } -} - -pub fn get_wan() -> Webauthn { - Webauthn::new(ComunicAdminWebauthnConfig { - origin: url::Url::parse(&conf().admin_url).unwrap(), - relying_party_id: conf().admin_url +pub fn get_wan() -> Webauthn { + WebauthnBuilder::new( + conf().admin_url .replace("https://", "") .replace("http://", "") - .split(":") + .split(':') .next() .unwrap() - .split("/") + .split('/') .next() - .unwrap() - .to_string(), - }) + .unwrap(), + &url::Url::parse(&conf().admin_url).unwrap(), + ) + .expect("Invalid Webauthn configuration!") + .rp_name("ComunicAdmin") + .build() + .expect("Failed to build Webauthn object") } \ No newline at end of file diff --git a/src/helpers/admin_account_helper.rs b/src/helpers/admin_account_helper.rs index 037d6ca..16991c5 100644 --- a/src/helpers/admin_account_helper.rs +++ b/src/helpers/admin_account_helper.rs @@ -21,7 +21,7 @@ pub fn create(new_admin: &NewAdmin) -> Res { .add_str("name", &new_admin.name) .add_str("email", &new_admin.email) .insert_expect_result() - .map(|i| AdminID::new(i)) + .map(AdminID::new) } /// Check out whether an admin exists or not diff --git a/src/helpers/admin_account_key_helper.rs b/src/helpers/admin_account_key_helper.rs index 0543529..9c741d6 100644 --- a/src/helpers/admin_account_key_helper.rs +++ b/src/helpers/admin_account_key_helper.rs @@ -3,7 +3,7 @@ //! @author Pierre Hubert use bcrypt::{DEFAULT_COST, hash_with_result}; -use webauthn_rs::proto::Credential; +use webauthn_rs::prelude::Passkey; use crate::constants::database_tables_names::ADMIN_KEYS_TABLE; use crate::data::admin::{AdminID, AdminKey}; @@ -12,7 +12,7 @@ use crate::helpers::database; use crate::utils::date_utils::time; /// Save a new key in the database -pub fn add_key(id: AdminID, name: &str, key: Credential, password: String) -> Res { +pub fn add_key(id: AdminID, name: &str, key: Passkey, password: String) -> Res { database::InsertQuery::new(ADMIN_KEYS_TABLE) .add_admin_id("admin_id", id) .add_str("name", name) diff --git a/src/helpers/admin_key_authentication_challenges_helper.rs b/src/helpers/admin_key_authentication_challenges_helper.rs index e4e1310..ce47ce4 100644 --- a/src/helpers/admin_key_authentication_challenges_helper.rs +++ b/src/helpers/admin_key_authentication_challenges_helper.rs @@ -7,11 +7,11 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use webauthn_rs::AuthenticationState; +use webauthn_rs::prelude::PasskeyAuthentication; use crate::data::error::Res; -static mut CACHE: Option>>> = None; +static mut CACHE: Option>>> = None; /// Initialize this helper's cache pub fn init() { @@ -22,7 +22,7 @@ pub fn init() { } /// Store a new entry in the cache -pub fn set(key_id: u64, state: AuthenticationState) -> Res { +pub fn set(key_id: u64, state: PasskeyAuthentication) -> Res { let cache = unsafe { CACHE.as_ref().unwrap().lock() }; @@ -32,7 +32,7 @@ pub fn set(key_id: u64, state: AuthenticationState) -> Res { Ok(()) } -pub fn get(key_id: u64) -> Res> { +pub fn get(key_id: u64) -> Res> { let cache = unsafe { CACHE.as_ref().unwrap().lock() }; diff --git a/src/helpers/admin_key_registration_challenges_helper.rs b/src/helpers/admin_key_registration_challenges_helper.rs index 46fef83..c14690c 100644 --- a/src/helpers/admin_key_registration_challenges_helper.rs +++ b/src/helpers/admin_key_registration_challenges_helper.rs @@ -7,12 +7,12 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use webauthn_rs::RegistrationState; +use webauthn_rs::prelude::PasskeyRegistration; use crate::data::admin::AdminID; use crate::data::error::Res; -static mut CACHE: Option>>> = None; +static mut CACHE: Option>>> = None; /// Initialize this helper's cache pub fn init() { @@ -23,7 +23,7 @@ pub fn init() { } /// Store a new entry in the cache -pub fn set(admin: AdminID, key: RegistrationState) -> Res { +pub fn set(admin: AdminID, key: PasskeyRegistration) -> Res { let cache = unsafe { CACHE.as_ref().unwrap().lock() }; @@ -33,7 +33,7 @@ pub fn set(admin: AdminID, key: RegistrationState) -> Res { Ok(()) } -pub fn get(admin: AdminID) -> Res> { +pub fn get(admin: AdminID) -> Res> { let cache = unsafe { CACHE.as_ref().unwrap().lock() };