Skip to content

Commit

Permalink
wire up more of the request, make test mode work
Browse files Browse the repository at this point in the history
  • Loading branch information
micolous committed Dec 30, 2023
1 parent 4bbf962 commit 13835d6
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 42 deletions.
47 changes: 41 additions & 6 deletions webauthn-authenticator-rs/examples/authenticate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::io::{stdin, stdout, Write};
use std::time::Duration;

use clap::clap_derive::ValueEnum;
#[cfg(any(feature = "cable", feature = "softtoken"))]
#[cfg(any(feature = "cable", feature = "softtoken", feature = "win10-rdp"))]
use clap::Args;
use clap::{Parser, Subcommand};
#[cfg(feature = "cable")]
Expand All @@ -29,7 +29,7 @@ use webauthn_authenticator_rs::ui::{Cli, UiCallback};
use webauthn_authenticator_rs::AuthenticatorBackend;
use webauthn_rs_core::proto::RequestAuthenticationExtensions;
use webauthn_rs_core::WebauthnCore as Webauthn;
use webauthn_rs_proto::{AttestationConveyancePreference, COSEAlgorithm, UserVerificationPolicy};
use webauthn_rs_proto::{AttestationConveyancePreference, COSEAlgorithm, UserVerificationPolicy, AuthenticatorAttachment};

#[derive(Debug, clap::Parser)]
#[clap(about = "Register and authenticate test")]
Expand All @@ -41,6 +41,10 @@ pub struct CliParser {
/// User verification policy for the request.
#[clap(short, long, value_enum, default_value_t)]
verification_policy: UvPolicy,

/// Authenticator attachment policy for the request.
#[clap(short, long, value_enum, default_value_t)]
attachment: Attachment,
}

#[derive(ValueEnum, Clone, Default, Debug)]
Expand All @@ -61,6 +65,24 @@ impl From<UvPolicy> for UserVerificationPolicy {
}
}

#[derive(ValueEnum, Clone, Default, Debug)]
pub enum Attachment {
#[default]
Any,
Platform,
CrossPlatform,
}

impl From<Attachment> for Option<AuthenticatorAttachment> {
fn from(value: Attachment) -> Self {
match value {
Attachment::Any => None,
Attachment::Platform => Some(AuthenticatorAttachment::Platform),
Attachment::CrossPlatform => Some(AuthenticatorAttachment::CrossPlatform),
}
}
}

#[cfg(feature = "ctap2")]
async fn select_transport<U: UiCallback>(ui: &U) -> impl AuthenticatorBackend + '_ {
use futures::StreamExt;
Expand Down Expand Up @@ -134,6 +156,15 @@ impl CableOpt {
}
}


#[cfg(feature = "win10-rdp")]
#[derive(Debug, Args, Clone)]
pub struct Win10RdpOpt {
#[clap(long)]
pub test_mode: bool,
}


