From 8b62dad247d7074bf38a428eea7cf3a0fa3477f8 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Mon, 3 Feb 2025 09:53:53 -0500 Subject: [PATCH] [PM-17619] Add Intial Windows WebAuthn Plugin Authenticator Bindings Crate (#13112) * PM-17619: Add intial Windows WebAuthn Plugin Authenticator bindings crate * PM-17619: Move crate to desktop_native * PM-17619: Update codeowners file * PM-17619: Fix failing Mac build * PM-17619: Switch to OUT_DIR * PM-17619: cargo fmt * PM-17619: Update bindings output dir * PM-17619: Use std::env::var for OUT_DIR * PM-17619: Prevent clippy failures on the generated Rust code * PM-17619: Test moving the clippy:all * PM-17619: Small updates * PM-17619: Remove todo * PM-17619: Ignore rustc warnings on the generated code * PM-17619: Address PR comments --- .github/CODEOWNERS | 1 + apps/desktop/desktop_native/.gitignore | 1 + apps/desktop/desktop_native/Cargo.lock | 72 ++++++ apps/desktop/desktop_native/Cargo.toml | 2 +- .../windows-plugin-authenticator/Cargo.toml | 9 + .../windows-plugin-authenticator/README.md | 23 ++ .../windows-plugin-authenticator/build.rs | 22 ++ .../pluginauthenticator.hpp | 231 ++++++++++++++++++ .../windows-plugin-authenticator/src/lib.rs | 11 + .../windows-plugin-authenticator/src/pa.rs | 15 ++ 10 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 apps/desktop/desktop_native/windows-plugin-authenticator/Cargo.toml create mode 100644 apps/desktop/desktop_native/windows-plugin-authenticator/README.md create mode 100644 apps/desktop/desktop_native/windows-plugin-authenticator/build.rs create mode 100644 apps/desktop/desktop_native/windows-plugin-authenticator/pluginauthenticator.hpp create mode 100644 apps/desktop/desktop_native/windows-plugin-authenticator/src/lib.rs create mode 100644 apps/desktop/desktop_native/windows-plugin-authenticator/src/pa.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 78639a06f9..94f0e939b7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -105,6 +105,7 @@ apps/browser/src/autofill @bitwarden/team-autofill-dev apps/desktop/src/autofill @bitwarden/team-autofill-dev libs/common/src/autofill @bitwarden/team-autofill-dev apps/desktop/macos/autofill-extension @bitwarden/team-autofill-dev +apps/desktop/desktop_native/windows-plugin-authenticator @bitwarden/team-autofill-dev # DuckDuckGo integration apps/desktop/native-messaging-test-runner @bitwarden/team-autofill-dev apps/desktop/src/services/duckduckgo-message-handler.service.ts @bitwarden/team-autofill-dev diff --git a/apps/desktop/desktop_native/.gitignore b/apps/desktop/desktop_native/.gitignore index 1cfa7dafc2..a0a01b28f5 100644 --- a/apps/desktop/desktop_native/.gitignore +++ b/apps/desktop/desktop_native/.gitignore @@ -5,3 +5,4 @@ index.node npm-debug.log* *.node dist +windows_pluginauthenticator_bindings.rs diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index 95fbd0850b..1e1af53d53 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -410,6 +410,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + [[package]] name = "bitflags" version = "2.8.0" @@ -553,6 +573,15 @@ dependencies = [ "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -593,6 +622,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.27" @@ -1458,6 +1498,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -2259,6 +2308,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "483f8c21f64f3ea09fe0f30f5d48c3e8eefe5dac9129f0075f76593b4c1da705" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -2437,6 +2496,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + [[package]] name = "rustc_version" version = "0.4.1" @@ -3447,6 +3512,13 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-plugin-authenticator" +version = "0.0.0" +dependencies = [ + "bindgen", +] + [[package]] name = "windows-registry" version = "0.4.0" diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index 8f966e2188..78142d618e 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["napi", "core", "proxy", "macos_provider"] +members = ["napi", "core", "proxy", "macos_provider", "windows-plugin-authenticator"] [workspace.dependencies] anyhow = "=1.0.94" diff --git a/apps/desktop/desktop_native/windows-plugin-authenticator/Cargo.toml b/apps/desktop/desktop_native/windows-plugin-authenticator/Cargo.toml new file mode 100644 index 0000000000..b8759cfca3 --- /dev/null +++ b/apps/desktop/desktop_native/windows-plugin-authenticator/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "windows-plugin-authenticator" +version = "0.0.0" +edition = "2021" +license = "GPL-3.0" +publish = false + +[target.'cfg(target_os = "windows")'.build-dependencies] +bindgen = "0.71.1" diff --git a/apps/desktop/desktop_native/windows-plugin-authenticator/README.md b/apps/desktop/desktop_native/windows-plugin-authenticator/README.md new file mode 100644 index 0000000000..6dc72ceed4 --- /dev/null +++ b/apps/desktop/desktop_native/windows-plugin-authenticator/README.md @@ -0,0 +1,23 @@ +# windows-plugin-authenticator + +This is an internal crate that's meant to be a safe abstraction layer over the generated Rust bindings for the Windows WebAuthn Plugin Authenticator API's. + +You can find more information about the Windows WebAuthn API's [here](https://github.com/microsoft/webauthn). + +## Building + +To build this crate, set the following environment variables: + +- `LIBCLANG_PATH` -> the path to the `bin` directory of your LLVM install ([more info](https://rust-lang.github.io/rust-bindgen/requirements.html?highlight=libclang_path#installing-clang)) + +### Bash Example + +``` +export LIBCLANG_PATH='C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin' +``` + +### PowerShell Example + +``` +$env:LIBCLANG_PATH = 'C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin' +``` diff --git a/apps/desktop/desktop_native/windows-plugin-authenticator/build.rs b/apps/desktop/desktop_native/windows-plugin-authenticator/build.rs new file mode 100644 index 0000000000..7bc311fb12 --- /dev/null +++ b/apps/desktop/desktop_native/windows-plugin-authenticator/build.rs @@ -0,0 +1,22 @@ +fn main() { + #[cfg(target_os = "windows")] + windows(); +} + +#[cfg(target_os = "windows")] +fn windows() { + let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR not set"); + + let bindings = bindgen::Builder::default() + .header("pluginauthenticator.hpp") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Unable to generate bindings."); + + bindings + .write_to_file(format!( + "{}\\windows_pluginauthenticator_bindings.rs", + out_dir + )) + .expect("Couldn't write bindings."); +} diff --git a/apps/desktop/desktop_native/windows-plugin-authenticator/pluginauthenticator.hpp b/apps/desktop/desktop_native/windows-plugin-authenticator/pluginauthenticator.hpp new file mode 100644 index 0000000000..c800266a3e --- /dev/null +++ b/apps/desktop/desktop_native/windows-plugin-authenticator/pluginauthenticator.hpp @@ -0,0 +1,231 @@ +/* + Bitwarden's pluginauthenticator.hpp + + Source: https://github.com/microsoft/webauthn/blob/master/experimental/pluginauthenticator.h + + This is a C++ header file, so the extension has been manually + changed from `.h` to `.hpp`, so bindgen will automatically + generate the correct C++ bindings. + + More Info: https://rust-lang.github.io/rust-bindgen/cpp.html +*/ + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + +/* File created by MIDL compiler version 8.01.0628 */ +/* @@MIDL_FILE_HEADING( ) */ + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 501 +#endif + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCSAL_H_VERSION__ +#define __REQUIRED_RPCSAL_H_VERSION__ 100 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of +#endif /* __RPCNDR_H_VERSION__ */ + +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __pluginauthenticator_h__ +#define __pluginauthenticator_h__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#ifndef DECLSPEC_XFGVIRT +#if defined(_CONTROL_FLOW_GUARD_XFG) +#define DECLSPEC_XFGVIRT(base, func) __declspec(xfg_virtual(base, func)) +#else +#define DECLSPEC_XFGVIRT(base, func) +#endif +#endif + +/* Forward Declarations */ + +#ifndef __EXPERIMENTAL_IPluginAuthenticator_FWD_DEFINED__ +#define __EXPERIMENTAL_IPluginAuthenticator_FWD_DEFINED__ +typedef interface EXPERIMENTAL_IPluginAuthenticator EXPERIMENTAL_IPluginAuthenticator; + +#endif /* __EXPERIMENTAL_IPluginAuthenticator_FWD_DEFINED__ */ + +/* header files for imported files */ +#include "oaidl.h" +#include "webauthn.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +/* interface __MIDL_itf_pluginauthenticator_0000_0000 */ +/* [local] */ + +typedef struct _EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_REQUEST + { + HWND hWnd; + GUID transactionId; + DWORD cbRequestSignature; + /* [size_is] */ byte *pbRequestSignature; + DWORD cbEncodedRequest; + /* [size_is] */ byte *pbEncodedRequest; + } EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_REQUEST; + +typedef struct _EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_REQUEST *EXPERIMENTAL_PWEBAUTHN_PLUGIN_OPERATION_REQUEST; + +typedef const EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_REQUEST *EXPERIMENTAL_PCWEBAUTHN_PLUGIN_OPERATION_REQUEST; + +typedef struct _EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_RESPONSE + { + DWORD cbEncodedResponse; + /* [size_is] */ byte *pbEncodedResponse; + } EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_RESPONSE; + +typedef struct _EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_RESPONSE *EXPERIMENTAL_PWEBAUTHN_PLUGIN_OPERATION_RESPONSE; + +typedef const EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_RESPONSE *EXPERIMENTAL_PCWEBAUTHN_PLUGIN_OPERATION_RESPONSE; + +typedef struct _EXPERIMENTAL_WEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST + { + GUID transactionId; + DWORD cbRequestSignature; + /* [size_is] */ byte *pbRequestSignature; + } EXPERIMENTAL_WEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST; + +typedef struct _EXPERIMENTAL_WEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST *EXPERIMENTAL_PWEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST; + +typedef const EXPERIMENTAL_WEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST *EXPERIMENTAL_PCWEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST; + +extern RPC_IF_HANDLE __MIDL_itf_pluginauthenticator_0000_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_pluginauthenticator_0000_0000_v0_0_s_ifspec; + +#ifndef __EXPERIMENTAL_IPluginAuthenticator_INTERFACE_DEFINED__ +#define __EXPERIMENTAL_IPluginAuthenticator_INTERFACE_DEFINED__ + +/* interface EXPERIMENTAL_IPluginAuthenticator */ +/* [unique][version][uuid][object] */ + +EXTERN_C const IID IID_EXPERIMENTAL_IPluginAuthenticator; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("e6466e9a-b2f3-47c5-b88d-89bc14a8d998") + EXPERIMENTAL_IPluginAuthenticator : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE EXPERIMENTAL_PluginMakeCredential( + /* [in] */ __RPC__in EXPERIMENTAL_PCWEBAUTHN_PLUGIN_OPERATION_REQUEST request, + /* [out] */ __RPC__deref_out_opt EXPERIMENTAL_PWEBAUTHN_PLUGIN_OPERATION_RESPONSE *response) = 0; + + virtual HRESULT STDMETHODCALLTYPE EXPERIMENTAL_PluginGetAssertion( + /* [in] */ __RPC__in EXPERIMENTAL_PCWEBAUTHN_PLUGIN_OPERATION_REQUEST request, + /* [out] */ __RPC__deref_out_opt EXPERIMENTAL_PWEBAUTHN_PLUGIN_OPERATION_RESPONSE *response) = 0; + + virtual HRESULT STDMETHODCALLTYPE EXPERIMENTAL_PluginCancelOperation( + /* [in] */ __RPC__in EXPERIMENTAL_PCWEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST request) = 0; + + }; + +#else /* C style interface */ + + typedef struct EXPERIMENTAL_IPluginAuthenticatorVtbl + { + BEGIN_INTERFACE + + DECLSPEC_XFGVIRT(IUnknown, QueryInterface) + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + __RPC__in EXPERIMENTAL_IPluginAuthenticator * This, + /* [in] */ __RPC__in REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + DECLSPEC_XFGVIRT(IUnknown, AddRef) + ULONG ( STDMETHODCALLTYPE *AddRef )( + __RPC__in EXPERIMENTAL_IPluginAuthenticator * This); + + DECLSPEC_XFGVIRT(IUnknown, Release) + ULONG ( STDMETHODCALLTYPE *Release )( + __RPC__in EXPERIMENTAL_IPluginAuthenticator * This); + + DECLSPEC_XFGVIRT(EXPERIMENTAL_IPluginAuthenticator, EXPERIMENTAL_PluginMakeCredential) + HRESULT ( STDMETHODCALLTYPE *EXPERIMENTAL_PluginMakeCredential )( + __RPC__in EXPERIMENTAL_IPluginAuthenticator * This, + /* [in] */ __RPC__in EXPERIMENTAL_PCWEBAUTHN_PLUGIN_OPERATION_REQUEST request, + /* [out] */ __RPC__deref_out_opt EXPERIMENTAL_PWEBAUTHN_PLUGIN_OPERATION_RESPONSE *response); + + DECLSPEC_XFGVIRT(EXPERIMENTAL_IPluginAuthenticator, EXPERIMENTAL_PluginGetAssertion) + HRESULT ( STDMETHODCALLTYPE *EXPERIMENTAL_PluginGetAssertion )( + __RPC__in EXPERIMENTAL_IPluginAuthenticator * This, + /* [in] */ __RPC__in EXPERIMENTAL_PCWEBAUTHN_PLUGIN_OPERATION_REQUEST request, + /* [out] */ __RPC__deref_out_opt EXPERIMENTAL_PWEBAUTHN_PLUGIN_OPERATION_RESPONSE *response); + + DECLSPEC_XFGVIRT(EXPERIMENTAL_IPluginAuthenticator, EXPERIMENTAL_PluginCancelOperation) + HRESULT ( STDMETHODCALLTYPE *EXPERIMENTAL_PluginCancelOperation )( + __RPC__in EXPERIMENTAL_IPluginAuthenticator * This, + /* [in] */ __RPC__in EXPERIMENTAL_PCWEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST request); + + END_INTERFACE + } EXPERIMENTAL_IPluginAuthenticatorVtbl; + + interface EXPERIMENTAL_IPluginAuthenticator + { + CONST_VTBL struct EXPERIMENTAL_IPluginAuthenticatorVtbl *lpVtbl; + }; + +#ifdef COBJMACROS + + +#define EXPERIMENTAL_IPluginAuthenticator_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define EXPERIMENTAL_IPluginAuthenticator_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define EXPERIMENTAL_IPluginAuthenticator_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define EXPERIMENTAL_IPluginAuthenticator_EXPERIMENTAL_PluginMakeCredential(This,request,response) \ + ( (This)->lpVtbl -> EXPERIMENTAL_PluginMakeCredential(This,request,response) ) + +#define EXPERIMENTAL_IPluginAuthenticator_EXPERIMENTAL_PluginGetAssertion(This,request,response) \ + ( (This)->lpVtbl -> EXPERIMENTAL_PluginGetAssertion(This,request,response) ) + +#define EXPERIMENTAL_IPluginAuthenticator_EXPERIMENTAL_PluginCancelOperation(This,request) \ + ( (This)->lpVtbl -> EXPERIMENTAL_PluginCancelOperation(This,request) ) + +#endif /* COBJMACROS */ + +#endif /* C style interface */ + +#endif /* __EXPERIMENTAL_IPluginAuthenticator_INTERFACE_DEFINED__ */ + +/* Additional Prototypes for ALL interfaces */ + +unsigned long __RPC_USER HWND_UserSize( __RPC__in unsigned long *, unsigned long , __RPC__in HWND * ); +unsigned char * __RPC_USER HWND_UserMarshal( __RPC__in unsigned long *, __RPC__inout_xcount(0) unsigned char *, __RPC__in HWND * ); +unsigned char * __RPC_USER HWND_UserUnmarshal(__RPC__in unsigned long *, __RPC__in_xcount(0) unsigned char *, __RPC__out HWND * ); +void __RPC_USER HWND_UserFree( __RPC__in unsigned long *, __RPC__in HWND * ); + +unsigned long __RPC_USER HWND_UserSize64( __RPC__in unsigned long *, unsigned long , __RPC__in HWND * ); +unsigned char * __RPC_USER HWND_UserMarshal64( __RPC__in unsigned long *, __RPC__inout_xcount(0) unsigned char *, __RPC__in HWND * ); +unsigned char * __RPC_USER HWND_UserUnmarshal64(__RPC__in unsigned long *, __RPC__in_xcount(0) unsigned char *, __RPC__out HWND * ); +void __RPC_USER HWND_UserFree64( __RPC__in unsigned long *, __RPC__in HWND * ); + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/apps/desktop/desktop_native/windows-plugin-authenticator/src/lib.rs b/apps/desktop/desktop_native/windows-plugin-authenticator/src/lib.rs new file mode 100644 index 0000000000..e226000e6f --- /dev/null +++ b/apps/desktop/desktop_native/windows-plugin-authenticator/src/lib.rs @@ -0,0 +1,11 @@ +#![cfg(target_os = "windows")] + +mod pa; + +pub fn get_version_number() -> u64 { + unsafe { pa::WebAuthNGetApiVersionNumber() }.into() +} + +pub fn add_authenticator() { + unimplemented!(); +} diff --git a/apps/desktop/desktop_native/windows-plugin-authenticator/src/pa.rs b/apps/desktop/desktop_native/windows-plugin-authenticator/src/pa.rs new file mode 100644 index 0000000000..3da5a77a24 --- /dev/null +++ b/apps/desktop/desktop_native/windows-plugin-authenticator/src/pa.rs @@ -0,0 +1,15 @@ +/* + The 'pa' (plugin authenticator) module will contain the generated + bindgen code. + + The attributes below will suppress warnings from the generated code. +*/ + +#![cfg(target_os = "windows")] +#![allow(clippy::all)] +#![allow(warnings)] + +include!(concat!( + env!("OUT_DIR"), + "/windows_pluginauthenticator_bindings.rs" +));