diff --git a/apps/desktop/desktop_native/win_webauthn/src/plugin/crypto.rs b/apps/desktop/desktop_native/win_webauthn/src/plugin/crypto.rs index 2db2020838f..622fc04197b 100644 --- a/apps/desktop/desktop_native/win_webauthn/src/plugin/crypto.rs +++ b/apps/desktop/desktop_native/win_webauthn/src/plugin/crypto.rs @@ -20,21 +20,51 @@ use windows::{ use crate::{util::webauthn_call, ErrorKind, WinWebAuthnError}; -webauthn_call!("WebAuthNPluginGetUserVerificationPublicKey" as fn webauthn_plugin_get_user_verification_public_key( - rclsid: *const GUID, - pcbPublicKey: *mut u32, - ppbPublicKey: *mut *mut u8) -> HRESULT); // Free using WebAuthNPluginFreePublicKeyResponse +webauthn_call!("WebAuthNPluginGetUserVerificationPublicKey" as +/// Retrieve the public key used to verify user verification responses from the OS. +/// +/// Returns [S_OK](windows::Win32::Foundation::S_OK) on success. +/// +/// # Arguments +/// - `rclsid`: The CLSID corresponding to this plugin's COM server. +/// - `pcbPublicKey`: A pointer to an unsigned integer, which will be filled in with the length of the buffer at `ppbPublicKey`. +/// - `ppbPublicKey`: A pointer to a [BCRYPT_PUBLIC_KEY_BLOB], which will be written to on success. +/// On success, this must be freed by a call to [webauthn_plugin_free_public_key_response]. +fn webauthn_plugin_get_user_verification_public_key( + rclsid: *const GUID, + pcbPublicKey: *mut u32, + ppbPublicKey: *mut *mut u8 +) -> HRESULT); // Free using WebAuthNPluginFreePublicKeyResponse -webauthn_call!("WebAuthNPluginGetOperationSigningPublicKey" as fn webauthn_plugin_get_operation_signing_public_key( - rclsid: *const GUID, - pcbOpSignPubKey: *mut u32, - ppbOpSignPubKey: *mut *mut u8 - ) -> HRESULT); // Free using WebAuthNPluginFreePublicKeyResponse +webauthn_call!("WebAuthNPluginGetOperationSigningPublicKey" as +/// Retrieve the public key used to verify plugin operation reqeusts from the OS. +/// +/// Returns [S_OK](windows::Win32::Foundation::S_OK) on success. +/// +/// # Arguments +/// - `rclsid`: The CLSID corresponding to this plugin's COM server. +/// - `pcbOpSignPubKey`: A pointer to an unsigned integer, which will be filled in with the length of the buffer at `ppbOpSignPubKey`. +/// - `ppbOpSignPubKey`: An indirect pointer to a [BCRYPT_PUBLIC_KEY_BLOB], which will be written to on success. +/// On success, this must be freed by a call to [webauthn_plugin_free_public_key_response]. +fn webauthn_plugin_get_operation_signing_public_key( + rclsid: *const GUID, + pcbOpSignPubKey: *mut u32, + ppbOpSignPubKey: *mut *mut u8 +) -> HRESULT); // Free using WebAuthNPluginFreePublicKeyResponse -webauthn_call!("WebAuthNPluginFreePublicKeyResponse" as fn webauthn_plugin_free_public_key_response( +webauthn_call!("WebAuthNPluginFreePublicKeyResponse" as +/// Free public key memory retrieved from the OS. +/// +/// # Arguments +/// - `pbOpSignPubKey`: A pointer to a [BCRYPT_PUBLIC_KEY_BLOB] retrieved from a method in this library. +fn webauthn_plugin_free_public_key_response( pbOpSignPubKey: *mut u8 ) -> ()); +/// Retrieve the public key used to verify plugin operation reqeusts from the OS. +/// +/// # Arguments +/// - `clsid`: The CLSID corresponding to this plugin's COM server. pub(super) fn get_operation_signing_public_key( clsid: &GUID, ) -> Result { @@ -66,6 +96,10 @@ pub(super) fn get_operation_signing_public_key( } } +/// Retrieve the public key used to verify user verification responses from the OS. +/// +/// # Arguments +/// - `clsid`: The CLSID corresponding to this plugin's COM server. pub(super) fn get_user_verification_public_key( clsid: &GUID, ) -> Result { @@ -95,6 +129,7 @@ pub(super) fn get_user_verification_public_key( } } +/// Verify a public key signature over a hash using Windows Crypto APIs. fn verify_signature( public_key: &SigningKey, hash: &[u8], @@ -128,6 +163,18 @@ fn verify_signature( if public_key.len() < size_of::() { return Err(windows::core::Error::from_hresult(E_INVALIDARG)); } + + // BCRYPT_KEY_BLOB is a base structure for all types of keys used in the BCRYPT API. + // Cf. https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_key_blob. + // + // The first field is a "magic" field that denotes the algorithm (RSA, + // P-256, P-384, etc.) and subtype (public, private; RSA also has a + // "full private" key that includes the key exponents and coefficients). + // + // The exact key types which the OS can return from webauthn.dll + // operations is not documented, but we have observed at least RSA + // public keys being used. For forward compatibility, we'll implement + // RSA, P-256, P-384 and P-512. let key_blob: &BCRYPT_KEY_BLOB = &*public_key.as_ptr().cast(); tracing::debug!(" got key magic: {}", key_blob.Magic); let (padding_info, cng_flags) = if key_blob.Magic == BCRYPT_RSAPUBLIC_MAGIC.0 { @@ -156,6 +203,7 @@ fn verify_signature( } } +/// Calculate a SHA-256 hash over some data. pub(super) fn hash_sha256(data: &[u8]) -> Result, windows::core::Error> { unsafe { // Hash data @@ -256,7 +304,9 @@ impl Drop for BcryptHash { /// Signing key for an operation request or user verification response buffer. pub struct SigningKey { + /// Length of buffer cbPublicKey: u32, + /// Pointer to a [BCRYPT_KEY_BLOB] pbPublicKey: NonNull, } @@ -272,6 +322,7 @@ impl SigningKey { }) } } + impl Drop for SigningKey { fn drop(&mut self) { unsafe { @@ -279,6 +330,7 @@ impl Drop for SigningKey { } } } + impl AsRef<[u8]> for SigningKey { fn as_ref(&self) -> &[u8] { // SAFETY: We only support platforms where usize >= 32-bts diff --git a/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs b/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs index f6a9d8575c0..f0f19d9d144 100644 --- a/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs +++ b/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs @@ -166,7 +166,9 @@ impl PluginAddAuthenticatorOptions { #[repr(C)] #[derive(Debug, Copy, Clone)] pub(super) struct WebAuthnPluginAddAuthenticatorResponse { + /// Size in bytes of the public key pointed to by `pbOpSignPubKey`. cbOpSignPubKey: u32, + /// Pointer to a [BCRYPT_KEY_BLOB](windows::Win32::Security::Cryptography::BCRYPT_KEY_BLOB). pbOpSignPubKey: *mut u8, } @@ -217,14 +219,27 @@ impl Drop for PluginAddAuthenticatorResponse { } webauthn_call!("WebAuthNPluginAddAuthenticator" as +/// Register authenticator info for a plugin COM server. +/// +/// Returns [S_OK](windows::Win32::Foundation::S_OK) on success. +/// +/// # Arguments +/// - `pPluginAddAuthenticatorOptions`: Details about the authenticator to set. +/// - `ppPluginAddAuthenticatorResponse`: +/// An indirect pointer to a [WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE], which will be written to on success. +/// If the request succeeds, the data must be freed by a call to [webauthn_plugin_free_add_authenticator_response]. fn webauthn_plugin_add_authenticator( pPluginAddAuthenticatorOptions: *const WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS, ppPluginAddAuthenticatorResponse: *mut *mut WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE ) -> HRESULT); webauthn_call!("WebAuthNPluginFreeAddAuthenticatorResponse" as +/// Free memory from a [WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE]. +/// +/// # Arguments +/// - `pPluginAddAuthenticatorResponse`: An pointer to a [WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE] to be freed. fn webauthn_plugin_free_add_authenticator_response( - pPluginAddAuthenticatorOptions: *mut WebAuthnPluginAddAuthenticatorResponse + pPluginAddAuthenticatorResponse: *mut WebAuthnPluginAddAuthenticatorResponse ) -> ()); // Credential syncing types @@ -271,15 +286,34 @@ pub struct PluginCredentialDetails { pub user_display_name: String, } -webauthn_call!("WebAuthNPluginAuthenticatorAddCredentials" as fn webauthn_plugin_authenticator_add_credentials( +webauthn_call!("WebAuthNPluginAuthenticatorAddCredentials" as +/// Add metadata for a list of WebAuthn credentials to the autofill store for +/// this plugin authenticator. +/// +/// This will make the credentials available for discovery in Windows Hello +/// WebAuthn autofill dialogs. +/// +/// Returns [S_OK](windows::Win32::Foundation::S_OK) on success. +/// +/// # Arguments +/// - `rclsid`: The CLSID corresponding to this plugin's COM server. +/// - `cCredentialDetails`: The number of credentials in the array pointed to by `pCredentialDetails`. +/// - `pCredentialDetails`: An array of credential metadata. +fn webauthn_plugin_authenticator_add_credentials( rclsid: *const GUID, cCredentialDetails: u32, pCredentialDetails: *const WEBAUTHN_PLUGIN_CREDENTIAL_DETAILS ) -> HRESULT); -webauthn_call!("WebAuthNPluginAuthenticatorRemoveAllCredentials" as fn webauthn_plugin_authenticator_remove_all_credentials( - rclsid: *const GUID -) -> HRESULT); +webauthn_call!("WebAuthNPluginAuthenticatorRemoveAllCredentials" as +/// Removes metadata for all credentials currently stored in the autofill store +/// for this plugin authenticator. +/// +/// Returns [S_OK](windows::Win32::Foundation::S_OK) on success. +/// +/// # Arguments +/// - `rclsid`: The CLSID corresponding to this plugin's COM server. +fn webauthn_plugin_authenticator_remove_all_credentials(rclsid: *const GUID) -> HRESULT); #[repr(C)] #[derive(Debug)] @@ -323,13 +357,34 @@ pub struct PluginUserVerificationResponse { pub signature: Vec, } -webauthn_call!("WebAuthNPluginPerformUserVerification" as fn webauthn_plugin_perform_user_verification( +webauthn_call!("WebAuthNPluginPerformUserVerification" as +/// Request user verification for a WebAuthn operation. +/// +/// The OS will prompt the user for verification, and if the user is +/// successfully verified, will write a signature to `ppbResponse`, which must +/// be freed by a call to [webauthn_plugin_free_user_verification_response]. +/// +/// The signature is over the SHA-256 hash of the original WebAuthn operation request buffer +/// corresponding to `pPluginUserVerification.rguidTransactionId`. It can be +/// verified using the user verification public key, which can be retrieved +/// using +/// [webauthn_plugin_get_user_verification_public_key][crate::plugin::crypto::webauthn_plugin_get_user_verification_public_key]. +/// +/// This request will block while the user interacts with the dialog. +/// +/// # Arguments +/// - `pPluginUserVerification`: The user verification prompt and transaction context for the request. +/// - `pcbResponse`: Length in bytes of the signature. +/// - `ppbResponse`: The signature of the request. +fn webauthn_plugin_perform_user_verification( pPluginUserVerification: *const WEBAUTHN_PLUGIN_USER_VERIFICATION_REQUEST, pcbResponse: *mut u32, ppbResponse: *mut *mut u8 ) -> HRESULT); -webauthn_call!("WebAuthNPluginFreeUserVerificationResponse" as fn webauthn_plugin_free_user_verification_response( +webauthn_call!("WebAuthNPluginFreeUserVerificationResponse" as +/// Free a user verification response received from a call to [webauthn_plugin_perform_user_verification]. +fn webauthn_plugin_free_user_verification_response( pbResponse: *mut u8 ) -> ()); @@ -594,13 +649,32 @@ impl Drop for PluginMakeCredentialRequest { } // Windows API function signatures for decoding make credential requests -webauthn_call!("WebAuthNDecodeMakeCredentialRequest" as fn webauthn_decode_make_credential_request( +webauthn_call!("WebAuthNDecodeMakeCredentialRequest" as +/// Decodes a CTAP CBOR `authenticatorMakeCredential` request. +/// +/// On success, a [WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST] will be written to +/// `ppMakeCredentialRequest`, which must be freed by a call to +/// [webauthn_free_decoded_make_credential_request]. +/// +/// # Arguments +/// - `pbEncoded`: a COM-allocated buffer pointing to a CTAP CBOR make credential request. +/// - `ppMakeCredentialRequest`: An indirect pointer to a [WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST]. +/// +/// # Safety +/// - `pbEncoded` must have been allocated by Windows COM. +/// - `pbEncoded` must be non-null and have the length specified in cbEncoded. +fn webauthn_decode_make_credential_request( cbEncoded: u32, pbEncoded: *const u8, ppMakeCredentialRequest: *mut *mut WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST ) -> HRESULT); -webauthn_call!("WebAuthNFreeDecodedMakeCredentialRequest" as fn webauthn_free_decoded_make_credential_request( +webauthn_call!("WebAuthNFreeDecodedMakeCredentialRequest" as +/// Frees a decoded make credential request from [webauthn_free_decoded_make_credential_request]. +/// +/// # Arguments +/// - `pMakeCredentialRequest`: An pointer to a [WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST] to be freed. +fn webauthn_free_decoded_make_credential_request( pMakeCredentialRequest: *mut WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST ) -> ()); @@ -823,10 +897,20 @@ impl TryFrom for WEBAUTHN_CREDENTIAL_ATTESTATION { } } -webauthn_call!("WebAuthNEncodeMakeCredentialResponse" as fn webauthn_encode_make_credential_response( - cbEncoded: *const WEBAUTHN_CREDENTIAL_ATTESTATION, - pbEncoded: *mut u32, - response_bytes: *mut *mut u8 +webauthn_call!("WebAuthNEncodeMakeCredentialResponse" as +/// Encode a credential attestation response to a COM-allocated byte buffer +/// containing a CTAP CBOR `authenticatorMakeCredential` response structure. +/// +/// Returns [S_OK](windows::Win32::Foundation::S_OK) on success. +/// +/// # Arguments +/// - `pCredentialAttestation`: A pointer to [WEBAUTHN_CREDENTIAL_ATTESTATION] to encode. +/// - `pcbResp`: A pointer to a u32, which will be filled with the length of the response buffer. +/// - `ppbResponse`: An indirect pointer to a byte buffer, which will be written to on succces. +fn webauthn_encode_make_credential_response( + pCredentialAttestation: *const WEBAUTHN_CREDENTIAL_ATTESTATION, + pcbResp: *mut u32, + ppbResponse: *mut *mut u8 ) -> HRESULT); // GetAssertion types @@ -1044,13 +1128,32 @@ impl Drop for PluginGetAssertionRequest { } // Windows API function signatures for decoding get assertion requests -webauthn_call!("WebAuthNDecodeGetAssertionRequest" as fn webauthn_decode_get_assertion_request( +webauthn_call!("WebAuthNDecodeGetAssertionRequest" as +/// Decodes a CTAP GetAssertion request. +/// +/// On success, a [WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST] will be written to +/// `ppGetAssertionRequest`, which must be freed by a call to +/// [webauthn_free_decoded_get_assertion_request]. +/// +/// # Arguments +/// - `pbEncoded`: a COM-allocated buffer pointing to a CTAP CBOR get assertion request. +/// - `ppGetAssertionRequest`: An indirect pointer to a [WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST]. +/// +/// # Safety +/// - `pbEncoded` must have been allocated by Windows COM. +/// - `pbEncoded` must be non-null and have the length specified in cbEncoded. +fn webauthn_decode_get_assertion_request( cbEncoded: u32, pbEncoded: *const u8, ppGetAssertionRequest: *mut *mut WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST ) -> HRESULT); -webauthn_call!("WebAuthNFreeDecodedGetAssertionRequest" as fn webauthn_free_decoded_get_assertion_request( +webauthn_call!("WebAuthNFreeDecodedGetAssertionRequest" as +/// Frees a decoded get assertion request from [webauthn_free_decoded_get_assertion_request]. +/// +/// # Arguments +/// - `pGetAssertionRequest`: An pointer to a [WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST] to be freed. +fn webauthn_free_decoded_get_assertion_request( pGetAssertionRequest: *mut WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST ) -> ());