#[derive(Debug, Clone, Subcommand)]
enum Provider {
#[cfg(feature = "softtoken")]
Expand Down Expand Up @@ -163,7 +194,7 @@ enum Provider {

#[cfg(feature = "win10-rdp")]
/// Windows 10 WebAuthn API, via an RDP Virtual Channel.
Win10Rdp,
Win10Rdp(Win10RdpOpt),
}

impl Provider {
Expand Down Expand Up @@ -215,8 +246,12 @@ impl Provider {
#[cfg(feature = "win10")]
Provider::Win10 => Box::<webauthn_authenticator_rs::win10::Win10>::default(),
#[cfg(feature = "win10-rdp")]
Provider::Win10Rdp => {
Box::new(webauthn_authenticator_rs::win10::rdp::Win10Rdp::new().unwrap())
Provider::Win10Rdp(o) => {
let mut rdp = webauthn_authenticator_rs::win10::rdp::Win10Rdp::new().unwrap();
if o.test_mode {
rdp.enable_test_mode();
}
Box::new(rdp)
}
}
}
Expand Down Expand Up @@ -266,7 +301,7 @@ async fn main() {
None,
COSEAlgorithm::secure_algs(),
false,
None,
opt.attachment.into(),
false,
)
.unwrap();
Expand Down
62 changes: 38 additions & 24 deletions webauthn-authenticator-rs/src/win10/rdp/message.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use serde_bytes::{ByteArray, ByteBuf};
use uuid::Uuid;
use webauthn_rs_proto::AuthenticatorTransport;

/// <https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpewa/3012640f-f57a-45a4-aa87-e2afbad42a68>
#[derive(Default, Deserialize, Serialize, Debug, Clone, PartialEq)]
Expand All @@ -22,12 +23,12 @@ pub struct ChannelRequest {
#[serde(rename_all = "camelCase")]
pub struct WebauthnPara {
pub wnd: isize,
pub attachment: u8,
pub attachment: u32,
pub require_resident: bool,
pub prefer_resident: bool,
pub user_verification: u8,
pub attestation_preference: u8,
pub enterprise_attestation: u8,
pub user_verification: u32,
pub attestation_preference: u32,
pub enterprise_attestation: u32,
#[serde(with = "UuidDef")]
pub cancellation_id: Uuid,
}
Expand All @@ -36,31 +37,44 @@ pub struct WebauthnPara {
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ChannelResponse {
// pub device_info: DeviceInfo,
pub device_info: Option<DeviceInfo>,
pub status: u8,
pub response: Option<ByteBuf>,
// TODO: deviceInfoList
}

// /// <https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpewa/ef4bafb6-0801-4c17-9238-99e3efdc0798>
// #[derive(Deserialize, Serialize, Debug, Clone)]
// #[serde(rename_all = "camelCase")]
// pub struct DeviceInfo {
// max_msg_size: Option<u32>,
// max_serialized_large_blob_array: Option<u32>,
// provider_type: String,
// provider_name: String,
// device_path: Option<String>,
// #[serde(rename = "Manufacturer")]
// manufacturer: Option<String>,
// #[serde(rename = "Product")]
// product: Option<String>,
// #[serde(rename = "aaGuid")]
// aaguid: Uuid,
// resident_key: Option<bool>,
// uv_status: Option<u8>,
// uv_retries: Option<u8>,
// }
/// <https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpewa/ef4bafb6-0801-4c17-9238-99e3efdc0798>
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct DeviceInfo {
max_msg_size: Option<u32>,
max_serialized_large_blob_array: Option<u32>,
provider_type: String,
provider_name: String,
device_path: Option<String>,
manufacturer: Option<String>,
product: Option<String>,
#[serde(rename = "aaGuid")]
aaguid: Option<Uuid>,
resident_key: Option<bool>,
uv_status: Option<u8>,
uv_retries: Option<u8>,
u2f_protocol: Option<bool>,
}

impl DeviceInfo {
pub fn get_transport(&self) -> Option<AuthenticatorTransport> {
let provider = self.provider_type.to_ascii_lowercase();
match provider.as_str() {
"hid" => Some(AuthenticatorTransport::Usb),
"nfc" => Some(AuthenticatorTransport::Nfc),
"ble" => Some(AuthenticatorTransport::Ble),
"platform" => Some(AuthenticatorTransport::Internal),
"test" => Some(AuthenticatorTransport::Test),
_ => None,
}
}
}

type UuidByteArray = ByteArray<{ std::mem::size_of::<uuid::Bytes>() }>;

Expand Down
90 changes: 78 additions & 12 deletions webauthn-authenticator-rs/src/win10/rdp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,41 @@ use crate::{
use base64urlsafedata::Base64UrlSafeData;
use uuid::Uuid;
use webauthn_rs_proto::{
AuthenticatorAttestationResponseRaw, RegisterPublicKeyCredential,
RegistrationExtensionsClientOutputs,
AttestationConveyancePreference, AuthenticatorAttachment, AuthenticatorAttestationResponseRaw,
RegisterPublicKeyCredential, RegistrationExtensionsClientOutputs, ResidentKeyRequirement,
UserVerificationPolicy,
};
use windows::{
core::{AsImpl as _, Result as WinResult},
Win32::{
Foundation::{E_FAIL, NTE_BAD_LEN},
Networking::WindowsWebServices::{
WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY,
WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT,
WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT,
WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE, WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY,
WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM,
WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM,
WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED,
WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED,
WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED,
},
System::RemoteDesktop::{IWTSPlugin, IWTSVirtualChannelManager},
},
};

pub struct Win10Rdp {
plugin: IWTSPlugin,
iface: IWTSVirtualChannelManager,
test_mode: bool,
}

impl Win10Rdp {
pub fn new() -> WinResult<Self> {
let o = Self {
plugin: plugin::get_webauthn_iwtsplugin()?,
iface: VirtualChannelManager::new().into(),
test_mode: false,
};

unsafe {
Expand All @@ -49,6 +63,10 @@ impl Win10Rdp {
Ok(o)
}

pub fn enable_test_mode(&mut self) {
self.test_mode = true;
}

fn connect(&self) -> WinResult<Connection> {
// Get back the IWTSListenerCallback
let Some(c) = unsafe { self.iface.as_impl() }.get_webauthn_callback() else {
Expand Down Expand Up @@ -114,19 +132,65 @@ impl AuthenticatorBackendHashedClientData for Win10Rdp {
};

let window = Window::new()?;

let flags = if self.test_mode {
warn!("using test mode!");
0x8800_0000
} else {
0x0004_0000 // CTAPCLT_DUAL_FLAG
| match authenticator_selection.user_verification {
UserVerificationPolicy::Discouraged_DO_NOT_USE => 0x0100_0000,
UserVerificationPolicy::Preferred => 0x0080_0000,
UserVerificationPolicy::Required => 0x0040_0000,
}
};
let webauthn_para = WebauthnPara {
wnd: window.hwnd.0,
attachment: 0,
require_resident: false,
prefer_resident: false,
user_verification: 0,
attestation_preference: 0,
attachment: match authenticator_selection.authenticator_attachment {
None => WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY,
Some(AuthenticatorAttachment::CrossPlatform) => {
WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM
}
Some(AuthenticatorAttachment::Platform) => {
WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM
}
},
require_resident: authenticator_selection.require_resident_key
|| matches!(
authenticator_selection.resident_key,
Some(ResidentKeyRequirement::Required)
),
prefer_resident: matches!(
authenticator_selection.resident_key,
Some(ResidentKeyRequirement::Preferred)
),
user_verification: match authenticator_selection.user_verification {
UserVerificationPolicy::Required => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED,
UserVerificationPolicy::Preferred => {
WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED
}
UserVerificationPolicy::Discouraged_DO_NOT_USE => {
WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED
}
},
attestation_preference: match options.attestation {
None => WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY,
Some(AttestationConveyancePreference::None) => {
WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE
}
Some(AttestationConveyancePreference::Indirect) => {
WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT
}
Some(AttestationConveyancePreference::Direct) => {
WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT
}
},
enterprise_attestation: 0,
cancellation_id: Uuid::nil(),
};

let (channel_response, ret) = c
.transcieve_cbor(mc, 4194304, 60000, Uuid::nil(), webauthn_para)
.transcieve_cbor(mc, flags, 60000, Uuid::nil(), webauthn_para)
.map_err(|_| WebauthnCError::Internal)?;

drop(window);
Expand Down Expand Up @@ -160,10 +224,12 @@ impl AuthenticatorBackendHashedClientData for Win10Rdp {
response: AuthenticatorAttestationResponseRaw {
attestation_object: Base64UrlSafeData(raw),
client_data_json: Base64UrlSafeData(vec![]),
// All transports the token supports, as opposed to the
// transport which was actually used.
// TODO
transports: None,
// The transport actually used
transports: channel_response
.device_info
.as_ref()
.and_then(|device_info| device_info.get_transport())
.map(|t| vec![t]),
},
})
}
Expand Down

0 comments on commit 13835d6

Please sign in to comment.