1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-07 20:24:01 +00:00
Remove file logging
This commit is contained in:
Anders Åberg
2025-07-10 10:54:39 +02:00
parent 6d12ed13cc
commit 432d593389
14 changed files with 48 additions and 3201 deletions

Binary file not shown.

View File

@@ -13,10 +13,10 @@ xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
IgnorableNamespaces="uap rescap com uap10 build"
xmlns:build="http://schemas.microsoft.com/developer/appx/2015/build">
<!-- use single quotes to avoid double quotes escaping in the publisher value -->
<Identity Name="${applicationId}"
ProcessorArchitecture="${arch}"
Publisher='${publisher}'
Version="${version}" />
<Identity Name="8bitSolutionsLLC.bitwardendesktop"
ProcessorArchitecture="x64"
Publisher='CN=com.bitwarden.localdevelopment'
Version="2025.6.0.0" />
<Properties>
<DisplayName>Bitwarden</DisplayName>
<PublisherDisplayName>Bitwarden Inc</PublisherDisplayName>

View File

@@ -1,977 +0,0 @@
#include "pch.h"
#include "PluginAuthenticatorImpl.h"
#include <App.xaml.h>
#include <PluginManagement/PluginRegistrationManager.h>
#include <PluginManagement/PluginCredentialManager.h>
#include <include/cbor-lite/codec.h>
#include <string>
#include <iostream>
#include <fstream>
#include <helpers/buffer_read_write.h>
namespace winrt
{
using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::UI::Windowing;
using namespace winrt::Microsoft::UI::Xaml;
using namespace winrt::Microsoft::UI::Xaml::Controls;
using namespace winrt::Microsoft::UI::Xaml::Navigation;
using namespace PasskeyManager;
using namespace PasskeyManager::implementation;
using namespace CborLite;
}
namespace winrt::PasskeyManager::implementation
{
static std::vector<uint8_t> GetRequestSigningPubKey()
{
return wil::reg::get_value_binary(HKEY_CURRENT_USER, c_pluginRegistryPath, c_windowsPluginRequestSigningKeyRegKeyName, REG_BINARY);
}
/*
* This function is used to verify the signature of a request buffer.
* The public key is part of response to plugin registration.
*/
static HRESULT VerifySignatureHelper(
std::vector<BYTE>& dataBuffer,
PBYTE pbKeyData,
DWORD cbKeyData,
PBYTE pbSignature,
DWORD cbSignature)
{
// Create key provider
wil::unique_ncrypt_prov hProvider;
wil::unique_ncrypt_key reqSigningKey;
// Get the provider
RETURN_IF_FAILED(NCryptOpenStorageProvider(&hProvider, nullptr, 0));
// Create a NCrypt key handle from the public key
RETURN_IF_FAILED(NCryptImportKey(
hProvider.get(),
NULL,
BCRYPT_ECCPUBLIC_BLOB,
NULL,
&reqSigningKey,
pbKeyData,
cbKeyData, 0));
// Verify the signature over the hash of dataBuffer using the hKey
DWORD objLenSize = 0;
DWORD bytesRead = 0;
RETURN_IF_NTSTATUS_FAILED(BCryptGetProperty(
BCRYPT_SHA256_ALG_HANDLE,
BCRYPT_OBJECT_LENGTH,
reinterpret_cast<PBYTE>(&objLenSize),
sizeof(objLenSize),
&bytesRead, 0));
auto objLen = wil::make_unique_cotaskmem<BYTE[]>(objLenSize);
wil::unique_bcrypt_hash hashHandle;
RETURN_IF_NTSTATUS_FAILED(BCryptCreateHash(
BCRYPT_SHA256_ALG_HANDLE,
wil::out_param(hashHandle),
objLen.get(),
objLenSize,
nullptr, 0, 0));
RETURN_IF_NTSTATUS_FAILED(BCryptHashData(
hashHandle.get(),
dataBuffer.data(),
static_cast<ULONG>(dataBuffer.size()), 0));
DWORD localHashByteCount = 0;
RETURN_IF_NTSTATUS_FAILED(BCryptGetProperty(
BCRYPT_SHA256_ALG_HANDLE,
BCRYPT_HASH_LENGTH,
reinterpret_cast<PBYTE>(&localHashByteCount),
sizeof(localHashByteCount),
&bytesRead, 0));
auto localHashBuffer = wil::make_unique_cotaskmem<BYTE[]>(localHashByteCount);
RETURN_IF_NTSTATUS_FAILED(BCryptFinishHash(hashHandle.get(), localHashBuffer.get(), localHashByteCount, 0));
RETURN_IF_WIN32_ERROR(NCryptVerifySignature(
reqSigningKey.get(),
nullptr,
localHashBuffer.get(),
localHashByteCount,
pbSignature,
cbSignature, 0));
return S_OK;
}
HRESULT CheckHelloConsentCompleted()
{
winrt::com_ptr<App> curApp = winrt::Microsoft::UI::Xaml::Application::Current().as<App>();
HANDLE handles[2] = { curApp->m_hVaultConsentComplete.get(), curApp->m_hVaultConsentFailed.get() };
DWORD cWait = ARRAYSIZE(handles);
DWORD hIndex = 0;
RETURN_IF_FAILED(CoWaitForMultipleHandles(COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS, INFINITE, cWait, handles, &hIndex));
if (hIndex == 1) // Consent failed
{
RETURN_HR(E_FAIL);
}
return S_OK;
}
HRESULT PerformUv(
winrt::com_ptr<winrt::PasskeyManager::implementation::App>& curApp,
HWND hWnd,
wil::shared_hmodule webauthnDll,
GUID transactionId,
PluginOperationType operationType,
std::vector<BYTE> requestBuffer,
wil::shared_cotaskmem_string rpName,
wil::shared_cotaskmem_string userName)
{
curApp->SetPluginPerformOperationOptions(hWnd, operationType, rpName.get(), userName.get());
// Wait for the app main window to be ready.
DWORD hIndex = 0;
RETURN_IF_FAILED(CoWaitForMultipleHandles(COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS, INFINITE, 1, curApp->m_hWindowReady.addressof(), &hIndex));
// Trigger a Consent Verifier Dialog to simulate a Windows Hello unlock flow
// This is to demonstrate a vault unlock flow using Windows Hello and is not the recommended way to secure the vault
if (PluginCredentialManager::getInstance().GetVaultLock())
{
curApp->GetDispatcherQueue().TryEnqueue([curApp]()
{
curApp->SimulateUnLockVaultUsingConsentVerifier();
});
RETURN_IF_FAILED(CheckHelloConsentCompleted());
}
else
{
SetEvent(curApp->m_hVaultConsentComplete.get());
}
// Wait for user confirmation to proceed with the operation Create/Signin/Cancel button
// This is a mock up for plugin requiring UI.
{
HANDLE handles[2] = { curApp->m_hPluginProceedButtonEvent.get(), curApp->m_hPluginUserCancelEvent.get() };
DWORD cWait = ARRAYSIZE(handles);
RETURN_IF_FAILED(CoWaitForMultipleHandles(COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS, INFINITE, cWait, handles, &hIndex));
if (hIndex == 1) // Cancel button clicked
{
// User cancelled the operation. NTE_USER_CANCELLED allows Windows to distinguish between user cancellation and other errors.
return NTE_USER_CANCELLED;
}
}
// Skip user verification if the user has already performed a gesture to unlock the vault to avoid double prompting
if (PluginCredentialManager::getInstance().GetVaultLock())
{
return S_OK;
}
EXPERIMENTAL_WEBAUTHN_PLUGIN_PERFORM_UV pluginPerformUv{};
pluginPerformUv.transactionId = &transactionId;
if (curApp->m_silentMode)
{
// If the app did not display any UI, use the hwnd of the caller here. This was included in the request to the plugin. Refer: EXPERIMENTAL_PCWEBAUTHN_PLUGIN_OPERATION_REQUEST
pluginPerformUv.hwnd = hWnd;
}
else
{
// If the app displayed UI, use the hwnd of the app window here
pluginPerformUv.hwnd = curApp->GetNativeWindowHandle();
}
EXPERIMENTAL_PWEBAUTHN_PLUGIN_PERFORM_UV_RESPONSE pPluginPerformUvResponse = nullptr;
auto webAuthNPluginPerformUv = GetProcAddressByFunctionDeclaration(webauthnDll.get(), EXPERIMENTAL_WebAuthNPluginPerformUv);
RETURN_HR_IF_NULL(E_NOTIMPL, webAuthNPluginPerformUv);
// Step 1: Get the UV count
pluginPerformUv.type = EXPERIMENTAL_WEBAUTHN_PLUGIN_PERFORM_UV_OPERATION_TYPE::GetUvCount;
RETURN_IF_FAILED(webAuthNPluginPerformUv(&pluginPerformUv, &pPluginPerformUvResponse));
/*
* pPluginPerformUvResponse->pbResponse contains the UV count
* The UV count tracks the number of times the user has performed a gesture to unlock the vault
*/
// Step 2: Get the public key
pluginPerformUv.type = EXPERIMENTAL_WEBAUTHN_PLUGIN_PERFORM_UV_OPERATION_TYPE::GetPubKey;
RETURN_IF_FAILED(webAuthNPluginPerformUv(&pluginPerformUv, &pPluginPerformUvResponse));
// stash public key in a new buffer for later use
DWORD cbPubData = pPluginPerformUvResponse->cbResponse;
wil::unique_hlocal_ptr<BYTE[]> ppbPubKeyData = wil::make_unique_hlocal<BYTE[]>(cbPubData);
memcpy_s(ppbPubKeyData.get(), cbPubData, pPluginPerformUvResponse->pbResponse, pPluginPerformUvResponse->cbResponse);
// Step 3: Perform UV. This step uses a Windows Hello prompt to authenticate the user
pluginPerformUv.type = EXPERIMENTAL_WEBAUTHN_PLUGIN_PERFORM_UV_OPERATION_TYPE::PerformUv;
pluginPerformUv.pwszUsername = wil::make_cotaskmem_string(userName.get()).release();
// pwszContext can be used to provide additional context to the user. This is displayed alongside the username in the Windows Hello passkey user verification dialog.
pluginPerformUv.pwszContext = wil::make_cotaskmem_string(L"Context String").release();
RETURN_IF_FAILED(webAuthNPluginPerformUv(&pluginPerformUv, &pPluginPerformUvResponse));
// Verify the signature over the hash of requestBuffer using the hKey
auto signatureVerifyResult = VerifySignatureHelper(
requestBuffer,
ppbPubKeyData.get(),
cbPubData,
pPluginPerformUvResponse->pbResponse,
pPluginPerformUvResponse->cbResponse);
curApp->GetDispatcherQueue().TryEnqueue([curApp, signatureVerifyResult]()
{
if (FAILED(signatureVerifyResult))
{
curApp->m_pluginOperationStatus.uvSignatureVerificationStatus = signatureVerifyResult;
}
});
return S_OK;
}
/*
* This function is used to create a simplified version of authenticator data for the webauthn authenticator operations.
* Refer: https://www.w3.org/TR/webauthn-3/#authenticator-data for more details.
*/
HRESULT CreateAuthenticatorData(wil::shared_ncrypt_key hKey,
DWORD cbRpId,
PBYTE pbRpId,
DWORD& pcbPackedAuthenticatorData,
wil::unique_hlocal_ptr<BYTE[]>& ppbpackedAuthenticatorData,
std::vector<uint8_t>& vCredentialIdBuffer)
{
// Get the public key blob
DWORD cbPubKeyBlob = 0;
THROW_IF_FAILED(NCryptExportKey(
hKey.get(),
NULL,
BCRYPT_ECCPUBLIC_BLOB,
NULL,
NULL,
0,
&cbPubKeyBlob,
0));
auto pbPubKeyBlob = std::make_unique<BYTE[]>(cbPubKeyBlob);
THROW_HR_IF(E_UNEXPECTED, pbPubKeyBlob == nullptr);
DWORD cbPubKeyBlobOutput = 0;
THROW_IF_FAILED(NCryptExportKey(
hKey.get(),
NULL,
BCRYPT_ECCPUBLIC_BLOB,
NULL,
pbPubKeyBlob.get(),
cbPubKeyBlob,
&cbPubKeyBlobOutput,
0));
BCRYPT_ECCKEY_BLOB* pPubKeyBlobHeader = reinterpret_cast<BCRYPT_ECCKEY_BLOB*>(pbPubKeyBlob.get());
DWORD cbXCoord = pPubKeyBlobHeader->cbKey;
PBYTE pbXCoord = reinterpret_cast<PBYTE>(&pPubKeyBlobHeader[1]);
DWORD cbYCoord = pPubKeyBlobHeader->cbKey;
PBYTE pbYCoord = pbXCoord + cbXCoord;
// create byte span for x and y
std::span<const BYTE> xCoord(pbXCoord, cbXCoord);
std::span<const BYTE> yCoord(pbYCoord, cbYCoord);
// CBOR encode the public key in this order: kty, alg, crv, x, y
std::vector<BYTE> buffer;
#pragma warning(push)
#pragma warning(disable: 4293)
size_t bufferSize = CborLite::encodeMapSize(buffer, 5u);
#pragma warning(pop)
// COSE CBOR encoding format. Refer to https://datatracker.ietf.org/doc/html/rfc9052#section-7 for more details.
const int8_t ktyIndex = 1;
const int8_t algIndex = 3;
const int8_t crvIndex = -1;
const int8_t xIndex = -2;
const int8_t yIndex = -3;
// Example values for EC2 P-256 ES256 Keys. Refer to https://www.w3.org/TR/webauthn-3/#example-bdbd14cc
// Note that this sample authenticator only supports ES256 keys.
const int8_t kty = 2; // Key type is EC2
const int8_t crv = 1; // Curve is P-256
const int8_t alg = -7; // Algorithm is ES256
bufferSize += CborLite::encodeInteger(buffer, ktyIndex);
bufferSize += CborLite::encodeInteger(buffer, kty);
bufferSize += CborLite::encodeInteger(buffer, algIndex);
bufferSize += CborLite::encodeInteger(buffer, alg);
bufferSize += CborLite::encodeInteger(buffer, crvIndex);
bufferSize += CborLite::encodeInteger(buffer, crv);
bufferSize += CborLite::encodeInteger(buffer, xIndex);
bufferSize += CborLite::encodeBytes(buffer, xCoord);
bufferSize += CborLite::encodeInteger(buffer, yIndex);
bufferSize += CborLite::encodeBytes(buffer, yCoord);
wil::unique_bcrypt_hash hashHandle;
THROW_IF_NTSTATUS_FAILED(BCryptCreateHash(
BCRYPT_SHA256_ALG_HANDLE,
&hashHandle,
nullptr,
0,
nullptr,
0,
0));
THROW_IF_NTSTATUS_FAILED(BCryptHashData(hashHandle.get(), reinterpret_cast<PUCHAR>(pbXCoord), cbXCoord, 0));
THROW_IF_NTSTATUS_FAILED(BCryptHashData(hashHandle.get(), reinterpret_cast<PUCHAR>(pbYCoord), cbYCoord, 0));
DWORD cbHash = 0;
DWORD bytesRead = 0;
THROW_IF_NTSTATUS_FAILED(BCryptGetProperty(
hashHandle.get(),
BCRYPT_HASH_LENGTH,
reinterpret_cast<PBYTE>(&cbHash),
sizeof(cbHash),
&bytesRead,
0));
wil::unique_hlocal_ptr<BYTE[]> pbCredentialId = wil::make_unique_hlocal<BYTE[]>(cbHash);
THROW_IF_NTSTATUS_FAILED(BCryptFinishHash(hashHandle.get(), pbCredentialId.get(), cbHash, 0));
// Close the key and hash handle
hKey.reset();
hashHandle.reset();
com_ptr<App> curApp = winrt::Microsoft::UI::Xaml::Application::Current().as<App>();
PluginOperationType operationType = PLUGIN_OPERATION_TYPE_MAKE_CREDENTIAL;
if (curApp &&
curApp->m_pluginOperationOptions.operationType == PLUGIN_OPERATION_TYPE_GET_ASSERTION)
{
operationType = PLUGIN_OPERATION_TYPE_GET_ASSERTION;
}
// Refer to learn about packing credential data https://www.w3.org/TR/webauthn-3/#sctn-authenticator-data
const DWORD rpidsha256Size = 32; // SHA256 hash of rpId
const DWORD flagsSize = 1; // flags
const DWORD signCountSize = 4; // signCount
DWORD cbPackedAuthenticatorData = rpidsha256Size + flagsSize + signCountSize;
if (operationType == PLUGIN_OPERATION_TYPE_MAKE_CREDENTIAL)
{
cbPackedAuthenticatorData += sizeof(GUID); // aaGuid
cbPackedAuthenticatorData += sizeof(WORD); // credentialId length
cbPackedAuthenticatorData += cbHash; // credentialId
cbPackedAuthenticatorData += static_cast<DWORD>(buffer.size()); // public key
}
std::vector<BYTE> vPackedAuthenticatorData(cbPackedAuthenticatorData);
auto writer = buffer_writer{ vPackedAuthenticatorData };
auto rgbRpIdHash = writer.reserve_space<std::array<BYTE, rpidsha256Size>>(); // 32 bytes of rpIdHash which is SHA256 hash of rpName. https://www.w3.org/TR/webauthn-3/#sctn-authenticator-data
DWORD cbRpIdHash;
THROW_IF_WIN32_BOOL_FALSE(CryptHashCertificate2(BCRYPT_SHA256_ALGORITHM,
0,
nullptr,
pbRpId,
cbRpId,
rgbRpIdHash->data(),
&cbRpIdHash));
// Flags uv, up, be, and at are set
if (operationType == PLUGIN_OPERATION_TYPE_GET_ASSERTION)
{
// Refer https://www.w3.org/TR/webauthn-3/#authdata-flags
*writer.reserve_space<uint8_t>() = 0x1d; // credential data flags of size 1 byte
*writer.reserve_space<uint32_t>() = 0u; // Sign count of size 4 bytes is set to 0
vCredentialIdBuffer.assign(pbCredentialId.get(), pbCredentialId.get() + cbHash);
}
else
{
// Refer https://www.w3.org/TR/webauthn-3/#authdata-flags
*writer.reserve_space<uint8_t>() = 0x5d; // credential data flags of size 1 byte
*writer.reserve_space<uint32_t>() = 0u; // Sign count of size 4 bytes is set to 0
*writer.reserve_space<GUID>() = GUID_NULL; // aaGuid of size 16 bytes is set to 0
// Retrieve credential id
WORD cbCredentialId = static_cast<WORD>(cbHash);
WORD cbCredentialIdBigEndian = _byteswap_ushort(cbCredentialId);
*writer.reserve_space<WORD>() = cbCredentialIdBigEndian; // Size of credential id in unsigned big endian of size 2 bytes
writer.add(std::span<BYTE>(pbCredentialId.get(), cbHash)); // Set credential id
vCredentialIdBuffer.assign(pbCredentialId.get(), pbCredentialId.get() + cbHash);
writer.add(std::span<BYTE>(buffer.data(), buffer.size())); // Set CBOR encoded public key
}
pcbPackedAuthenticatorData = static_cast<DWORD>(vPackedAuthenticatorData.size());
ppbpackedAuthenticatorData = wil::make_unique_hlocal<BYTE[]>(pcbPackedAuthenticatorData);
memcpy_s(ppbpackedAuthenticatorData.get(), pcbPackedAuthenticatorData, vPackedAuthenticatorData.data(), pcbPackedAuthenticatorData);
return S_OK;
}
/*
* This function is invoked by the platform to request the plugin to handle a make credential operation.
* Refer: pluginauthenticator.h/pluginauthenticator.idl
*/
HRESULT STDMETHODCALLTYPE ContosoPlugin::EXPERIMENTAL_PluginMakeCredential(
/* [in] */ __RPC__in EXPERIMENTAL_PCWEBAUTHN_PLUGIN_OPERATION_REQUEST pPluginMakeCredentialRequest,
/* [out] */ __RPC__deref_out_opt EXPERIMENTAL_PWEBAUTHN_PLUGIN_OPERATION_RESPONSE* response) noexcept
{
try
{
SetEvent(App::s_pluginOpRequestRecievedEvent.get()); // indicate COM message received
DWORD hIndex = 0;
RETURN_IF_FAILED(CoWaitForMultipleHandles( // wait for app to be ready
COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS,
INFINITE,
1,
App::s_hAppReadyForPluginOpEvent.addressof(),
&hIndex));
com_ptr<App> curApp = winrt::Microsoft::UI::Xaml::Application::Current().as<App>();
wil::shared_hmodule webauthnDll(LoadLibraryExW(L"webauthn.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
if (webauthnDll == nullptr)
{
return E_ABORT;
}
wil::unique_cotaskmem_ptr<EXPERIMENTAL_WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST> pDecodedMakeCredentialRequest;
auto webauthnDecodeMakeCredentialRequest = GetProcAddressByFunctionDeclaration(webauthnDll.get(), EXPERIMENTAL_WebAuthNDecodeMakeCredentialRequest);
THROW_IF_FAILED(webauthnDecodeMakeCredentialRequest(
pPluginMakeCredentialRequest->cbEncodedRequest,
pPluginMakeCredentialRequest->pbEncodedRequest,
wil::out_param(pDecodedMakeCredentialRequest)));
auto rpName = wil::make_cotaskmem_string(pDecodedMakeCredentialRequest->pRpInformation->pwszName);
auto userName = wil::make_cotaskmem_string(pDecodedMakeCredentialRequest->pUserInformation->pwszName);
std::vector<BYTE> requestBuffer(
pPluginMakeCredentialRequest->pbEncodedRequest,
pPluginMakeCredentialRequest->pbEncodedRequest + pPluginMakeCredentialRequest->cbEncodedRequest);
auto ppbPubKeyData = GetRequestSigningPubKey();
HRESULT requestSignResult = E_FAIL;
if (!ppbPubKeyData.empty())
{
requestSignResult = VerifySignatureHelper(
requestBuffer,
ppbPubKeyData.data(),
static_cast<DWORD>(ppbPubKeyData.size()),
pPluginMakeCredentialRequest->pbRequestSignature,
pPluginMakeCredentialRequest->cbRequestSignature);
}
{
std::lock_guard<std::mutex> lock(curApp->m_pluginOperationOptionsMutex);
curApp->m_pluginOperationStatus.requestSignatureVerificationStatus = requestSignResult;
}
THROW_IF_FAILED(PerformUv(curApp,
pPluginMakeCredentialRequest->hWnd,
webauthnDll,
pPluginMakeCredentialRequest->transactionId,
PLUGIN_OPERATION_TYPE_MAKE_CREDENTIAL,
requestBuffer,
std::move(rpName),
std::move(userName)));
//create a persisted key using ncrypt
wil::unique_ncrypt_prov hProvider;
wil::unique_ncrypt_key hKey;
// get the provider
THROW_IF_FAILED(NCryptOpenStorageProvider(&hProvider, nullptr, 0));
// get the user handle as a string
std::wstring keyNameStr = contosoplugin_key_domain;
std::wstringstream keyNameStream;
for (DWORD idx = 0; idx < pDecodedMakeCredentialRequest->pUserInformation->cbId; idx++)
{
keyNameStream << std::hex << std::setw(2) << std::setfill(L'0') <<
static_cast<int>(pDecodedMakeCredentialRequest->pUserInformation->pbId[idx]);
}
keyNameStr += keyNameStream.str();
// create the key
THROW_IF_FAILED(NCryptCreatePersistedKey(
hProvider.get(),
&hKey,
BCRYPT_ECDH_P256_ALGORITHM,
keyNameStr.c_str(),
0,
NCRYPT_OVERWRITE_KEY_FLAG));
// set the export policy
DWORD exportPolicy = NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
THROW_IF_FAILED(NCryptSetProperty(
hKey.get(),
NCRYPT_EXPORT_POLICY_PROPERTY,
reinterpret_cast<PBYTE>(&exportPolicy),
sizeof(exportPolicy),
NCRYPT_PERSIST_FLAG));
// allow both signing and encryption
DWORD keyUsage = NCRYPT_ALLOW_SIGNING_FLAG | NCRYPT_ALLOW_DECRYPT_FLAG;
THROW_IF_FAILED(NCryptSetProperty(
hKey.get(),
NCRYPT_KEY_USAGE_PROPERTY,
reinterpret_cast<PBYTE>(&keyUsage),
sizeof(keyUsage),
NCRYPT_PERSIST_FLAG));
HWND hWnd;
if (curApp->m_silentMode)
{
hWnd = curApp->m_pluginOperationOptions.hWnd;
}
else
{
hWnd = curApp->GetNativeWindowHandle();
}
THROW_IF_FAILED(NCryptSetProperty(
hKey.get(),
NCRYPT_WINDOW_HANDLE_PROPERTY,
reinterpret_cast<PBYTE>(&hWnd),
sizeof(HWND),
0));
// finalize the key
THROW_IF_FAILED(NCryptFinalizeKey(hKey.get(), 0));
DWORD cbPackedAuthenticatorData = 0;
wil::unique_hlocal_ptr<BYTE[]> packedAuthenticatorData;
std::vector<uint8_t> vCredentialIdBuffer;
THROW_IF_FAILED(CreateAuthenticatorData(
std::move(hKey),
pDecodedMakeCredentialRequest->cbRpId,
pDecodedMakeCredentialRequest->pbRpId,
cbPackedAuthenticatorData,
packedAuthenticatorData,
vCredentialIdBuffer));
auto operationResponse = wil::make_unique_cotaskmem<EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_RESPONSE>();
WEBAUTHN_CREDENTIAL_ATTESTATION attestationResponse{};
attestationResponse.dwVersion = WEBAUTHN_CREDENTIAL_ATTESTATION_CURRENT_VERSION;
attestationResponse.pwszFormatType = WEBAUTHN_ATTESTATION_TYPE_NONE;
attestationResponse.cbAttestation = 0;
attestationResponse.pbAttestation = nullptr;
attestationResponse.cbAuthenticatorData = 0;
attestationResponse.pbAuthenticatorData = nullptr;
attestationResponse.pbAuthenticatorData = packedAuthenticatorData.get();
attestationResponse.cbAuthenticatorData = cbPackedAuthenticatorData;
DWORD cbAttestationBuffer = 0;
PBYTE pbattestationBuffer;
auto webauthnEncodeMakeCredentialResponse = GetProcAddressByFunctionDeclaration(webauthnDll.get(), EXPERIMENTAL_WebAuthNEncodeMakeCredentialResponse);
THROW_IF_FAILED(webauthnEncodeMakeCredentialResponse(
&attestationResponse,
&cbAttestationBuffer,
&pbattestationBuffer));
operationResponse->cbEncodedResponse = cbAttestationBuffer;
operationResponse->pbEncodedResponse = wil::make_unique_cotaskmem<BYTE[]>(cbAttestationBuffer).release();
memcpy_s(operationResponse->pbEncodedResponse,
operationResponse->cbEncodedResponse,
pbattestationBuffer,
cbAttestationBuffer);
*response = operationResponse.release();
WEBAUTHN_CREDENTIAL_DETAILS credentialDetails{};
credentialDetails.dwVersion = WEBAUTHN_CREDENTIAL_DETAILS_CURRENT_VERSION;
credentialDetails.pUserInformation = const_cast<PWEBAUTHN_USER_ENTITY_INFORMATION>(pDecodedMakeCredentialRequest->pUserInformation);
credentialDetails.pRpInformation = const_cast<PWEBAUTHN_RP_ENTITY_INFORMATION>(pDecodedMakeCredentialRequest->pRpInformation);
credentialDetails.cbCredentialID = static_cast<DWORD>(vCredentialIdBuffer.size());
credentialDetails.pbCredentialID = wil::make_unique_cotaskmem<BYTE[]>(vCredentialIdBuffer.size()).release();
memcpy_s(credentialDetails.pbCredentialID, credentialDetails.cbCredentialID, vCredentialIdBuffer.data(), static_cast<DWORD>(vCredentialIdBuffer.size()));
if (!PluginCredentialManager::getInstance().SaveCredentialMetadataToMockDB(credentialDetails))
{
std::lock_guard<std::mutex> lock(curApp->m_pluginOperationOptionsMutex);
curApp->m_pluginOperationStatus.performOperationStatus = E_FAIL;
}
pDecodedMakeCredentialRequest.reset();
SetEvent(App::s_hPluginOpCompletedEvent.get());
return S_OK;
}
catch (...)
{
HRESULT hr = wil::ResultFromCaughtException();
com_ptr<App> curApp = winrt::Microsoft::UI::Xaml::Application::Current().as<App>();
if (curApp)
{
hr = winrt::to_hresult();
std::lock_guard<std::mutex> lock(curApp->m_pluginOperationOptionsMutex);
curApp->m_pluginOperationStatus.performOperationStatus = hr;
};
SetEvent(App::s_hPluginOpCompletedEvent.get());
return hr;
}
}
/*
* This function is invoked by the platform to request the plugin to handle a get assertion operation.
* Refer: pluginauthenticator.h/pluginauthenticator.idl
*/
HRESULT STDMETHODCALLTYPE ContosoPlugin::EXPERIMENTAL_PluginGetAssertion(
/* [in] */ __RPC__in EXPERIMENTAL_PCWEBAUTHN_PLUGIN_OPERATION_REQUEST pPluginGetAssertionRequest,
/* [out] */ __RPC__deref_out_opt EXPERIMENTAL_PWEBAUTHN_PLUGIN_OPERATION_RESPONSE* response) noexcept
{
try
{
SetEvent(App::s_pluginOpRequestRecievedEvent.get());
DWORD hIndex = 0;
RETURN_IF_FAILED(CoWaitForMultipleHandles(
COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS,
INFINITE,
1,
App::s_hAppReadyForPluginOpEvent.addressof(),
&hIndex));
com_ptr<App> curApp = winrt::Microsoft::UI::Xaml::Application::Current().as<App>();
wil::shared_hmodule webauthnDll(LoadLibraryExW(L"webauthn.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
if (webauthnDll == nullptr)
{
return E_ABORT;
}
wil::unique_cotaskmem_ptr<EXPERIMENTAL_WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST> pDecodedAssertionRequest;
// The EXPERIMENTAL_WebAuthNDecodeGetAssertionRequest function can be optionally used to decode the CBOR encoded request to a EXPERIMENTAL_WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST structure.
auto webauthnDecodeGetAssertionRequest = GetProcAddressByFunctionDeclaration(webauthnDll.get(), EXPERIMENTAL_WebAuthNDecodeGetAssertionRequest);
webauthnDecodeGetAssertionRequest(pPluginGetAssertionRequest->cbEncodedRequest, pPluginGetAssertionRequest->pbEncodedRequest, wil::out_param(pDecodedAssertionRequest));
wil::shared_cotaskmem_string rpName = wil::make_cotaskmem_string(pDecodedAssertionRequest->pwszRpId);
//load the user handle
auto& credManager = PluginCredentialManager::getInstance();
const WEBAUTHN_CREDENTIAL_DETAILS* selectedCredential{};
// create a list of credentials
std::vector<const WEBAUTHN_CREDENTIAL_DETAILS *> selectedCredentials;
while (true)
{
Sleep(100);
if (credManager.IsLocalCredentialMetadataLoaded())
{
credManager.GetLocalCredsByRpIdAndAllowList(pDecodedAssertionRequest->pwszRpId,
pDecodedAssertionRequest->CredentialList.ppCredentials,
pDecodedAssertionRequest->CredentialList.cCredentials,
selectedCredentials);
break;
}
}
if (selectedCredentials.empty())
{
{
std::lock_guard<std::mutex> lock(curApp->m_pluginOperationOptionsMutex);
curApp->m_pluginOperationStatus.performOperationStatus = NTE_NOT_FOUND;
}
SetEvent(App::s_hPluginOpCompletedEvent.get());
return NTE_NOT_FOUND;
}
else if (selectedCredentials.size() == 1 && credManager.GetSilentOperation())
{
selectedCredential = selectedCredentials[0];
}
else
{
curApp->SetMatchingCredentials(pDecodedAssertionRequest->pwszRpId, selectedCredentials, pPluginGetAssertionRequest->hWnd);
hIndex = 0;
RETURN_IF_FAILED(CoWaitForMultipleHandles(COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS, INFINITE, 1, curApp->m_hPluginCredentialSelected.addressof(), &hIndex));
{
std::lock_guard<std::mutex> lock(curApp->m_pluginOperationOptionsMutex);
selectedCredential = curApp->m_pluginOperationOptions.selectedCredential;
}
// Failed to select a credential
if (selectedCredential->cbCredentialID == 0 ||
selectedCredential->pbCredentialID == nullptr ||
selectedCredential->pUserInformation == nullptr ||
selectedCredential->pUserInformation->pwszName == nullptr)
{
{
std::lock_guard<std::mutex> lock(curApp->m_pluginOperationOptionsMutex);
curApp->m_pluginOperationStatus.performOperationStatus = NTE_NOT_FOUND;
}
SetEvent(App::s_hPluginOpCompletedEvent.get());
return NTE_NOT_FOUND;
}
}
wil::shared_cotaskmem_string userName = wil::make_cotaskmem_string(selectedCredential->pUserInformation->pwszName);
std::vector<BYTE> requestBuffer(
pPluginGetAssertionRequest->pbEncodedRequest,
pPluginGetAssertionRequest->pbEncodedRequest + pPluginGetAssertionRequest->cbEncodedRequest);
auto ppbPubKeyData = GetRequestSigningPubKey();
HRESULT requestSignResult = E_FAIL;
if (!ppbPubKeyData.empty())
{
requestSignResult = VerifySignatureHelper(
requestBuffer,
ppbPubKeyData.data(),
static_cast<DWORD>(ppbPubKeyData.size()),
pPluginGetAssertionRequest->pbRequestSignature,
pPluginGetAssertionRequest->cbRequestSignature);
}
{
std::lock_guard<std::mutex> lock(curApp->m_pluginOperationOptionsMutex);
curApp->m_pluginOperationStatus.requestSignatureVerificationStatus = requestSignResult;
}
THROW_IF_FAILED(PerformUv(curApp,
pPluginGetAssertionRequest->hWnd,
webauthnDll,
pPluginGetAssertionRequest->transactionId,
PLUGIN_OPERATION_TYPE_GET_ASSERTION,
requestBuffer,
rpName,
userName));
// convert user handle to a string
std::wstring keyNameStr = contosoplugin_key_domain;
std::wstringstream keyNameStream;
for (DWORD idx = 0; idx < selectedCredential->pUserInformation->cbId; idx++)
{
keyNameStream << std::hex << std::setw(2) << std::setfill(L'0') <<
static_cast<int>(selectedCredential->pUserInformation->pbId[idx]);
}
keyNameStr += keyNameStream.str();
//open the key using ncrypt and sign the data
wil::unique_ncrypt_prov hProvider;
wil::shared_ncrypt_key hKey;
// get the provider
THROW_IF_FAILED(NCryptOpenStorageProvider(&hProvider, nullptr, 0));
// open the key
THROW_IF_FAILED(NCryptOpenKey(hProvider.get(), &hKey, keyNameStr.c_str(), 0, 0));
// set hwnd property
wil::unique_hwnd hWnd;
if (curApp->m_silentMode)
{
hWnd.reset(curApp->m_pluginOperationOptions.hWnd);
}
else
{
hWnd.reset(curApp->GetNativeWindowHandle());
}
THROW_IF_FAILED(NCryptSetProperty(
hKey.get(),
NCRYPT_WINDOW_HANDLE_PROPERTY,
(BYTE*)(hWnd.addressof()),
sizeof(HWND),
0));
// create authenticator data
DWORD cbPackedAuthenticatorData = 0;
wil::unique_hlocal_ptr<BYTE[]> packedAuthenticatorData;
std::vector<uint8_t> vCredentialIdBuffer;
THROW_IF_FAILED(CreateAuthenticatorData(hKey,
pDecodedAssertionRequest->cbRpId,
pDecodedAssertionRequest->pbRpId,
cbPackedAuthenticatorData,
packedAuthenticatorData,
vCredentialIdBuffer));
wil::unique_hlocal_ptr<BYTE[]> pbSignature = nullptr;
DWORD cbSignature = 0;
{
wil::unique_bcrypt_hash hashHandle;
THROW_IF_NTSTATUS_FAILED(BCryptCreateHash(
BCRYPT_SHA256_ALG_HANDLE,
&hashHandle,
nullptr,
0,
nullptr,
0,
0));
THROW_IF_NTSTATUS_FAILED(BCryptHashData(hashHandle.get(), const_cast<PUCHAR>(packedAuthenticatorData.get()), cbPackedAuthenticatorData, 0));
THROW_IF_NTSTATUS_FAILED(BCryptHashData(hashHandle.get(), const_cast<PUCHAR>(pDecodedAssertionRequest->pbClientDataHash), pDecodedAssertionRequest->cbClientDataHash, 0));
DWORD bytesRead = 0;
DWORD cbSignatureBuffer = 0;
THROW_IF_NTSTATUS_FAILED(BCryptGetProperty(
hashHandle.get(),
BCRYPT_HASH_LENGTH,
reinterpret_cast<PBYTE>(&cbSignatureBuffer),
sizeof(cbSignatureBuffer),
&bytesRead,
0));
wil::unique_hlocal_ptr<BYTE[]> signatureBuffer = wil::make_unique_hlocal<BYTE[]>(cbSignatureBuffer);
THROW_HR_IF(E_UNEXPECTED, signatureBuffer == nullptr);
THROW_IF_NTSTATUS_FAILED(BCryptFinishHash(hashHandle.get(), signatureBuffer.get(), cbSignatureBuffer, 0));
// sign the data
THROW_IF_FAILED(NCryptSignHash(hKey.get(), nullptr, signatureBuffer.get(), cbSignatureBuffer, nullptr, 0, &cbSignature, 0));
pbSignature = wil::make_unique_hlocal<BYTE[]>(cbSignature);
THROW_HR_IF(E_UNEXPECTED, pbSignature == nullptr);
THROW_IF_FAILED(NCryptSignHash(hKey.get(), nullptr, signatureBuffer.get(), cbSignatureBuffer, pbSignature.get(), cbSignature, &cbSignature, 0));
signatureBuffer.reset();
auto encodeSignature = [](PBYTE signature, size_t signatureSize)
{
std::vector<BYTE> encodedSignature{};
encodedSignature.push_back(0x02); // ASN integer tag
encodedSignature.push_back(static_cast<BYTE>(signatureSize)); // length of the signature
if (WI_IsFlagSet(signature[0], 0x80))
{
encodedSignature[encodedSignature.size() - 1]++;
encodedSignature.push_back(0x00); // add a padding byte if the first byte has the high bit set
}
encodedSignature.insert(encodedSignature.end(), signature, signature + signatureSize);
return encodedSignature;
};
auto signatureR = encodeSignature(pbSignature.get(), cbSignature / 2);
auto signatureS = encodeSignature(pbSignature.get() + cbSignature / 2, cbSignature / 2);
std::vector<BYTE> encodedSignature{};
encodedSignature.push_back(0x30); // ASN sequence tag
encodedSignature.push_back(static_cast<BYTE>(signatureR.size() + signatureS.size())); // length of the sequence
encodedSignature.insert(encodedSignature.end(), signatureR.begin(), signatureR.end());
encodedSignature.insert(encodedSignature.end(), signatureS.begin(), signatureS.end());
cbSignature = static_cast<DWORD>(encodedSignature.size());
pbSignature.reset();
pbSignature = wil::make_unique_hlocal<BYTE[]>(cbSignature);
THROW_HR_IF(E_UNEXPECTED, pbSignature == nullptr);
memcpy_s(pbSignature.get(), cbSignature, encodedSignature.data(), static_cast<DWORD>(cbSignature));
}
// create the response
auto operationResponse = wil::make_unique_cotaskmem<EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_RESPONSE>();
auto assertionResponse = wil::make_unique_cotaskmem<WEBAUTHN_ASSERTION>();
assertionResponse->dwVersion = WEBAUTHN_ASSERTION_CURRENT_VERSION;
// [1] Credential (optional)
assertionResponse->Credential.dwVersion = WEBAUTHN_CREDENTIAL_CURRENT_VERSION;
assertionResponse->Credential.cbId = static_cast<DWORD>(vCredentialIdBuffer.size());
assertionResponse->Credential.pbId = vCredentialIdBuffer.data();
assertionResponse->Credential.pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY;
// [2] AuthenticatorData
assertionResponse->cbAuthenticatorData = cbPackedAuthenticatorData;
assertionResponse->pbAuthenticatorData = packedAuthenticatorData.get();
// [3] Signature
assertionResponse->cbSignature = cbSignature;
assertionResponse->pbSignature = pbSignature.get();
// [4] User (optional)
assertionResponse->cbUserId = selectedCredential->pUserInformation->cbId;
auto userIdBuffer = wil::make_unique_cotaskmem<BYTE[]>(selectedCredential->pUserInformation->cbId);
memcpy_s(userIdBuffer.get(),
selectedCredential->pUserInformation->cbId,
selectedCredential->pUserInformation->pbId,
selectedCredential->pUserInformation->cbId);
assertionResponse->pbUserId = userIdBuffer.get();
WEBAUTHN_USER_ENTITY_INFORMATION userEntityInformation{};
userEntityInformation.dwVersion = WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION;
userEntityInformation.cbId = assertionResponse->cbUserId;
userEntityInformation.pbId = assertionResponse->pbUserId;
auto ctapGetAssertionResponse = wil::make_unique_cotaskmem<EXPERIMENTAL_WEBAUTHN_CTAPCBOR_GET_ASSERTION_RESPONSE>();
ctapGetAssertionResponse->WebAuthNAssertion = *(assertionResponse.get()); // [1] Credential, [2] AuthenticatorData, [3] Signature
ctapGetAssertionResponse->pUserInformation = &userEntityInformation; // [4] User
ctapGetAssertionResponse->dwNumberOfCredentials = 1; // [5] NumberOfCredentials
DWORD cbAssertionBuffer = 0;
PBYTE pbAssertionBuffer;
// The EXPERIMENTAL_WebAuthNEncodeGetAssertionResponse function can be optionally used to encode the
// EXPERIMENTAL_WEBAUTHN_CTAPCBOR_GET_ASSERTION_RESPONSE structure to a CBOR encoded response.
auto webAuthNEncodeGetAssertionResponse = GetProcAddressByFunctionDeclaration(webauthnDll.get(), EXPERIMENTAL_WebAuthNEncodeGetAssertionResponse);
THROW_IF_FAILED(webAuthNEncodeGetAssertionResponse(
(EXPERIMENTAL_PCWEBAUTHN_CTAPCBOR_GET_ASSERTION_RESPONSE)(ctapGetAssertionResponse.get()),
&cbAssertionBuffer,
&pbAssertionBuffer));
assertionResponse.reset();
ctapGetAssertionResponse.reset();
userIdBuffer.reset();
packedAuthenticatorData.reset();
pbSignature.reset();
pDecodedAssertionRequest.reset();
operationResponse->cbEncodedResponse = cbAssertionBuffer;
// pbEncodedResponse must contain a CBOR encoded response as specified the FIDO CTAP.
// Refer: https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#message-encoding.
operationResponse->pbEncodedResponse = wil::make_unique_cotaskmem<BYTE[]>(cbAssertionBuffer).release();
memcpy_s(
operationResponse->pbEncodedResponse,
operationResponse->cbEncodedResponse,
pbAssertionBuffer,
cbAssertionBuffer);
*response = operationResponse.release();
SetEvent(App::s_hPluginOpCompletedEvent.get());
return S_OK;
}
catch (...)
{
HRESULT localHr = wil::ResultFromCaughtException();
{
winrt::com_ptr<App> curApp = winrt::Microsoft::UI::Xaml::Application::Current().as<App>();
std::lock_guard<std::mutex> lock(curApp->m_pluginOperationOptionsMutex);
curApp->m_pluginOperationStatus.performOperationStatus = localHr;
}
SetEvent(App::s_hPluginOpCompletedEvent.get());
return localHr;
}
}
/*
* This function is invoked by the platform to request the plugin to cancel an ongoing operation.
*/
HRESULT STDMETHODCALLTYPE ContosoPlugin::EXPERIMENTAL_PluginCancelOperation(
/* [out] */ __RPC__in EXPERIMENTAL_PCWEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST)
{
SetEvent(App::s_pluginOpRequestRecievedEvent.get());
com_ptr<App> curApp = winrt::Microsoft::UI::Xaml::Application::Current().as<App>();
curApp->GetDispatcherQueue().TryEnqueue([curApp]()
{
curApp->PluginCancelAction();
});
return S_OK;
}
/*
* This is a sample implementation of a factory method that creates an instance of the Class that implements the EXPERIMENTAL_IPluginAuthenticator interface.
* Refer: pluginauthenticator.h/pluginauthenticator.idl for the interface definition.
*/
HRESULT __stdcall ContosoPluginFactory::CreateInstance(
::IUnknown* outer,
GUID const& iid,
void** result) noexcept
{
*result = nullptr;
if (outer)
{
return CLASS_E_NOAGGREGATION;
}
try
{
return make<ContosoPlugin>()->QueryInterface(iid, result);
}
catch (...)
{
return winrt::to_hresult();
}
}
HRESULT __stdcall ContosoPluginFactory::LockServer(BOOL) noexcept
{
return S_OK;
}
}

View File

@@ -1,126 +0,0 @@
#include "pch.h"
#include "MainPage.xaml.h"
#include "PluginRegistrationManager.h"
#include <CorError.h>
namespace winrt::PasskeyManager::implementation {
PluginRegistrationManager::PluginRegistrationManager() :
m_pluginRegistered(false),
m_initialized(false),
m_pluginState(EXPERIMENTAL_PLUGIN_AUTHENTICATOR_STATE::PluginAuthenticatorState_Unknown)
{
Initialize();
m_webAuthnDll.reset(LoadLibraryExW(L"webauthn.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
}
PluginRegistrationManager::~PluginRegistrationManager()
{
}
HRESULT PluginRegistrationManager::Initialize()
{
HRESULT hr = RefreshPluginState();
RETURN_HR_IF_EXPECTED(S_OK, RefreshPluginState() == NTE_NOT_FOUND);
RETURN_HR(hr);
}
HRESULT PluginRegistrationManager::RegisterPlugin()
{
// Get the function pointer of WebAuthNPluginAddAuthenticator
auto webAuthNPluginAddAuthenticator = GetProcAddressByFunctionDeclaration(
m_webAuthnDll.get(),
EXPERIMENTAL_WebAuthNPluginAddAuthenticator);
RETURN_HR_IF_NULL(E_FAIL, webAuthNPluginAddAuthenticator);
/*
* This section creates a sample authenticatorInfo blob to include in the registration
* request. This blob must CBOR encoded using the format defined
* in https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorGetInfo
*
* 'AAGUID' maybe used to fetch information about the authenticator from the FIDO Metadata Service and other sources.
* Refer: https://fidoalliance.org/metadata/
*
* 'extensions' field is used to perform feature detection on the authenticator
* and maybe used to determine if the authenticator is filtered out.
*/
std::string tempAaguidStr{ c_pluginAaguid };
tempAaguidStr.erase(std::remove(tempAaguidStr.begin(), tempAaguidStr.end(), L'-'), tempAaguidStr.end());
std::transform(tempAaguidStr.begin(), tempAaguidStr.end(), tempAaguidStr.begin(), [](unsigned char c) { return static_cast<char>(std::toupper(c)); });
// The following hex strings represent the encoding of
// {1: ["FIDO_2_0", "FIDO_2_1"], 2: ["prf", "hmac-secret"], 3: h'/* AAGUID */', 4: {"rk": true, "up": true, "uv": true},
// 9: ["internal"], 10: [{"alg": -7, "type": "public-key"}]}
std::string authenticatorInfoStrPart1 = "A60182684649444F5F325F30684649444F5F325F310282637072666B686D61632D7365637265740350";
std::string authenticatorInfoStrPart2 = "04A362726BF5627570F5627576F5098168696E7465726E616C0A81A263616C672664747970656A7075626C69632D6B6579";
std::string fullAuthenticatorInfoStr = authenticatorInfoStrPart1 + tempAaguidStr + authenticatorInfoStrPart2;
std::vector<BYTE> authenticatorInfo = hexStringToBytes(fullAuthenticatorInfoStr);
// Validate that c_pluginClsid is a valid CLSID
CLSID CLSID_ContosoPluginAuthenticator;
RETURN_IF_FAILED(CLSIDFromString(c_pluginClsid, &CLSID_ContosoPluginAuthenticator));
EXPERIMENTAL_WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS addOptions{};
addOptions.pwszAuthenticatorName = c_pluginName;
addOptions.pwszPluginRpId = c_pluginRpId;
addOptions.pwszPluginClsId = c_pluginClsid;
addOptions.pbAuthenticatorInfo = authenticatorInfo.data();
addOptions.cbAuthenticatorInfo = static_cast<DWORD>(authenticatorInfo.size());
EXPERIMENTAL_PWEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE addResponse;
RETURN_IF_FAILED(webAuthNPluginAddAuthenticator(&addOptions, &addResponse));
// The response from plugin contains the public key used to sign plugin operation requests. Stash it for later use.
wil::unique_hkey hKey;
RETURN_IF_WIN32_ERROR(RegCreateKeyEx(
HKEY_CURRENT_USER,
c_pluginRegistryPath,
0,
nullptr,
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
nullptr,
&hKey,
nullptr));
RETURN_IF_WIN32_ERROR(RegSetValueEx(
hKey.get(),
c_windowsPluginRequestSigningKeyRegKeyName,
0,
REG_BINARY,
addResponse->pbOpSignPubKey,
addResponse->cbOpSignPubKey));
return S_OK;
}
HRESULT PluginRegistrationManager::UnregisterPlugin()
{
// Get the function pointer of WebAuthNPluginRemoveAuthenticator
auto webAuthNPluginRemoveAuthenticator = GetProcAddressByFunctionDeclaration(
m_webAuthnDll.get(),
EXPERIMENTAL_WebAuthNPluginRemoveAuthenticator);
RETURN_HR_IF_NULL(E_FAIL, webAuthNPluginRemoveAuthenticator);
RETURN_HR(webAuthNPluginRemoveAuthenticator(c_pluginClsid));
}
HRESULT PluginRegistrationManager::RefreshPluginState()
{
// Reset the plugin state and registration status
m_pluginRegistered = false;
m_pluginState = EXPERIMENTAL_PLUGIN_AUTHENTICATOR_STATE::PluginAuthenticatorState_Unknown;
// Get handle to EXPERIMENTAL_WebAuthNPluginGetAuthenticatorState which takes in a GUID and returns EXPERIMENTAL_PLUGIN_AUTHENTICATOR_STATE
auto webAuthNPluginGetAuthenticatorState = GetProcAddressByFunctionDeclaration(
m_webAuthnDll.get(),
EXPERIMENTAL_WebAuthNPluginGetAuthenticatorState);
RETURN_HR_IF_NULL(E_FAIL, webAuthNPluginGetAuthenticatorState);
// Get the plugin state
EXPERIMENTAL_PLUGIN_AUTHENTICATOR_STATE localPluginState;
RETURN_IF_FAILED(webAuthNPluginGetAuthenticatorState(c_pluginClsid, &localPluginState));
// If the EXPERIMENTAL_WebAuthNPluginGetAuthenticatorState function succeeded, that indicates the plugin is registered and localPluginState is the valid plugin state
m_pluginRegistered = true;
m_pluginState = localPluginState;
return S_OK;
}
}

View File

@@ -1,80 +0,0 @@
#pragma once
#include "pch.h"
#include <winrt/Microsoft.UI.Xaml.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <App.xaml.h>
#include <MainWindow.xaml.h>
#include <MainPage.xaml.h>
constexpr wchar_t c_pluginName[] = L"Contoso Passkey Manager";
constexpr wchar_t c_pluginRpId[] = L"contoso.com";
/* The AAGUID is a unique identifier for the FIDO authenticator model.
*'AAGUID' maybe used to fetch information about the authenticator from the FIDO Metadata Service and other sources.
* Refer: https://fidoalliance.org/metadata/
*/
constexpr char c_pluginAaguid[] = "########-####-####-####-############";
static_assert(c_pluginAaguid[1] != '#', "Please replace the ##### above with your AAGUID or a value you generated by running guidgen");
/* Generate a GUID using guidgen and replace below and in Package.appxmanifest file */
constexpr wchar_t c_pluginClsid[] = L"{########-####-####-####-############}";
static_assert(c_pluginClsid[1] != '#', "Please replace the ##### above with a GUID you generated by running guidgen");
constexpr wchar_t c_pluginSigningKeyName[] = L"TestAppPluginIdKey";
constexpr wchar_t c_pluginRegistryPath[] = L"Software\\Contoso\\PasskeyManager";
constexpr wchar_t c_windowsPluginRequestSigningKeyRegKeyName[] = L"RequestSigningKeyBlob";
constexpr wchar_t c_windowsPluginVaultLockedRegKeyName[] = L"VaultLocked";
constexpr wchar_t c_windowsPluginSilentOperationRegKeyName[] = L"SilentOperation";
constexpr wchar_t c_windowsPluginDBUpdateInd[] = L"SilentOperation";
namespace winrt::PasskeyManager::implementation
{
class PluginRegistrationManager
{
public:
static PluginRegistrationManager& getInstance()
{
static PluginRegistrationManager instance;
return instance;
}
// Initialize function which calls GetPluginState to check if the plugin is already registered
HRESULT Initialize();
HRESULT RegisterPlugin();
HRESULT UnregisterPlugin();
HRESULT RefreshPluginState();
bool IsPluginRegistered() const
{
return m_pluginRegistered;
}
EXPERIMENTAL_PLUGIN_AUTHENTICATOR_STATE GetPluginState() const
{
return m_pluginState;
}
private:
EXPERIMENTAL_PLUGIN_AUTHENTICATOR_STATE m_pluginState;
bool m_initialized = false;
bool m_pluginRegistered = false;
wil::unique_hmodule m_webAuthnDll;
PluginRegistrationManager();
~PluginRegistrationManager();
PluginRegistrationManager(const PluginRegistrationManager&) = delete;
PluginRegistrationManager& operator=(const PluginRegistrationManager&) = delete;
void UpdatePasskeyOperationStatusText(hstring const& statusText)
{
com_ptr<App> curApp = winrt::Microsoft::UI::Xaml::Application::Current().as<App>();
curApp->GetDispatcherQueue().TryEnqueue([curApp, statusText]()
{
curApp->m_window.Content().try_as<Microsoft::UI::Xaml::Controls::Frame>().Content().try_as<MainPage>()->UpdatePasskeyOperationStatusText(statusText);
});
}
};
};

View File

@@ -1,239 +0,0 @@
/* 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 <rpcndr.h> version is high enough to compile this file*/
#ifndef __REQUIRED_RPCNDR_H_VERSION__
#define __REQUIRED_RPCNDR_H_VERSION__ 501
#endif
/* verify that the <rpcsal.h> 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 <rpcndr.h>
#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

View File

@@ -77,9 +77,7 @@ pub fn file_log(msg: &str) {
}
}
pub fn debug_log(message: &str) {
file_log(message)
}
pub fn debug_log(message: &str) {}
// Helper function to convert Windows wide string (UTF-16) to Rust String
pub unsafe fn wstr_to_string(

View File

@@ -1,6 +1,4 @@
{
"$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json",
"extraMetadata": {
"name": "bitwarden"
},
@@ -90,10 +88,9 @@
},
"win": {
"electronUpdaterCompatibility": ">=0.0.1",
"target": ["appx"],
"target": ["portable", "nsis-web", "appx"],
"signtoolOptions": {
"sign": "./sign.js",
"publisherName": "CN=com.bitwarden.localdevelopment"
"sign": "./sign.js"
},
"extraFiles": [
{
@@ -165,9 +162,8 @@
"artifactName": "${productName}-Portable-${version}.${ext}"
},
"appx": {
"artifactName": "${productName}-${arch}.${ext}",
"customManifestPath": "./custom-appx-manifest.xml",
"publisher": "CN=com.bitwarden.localdevelopment"
"artifactName": "${productName}-${version}-${arch}.${ext}",
"customManifestPath": "./custom-appx-manifest.xml"
},
"deb": {
"artifactName": "${productName}-${version}-${arch}.${ext}",

View File

@@ -1,7 +1,7 @@
{
"name": "@bitwarden/desktop",
"description": "A secure and free password manager for all of your devices.",
"version": "2025.6.4",
"version": "2025.6.0",
"keywords": [
"bitwarden",
"password",
@@ -67,7 +67,6 @@
"upload:mas": "xcrun altool --upload-app --type osx --file \"$(find ./dist/mas-universal/Bitwarden*.pkg)\" --apiKey $APP_STORE_CONNECT_AUTH_KEY --apiIssuer $APP_STORE_CONNECT_TEAM_ISSUER",
"test": "jest",
"test:watch": "jest --watch",
"test:watch:all": "jest --watchAll",
"local:win": "cd desktop_native/napi && npm run build && cd ../.. && npm run build:dev && npm run pack:win"
"test:watch:all": "jest --watchAll"
}
}

Binary file not shown.

View File

@@ -61,6 +61,10 @@ export class DesktopAutofillService implements OnDestroy {
.pipe(
distinctUntilChanged(),
switchMap((enabled) => {
if (!enabled) {
return EMPTY;
}
return this.accountService.activeAccount$.pipe(
map((account) => account?.id),
filter((userId): userId is UserId => userId != null),
@@ -80,44 +84,43 @@ export class DesktopAutofillService implements OnDestroy {
/** Give metadata about all available credentials in the users vault */
async sync(cipherViews: CipherView[]) {
this.logService.info("Syncing autofill credentials: ", cipherViews.length);
// const status = await this.status();
// if (status.type === "error") {
// return this.logService.error("Error getting autofill status", status.error);
// }
const status = await this.status();
if (status.type === "error") {
return this.logService.error("Error getting autofill status", status.error);
}
// if (!status.value.state.enabled) {
// // Autofill is disabled
// return;
// }
if (!status.value.state.enabled) {
// Autofill is disabled
return;
}
let fido2Credentials: NativeAutofillFido2Credential[];
let passwordCredentials: NativeAutofillPasswordCredential[];
fido2Credentials = (await getCredentialsForAutofill(cipherViews)).map((credential) => ({
type: "fido2",
...credential,
}));
if (status.value.support.password) {
passwordCredentials = cipherViews
.filter(
(cipher) =>
cipher.type === CipherType.Login &&
cipher.login.uris?.length > 0 &&
cipher.login.uris.some((uri) => uri.match !== UriMatchStrategy.Never) &&
cipher.login.uris.some((uri) => !Utils.isNullOrWhitespace(uri.uri)) &&
!Utils.isNullOrWhitespace(cipher.login.username),
)
.map((cipher) => ({
type: "password",
cipherId: cipher.id,
uri: cipher.login.uris.find((uri) => uri.match !== UriMatchStrategy.Never).uri,
username: cipher.login.username,
}));
}
// Mock a couple of passkeys for testing purposes
fido2Credentials.push({
type: "fido2",
cipherId: "mock-cipher-id-1",
credentialId: "passkey1",
rpId: "webauthn.io",
userHandle: "passkey1",
userName: "Mock passkey1",
});
fido2Credentials.push({
type: "fido2",
cipherId: "mock-cipher-id-2",
credentialId: "passkey2",
rpId: "webauthn.io",
userHandle: "passkey2",
userName: "Mock passkey2",
});
this.logService.info("Found FIDO2 credentials", fido2Credentials.length);
if (status.value.support.fido2) {
fido2Credentials = (await getCredentialsForAutofill(cipherViews)).map((credential) => ({
type: "fido2",
...credential,
}));
}
const syncResult = await ipc.autofill.runCommand<NativeAutofillSyncCommand>({
namespace: "autofill",

View File

@@ -2,7 +2,7 @@
"name": "@bitwarden/desktop",
"productName": "Bitwarden",
"description": "A secure and free password manager for all of your devices.",
"version": "2025.6.19",
"version": "2025.6.0",
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
"homepage": "https://bitwarden.com",
"license": "GPL-3.0",

View File

@@ -28,7 +28,7 @@ export class NativeAutofillMain {
}
async init() {
const enableWindowsPasskeyProvider = true;
const enableWindowsPasskeyProvider = false;
if (enableWindowsPasskeyProvider) {
this.windowsMain.initWindows();
this.windowsMain.setupWindowsRendererIPCHandlers();