From dafbfdb920849d1722c5fe64b09a64ef95c90f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Fri, 4 Jul 2025 10:55:17 +0200 Subject: [PATCH] Properly handle uv requirement --- .../src/assert.rs | 15 ++++++- .../src/com_provider.rs | 26 ++++++++++++ .../src/make_credential.rs | 13 +++++- .../windows_plugin_authenticator/src/types.rs | 40 ++++++++++++++++++- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs index a306d42f60b..8c1c78406f4 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs @@ -7,6 +7,16 @@ use crate::types::*; use crate::utils::{self as util, delay_load}; use crate::com_provider::ExperimentalWebAuthnPluginOperationResponse; +// Authenticator Options from WebAuthn header +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct EXPERIMENTAL_WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS { + pub dwVersion: u32, + pub lUp: i32, // User presence: +1=TRUE, 0=Not defined, -1=FALSE + pub lUv: i32, // User verification: +1=TRUE, 0=Not defined, -1=FALSE + pub lRequireResidentKey: i32, // Resident key: +1=TRUE, 0=Not defined, -1=FALSE +} + // Windows API types for WebAuthn (from webauthn.h.sample) #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -20,6 +30,7 @@ pub struct EXPERIMENTAL_WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST { pub CredentialList: WEBAUTHN_CREDENTIAL_LIST, pub cbCborExtensionsMap: u32, pub pbCborExtensionsMap: *const u8, + pub pAuthenticatorOptions: *const EXPERIMENTAL_WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS, // Add other fields as needed... } @@ -109,7 +120,7 @@ pub unsafe fn decode_get_assertion_request(encoded_request: &[u8]) -> Result, pub allowed_credentials: Vec>, - pub user_verification: Option, + pub user_verification: Option, pub user_id: Option>, pub user_name: Option, pub user_display_name: Option, @@ -148,7 +159,7 @@ pub fn send_assertion_request(rpid: &str, transaction_id: &str, context: &Reques transaction_id: transaction_id.to_string(), client_data_hash, allowed_credentials: context.allowed_credentials.clone(), - user_verification: context.user_verification.unwrap_or(false), + user_verification: context.user_verification.unwrap_or_default(), }; util::message(&format!("Assertion request data - RP ID: {}, Client data hash: {} bytes, Allowed credentials: {}", diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_provider.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_provider.rs index 55a9b23e240..8ff9812bab4 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_provider.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_provider.rs @@ -212,6 +212,18 @@ impl EXPERIMENTAL_IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Imp Vec::new() }; + // Extract user verification requirement from authenticator options + let user_verification = if !decoded_request.pAuthenticatorOptions.is_null() { + let auth_options = &*decoded_request.pAuthenticatorOptions; + match auth_options.lUv { + 1 => Some(UserVerificationRequirement::Required), + -1 => Some(UserVerificationRequirement::Discouraged), + 0 | _ => Some(UserVerificationRequirement::Preferred), // Default or undefined + } + } else { + None + }; + // Create request context from properly decoded data let mut request_context = RequestContext::default(); request_context.rpid = Some(rpid.clone()); @@ -220,6 +232,7 @@ impl EXPERIMENTAL_IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Imp request_context.user_display_name = user_info.2; request_context.client_data_hash = Some(client_data_hash); request_context.supported_algorithms = supported_algorithms; + request_context.user_verification = user_verification; util::message(&format!("Make credential request - RP: {}, User: {}", rpid, @@ -336,10 +349,23 @@ impl EXPERIMENTAL_IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Imp hash_slice.to_vec() }; + // Extract user verification requirement from authenticator options + let user_verification = if !decoded_request.pAuthenticatorOptions.is_null() { + let auth_options = &*decoded_request.pAuthenticatorOptions; + match auth_options.lUv { + 1 => Some(UserVerificationRequirement::Required), + -1 => Some(UserVerificationRequirement::Discouraged), + 0 | _ => Some(UserVerificationRequirement::Preferred), // Default or undefined + } + } else { + None + }; + // Create request context from properly decoded data let mut request_context = RequestContext::default(); request_context.rpid = Some(rpid.clone()); request_context.client_data_hash = Some(client_data_hash); + request_context.user_verification = user_verification; // TODO: Extract allowed credentials from CredentialList if available util::message(&format!("Get assertion request - RP: {}", rpid)); diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/make_credential.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/make_credential.rs index 271e2911fb5..ac955a54812 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/make_credential.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/make_credential.rs @@ -51,6 +51,16 @@ pub struct WEBAUTHN_CREDENTIAL_LIST { pub pCredentials: *const u8, // Placeholder } +// Authenticator Options from WebAuthn header +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct EXPERIMENTAL_WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS { + pub dwVersion: u32, + pub lUp: i32, // User presence: +1=TRUE, 0=Not defined, -1=FALSE + pub lUv: i32, // User verification: +1=TRUE, 0=Not defined, -1=FALSE + pub lRequireResidentKey: i32, // Resident key: +1=TRUE, 0=Not defined, -1=FALSE +} + // Make Credential Request structure (from sample header) #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -66,6 +76,7 @@ pub struct EXPERIMENTAL_WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST { pub CredentialList: WEBAUTHN_CREDENTIAL_LIST, pub cbCborExtensionsMap: u32, pub pbCborExtensionsMap: *const u8, + pub pAuthenticatorOptions: *const EXPERIMENTAL_WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS, // Add other fields as needed... } @@ -203,7 +214,7 @@ pub fn send_registration_request(rpid: &str, transaction_id: &str, context: &Req user_id, user_name, client_data_hash, - user_verification: context.user_verification.unwrap_or(false), + user_verification: context.user_verification.unwrap_or_default(), supported_algorithms: context.supported_algorithms.clone(), }; diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/types.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/types.rs index 43aafd0b975..ef48944c116 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/types.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/types.rs @@ -1,5 +1,41 @@ use tokio::sync::oneshot; +/// User verification requirement as defined by WebAuthn spec +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum UserVerificationRequirement { + Required, + Preferred, + Discouraged, +} + +impl Default for UserVerificationRequirement { + fn default() -> Self { + UserVerificationRequirement::Preferred + } +} + +impl From for UserVerificationRequirement { + fn from(value: u32) -> Self { + match value { + 1 => UserVerificationRequirement::Required, + 2 => UserVerificationRequirement::Preferred, + 3 => UserVerificationRequirement::Discouraged, + _ => UserVerificationRequirement::Preferred, // Default fallback + } + } +} + +impl Into for UserVerificationRequirement { + fn into(self) -> String { + match self { + UserVerificationRequirement::Required => "required".to_string(), + UserVerificationRequirement::Preferred => "preferred".to_string(), + UserVerificationRequirement::Discouraged => "discouraged".to_string(), + } + } +} + /// Assertion request structure #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "camelCase")] @@ -8,7 +44,7 @@ pub struct PasskeyAssertionRequest { pub transaction_id: String, pub client_data_hash: Vec, pub allowed_credentials: Vec>, - pub user_verification: bool, + pub user_verification: UserVerificationRequirement, } /// Registration request structure @@ -20,7 +56,7 @@ pub struct PasskeyRegistrationRequest { pub user_id: Vec, pub user_name: String, pub client_data_hash: Vec, - pub user_verification: bool, + pub user_verification: UserVerificationRequirement, pub supported_algorithms: Vec, // COSE algorithm identifiers }