Skip to content

Commit

Permalink
WIP: more docs, remove unneeded code
Browse files Browse the repository at this point in the history
  • Loading branch information
micolous committed Oct 7, 2022
1 parent 543010d commit a8d3e74
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 95 deletions.
2 changes: 1 addition & 1 deletion webauthn-authenticator-rs/src/win10/clientdata.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Client data wrappers.
//! Wrappers for [CollectedClientData].
use base64urlsafedata::Base64UrlSafeData;
use std::collections::BTreeMap;
use std::pin::Pin;
Expand Down
2 changes: 1 addition & 1 deletion webauthn-authenticator-rs/src/win10/cose.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! COSE algorithm identifier wrapper.
//! Wrappers for [PubKeyCredParams].
use crate::prelude::WebauthnCError;
use std::pin::Pin;
use webauthn_rs_proto::PubKeyCredParams;
Expand Down
2 changes: 1 addition & 1 deletion webauthn-authenticator-rs/src/win10/credential.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Credential wrapper.
//! Wrappers for [AllowCredentials] and [PublicKeyCredentialDescriptor].
use crate::prelude::WebauthnCError;
use base64urlsafedata::Base64UrlSafeData;
use std::pin::Pin;
Expand Down
52 changes: 38 additions & 14 deletions webauthn-authenticator-rs/src/win10/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Wrappers for extensions.
use crate::prelude::WebauthnCError;
use std::ffi::c_void;
use std::pin::Pin;
Expand All @@ -14,15 +15,14 @@ use windows::{
Win32::{Foundation::BOOL, Networking::WindowsWebServices::*},
};

// const WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET: &HSTRING = w!("hmac-secret");
// const WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT: &HSTRING = w!("credProtect");

#[derive(Debug)]
pub struct WinCredBlobSet {
native: WEBAUTHN_CRED_BLOB_EXTENSION,
blob: CredBlobSet,
}

