From 08969b1d7e1c0bdd0525849d827e8947067aca14 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 19 Dec 2025 17:49:26 +0100 Subject: [PATCH] Km/fido2 client windows (#17533) * tmp * tmp * Tmp * cleanup dependencies --- apps/desktop/desktop_native/Cargo.lock | 304 ++---------------- .../desktop_native/fido2_client/Cargo.toml | 13 +- .../desktop_native/fido2_client/src/lib.rs | 14 +- .../fido2_client/src/windows.rs | 225 ++++++++++--- apps/desktop/desktop_native/napi/src/lib.rs | 2 +- 5 files changed, 223 insertions(+), 335 deletions(-) diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index a64e42d402f..8a6a2e4231f 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -348,28 +348,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-task" version = "4.7.1" @@ -432,12 +410,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -450,15 +422,6 @@ version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" -[[package]] -name = "base64urlsafedata" -version = "0.5.3" -dependencies = [ - "base64 0.21.7", - "pastey", - "serde", -] - [[package]] name = "basic-toml" version = "0.1.10" @@ -488,12 +451,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.9.0" @@ -528,7 +485,7 @@ version = "0.0.0" dependencies = [ "aes-gcm", "anyhow", - "base64 0.22.1", + "base64", "chacha20poly1305", "chromium_importer", "clap", @@ -582,12 +539,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - [[package]] name = "byteorder" version = "1.5.0" @@ -694,7 +645,7 @@ dependencies = [ "aes-gcm", "anyhow", "async-trait", - "base64 0.22.1", + "base64", "cbc", "dirs 6.0.0", "hex", @@ -844,12 +795,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - [[package]] name = "crypto-bigint" version = "0.5.5" @@ -881,7 +826,7 @@ checksum = "ee6473a333d82796d5b23529fde19386de33820ad19d368402f8e9de780e1723" dependencies = [ "aes", "anyhow", - "base64 0.22.1", + "base64", "byteorder", "cbc", "hex", @@ -1065,7 +1010,7 @@ dependencies = [ "anyhow", "arboard", "ashpd", - "base64 0.22.1", + "base64", "bitwarden-russh", "byteorder", "bytes", @@ -1118,7 +1063,7 @@ version = "0.0.0" dependencies = [ "anyhow", "autotype", - "base64 0.22.1", + "base64", "chromium_importer", "desktop_core", "fido2_client", @@ -1224,7 +1169,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.0", + "bitflags", "objc2", ] @@ -1466,8 +1411,7 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" name = "fido2_client" version = "0.0.0" dependencies = [ - "base64 0.22.1", - "base64urlsafedata", + "base64", "ctap-hid-fido2", "futures-util", "hex", @@ -1478,8 +1422,7 @@ dependencies = [ "serde_json", "sha2", "tokio", - "webauthn-authenticator-rs", - "webauthn-rs-proto", + "windows 0.52.0", ] [[package]] @@ -1720,16 +1663,6 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" -[[package]] -name = "half" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" -dependencies = [ - "cfg-if", - "crunchy", -] - [[package]] name = "hashbrown" version = "0.15.3" @@ -1972,16 +1905,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" -[[package]] -name = "js-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - [[package]] name = "keytar" version = "0.1.6" @@ -2040,7 +1963,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags", "libc", ] @@ -2070,7 +1993,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" dependencies = [ - "bitflags 2.9.0", + "bitflags", "libc", ] @@ -2243,7 +2166,7 @@ version = "2.16.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3" dependencies = [ - "bitflags 2.9.0", + "bitflags", "ctor 0.2.9", "napi-derive", "napi-sys", @@ -2301,7 +2224,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.0", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -2313,7 +2236,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.0", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -2405,17 +2328,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -2472,7 +2384,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.9.0", + "bitflags", "objc2", "objc2-core-graphics", "objc2-foundation", @@ -2484,7 +2396,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.0", + "bitflags", "dispatch2", "objc2", ] @@ -2495,7 +2407,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ - "bitflags 2.9.0", + "bitflags", "dispatch2", "objc2", "objc2-core-foundation", @@ -2514,7 +2426,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.0", + "bitflags", "objc2", "objc2-core-foundation", ] @@ -2535,7 +2447,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" dependencies = [ - "bitflags 2.9.0", + "bitflags", "objc2", "objc2-core-foundation", ] @@ -2709,12 +2621,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - [[package]] name = "pbkdf2" version = "0.12.2" @@ -3077,7 +2983,7 @@ version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.9.0", + "bitflags", ] [[package]] @@ -3182,7 +3088,7 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ - "bitflags 2.9.0", + "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -3230,7 +3136,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.0", + "bitflags", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3243,7 +3149,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.9.0", + "bitflags", "errno", "libc", "linux-raw-sys 0.9.4", @@ -3383,7 +3289,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc198e42d9b7510827939c9a15f5062a0c913f3371d765977e586d2fe6c16f4a" dependencies = [ - "bitflags 2.9.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -3424,17 +3330,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ - "half 1.8.3", - "serde", -] - -[[package]] -name = "serde_cbor_2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aec2709de9078e077090abd848e967abab63c9fb3fdb5d4799ad359d8d482c" -dependencies = [ - "half 2.6.0", + "half", "serde", ] @@ -3869,21 +3765,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.45.0" @@ -4118,15 +3999,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -4315,17 +4187,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "uuid" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" -dependencies = [ - "getrandom 0.3.3", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.1" @@ -4391,51 +4252,6 @@ dependencies = [ "wit-bindgen-rt", ] -[[package]] -name = "wasm-bindgen" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" -dependencies = [ - "unicode-ident", -] - [[package]] name = "wayland-backend" version = "0.3.10" @@ -4455,7 +4271,7 @@ version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ - "bitflags 2.9.0", + "bitflags", "rustix 0.38.44", "wayland-backend", "wayland-scanner", @@ -4467,7 +4283,7 @@ version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" dependencies = [ - "bitflags 2.9.0", + "bitflags", "wayland-backend", "wayland-client", "wayland-scanner", @@ -4479,7 +4295,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" dependencies = [ - "bitflags 2.9.0", + "bitflags", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4506,70 +4322,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "webauthn-authenticator-rs" -version = "0.5.3" -dependencies = [ - "async-stream", - "async-trait", - "base64 0.21.7", - "base64urlsafedata", - "bitflags 1.3.2", - "futures", - "nom", - "num-derive", - "num-traits", - "once_cell", - "serde", - "serde_cbor_2", - "serde_json", - "thiserror 1.0.69", - "tracing", - "unicode-normalization", - "url", - "uuid", - "webauthn-rs-proto", - "windows 0.41.0", -] - -[[package]] -name = "webauthn-rs-core" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f1d80f3146382529fe70a3ab5d0feb2413a015204ed7843f9377cd39357fc4" -dependencies = [ - "base64 0.21.7", - "base64urlsafedata", - "der-parser", - "hex", - "nom", - "openssl", - "openssl-sys", - "rand 0.8.5", - "rand_chacha 0.3.1", - "serde", - "serde_cbor_2", - "serde_json", - "thiserror 1.0.69", - "tracing", - "url", - "uuid", - "webauthn-attestation-ca", - "webauthn-rs-proto", - "x509-parser", -] - -[[package]] -name = "webauthn-rs-proto" -version = "0.5.3" -dependencies = [ - "base64 0.21.7", - "base64urlsafedata", - "serde", - "serde_json", - "url", -] - [[package]] name = "weedle2" version = "5.0.0" @@ -5167,7 +4919,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags", ] [[package]] diff --git a/apps/desktop/desktop_native/fido2_client/Cargo.toml b/apps/desktop/desktop_native/fido2_client/Cargo.toml index 1c7ef19b8ba..36ecceb01eb 100644 --- a/apps/desktop/desktop_native/fido2_client/Cargo.toml +++ b/apps/desktop/desktop_native/fido2_client/Cargo.toml @@ -13,11 +13,16 @@ home = "=0.5.0" serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } secrecy = "0.8.0" -hex.workspace = true -webauthn-authenticator-rs = { version = "0.5.3", features = ["ctap2", "cable", "usb", "win10"] } +hex = { workspace = true } tokio.workspace = true futures-util = "0.3.31" sha2 = { workspace = true } -webauthn-rs-proto = { path = "../../../../../webauthn-rs/webauthn-rs-proto" } -base64urlsafedata = { path= "../../../../../webauthn-rs/base64urlsafedata" } +windows = { version = "=0.52.0", features = [ + "Win32_Graphics_Gdi", + "Win32_Networking_WindowsWebServices", + "Win32_Foundation", + "Win32_UI_WindowsAndMessaging", + "Win32_System_LibraryLoader", + "Win32_Graphics_Dwm", +]} diff --git a/apps/desktop/desktop_native/fido2_client/src/lib.rs b/apps/desktop/desktop_native/fido2_client/src/lib.rs index d8110a631a7..dcfb7adce85 100644 --- a/apps/desktop/desktop_native/fido2_client/src/lib.rs +++ b/apps/desktop/desktop_native/fido2_client/src/lib.rs @@ -15,21 +15,21 @@ fn prf_to_hmac(prf_salt: &[u8]) -> [u8; 32] { sha2::Sha256::digest(&[b"WebAuthn PRF".as_slice(), &[0], prf_salt].concat()).into() } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone, Copy)] pub enum UserVerification { Discouraged, Preferred, Required, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AssertionOptions { pub challenge: Vec, pub timeout: u64, pub rpid: String, pub user_verification: UserVerification, pub allow_credentials: Vec>, - pub prf_eval_first: [u8; 32], + pub prf_eval_first: Option<[u8; 32]>, pub prf_eval_second: Option<[u8; 32]>, } @@ -66,11 +66,9 @@ pub mod fido2_client { ) -> Result { println!("Calling Windows FIDO2 client get()"); // run in new thread - std::thread::spawn(move || { - super::get(assertion_options) - }) - .join() - .unwrap() + std::thread::spawn(move || super::get(assertion_options)) + .join() + .unwrap() } pub fn available() -> bool { diff --git a/apps/desktop/desktop_native/fido2_client/src/windows.rs b/apps/desktop/desktop_native/fido2_client/src/windows.rs index ad369ef6b0d..63fbd56acb4 100644 --- a/apps/desktop/desktop_native/fido2_client/src/windows.rs +++ b/apps/desktop/desktop_native/fido2_client/src/windows.rs @@ -1,55 +1,189 @@ -use webauthn_authenticator_rs::{AuthenticatorBackend, prelude::Url, win10::Win10}; -use webauthn_rs_proto::{HmacGetSecretInput, PublicKeyCredentialRequestOptions, RequestAuthenticationExtensions}; +use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine}; +use windows::{ + core::{w, HSTRING, PCWSTR}, + Win32::{ + Foundation::BOOL, + Networking::WindowsWebServices::{ + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS, + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION, WEBAUTHN_CLIENT_DATA, + WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, WEBAUTHN_CREDENTIALS, WEBAUTHN_CREDENTIAL_LIST, + WEBAUTHN_EXTENSION, WEBAUTHN_EXTENSIONS, WEBAUTHN_HMAC_SECRET_SALT, + WEBAUTHN_HMAC_SECRET_SALT_VALUES, WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED, + WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED, + WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED, + }, + UI::WindowsAndMessaging::GetForegroundWindow, + }, +}; use crate::{AssertionOptions, Fido2ClientError, PublicKeyCredential}; -pub fn get(options: AssertionOptions) -> Result { - let uv = match options.user_verification { - crate::UserVerification::Required => webauthn_rs_proto::UserVerificationPolicy::Required, - crate::UserVerification::Preferred => webauthn_rs_proto::UserVerificationPolicy::Preferred, - crate::UserVerification::Discouraged => webauthn_rs_proto::UserVerificationPolicy::Discouraged_DO_NOT_USE, - }; - println!("User verification policy: {:?}", uv); +fn to_native_uv(uv: crate::UserVerification) -> u32 { + match uv { + crate::UserVerification::Required => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED, + crate::UserVerification::Preferred => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED, + crate::UserVerification::Discouraged => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED, + } +} - let opts = PublicKeyCredentialRequestOptions { - challenge: base64urlsafedata::Base64UrlSafeData::from(options.challenge), - timeout: Some(options.timeout as u32), - rp_id: options.rpid, - allow_credentials: vec![], - user_verification: uv, - hints: None, - extensions: Some(RequestAuthenticationExtensions { - hmac_get_secret: Some(HmacGetSecretInput { - output1: base64urlsafedata::Base64UrlSafeData::from(&options.prf_eval_first), - output2: None, - }), - appid: None, - uvm: None, - }) +fn client_data_json(options: &AssertionOptions) -> String { + format!( + r#"{{"type":"webauthn.get","challenge":"{}","origin":"https://{}","crossOrigin": true}}"#, + BASE64_URL_SAFE_NO_PAD.encode(&options.challenge), + options.rpid + ) +} + +fn hmac_secret_salt(options: &AssertionOptions) -> WEBAUTHN_HMAC_SECRET_SALT { + let mut hmac_secret_salt = WEBAUTHN_HMAC_SECRET_SALT { + cbFirst: 0, + pbFirst: std::ptr::null_mut(), + cbSecond: 0, + pbSecond: std::ptr::null_mut(), + }; + if let Some(first) = &options.prf_eval_first { + hmac_secret_salt.cbFirst = first.len() as u32; + hmac_secret_salt.pbFirst = first.as_ptr() as *mut _; + } + if let Some(second) = &options.prf_eval_second { + hmac_secret_salt.cbSecond = second.len() as u32; + hmac_secret_salt.pbSecond = second.as_ptr() as *mut _; + } + hmac_secret_salt +} + +pub fn get(options: AssertionOptions) -> Result { + let uv = to_native_uv(options.user_verification); + let rp_id: HSTRING = options.rpid.clone().into(); + + // HMAC secret extension + let hmac_extension_enabled = options.prf_eval_first.is_some(); + let mut hmac_secret_salt = hmac_secret_salt(&options); + let mut hmac_secret_salt_values = WEBAUTHN_HMAC_SECRET_SALT_VALUES { + pGlobalHmacSalt: std::ptr::addr_of_mut!(hmac_secret_salt) as *mut _, + pCredWithHmacSecretSaltList: [].as_mut_ptr(), + cCredWithHmacSecretSaltList: 0, + }; + let used: BOOL = true.into(); + let hmac_extension: WEBAUTHN_EXTENSION = WEBAUTHN_EXTENSION { + pwszExtensionIdentifier: w!("hmacGetSecret"), + cbExtension: 1, + pvExtension: &used as *const _ as *mut _, + }; + let extensions: WEBAUTHN_EXTENSIONS = if hmac_extension_enabled { + WEBAUTHN_EXTENSIONS { + cExtensions: 1, + pExtensions: &hmac_extension as *const _ as *mut _, + } + } else { + WEBAUTHN_EXTENSIONS { + cExtensions: 0, + pExtensions: std::ptr::null_mut(), + } + }; + + let client_data_json = client_data_json(&options.clone()); + let client_data = WEBAUTHN_CLIENT_DATA { + dwVersion: WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, + cbClientDataJSON: client_data_json.len() as u32, + pbClientDataJSON: client_data_json.as_ptr() as *mut _, + pwszHashAlgId: w!("SHA-256"), + }; + + let mut list = WEBAUTHN_CREDENTIAL_LIST { + cCredentials: 0, + ppCredentials: std::ptr::null_mut(), + }; + + let mut app_id_used: BOOL = false.into(); + let getassertopts = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS { + dwVersion: WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION, + dwTimeoutMilliseconds: options.timeout as u32, + CredentialList: WEBAUTHN_CREDENTIALS { + cCredentials: 0, + pCredentials: [].as_mut_ptr(), + }, + Extensions: extensions, + dwAuthenticatorAttachment: 0, + dwUserVerificationRequirement: uv, + dwFlags: 0, + pwszU2fAppId: PCWSTR::null(), + pbU2fAppId: std::ptr::addr_of_mut!(app_id_used), + pCancellationId: std::ptr::null_mut(), + pAllowCredentialList: std::ptr::addr_of_mut!(list), + dwCredLargeBlobOperation: 0, + cbCredLargeBlob: 0, + pbCredLargeBlob: std::ptr::null_mut(), + bBrowserInPrivateMode: false.into(), + pHmacSecretSaltValues: if true { + std::ptr::addr_of_mut!(hmac_secret_salt_values) as *mut _ + } else { + std::ptr::null_mut() + }, + }; + + let assertion = unsafe { + *windows::Win32::Networking::WindowsWebServices::WebAuthNAuthenticatorGetAssertion( + GetForegroundWindow(), + &rp_id, + std::ptr::addr_of!(client_data) as *const _, + Some(&getassertopts), + ) + .map_err(|_| Fido2ClientError::AssertionError)? + }; + + let id = unsafe { + std::slice::from_raw_parts( + assertion.Credential.pbId as *const u8, + assertion.Credential.cbId as usize, + ) + }; + + let hmac = if hmac_extension_enabled { + Some(unsafe { + let hmac_secret = *assertion.pHmacSecret; + std::slice::from_raw_parts( + hmac_secret.pbFirst as *const u8, + hmac_secret.cbFirst as usize, + ) + }) + } else { + None }; - println!("Prepared PublicKeyCredentialRequestOptions: {:?}", opts); - let public_key_credential = Win10::default().perform_auth(Url::parse( - "https://vault.usdev.bitwarden.pw", - ).unwrap(), opts, 60000) - .map_err(|_e| Fido2ClientError::AssertionError)?; - println!("PublicKeyCredential: {:?}", public_key_credential); Ok(PublicKeyCredential { - id: public_key_credential.id, - raw_id: public_key_credential.raw_id.to_vec(), + id: BASE64_URL_SAFE_NO_PAD.encode(id), + raw_id: id.to_vec(), response: crate::AuthenticatorAssertionResponse { - authenticator_data: public_key_credential.response.authenticator_data.to_vec(), - client_data_json: public_key_credential.response.client_data_json.to_vec(), - signature: public_key_credential.response.signature.to_vec(), - user_handle: public_key_credential.response.user_handle.map(|h| h.to_vec()).unwrap_or_default(), + authenticator_data: unsafe { + std::slice::from_raw_parts( + assertion.pbAuthenticatorData as *const u8, + assertion.cbAuthenticatorData as usize, + ) + } + .to_vec(), + client_data_json: client_data_json.as_bytes().to_vec(), + signature: unsafe { + std::slice::from_raw_parts( + assertion.pbSignature as *const u8, + assertion.cbSignature as usize, + ) + } + .to_vec(), + user_handle: unsafe { + std::slice::from_raw_parts( + assertion.pbUserId as *const u8, + assertion.cbUserId as usize, + ) + } + .to_vec(), }, - prf: public_key_credential.extensions.hmac_get_secret.map(|hmac| { - let mut prf_bytes = [0u8; 32]; - prf_bytes.copy_from_slice(&hmac.output1.to_vec().as_slice()[..32]); - prf_bytes - }), + // PRF (hmac-get-secret) extension parsing is not implemented here yet; return None. + prf: hmac + .map(|h| h.try_into().map_err(|_| Fido2ClientError::AssertionError)) + .transpose()?, authenticator_attachment: "cross-platform".to_string(), - r#type: public_key_credential.type_, + r#type: "public-key".to_string(), }) } @@ -65,17 +199,16 @@ mod tests { #[test] fn test_get() { - let options = -AssertionOptions { + let options = AssertionOptions { challenge: vec![0u8; 32], timeout: 0, rpid: "vault.usdev.bitwarden.pw".to_string(), user_verification: crate::UserVerification::Required, allow_credentials: vec![], - prf_eval_first: [0u8; 32], + prf_eval_first: Some([0u8; 32]), prf_eval_second: None, }; let result = get(options); println!("{:?}", result.unwrap()); } -} \ No newline at end of file +} diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index 6943d553c82..407fc2e3434 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -1238,7 +1238,7 @@ impl Into for CredentialAssertionOptions { rpid: self.rpid, user_verification: self.user_verification.into(), allow_credentials: self.allow_credentials, - prf_eval_first: self.prf_eval_first.to_vec().try_into().unwrap_or([0u8; 32]), + prf_eval_first: Some(self.prf_eval_first.to_vec().try_into().unwrap_or([0u8; 32])), prf_eval_second: None, } }