From 4450a21225381605516cd0a8e445ffc7939e271f Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Wed, 24 Aug 2022 13:33:40 +0200 Subject: [PATCH] Update `webauthn-rs` dependency --- Cargo.lock | 182 +++++++++++++++++++++++++++++- Cargo.toml | 2 +- src/controllers/login_api.rs | 2 +- src/controllers/two_factor_api.rs | 2 +- src/data/webauthn_manager.rs | 72 +++++------- 5 files changed, 212 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 814ce86..e5e036c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,6 +420,45 @@ dependencies = [ "toml", ] +[[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", +] + +[[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-trait" version = "0.1.57" @@ -481,6 +520,17 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6b4d9b1225d28d360ec6a231d65af1fd99a2a095154c8040689617290569c5c" +[[package]] +name = "base64urlsafedata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d02340c3f25c8422ba85d481123406dd7506505485bac1c694b26eb538da8daf" +dependencies = [ + "base64", + "serde", + "serde_json", +] + [[package]] name = "basic-oidc" version = "0.1.1" @@ -722,6 +772,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", + "base64urlsafedata", + "openssl", + "serde", + "serde_json", + "tracing", + "url", + "uuid", +] + [[package]] name = "const-oid" version = "0.6.2" @@ -889,6 +955,12 @@ dependencies = [ "cipher 0.4.3", ] +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + [[package]] name = "deflate" version = "1.0.0" @@ -918,6 +990,20 @@ dependencies = [ "pem-rfc7468 0.3.1", ] +[[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", + "num-traits", + "rusticata-macros", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -951,6 +1037,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 = "ecdsa" version = "0.13.4" @@ -1667,6 +1764,17 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.7.0" @@ -1746,6 +1854,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" @@ -2177,6 +2294,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 = "ryu" version = "1.0.11" @@ -2706,6 +2832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" dependencies = [ "getrandom", + "serde", ] [[package]] @@ -2792,21 +2919,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", + "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", + "base64urlsafedata", + "compact_jwt", + "der-parser", "nom", "openssl", "rand", "serde", "serde_cbor", - "serde_derive", "serde_json", "thiserror", "tracing", "url", + "uuid", + "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]] @@ -2889,6 +3047,24 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "x509-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +dependencies = [ + "asn1-rs", + "base64", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + [[package]] name = "zeroize" version = "1.4.3" diff --git a/Cargo.toml b/Cargo.toml index 1b3fcf0..14dea6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ lazy-regex = "2.3.0" totp_rfc6238 = "0.5.0" base32 = "0.4.0" qrcode-generator = "4.1.6" -webauthn-rs = "0.3.2" +webauthn-rs = "0.4.3" url = "2.2.2" aes-gcm = { version = "0.10.1", features = ["aes"] } bincode = "1.3.3" \ No newline at end of file diff --git a/src/controllers/login_api.rs b/src/controllers/login_api.rs index c7ebdab..ef8cc5a 100644 --- a/src/controllers/login_api.rs +++ b/src/controllers/login_api.rs @@ -1,6 +1,6 @@ use actix_identity::Identity; use actix_web::{HttpRequest, HttpResponse, Responder, web}; -use webauthn_rs::proto::PublicKeyCredential; +use webauthn_rs::prelude::PublicKeyCredential; use crate::data::session_identity::{SessionIdentity, SessionStatus}; use crate::data::webauthn_manager::WebAuthManagerReq; diff --git a/src/controllers/two_factor_api.rs b/src/controllers/two_factor_api.rs index 239f84f..9f1b30d 100644 --- a/src/controllers/two_factor_api.rs +++ b/src/controllers/two_factor_api.rs @@ -1,7 +1,7 @@ use actix::Addr; use actix_web::{HttpResponse, Responder, web}; use uuid::Uuid; -use webauthn_rs::proto::RegisterPublicKeyCredential; +use webauthn_rs::prelude::RegisterPublicKeyCredential; use crate::actors::users_actor; use crate::actors::users_actor::UsersActor; diff --git a/src/data/webauthn_manager.rs b/src/data/webauthn_manager.rs index 8d88282..b9de5ae 100644 --- a/src/data/webauthn_manager.rs +++ b/src/data/webauthn_manager.rs @@ -2,39 +2,20 @@ use std::io::ErrorKind; use std::sync::Arc; use actix_web::web; -use webauthn_rs::{AuthenticationState, RegistrationState, Webauthn, WebauthnConfig}; -use webauthn_rs::proto::{CreationChallengeResponse, Credential, PublicKeyCredential, RegisterPublicKeyCredential, RequestChallengeResponse}; +use uuid::Uuid; +use webauthn_rs::{Webauthn, WebauthnBuilder}; +use webauthn_rs::prelude::{CreationChallengeResponse, Passkey, PublicKeyCredential, RegisterPublicKeyCredential, RequestChallengeResponse}; -use crate::constants::{APP_NAME, WEBAUTHN_LOGIN_CHALLENGE_EXPIRE, WEBAUTHN_REGISTER_CHALLENGE_EXPIRE}; +use crate::constants::{WEBAUTHN_LOGIN_CHALLENGE_EXPIRE, WEBAUTHN_REGISTER_CHALLENGE_EXPIRE}; use crate::data::app_config::AppConfig; use crate::data::crypto_wrapper::CryptoWrapper; use crate::data::user::{User, UserID}; use crate::utils::err::Res; use crate::utils::time::time; -#[derive(Debug)] -struct WebAuthnAppConfig { - origin: url::Url, - relying_party_id: String, -} - -impl WebauthnConfig for WebAuthnAppConfig { - fn get_relying_party_name(&self) -> &str { - APP_NAME - } - - fn get_origin(&self) -> &url::Url { - &self.origin - } - - fn get_relying_party_id(&self) -> &str { - &self.relying_party_id - } -} - #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct WebauthnPubKey { - creds: Credential, + creds: Passkey, } pub struct RegisterKeyRequest { @@ -44,7 +25,7 @@ pub struct RegisterKeyRequest { #[derive(Debug, serde::Serialize, serde::Deserialize)] struct RegisterKeyOpaqueData { - registration_state: RegistrationState, + registration_state: String, user_id: UserID, expire: u64, } @@ -56,7 +37,7 @@ pub struct AuthRequest { #[derive(Debug, serde::Serialize, serde::Deserialize)] struct AuthStateOpaqueData { - authentication_state: AuthenticationState, + authentication_state: String, user_id: UserID, expire: u64, } @@ -65,34 +46,40 @@ struct AuthStateOpaqueData { pub type WebAuthManagerReq = web::Data>; pub struct WebAuthManager { - core: Webauthn, + core: Webauthn, crypto_wrapper: CryptoWrapper, } impl WebAuthManager { pub fn init(conf: &AppConfig) -> Self { Self { - core: Webauthn::new(WebAuthnAppConfig { - origin: url::Url::parse(&conf.website_origin) - .expect("Failed to parse configuration origin!"), - relying_party_id: conf.domain_name().split_once(':') + core: WebauthnBuilder::new( + conf.domain_name().split_once(':') .map(|s| s.0) - .unwrap_or_else(|| conf.domain_name()) - .to_string(), - }), + .unwrap_or_else(|| conf.domain_name()), + &url::Url::parse(&conf.website_origin) + .expect("Failed to parse configuration origin!")) + .expect("Invalid Webauthn configuration") + .build() + .expect("Failed to build webauthn") + + , crypto_wrapper: CryptoWrapper::new_random(), } } pub fn start_register(&self, user: &User) -> Res { - let (creation_challenge, registration_state) = self.core.generate_challenge_register( + let (creation_challenge, registration_state) + = self.core.start_passkey_registration( + Uuid::parse_str(&user.uid.0).expect("Failed to parse user id"), &user.username, - false, + &user.full_name(), + None, )?; Ok(RegisterKeyRequest { opaque_state: self.crypto_wrapper.encrypt(&RegisterKeyOpaqueData { - registration_state, + registration_state: serde_json::to_string(®istration_state)?, user_id: user.uid.clone(), expire: time() + WEBAUTHN_REGISTER_CHALLENGE_EXPIRE, })?, @@ -114,19 +101,19 @@ impl WebAuthManager { } let res = self.core - .register_credential(&pub_cred, &state.registration_state, |_| Ok(false))?; + .finish_passkey_registration(&pub_cred, &serde_json::from_str(&state.registration_state)?)?; - Ok(WebauthnPubKey { creds: res.0 }) + Ok(WebauthnPubKey { creds: res }) } pub fn start_authentication(&self, user_id: &UserID, key: &WebauthnPubKey) -> Res { - let (login_challenge, authentication_state) = self.core.generate_challenge_authenticate(vec![ + let (login_challenge, authentication_state) = self.core.start_passkey_authentication(&vec![ key.creds.clone() ])?; Ok(AuthRequest { opaque_state: self.crypto_wrapper.encrypt(&AuthStateOpaqueData { - authentication_state, + authentication_state: serde_json::to_string(&authentication_state)?, user_id: user_id.clone(), expire: time() + WEBAUTHN_LOGIN_CHALLENGE_EXPIRE, })?, @@ -147,7 +134,8 @@ impl WebAuthManager { std::io::Error::new(ErrorKind::Other, "Challenge has expired!"))); } - self.core.authenticate_credential(pub_cred, &state.authentication_state)?; + self.core.finish_passkey_authentication(pub_cred, + &serde_json::from_str(&state.authentication_state)?)?; Ok(()) }