/// Represents a single extension for MakeCredential requests, analogous to a
/// single [RequestRegistrationExtensions] field.
#[derive(Debug)]
pub(crate) enum WinExtensionMakeCredentialRequest {
HmacSecret(BOOL),
Expand All @@ -31,19 +31,28 @@ pub(crate) enum WinExtensionMakeCredentialRequest {
MinPinLength(BOOL),
}

/// Represents a single extension for GetAssertion requests, analogous to a
/// single [RequestAuthenticationExtensions] field.
#[derive(Debug)]
pub(crate) enum WinExtensionGetAssertionRequest {
CredBlob(BOOL),
}

/// Generic request extension trait, for abstracting between
/// [WinExtensionMakeCredentialRequest] and [WinExtensionGetAssertionRequest].
pub(crate) trait WinExtensionRequestType
where
Self: Sized,
{
/// Extension identier, as string.
fn identifier(&self) -> &str;
/// Length of the native data structure, in bytes.
fn len(&self) -> u32;
/// Pointer to the native data structure.
fn ptr(&mut self) -> *mut c_void;
/// The `webauthn-authenticator-rs` type which this wraps.
type WrappedType;
/// Converts the [Self::WrappedType] to a [Vec] of Windows API types.
fn to_native(e: &Self::WrappedType) -> Vec<Self>;
}

Expand Down Expand Up @@ -128,6 +137,7 @@ impl WinExtensionRequestType for WinExtensionGetAssertionRequest {
}

impl From<&CredProtect> for WinExtensionMakeCredentialRequest {
/// Converts [CredProtect] into [WEBAUTHN_CRED_PROTECT_EXTENSION_IN].
fn from(c: &CredProtect) -> Self {
Self::CredProtect(WEBAUTHN_CRED_PROTECT_EXTENSION_IN {
dwCredProtect: c.credential_protection_policy as u32,
Expand Down Expand Up @@ -161,17 +171,8 @@ impl WinCredBlobSet {
}
}

enum WinExtensionMakeCredentialResponse {
HmacSecret(bool),
CredProtect(u32),
CredBlob(bool),
MinPinLength(u32),
}

enum WinExtensionGetAssertionResponse {
CredBlob(Vec<u8>),
}

/// Reads a [WEBAUTHN_EXTENSION] containing a Windows-specific primitive type,
/// converting it into a Rust datatype.
fn read_extension<'a, T: 'a, U: From<&'a T>>(
e: &'a WEBAUTHN_EXTENSION,
) -> Result<U, WebauthnCError> {
Expand All @@ -182,6 +183,7 @@ fn read_extension<'a, T: 'a, U: From<&'a T>>(
Ok(v.into())
}

/// Reads and copies a [WEBAUTHN_EXTENSION] containing a primitive type.
fn read_extension2<'a, T: 'a + Clone>(e: &'a WEBAUTHN_EXTENSION) -> Result<T, WebauthnCError> {
if (e.cbExtension as usize) < std::mem::size_of::<T>() {
return Err(WebauthnCError::Internal);
Expand All @@ -190,8 +192,19 @@ fn read_extension2<'a, T: 'a + Clone>(e: &'a WEBAUTHN_EXTENSION) -> Result<T, We
Ok(v.clone())
}

/// Represents a single extension for MakeCredential responses, analogous to a
/// single [RegistrationExtensionsClientOutputs] field.
enum WinExtensionMakeCredentialResponse {
HmacSecret(bool),
CredProtect(u32),
CredBlob(bool),
MinPinLength(u32),
}

impl TryFrom<&WEBAUTHN_EXTENSION> for WinExtensionMakeCredentialResponse {
type Error = WebauthnCError;

/// Reads a [WEBAUTHN_EXTENSION] for a response to a MakeCredential call.
fn try_from(e: &WEBAUTHN_EXTENSION) -> Result<Self, WebauthnCError> {
let id = unsafe {
e.pwszExtensionIdentifier
Expand Down Expand Up @@ -220,6 +233,8 @@ impl TryFrom<&WEBAUTHN_EXTENSION> for WinExtensionMakeCredentialResponse {
}
}

/// Converts [WEBAUTHN_EXTENSIONS] for a response to MakeCredential call into
/// a `webauthn-authenticator-rs` [RegistrationExtensionsClientOutputs] type.
pub fn native_to_registration_extensions(
native: &WEBAUTHN_EXTENSIONS,
) -> Result<RegistrationExtensionsClientOutputs, WebauthnCError> {
Expand All @@ -245,9 +260,16 @@ pub fn native_to_registration_extensions(
Ok(o)
}

/// Represents a single extension for GetAssertion responses, analogous to a
/// single [AuthenticationExtensionsClientOutputs] field.
enum WinExtensionGetAssertionResponse {
CredBlob(Vec<u8>),
}

impl TryFrom<&WEBAUTHN_EXTENSION> for WinExtensionGetAssertionResponse {
type Error = WebauthnCError;

/// Reads a [WEBAUTHN_EXTENSION] for a response to a GetAssertion call.
fn try_from(e: &WEBAUTHN_EXTENSION) -> Result<Self, Self::Error> {
let id = unsafe {
e.pwszExtensionIdentifier
Expand All @@ -267,6 +289,8 @@ impl TryFrom<&WEBAUTHN_EXTENSION> for WinExtensionGetAssertionResponse {
}
}

/// Converts [WEBAUTHN_EXTENSIONS] for a response to GetAssertion call into
/// a `webauthn-authenticator-rs` [AuthenticationExtensionsClientOutputs] type.
pub fn native_to_assertion_extensions(
native: &WEBAUTHN_EXTENSIONS,
) -> Result<AuthenticationExtensionsClientOutputs, WebauthnCError> {
Expand Down
144 changes: 66 additions & 78 deletions webauthn-authenticator-rs/src/win10/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! Bindings for Windows 10 Webauthn API.
//! Bindings for Windows 10 WebAuthn API.
//!
//! This API is available in Windows 10 bulid 1903 and later.
//!
//! ## API docs
//!
//! * [MSDN: WebAuthn](https://learn.microsoft.com/en-us/windows/win32/api/webauthn/)
//! * [MSDN: WebAuthn API](https://learn.microsoft.com/en-us/windows/win32/api/webauthn/)
//! * [webauthn.h](github.com/microsoft/webauthn) (describes versions)
//! * [windows-rs API](https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WindowsWebServices/index.html)
mod clientdata;
mod cose;
mod credential;
Expand All @@ -16,14 +17,13 @@ mod rp;
mod user;

use crate::error::WebauthnCError;
use crate::win10::extensions::native_to_assertion_extensions;
use crate::win10::{
clientdata::{creation_to_clientdata, get_to_clientdata, WinClientData},
cose::WinCoseCredentialParameters,
credential::{native_to_transports, WinCredentialList},
extensions::{
native_to_registration_extensions, WinExtensionGetAssertionRequest,
WinExtensionMakeCredentialRequest, WinExtensionsRequest,
native_to_assertion_extensions, native_to_registration_extensions,
WinExtensionGetAssertionRequest, WinExtensionMakeCredentialRequest, WinExtensionsRequest,
},
gui::Window,
native::{WinPtr, WinWrapper},
Expand All @@ -44,6 +44,9 @@ use windows::{
Win32::{Foundation::BOOL, Networking::WindowsWebServices::*},
};

use std::slice::from_raw_parts;

/// Authenticator backend for Windows 10 WebAuthn API.
pub struct Win10 {}

impl Default for Win10 {
Expand Down Expand Up @@ -165,31 +168,32 @@ impl AuthenticatorBackend for Win10 {
// trace!("got result from WebAuthNAuthenticatorMakeCredential");
// trace!("{:?}", (*a));

let cred_id =
copy_ptr(a.cbCredentialId, a.pbCredentialId).ok_or(WebauthnCError::Internal)?;
let attesation_object = copy_ptr(a.cbAttestationObject, a.pbAttestationObject)
.ok_or(WebauthnCError::Internal)?;
let type_: String = unsafe {
a.pwszFormatType
unsafe {
let cred_id: Vec<u8> =
from_raw_parts(a.pbCredentialId, a.cbCredentialId as usize).into();
let attesation_object =
from_raw_parts(a.pbAttestationObject, a.cbAttestationObject as usize).into();
let type_: String = a
.pwszFormatType
.to_string()
.map_err(|_| WebauthnCError::Internal)?
};
.map_err(|_| WebauthnCError::Internal)?;

let id: String = Base64UrlSafeData(cred_id.clone()).to_string();
let id: String = Base64UrlSafeData(cred_id.clone()).to_string();

Ok(RegisterPublicKeyCredential {
id,
raw_id: Base64UrlSafeData(cred_id),
type_,
extensions: native_to_registration_extensions(&a.Extensions)?,
response: AuthenticatorAttestationResponseRaw {
attestation_object: Base64UrlSafeData(attesation_object),
client_data_json: Base64UrlSafeData(
clientdata.client_data_json().as_bytes().to_vec(),
),
transports: Some(native_to_transports(a.dwUsedTransport)),
},
})
Ok(RegisterPublicKeyCredential {
id,
raw_id: Base64UrlSafeData(cred_id),
type_,
extensions: native_to_registration_extensions(&a.Extensions)?,
response: AuthenticatorAttestationResponseRaw {
attestation_object: Base64UrlSafeData(attesation_object),
client_data_json: Base64UrlSafeData(
clientdata.client_data_json().as_bytes().to_vec(),
),
transports: Some(native_to_transports(a.dwUsedTransport)),
},
})
}
}

/// Perform an authentication action using Windows WebAuth API.
Expand Down Expand Up @@ -288,79 +292,63 @@ impl AuthenticatorBackend for Win10 {
drop(hwnd);
// trace!("got result from WebAuthNAuthenticatorGetAssertion");

let user_id = copy_ptr(a.cbUserId, a.pbUserId);
let authenticator_data = copy_ptr(a.cbAuthenticatorData, a.pbAuthenticatorData)
.ok_or(WebauthnCError::Internal)?;
let signature = copy_ptr(a.cbSignature, a.pbSignature).ok_or(WebauthnCError::Internal)?;
unsafe {
let user_id = from_raw_parts(a.pbUserId, a.cbUserId as usize).into();
let authenticator_data =
from_raw_parts(a.pbAuthenticatorData, a.cbAuthenticatorData as usize).into();
let signature = from_raw_parts(a.pbSignature, a.cbSignature as usize).into();

let credential_id = Base64UrlSafeData(
copy_ptr(a.Credential.cbId, a.Credential.pbId).ok_or(WebauthnCError::Internal)?,
);
let type_: String = unsafe {
a.Credential
let credential_id = Base64UrlSafeData(
from_raw_parts(a.Credential.pbId, a.Credential.cbId as usize).into(),
);
let type_ = a
.Credential
.pwszCredentialType
.to_string()
.map_err(|_| WebauthnCError::Internal)?
};
.map_err(|_| WebauthnCError::Internal)?;

let mut extensions = native_to_assertion_extensions(&a.Extensions)?;
extensions.appid = Some(app_id_used.into());
let mut extensions = native_to_assertion_extensions(&a.Extensions)?;
extensions.appid = Some(app_id_used.into());

Ok(PublicKeyCredential {
id: credential_id.to_string(),
raw_id: credential_id,
response: AuthenticatorAssertionResponseRaw {
authenticator_data: Base64UrlSafeData(authenticator_data),
client_data_json: Base64UrlSafeData(
clientdata.client_data_json().as_bytes().to_vec(),
),
signature: Base64UrlSafeData(signature),
user_handle: user_id.map(Base64UrlSafeData),
},
type_,
extensions,
})
Ok(PublicKeyCredential {
id: credential_id.to_string(),
raw_id: credential_id,
response: AuthenticatorAssertionResponseRaw {
authenticator_data: Base64UrlSafeData(authenticator_data),
client_data_json: Base64UrlSafeData(
clientdata.client_data_json().as_bytes().to_vec(),
),
signature: Base64UrlSafeData(signature),
user_handle: Some(Base64UrlSafeData(user_id)),
},
type_,
extensions,
})
}
}
}

/// Converts an [AuthenticatorAttachment] into a value for
/// [WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS::dwAuthenticatorAttachment]
fn attachment_to_native(attachment: Option<AuthenticatorAttachment>) -> u32 {
use AuthenticatorAttachment::*;
match attachment {
None => WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY,
Some(AuthenticatorAttachment::CrossPlatform) => {
WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM
}
Some(AuthenticatorAttachment::Platform) => WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM,
Some(CrossPlatform) => WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM,
Some(Platform) => WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM,
}
}

/// Converts a [UserVerificationPolicy] into a value for
/// [WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS::dwUserVerificationRequirement]
fn user_verification_to_native(policy: Option<&UserVerificationPolicy>) -> u32 {
use UserVerificationPolicy::*;
match policy {
None => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY,
Some(p) => match p {
UserVerificationPolicy::Required => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED,
UserVerificationPolicy::Preferred => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED,
UserVerificationPolicy::Discouraged_DO_NOT_USE => {
WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED
}
Required => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED,
Preferred => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED,
Discouraged_DO_NOT_USE => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED,
},
}
}

fn copy_ptr<T>(cb: u32, pb: *const T) -> Option<Vec<T>>
where
T: Clone,
{
if pb.is_null() || cb == 0 {
return None;
}
let mut dst: Vec<T> = Vec::with_capacity(cb as usize);
unsafe {
std::ptr::copy(pb, dst.as_mut_ptr(), cb as usize);
dst.set_len(cb as usize)
}
Some(dst)
}
1 change: 1 addition & 0 deletions webauthn-authenticator-rs/src/win10/rp.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Wrappers for [RelyingParty].
use std::pin::Pin;

use webauthn_rs_proto::RelyingParty;
Expand Down
1 change: 1 addition & 0 deletions webauthn-authenticator-rs/src/win10/user.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Wrappers for [User].
use std::pin::Pin;

use webauthn_rs_proto::User;
Expand Down

0 comments on commit a8d3e74

Please sign in to comment.