1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-03 02:03:53 +00:00

Properly handle uv requirement

This commit is contained in:
Anders Åberg
2025-07-04 10:55:17 +02:00
parent 3ae74483dc
commit dafbfdb920
4 changed files with 89 additions and 5 deletions

View File

@@ -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<Dec
pub struct RequestContext {
pub rpid: Option<String>,
pub allowed_credentials: Vec<Vec<u8>>,
pub user_verification: Option<bool>,
pub user_verification: Option<UserVerificationRequirement>,
pub user_id: Option<Vec<u8>>,
pub user_name: Option<String>,
pub user_display_name: Option<String>,
@@ -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: {}",

View File

@@ -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));

View File

@@ -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(),
};

View File

@@ -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<u32> for UserVerificationRequirement {
fn from(value: u32) -> Self {
match value {
1 => UserVerificationRequirement::Required,
2 => UserVerificationRequirement::Preferred,
3 => UserVerificationRequirement::Discouraged,
_ => UserVerificationRequirement::Preferred, // Default fallback
}
}
}
impl Into<String> 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<u8>,
pub allowed_credentials: Vec<Vec<u8>>,
pub user_verification: bool,
pub user_verification: UserVerificationRequirement,
}
/// Registration request structure
@@ -20,7 +56,7 @@ pub struct PasskeyRegistrationRequest {
pub user_id: Vec<u8>,
pub user_name: String,
pub client_data_hash: Vec<u8>,
pub user_verification: bool,
pub user_verification: UserVerificationRequirement,
pub supported_algorithms: Vec<i32>, // COSE algorithm identifiers
}