mirror of
https://github.com/bitwarden/browser
synced 2026-02-01 01:03:39 +00:00
Begin updating Windows WebAuthn plugin to GA API
This commit is contained in:
27
apps/desktop/build.ps1
Normal file
27
apps/desktop/build.ps1
Normal file
@@ -0,0 +1,27 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
$PSNativeCommandUseErrorActionPreference = $true
|
||||
|
||||
$env:ELECTRON_BUILDER_SIGN_CERT = "C:\temp\code-signing.pfx"
|
||||
$env:ELECTRON_BUILDER_SIGN_CERT_PW = "1234"
|
||||
$bwFolder = "$env:LOCALAPPDATA\Packages\bitwardendesktop_h4e712dmw3xyy"
|
||||
|
||||
$package = Get-AppxPackage -name bitwardendesktop
|
||||
$appx = .\dist\Bitwarden-2025.10.2-arm64.appx
|
||||
$backupDataFile = C:\temp\bw-data.json
|
||||
$comLogFile = C:\temp\bitwarden_com_debug.log
|
||||
|
||||
# Build Appx
|
||||
npm run build:dev && npm run pack:win:arm64
|
||||
|
||||
# Backup tokens
|
||||
Copy-Item -Path "$bwFolder\LocalCache\Roaming\Bitwarden\data.json" -DestinationPath $backupDataFile
|
||||
|
||||
# Reinstall Appx
|
||||
Remove-AppxPackage $package && Add-AppxPackage $appx
|
||||
|
||||
# Delete log files
|
||||
Remove-Item -Path $comLogFile
|
||||
|
||||
# Restore tokens
|
||||
New-Item -Type Folder -Recurse -Path "$bwFolder\LocalCache\Roaming\Bitwarden\"
|
||||
Copy-Item -Path $backupDataFile -DestinationPath "$bwFolder\LocalCache\Roaming\Bitwarden\data.json"
|
||||
2
apps/desktop/desktop_native/napi/index.d.ts
vendored
2
apps/desktop/desktop_native/napi/index.d.ts
vendored
@@ -213,9 +213,11 @@ export declare namespace passkey_authenticator {
|
||||
requestJson: string
|
||||
}
|
||||
export interface SyncedCredential {
|
||||
/** base64url-encoded credential ID. */
|
||||
credentialId: string
|
||||
rpId: string
|
||||
userName: string
|
||||
/** base64url-encoded user ID. */
|
||||
userHandle: string
|
||||
}
|
||||
export interface PasskeySyncRequest {
|
||||
|
||||
@@ -943,9 +943,11 @@ pub mod passkey_authenticator {
|
||||
#[napi(object)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct SyncedCredential {
|
||||
/// base64url-encoded credential ID.
|
||||
pub credential_id: String, // base64url encoded
|
||||
pub rp_id: String,
|
||||
pub user_name: String,
|
||||
/// base64url-encoded user ID.
|
||||
pub user_handle: String, // base64url encoded
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ pub async fn on_request(
|
||||
match serde_json::from_str(&result) {
|
||||
Ok(resp) => resp,
|
||||
Err(e) => windows_plugin_authenticator::PasskeyResponse::Error {
|
||||
message: format!("JSON parse error: {}", e),
|
||||
message: format!("JSON parse error: {}\nJSON: {}", e, &result),
|
||||
},
|
||||
};
|
||||
let _ = event.response_sender.send(response);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[toolchain]
|
||||
channel = "1.85.0"
|
||||
channel = "1.87.0"
|
||||
components = [ "rustfmt", "clippy" ]
|
||||
profile = "minimal"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
|
||||
|
||||
/* 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 __IPluginAuthenticator_FWD_DEFINED__
|
||||
#define __IPluginAuthenticator_FWD_DEFINED__
|
||||
typedef interface IPluginAuthenticator IPluginAuthenticator;
|
||||
|
||||
#endif /* __IPluginAuthenticator_FWD_DEFINED__ */
|
||||
|
||||
|
||||
/* header files for imported files */
|
||||
#include "oaidl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
|
||||
/* interface __MIDL_itf_pluginauthenticator_0000_0000 */
|
||||
/* [local] */
|
||||
|
||||
typedef
|
||||
enum _WEBAUTHN_PLUGIN_REQUEST_TYPE
|
||||
{
|
||||
WEBAUTHN_PLUGIN_REQUEST_TYPE_CTAP2_CBOR = 0x1
|
||||
} WEBAUTHN_PLUGIN_REQUEST_TYPE;
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_OPERATION_REQUEST
|
||||
{
|
||||
HWND hWnd;
|
||||
GUID transactionId;
|
||||
DWORD cbRequestSignature;
|
||||
/* [size_is] */ byte *pbRequestSignature;
|
||||
WEBAUTHN_PLUGIN_REQUEST_TYPE requestType;
|
||||
DWORD cbEncodedRequest;
|
||||
/* [size_is] */ byte *pbEncodedRequest;
|
||||
} WEBAUTHN_PLUGIN_OPERATION_REQUEST;
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_OPERATION_REQUEST *PWEBAUTHN_PLUGIN_OPERATION_REQUEST;
|
||||
|
||||
typedef const WEBAUTHN_PLUGIN_OPERATION_REQUEST *PCWEBAUTHN_PLUGIN_OPERATION_REQUEST;
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_OPERATION_RESPONSE
|
||||
{
|
||||
DWORD cbEncodedResponse;
|
||||
/* [size_is] */ byte *pbEncodedResponse;
|
||||
} WEBAUTHN_PLUGIN_OPERATION_RESPONSE;
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_OPERATION_RESPONSE *PWEBAUTHN_PLUGIN_OPERATION_RESPONSE;
|
||||
|
||||
typedef const WEBAUTHN_PLUGIN_OPERATION_RESPONSE *PCWEBAUTHN_PLUGIN_OPERATION_RESPONSE;
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST
|
||||
{
|
||||
GUID transactionId;
|
||||
DWORD cbRequestSignature;
|
||||
/* [size_is] */ byte *pbRequestSignature;
|
||||
} WEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST;
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST *PWEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST;
|
||||
|
||||
typedef const WEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST *PCWEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST;
|
||||
|
||||
typedef
|
||||
enum _PLUGIN_LOCK_STATUS
|
||||
{
|
||||
PluginLocked = 0,
|
||||
PluginUnlocked = ( PluginLocked + 1 )
|
||||
} PLUGIN_LOCK_STATUS;
|
||||
|
||||
|
||||
|
||||
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 __IPluginAuthenticator_INTERFACE_DEFINED__
|
||||
#define __IPluginAuthenticator_INTERFACE_DEFINED__
|
||||
|
||||
/* interface IPluginAuthenticator */
|
||||
/* [ref][version][uuid][object] */
|
||||
|
||||
|
||||
EXTERN_C const IID IID_IPluginAuthenticator;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
|
||||
MIDL_INTERFACE("d26bcf6f-b54c-43ff-9f06-d5bf148625f7")
|
||||
IPluginAuthenticator : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE MakeCredential(
|
||||
/* [in] */ __RPC__in PCWEBAUTHN_PLUGIN_OPERATION_REQUEST request,
|
||||
/* [retval][out] */ __RPC__out PWEBAUTHN_PLUGIN_OPERATION_RESPONSE response) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAssertion(
|
||||
/* [in] */ __RPC__in PCWEBAUTHN_PLUGIN_OPERATION_REQUEST request,
|
||||
/* [retval][out] */ __RPC__out PWEBAUTHN_PLUGIN_OPERATION_RESPONSE response) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE CancelOperation(
|
||||
/* [in] */ __RPC__in PCWEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST request) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetLockStatus(
|
||||
/* [retval][out] */ __RPC__out PLUGIN_LOCK_STATUS *lockStatus) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#else /* C style interface */
|
||||
|
||||
typedef struct IPluginAuthenticatorVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
DECLSPEC_XFGVIRT(IUnknown, QueryInterface)
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
|
||||
__RPC__in IPluginAuthenticator * This,
|
||||
/* [in] */ __RPC__in REFIID riid,
|
||||
/* [annotation][iid_is][out] */
|
||||
_COM_Outptr_ void **ppvObject);
|
||||
|
||||
DECLSPEC_XFGVIRT(IUnknown, AddRef)
|
||||
ULONG ( STDMETHODCALLTYPE *AddRef )(
|
||||
__RPC__in IPluginAuthenticator * This);
|
||||
|
||||
DECLSPEC_XFGVIRT(IUnknown, Release)
|
||||
ULONG ( STDMETHODCALLTYPE *Release )(
|
||||
__RPC__in IPluginAuthenticator * This);
|
||||
|
||||
DECLSPEC_XFGVIRT(IPluginAuthenticator, MakeCredential)
|
||||
HRESULT ( STDMETHODCALLTYPE *MakeCredential )(
|
||||
__RPC__in IPluginAuthenticator * This,
|
||||
/* [in] */ __RPC__in PCWEBAUTHN_PLUGIN_OPERATION_REQUEST request,
|
||||
/* [retval][out] */ __RPC__out PWEBAUTHN_PLUGIN_OPERATION_RESPONSE response);
|
||||
|
||||
DECLSPEC_XFGVIRT(IPluginAuthenticator, GetAssertion)
|
||||
HRESULT ( STDMETHODCALLTYPE *GetAssertion )(
|
||||
__RPC__in IPluginAuthenticator * This,
|
||||
/* [in] */ __RPC__in PCWEBAUTHN_PLUGIN_OPERATION_REQUEST request,
|
||||
/* [retval][out] */ __RPC__out PWEBAUTHN_PLUGIN_OPERATION_RESPONSE response);
|
||||
|
||||
DECLSPEC_XFGVIRT(IPluginAuthenticator, CancelOperation)
|
||||
HRESULT ( STDMETHODCALLTYPE *CancelOperation )(
|
||||
__RPC__in IPluginAuthenticator * This,
|
||||
/* [in] */ __RPC__in PCWEBAUTHN_PLUGIN_CANCEL_OPERATION_REQUEST request);
|
||||
|
||||
DECLSPEC_XFGVIRT(IPluginAuthenticator, GetLockStatus)
|
||||
HRESULT ( STDMETHODCALLTYPE *GetLockStatus )(
|
||||
__RPC__in IPluginAuthenticator * This,
|
||||
/* [retval][out] */ __RPC__out PLUGIN_LOCK_STATUS *lockStatus);
|
||||
|
||||
END_INTERFACE
|
||||
} IPluginAuthenticatorVtbl;
|
||||
|
||||
interface IPluginAuthenticator
|
||||
{
|
||||
CONST_VTBL struct IPluginAuthenticatorVtbl *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef COBJMACROS
|
||||
|
||||
|
||||
#define IPluginAuthenticator_QueryInterface(This,riid,ppvObject) \
|
||||
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
|
||||
|
||||
#define IPluginAuthenticator_AddRef(This) \
|
||||
( (This)->lpVtbl -> AddRef(This) )
|
||||
|
||||
#define IPluginAuthenticator_Release(This) \
|
||||
( (This)->lpVtbl -> Release(This) )
|
||||
|
||||
|
||||
#define IPluginAuthenticator_MakeCredential(This,request,response) \
|
||||
( (This)->lpVtbl -> MakeCredential(This,request,response) )
|
||||
|
||||
#define IPluginAuthenticator_GetAssertion(This,request,response) \
|
||||
( (This)->lpVtbl -> GetAssertion(This,request,response) )
|
||||
|
||||
#define IPluginAuthenticator_CancelOperation(This,request) \
|
||||
( (This)->lpVtbl -> CancelOperation(This,request) )
|
||||
|
||||
#define IPluginAuthenticator_GetLockStatus(This,lockStatus) \
|
||||
( (This)->lpVtbl -> GetLockStatus(This,lockStatus) )
|
||||
|
||||
#endif /* COBJMACROS */
|
||||
|
||||
|
||||
#endif /* C style interface */
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __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
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,588 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <winapifamily.h>
|
||||
|
||||
#pragma region Desktop Family or OneCore Family
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef WINAPI
|
||||
#define WINAPI __stdcall
|
||||
#endif
|
||||
|
||||
#ifndef INITGUID
|
||||
#define INITGUID
|
||||
#include <guiddef.h>
|
||||
#undef INITGUID
|
||||
#else
|
||||
#include <guiddef.h>
|
||||
#endif
|
||||
|
||||
//+------------------------------------------------------------------------------------------
|
||||
// APIs.
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
typedef enum _PLUGIN_AUTHENTICATOR_STATE
|
||||
{
|
||||
AuthenticatorState_Disabled = 0,
|
||||
AuthenticatorState_Enabled
|
||||
} AUTHENTICATOR_STATE;
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginGetAuthenticatorState(
|
||||
_In_ REFCLSID rclsid,
|
||||
_Out_ AUTHENTICATOR_STATE* pluginAuthenticatorState
|
||||
);
|
||||
|
||||
//
|
||||
// Plugin Authenticator API: WebAuthNAddPluginAuthenticator: Add Plugin Authenticator
|
||||
//
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS {
|
||||
// Authenticator Name
|
||||
LPCWSTR pwszAuthenticatorName;
|
||||
|
||||
// Plugin COM ClsId
|
||||
REFCLSID rclsid;
|
||||
|
||||
// Plugin RPID (Optional. Required for a nested WebAuthN call originating from a plugin)
|
||||
LPCWSTR pwszPluginRpId;
|
||||
|
||||
// Plugin Authenticator Logo for the Light themes. base64 encoded SVG 1.1 (Optional)
|
||||
LPCWSTR pwszLightThemeLogoSvg;
|
||||
|
||||
// Plugin Authenticator Logo for the Dark themes. base64 encoded SVG 1.1 (Optional)
|
||||
LPCWSTR pwszDarkThemeLogoSvg;
|
||||
|
||||
// CTAP CBOR encoded authenticatorGetInfo
|
||||
DWORD cbAuthenticatorInfo;
|
||||
_Field_size_bytes_(cbAuthenticatorInfo)
|
||||
const BYTE* pbAuthenticatorInfo;
|
||||
|
||||
// List of supported RP IDs (Relying Party IDs). Should be 0/nullptr if all RPs are supported.
|
||||
DWORD cSupportedRpIds;
|
||||
const LPCWSTR* ppwszSupportedRpIds;
|
||||
|
||||
} WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS, *PWEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS;
|
||||
typedef const WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS *PCWEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS;
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE {
|
||||
// Plugin operation signing Public Key - Used to sign the request in PCWEBAUTHN_PLUGIN_OPERATION_REQUEST. Refer pluginauthenticator.h.
|
||||
DWORD cbOpSignPubKey;
|
||||
_Field_size_bytes_(cbOpSignPubKey)
|
||||
PBYTE pbOpSignPubKey;
|
||||
|
||||
} WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE, *PWEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE;
|
||||
typedef const WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE *PCWEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE;
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginAddAuthenticator(
|
||||
_In_ PCWEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS pPluginAddAuthenticatorOptions,
|
||||
_Outptr_result_maybenull_ PWEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE* ppPluginAddAuthenticatorResponse);
|
||||
|
||||
void
|
||||
WINAPI
|
||||
WebAuthNPluginFreeAddAuthenticatorResponse(
|
||||
_In_opt_ PWEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE pPluginAddAuthenticatorResponse);
|
||||
|
||||
//
|
||||
// Plugin Authenticator API: WebAuthNRemovePluginAuthenticator: Remove Plugin Authenticator
|
||||
//
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginRemoveAuthenticator(
|
||||
_In_ REFCLSID rclsid);
|
||||
|
||||
//
|
||||
// Plugin Authenticator API: WebAuthNPluginAuthenticatorUpdateDetails: Update Credential Metadata for Browser AutoFill Scenarios
|
||||
//
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_UPDATE_AUTHENTICATOR_DETAILS {
|
||||
// Authenticator Name (Optional)
|
||||
LPCWSTR pwszAuthenticatorName;
|
||||
|
||||
// Plugin COM ClsId
|
||||
REFCLSID rclsid;
|
||||
|
||||
// New Plugin COM ClsId (Optional)
|
||||
REFCLSID rclsidNew;
|
||||
|
||||
// Plugin Authenticator Logo for the Light themes. base64 encoded SVG 1.1 (Optional)
|
||||
LPCWSTR pwszLightThemeLogoSvg;
|
||||
|
||||
// Plugin Authenticator Logo for the Dark themes. base64 encoded SVG 1.1 (Optional)
|
||||
LPCWSTR pwszDarkThemeLogoSvg;
|
||||
|
||||
// CTAP CBOR encoded authenticatorGetInfo
|
||||
DWORD cbAuthenticatorInfo;
|
||||
_Field_size_bytes_(cbAuthenticatorInfo)
|
||||
const BYTE* pbAuthenticatorInfo;
|
||||
|
||||
// List of supported RP IDs (Relying Party IDs). Should be 0/nullptr if all RPs are supported.
|
||||
DWORD cSupportedRpIds;
|
||||
const LPCWSTR* ppwszSupportedRpIds;
|
||||
|
||||
} WEBAUTHN_PLUGIN_UPDATE_AUTHENTICATOR_DETAILS, *PWEBAUTHN_PLUGIN_UPDATE_AUTHENTICATOR_DETAILS;
|
||||
typedef const WEBAUTHN_PLUGIN_UPDATE_AUTHENTICATOR_DETAILS *PCWEBAUTHN_PLUGIN_UPDATE_AUTHENTICATOR_DETAILS;
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginUpdateAuthenticatorDetails(
|
||||
_In_ PCWEBAUTHN_PLUGIN_UPDATE_AUTHENTICATOR_DETAILS pPluginUpdateAuthenticatorDetails);
|
||||
|
||||
//
|
||||
// Plugin Authenticator API: WebAuthNPluginAuthenticatorAddCredentials: Add Credential Metadata for Browser AutoFill Scenarios
|
||||
//
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_CREDENTIAL_DETAILS {
|
||||
// Size of pbCredentialId.
|
||||
DWORD cbCredentialId;
|
||||
|
||||
// Credential Identifier bytes. This field is required.
|
||||
_Field_size_bytes_(cbCredentialId)
|
||||
const BYTE* pbCredentialId;
|
||||
|
||||
// Identifier for the RP. This field is required.
|
||||
LPCWSTR pwszRpId;
|
||||
|
||||
// Contains the friendly name of the Relying Party, such as "Acme Corporation", "Widgets Inc" or "Awesome Site".
|
||||
// This field is required.
|
||||
LPCWSTR pwszRpName;
|
||||
|
||||
// Identifier for the User. This field is required.
|
||||
DWORD cbUserId;
|
||||
|
||||
// User Identifier bytes. This field is required.
|
||||
_Field_size_bytes_(cbUserId)
|
||||
const BYTE* pbUserId;
|
||||
|
||||
// Contains a detailed name for this account, such as "john.p.smith@example.com".
|
||||
LPCWSTR pwszUserName;
|
||||
|
||||
// For User: Contains the friendly name associated with the user account such as "John P. Smith".
|
||||
LPCWSTR pwszUserDisplayName;
|
||||
|
||||
} WEBAUTHN_PLUGIN_CREDENTIAL_DETAILS, *PWEBAUTHN_PLUGIN_CREDENTIAL_DETAILS;
|
||||
typedef const WEBAUTHN_PLUGIN_CREDENTIAL_DETAILS *PCWEBAUTHN_PLUGIN_CREDENTIAL_DETAILS;
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginAuthenticatorAddCredentials(
|
||||
_In_ REFCLSID rclsid,
|
||||
_In_ DWORD cCredentialDetails,
|
||||
_In_reads_(cCredentialDetails) PCWEBAUTHN_PLUGIN_CREDENTIAL_DETAILS pCredentialDetails);
|
||||
|
||||
//
|
||||
// Plugin Authenticator API: WebAuthNPluginAuthenticatorRemoveCredentials: Remove Credential Metadata for Browser AutoFill Scenarios
|
||||
//
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginAuthenticatorRemoveCredentials(
|
||||
_In_ REFCLSID rclsid,
|
||||
_In_ DWORD cCredentialDetails,
|
||||
_In_reads_(cCredentialDetails) PCWEBAUTHN_PLUGIN_CREDENTIAL_DETAILS pCredentialDetails);
|
||||
|
||||
//
|
||||
// Plugin Authenticator API: WebAuthNPluginAuthenticatorRemoveCredentials: Remove All Credential Metadata for Browser AutoFill Scenarios
|
||||
//
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginAuthenticatorRemoveAllCredentials(
|
||||
_In_ REFCLSID rclsid);
|
||||
|
||||
//
|
||||
// Plugin Authenticator API: WebAuthNPluginAuthenticatorGetAllCredentials: Get All Credential Metadata cached for Browser AutoFill Scenarios
|
||||
//
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginAuthenticatorGetAllCredentials(
|
||||
_In_ REFCLSID rclsid,
|
||||
_Out_ DWORD* pcCredentialDetails,
|
||||
_Outptr_result_buffer_maybenull_(*pcCredentialDetails) PWEBAUTHN_PLUGIN_CREDENTIAL_DETAILS* ppCredentialDetailsArray);
|
||||
|
||||
//
|
||||
// Plugin Authenticator API: WebAuthNPluginAuthenticatorFreeCredentialDetailsList: Free Credential Metadata cached for Browser AutoFill Scenarios
|
||||
//
|
||||
|
||||
void
|
||||
WINAPI
|
||||
WebAuthNPluginAuthenticatorFreeCredentialDetailsArray(
|
||||
_In_ DWORD cCredentialDetails,
|
||||
_In_reads_(cCredentialDetails) PWEBAUTHN_PLUGIN_CREDENTIAL_DETAILS pCredentialDetailsArray);
|
||||
|
||||
//
|
||||
// Hello UV API for Plugin: WebAuthNPluginPerformUv: Perform Hello UV related operations
|
||||
//
|
||||
|
||||
typedef enum _WEBAUTHN_PLUGIN_PERFORM_UV_OPERATION_TYPE
|
||||
{
|
||||
PerformUserVerification = 1,
|
||||
GetUserVerificationCount,
|
||||
GetPublicKey
|
||||
} WEBAUTHN_PLUGIN_PERFORM_UV_OPERATION_TYPE;
|
||||
|
||||
typedef struct _WEBAUTHN_PLUGIN_USER_VERIFICATION_REQUEST {
|
||||
|
||||
// Windows handle of the top-level window displayed by the plugin and currently is in foreground as part of the ongoing webauthn operation.
|
||||
HWND hwnd;
|
||||
|
||||
// The webauthn transaction id from the WEBAUTHN_PLUGIN_OPERATION_REQUEST
|
||||
REFGUID rguidTransactionId;
|
||||
|
||||
// The username attached to the credential that is in use for this webauthn operation
|
||||
LPCWSTR pwszUsername;
|
||||
|
||||
// A text hint displayed on the windows hello prompt
|
||||
LPCWSTR pwszDisplayHint;
|
||||
} WEBAUTHN_PLUGIN_USER_VERIFICATION_REQUEST, *PWEBAUTHN_PLUGIN_USER_VERIFICATION_REQUEST;
|
||||
typedef const WEBAUTHN_PLUGIN_USER_VERIFICATION_REQUEST *PCWEBAUTHN_PLUGIN_USER_VERIFICATION_REQUEST;
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginPerformUserVerification(
|
||||
_In_ PCWEBAUTHN_PLUGIN_USER_VERIFICATION_REQUEST pPluginUserVerification,
|
||||
_Out_ DWORD* pcbResponse,
|
||||
_Outptr_result_bytebuffer_maybenull_(*pcbResponse) PBYTE* ppbResponse);
|
||||
|
||||
void
|
||||
WINAPI
|
||||
WebAuthNPluginFreeUserVerificationResponse(
|
||||
_In_opt_ PBYTE ppbResponse);
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginGetUserVerificationCount(
|
||||
_In_ REFCLSID rclsid,
|
||||
_Out_ DWORD* pdwVerificationCount);
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginGetUserVerificationPublicKey(
|
||||
_In_ REFCLSID rclsid,
|
||||
_Out_ DWORD* pcbPublicKey,
|
||||
_Outptr_result_bytebuffer_(*pcbPublicKey) PBYTE* ppbPublicKey); // Free using WebAuthNPluginFreePublicKeyResponse
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginGetOperationSigningPublicKey(
|
||||
_In_ REFCLSID rclsid,
|
||||
_Out_ DWORD* pcbOpSignPubKey,
|
||||
_Outptr_result_buffer_maybenull_(*pcbOpSignPubKey) PBYTE* ppbOpSignPubKey); // Free using WebAuthNPluginFreePublicKeyResponse
|
||||
|
||||
void WINAPI WebAuthNPluginFreePublicKeyResponse(
|
||||
_In_opt_ PBYTE pbOpSignPubKey);
|
||||
|
||||
#define WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS_VERSION_1 1
|
||||
#define WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS_CURRENT_VERSION WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS_VERSION_1
|
||||
typedef struct _WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS {
|
||||
//Version of this structure, to allow for modifications in the future.
|
||||
DWORD dwVersion;
|
||||
|
||||
// Following have following values:
|
||||
// +1 - TRUE
|
||||
// 0 - Not defined
|
||||
// -1 - FALSE
|
||||
//up: "true" | "false"
|
||||
LONG lUp;
|
||||
//uv: "true" | "false"
|
||||
LONG lUv;
|
||||
//rk: "true" | "false"
|
||||
LONG lRequireResidentKey;
|
||||
} WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS, *PWEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS;
|
||||
typedef const WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS *PCWEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS;
|
||||
|
||||
#define WEBAUTHN_CTAPCBOR_ECC_PUBLIC_KEY_VERSION_1 1
|
||||
#define WEBAUTHN_CTAPCBOR_ECC_PUBLIC_KEY_CURRENT_VERSION WEBAUTHN_CTAPCBOR_ECC_PUBLIC_KEY_VERSION_1
|
||||
typedef struct _WEBAUTHN_CTAPCBOR_ECC_PUBLIC_KEY {
|
||||
//Version of this structure, to allow for modifications in the future.
|
||||
DWORD dwVersion;
|
||||
|
||||
// Key type
|
||||
LONG lKty;
|
||||
|
||||
// Hash Algorithm: ES256, ES384, ES512
|
||||
LONG lAlg;
|
||||
|
||||
// Curve
|
||||
LONG lCrv;
|
||||
|
||||
//Size of "x" (X Coordinate)
|
||||
DWORD cbX;
|
||||
|
||||
//"x" (X Coordinate) data. Big Endian.
|
||||
PBYTE pbX;
|
||||
|
||||
//Size of "y" (Y Coordinate)
|
||||
DWORD cbY;
|
||||
|
||||
//"y" (Y Coordinate) data. Big Endian.
|
||||
PBYTE pbY;
|
||||
} WEBAUTHN_CTAPCBOR_ECC_PUBLIC_KEY, *PWEBAUTHN_CTAPCBOR_ECC_PUBLIC_KEY;
|
||||
typedef const WEBAUTHN_CTAPCBOR_ECC_PUBLIC_KEY *PCWEBAUTHN_CTAPCBOR_ECC_PUBLIC_KEY;
|
||||
|
||||
#define WEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION_VERSION_1 1
|
||||
#define WEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION_CURRENT_VERSION WEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION_VERSION_1
|
||||
typedef struct _WEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION {
|
||||
//Version of this structure, to allow for modifications in the future.
|
||||
DWORD dwVersion;
|
||||
|
||||
// Platform's key agreement public key
|
||||
PWEBAUTHN_CTAPCBOR_ECC_PUBLIC_KEY pKeyAgreement;
|
||||
|
||||
DWORD cbEncryptedSalt;
|
||||
PBYTE pbEncryptedSalt;
|
||||
|
||||
DWORD cbSaltAuth;
|
||||
PBYTE pbSaltAuth;
|
||||
} WEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION, *PWEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION;
|
||||
typedef const WEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION *PCWEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION;
|
||||
|
||||
#define WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST_VERSION_1 1
|
||||
#define WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST_CURRENT_VERSION WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST_VERSION_1
|
||||
typedef struct _WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST {
|
||||
//Version of this structure, to allow for modifications in the future.
|
||||
DWORD dwVersion;
|
||||
|
||||
//Input RP ID. Raw UTF8 bytes before conversion.
|
||||
//These are the bytes to be hashed in the Authenticator Data.
|
||||
DWORD cbRpId;
|
||||
PBYTE pbRpId;
|
||||
|
||||
//Client Data Hash
|
||||
DWORD cbClientDataHash;
|
||||
PBYTE pbClientDataHash;
|
||||
|
||||
//RP Information
|
||||
PCWEBAUTHN_RP_ENTITY_INFORMATION pRpInformation;
|
||||
|
||||
//User Information
|
||||
PCWEBAUTHN_USER_ENTITY_INFORMATION pUserInformation;
|
||||
|
||||
// Crypto Parameters
|
||||
WEBAUTHN_COSE_CREDENTIAL_PARAMETERS WebAuthNCredentialParameters;
|
||||
|
||||
//Credentials used for exclusion
|
||||
WEBAUTHN_CREDENTIAL_LIST CredentialList;
|
||||
|
||||
//Optional extensions to parse when performing the operation.
|
||||
DWORD cbCborExtensionsMap;
|
||||
PBYTE pbCborExtensionsMap;
|
||||
|
||||
// Authenticator Options (Optional)
|
||||
PWEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS pAuthenticatorOptions;
|
||||
|
||||
// Pin Auth (Optional)
|
||||
BOOL fEmptyPinAuth; // Zero length PinAuth is included in the request
|
||||
DWORD cbPinAuth;
|
||||
PBYTE pbPinAuth;
|
||||
|
||||
//"hmac-secret": true extension
|
||||
LONG lHmacSecretExt;
|
||||
|
||||
// "hmac-secret-mc" extension
|
||||
PWEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION pHmacSecretMcExtension;
|
||||
|
||||
//"prf" extension
|
||||
LONG lPrfExt;
|
||||
DWORD cbHmacSecretSaltValues;
|
||||
PBYTE pbHmacSecretSaltValues;
|
||||
|
||||
//"credProtect" extension. Nonzero if present
|
||||
DWORD dwCredProtect;
|
||||
|
||||
// Nonzero if present
|
||||
DWORD dwPinProtocol;
|
||||
|
||||
// Nonzero if present
|
||||
DWORD dwEnterpriseAttestation;
|
||||
|
||||
//"credBlob" extension. Nonzero if present
|
||||
DWORD cbCredBlobExt;
|
||||
PBYTE pbCredBlobExt;
|
||||
|
||||
//"largeBlobKey": true extension
|
||||
LONG lLargeBlobKeyExt;
|
||||
|
||||
//"largeBlob": extension
|
||||
DWORD dwLargeBlobSupport;
|
||||
|
||||
//"minPinLength": true extension
|
||||
LONG lMinPinLengthExt;
|
||||
|
||||
// "json" extension. Nonzero if present
|
||||
DWORD cbJsonExt;
|
||||
PBYTE pbJsonExt;
|
||||
} WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST, *PWEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST;
|
||||
typedef const WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST *PCWEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST;
|
||||
|
||||
_Success_(return == S_OK)
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNEncodeMakeCredentialResponse(
|
||||
_In_ PCWEBAUTHN_CREDENTIAL_ATTESTATION pCredentialAttestation,
|
||||
_Out_ DWORD* pcbResp,
|
||||
_Outptr_result_buffer_maybenull_(*pcbResp) BYTE** ppbResp
|
||||
);
|
||||
|
||||
_Success_(return == S_OK)
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNDecodeMakeCredentialRequest(
|
||||
_In_ DWORD cbEncoded,
|
||||
_In_reads_bytes_(cbEncoded) const BYTE* pbEncoded,
|
||||
_Outptr_ PWEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST* ppMakeCredentialRequest
|
||||
);
|
||||
|
||||
void
|
||||
WINAPI
|
||||
WebAuthNFreeDecodedMakeCredentialRequest(
|
||||
_In_opt_ PWEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST pMakeCredentialRequest
|
||||
);
|
||||
|
||||
#define WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST_VERSION_1 1
|
||||
#define WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST_CURRENT_VERSION WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST_VERSION_1
|
||||
typedef struct _WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST {
|
||||
//Version of this structure, to allow for modifications in the future.
|
||||
DWORD dwVersion;
|
||||
|
||||
//RP ID. After UTF8 to Unicode conversion,
|
||||
PCWSTR pwszRpId;
|
||||
|
||||
//Input RP ID. Raw UTF8 bytes before conversion.
|
||||
//These are the bytes to be hashed in the Authenticator Data.
|
||||
DWORD cbRpId;
|
||||
PBYTE pbRpId;
|
||||
|
||||
//Client Data Hash
|
||||
DWORD cbClientDataHash;
|
||||
PBYTE pbClientDataHash;
|
||||
|
||||
//Credentials used for inclusion
|
||||
WEBAUTHN_CREDENTIAL_LIST CredentialList;
|
||||
|
||||
//Optional extensions to parse when performing the operation.
|
||||
DWORD cbCborExtensionsMap;
|
||||
PBYTE pbCborExtensionsMap;
|
||||
|
||||
// Authenticator Options (Optional)
|
||||
PWEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS pAuthenticatorOptions;
|
||||
|
||||
// Pin Auth (Optional)
|
||||
BOOL fEmptyPinAuth; // Zero length PinAuth is included in the request
|
||||
DWORD cbPinAuth;
|
||||
PBYTE pbPinAuth;
|
||||
|
||||
// HMAC Salt Extension (Optional)
|
||||
PWEBAUTHN_CTAPCBOR_HMAC_SALT_EXTENSION pHmacSaltExtension;
|
||||
|
||||
// PRF Extension
|
||||
DWORD cbHmacSecretSaltValues;
|
||||
PBYTE pbHmacSecretSaltValues;
|
||||
|
||||
DWORD dwPinProtocol;
|
||||
|
||||
//"credBlob": true extension
|
||||
LONG lCredBlobExt;
|
||||
|
||||
//"largeBlobKey": true extension
|
||||
LONG lLargeBlobKeyExt;
|
||||
|
||||
//"largeBlob" extension
|
||||
DWORD dwCredLargeBlobOperation;
|
||||
DWORD cbCredLargeBlobCompressed;
|
||||
PBYTE pbCredLargeBlobCompressed;
|
||||
DWORD dwCredLargeBlobOriginalSize;
|
||||
|
||||
// "json" extension. Nonzero if present
|
||||
DWORD cbJsonExt;
|
||||
PBYTE pbJsonExt;
|
||||
} WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST, *PWEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST;
|
||||
typedef const WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST *PCWEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST;
|
||||
|
||||
_Success_(return == S_OK)
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNDecodeGetAssertionRequest(
|
||||
_In_ DWORD cbEncoded,
|
||||
_In_reads_bytes_(cbEncoded) const BYTE* pbEncoded,
|
||||
_Outptr_ PWEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST* ppGetAssertionRequest
|
||||
);
|
||||
|
||||
void
|
||||
WINAPI
|
||||
WebAuthNFreeDecodedGetAssertionRequest(
|
||||
_In_opt_ PWEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST pGetAssertionRequest
|
||||
);
|
||||
|
||||
typedef struct _WEBAUTHN_CTAPCBOR_GET_ASSERTION_RESPONSE {
|
||||
// [1] credential (optional)
|
||||
// [2] authenticatorData
|
||||
// [3] signature
|
||||
WEBAUTHN_ASSERTION WebAuthNAssertion;
|
||||
|
||||
// [4] user (optional)
|
||||
PCWEBAUTHN_USER_ENTITY_INFORMATION pUserInformation;
|
||||
|
||||
// [5] numberOfCredentials (optional)
|
||||
DWORD dwNumberOfCredentials;
|
||||
|
||||
// [6] userSelected (optional)
|
||||
LONG lUserSelected;
|
||||
|
||||
// [7] largeBlobKey (optional)
|
||||
DWORD cbLargeBlobKey;
|
||||
PBYTE pbLargeBlobKey;
|
||||
|
||||
// [8] unsignedExtensionOutputs
|
||||
DWORD cbUnsignedExtensionOutputs;
|
||||
PBYTE pbUnsignedExtensionOutputs;
|
||||
} WEBAUTHN_CTAPCBOR_GET_ASSERTION_RESPONSE, *PWEBAUTHN_CTAPCBOR_GET_ASSERTION_RESPONSE;
|
||||
typedef const WEBAUTHN_CTAPCBOR_GET_ASSERTION_RESPONSE *PCWEBAUTHN_CTAPCBOR_GET_ASSERTION_RESPONSE;
|
||||
|
||||
_Success_(return == S_OK)
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNEncodeGetAssertionResponse(
|
||||
_In_ PCWEBAUTHN_CTAPCBOR_GET_ASSERTION_RESPONSE pGetAssertionResponse,
|
||||
_Out_ DWORD* pcbResp,
|
||||
_Outptr_result_buffer_maybenull_(*pcbResp) BYTE** ppbResp
|
||||
);
|
||||
|
||||
typedef void (CALLBACK* WEBAUTHN_PLUGIN_STATUS_CHANGE_CALLBACK )(void* context);
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginRegisterStatusChangeCallback(
|
||||
_In_ WEBAUTHN_PLUGIN_STATUS_CHANGE_CALLBACK callback,
|
||||
_In_ void* context,
|
||||
_In_ REFCLSID rclsid,
|
||||
_Out_ DWORD* pdwRegister
|
||||
);
|
||||
|
||||
HRESULT
|
||||
WINAPI
|
||||
WebAuthNPluginUnregisterStatusChangeCallback(
|
||||
_In_ DWORD* pdwRegister
|
||||
);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // Balance extern "C" above
|
||||
#endif
|
||||
|
||||
#endif // WINAPI_FAMILY_PARTITION
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@@ -142,10 +142,10 @@ fn send_assertion_request(
|
||||
};
|
||||
|
||||
debug_log(&format!(
|
||||
"Assertion request data - RP ID: {}, Client data hash: {} bytes, Allowed credentials: {}",
|
||||
request.rpid,
|
||||
request.client_data_hash.len(),
|
||||
request.allowed_credentials.len()
|
||||
"Assertion request data - RP ID: {}, Client data hash: {} bytes, Allowed credentials: {:?}",
|
||||
passkey_request.rp_id,
|
||||
passkey_request.client_data_hash.len(),
|
||||
passkey_request.allowed_credentials,
|
||||
));
|
||||
|
||||
debug_log(format!(
|
||||
@@ -219,11 +219,17 @@ unsafe fn create_get_assertion_response(
|
||||
));
|
||||
}
|
||||
|
||||
// [5] numberOfCredentials (optional)
|
||||
cbor_response.push((
|
||||
ciborium::Value::Integer(5.into()),
|
||||
ciborium::Value::Integer(1.into()),
|
||||
));
|
||||
|
||||
let cbor_value = ciborium::Value::Map(cbor_response);
|
||||
|
||||
// Encode to CBOR with error handling
|
||||
let mut cbor_data = Vec::new();
|
||||
cbor_data.push(0); // CTAP_STATUS_OK
|
||||
// cbor_data.push(0x00); // CTAP2_OK
|
||||
if let Err(e) = ciborium::ser::into_writer(&cbor_value, &mut cbor_data) {
|
||||
debug_log(&format!(
|
||||
"ERROR: Failed to encode CBOR assertion response: {:?}",
|
||||
@@ -232,6 +238,11 @@ unsafe fn create_get_assertion_response(
|
||||
return Err(HRESULT(-1));
|
||||
}
|
||||
|
||||
debug_log(&format!(
|
||||
"Formatted CBOR assertion response: {:?}",
|
||||
cbor_data
|
||||
));
|
||||
|
||||
let response_len = cbor_data.len();
|
||||
|
||||
// Allocate memory for the response data
|
||||
@@ -357,9 +368,8 @@ pub unsafe fn experimental_plugin_get_assertion(
|
||||
};
|
||||
|
||||
debug_log(&format!(
|
||||
"Get assertion request - RP: {}, Allowed credentials: {}",
|
||||
rpid,
|
||||
allowed_credentials.len()
|
||||
"Get assertion request - RP: {}, Allowed credentials: {:?}",
|
||||
rpid, allowed_credentials
|
||||
));
|
||||
|
||||
// Send assertion request
|
||||
@@ -433,4 +443,31 @@ pub unsafe fn experimental_plugin_get_assertion(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
mod tests {
|
||||
use std::ptr::slice_from_raw_parts;
|
||||
|
||||
use super::create_get_assertion_response;
|
||||
|
||||
#[test]
|
||||
fn test_create_native_assertion_response() {
|
||||
let credential_id = vec![1, 2, 3, 4];
|
||||
let authenticator_data = vec![5, 6, 7, 8];
|
||||
let signature = vec![9, 10, 11, 12];
|
||||
let user_handle = vec![13, 14, 15, 16];
|
||||
let slice = unsafe {
|
||||
let response = *create_get_assertion_response(
|
||||
credential_id,
|
||||
authenticator_data,
|
||||
signature,
|
||||
user_handle,
|
||||
)
|
||||
.unwrap();
|
||||
&*slice_from_raw_parts(
|
||||
response.encoded_response_pointer,
|
||||
response.encoded_response_byte_count as usize,
|
||||
)
|
||||
};
|
||||
// CTAP2_OK, Map(5 elements)
|
||||
assert_eq!([0x00, 0xa5], slice[..2]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,16 +126,16 @@ pub unsafe fn parse_credential_list(credential_list: &WEBAUTHN_CREDENTIAL_LIST)
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract credential ID bytes
|
||||
// For some reason, we're getting hex strings from Windows instead of bytes.
|
||||
let credential_id_slice =
|
||||
std::slice::from_raw_parts(credential.pbId, credential.cbId as usize);
|
||||
|
||||
allowed_credentials.push(credential_id_slice.to_vec());
|
||||
debug_log(&format!(
|
||||
"Parsed credential {}: {} bytes",
|
||||
i, credential.cbId
|
||||
"Parsed credential {}: {} bytes, {:?}",
|
||||
i, credential.cbId, &credential_id_slice,
|
||||
));
|
||||
allowed_credentials.push(credential_id_slice.to_vec());
|
||||
}
|
||||
|
||||
debug_log(&format!(
|
||||
|
||||
@@ -276,27 +276,27 @@ mod tests {
|
||||
// Verify it's a map with expected keys
|
||||
if let Value::Map(map) = decoded.unwrap() {
|
||||
assert!(
|
||||
map.contains_key(&Value::Integer(1.into())),
|
||||
map.iter().any(|(k, _)| k == &Value::Integer(1.into())),
|
||||
"Should contain versions (key 1)"
|
||||
);
|
||||
assert!(
|
||||
map.contains_key(&Value::Integer(2.into())),
|
||||
map.iter().any(|(k, _)| k == &Value::Integer(2.into())),
|
||||
"Should contain extensions (key 2)"
|
||||
);
|
||||
assert!(
|
||||
map.contains_key(&Value::Integer(3.into())),
|
||||
map.iter().any(|(k, _)| k == &Value::Integer(3.into())),
|
||||
"Should contain aaguid (key 3)"
|
||||
);
|
||||
assert!(
|
||||
map.contains_key(&Value::Integer(4.into())),
|
||||
map.iter().any(|(k, _)| k == &Value::Integer(4.into())),
|
||||
"Should contain options (key 4)"
|
||||
);
|
||||
assert!(
|
||||
map.contains_key(&Value::Integer(9.into())),
|
||||
map.iter().any(|(k, _)| k == &Value::Integer(9.into())),
|
||||
"Should contain transports (key 9)"
|
||||
);
|
||||
assert!(
|
||||
map.contains_key(&Value::Integer(10.into())),
|
||||
map.iter().any(|(k, _)| k == &Value::Integer(10.into())),
|
||||
"Should contain algorithms (key 10)"
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -7,18 +7,18 @@
|
||||
|
||||
use windows_core::*;
|
||||
|
||||
use crate::util::{debug_log, delay_load, WindowsString};
|
||||
use crate::com_buffer::ComBuffer;
|
||||
use crate::util::{debug_log, delay_load, WindowsString};
|
||||
|
||||
/// Windows WebAuthn Authenticator Options structure
|
||||
/// Header File Name: _EXPERIMENTAL_WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ExperimentalWebAuthnCtapCborAuthenticatorOptions {
|
||||
pub version: u32, // DWORD dwVersion
|
||||
pub user_presence: i32, // LONG lUp: +1=TRUE, 0=Not defined, -1=FALSE
|
||||
pub user_verification: i32, // LONG lUv: +1=TRUE, 0=Not defined, -1=FALSE
|
||||
pub require_resident_key: i32, // LONG lRequireResidentKey: +1=TRUE, 0=Not defined, -1=FALSE
|
||||
pub version: u32, // DWORD dwVersion
|
||||
pub user_presence: i32, // LONG lUp: +1=TRUE, 0=Not defined, -1=FALSE
|
||||
pub user_verification: i32, // LONG lUv: +1=TRUE, 0=Not defined, -1=FALSE
|
||||
pub require_resident_key: i32, // LONG lRequireResidentKey: +1=TRUE, 0=Not defined, -1=FALSE
|
||||
}
|
||||
|
||||
/// Used when adding a Windows plugin authenticator (stable API).
|
||||
@@ -27,15 +27,15 @@ pub struct ExperimentalWebAuthnCtapCborAuthenticatorOptions {
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WebAuthnPluginAddAuthenticatorOptions {
|
||||
pub authenticator_name: *const u16, // LPCWSTR
|
||||
pub rclsid: *const GUID, // REFCLSID (changed from string)
|
||||
pub rpid: *const u16, // LPCWSTR (optional)
|
||||
pub light_theme_logo_svg: *const u16, // LPCWSTR (optional, base64 SVG)
|
||||
pub dark_theme_logo_svg: *const u16, // LPCWSTR (optional, base64 SVG)
|
||||
pub authenticator_name: *const u16, // LPCWSTR
|
||||
pub rclsid: *const GUID, // REFCLSID (changed from string)
|
||||
pub rpid: *const u16, // LPCWSTR (optional)
|
||||
pub light_theme_logo_svg: *const u16, // LPCWSTR (optional, base64 SVG)
|
||||
pub dark_theme_logo_svg: *const u16, // LPCWSTR (optional, base64 SVG)
|
||||
pub cbor_authenticator_info_byte_count: u32,
|
||||
pub cbor_authenticator_info: *const u8, // const BYTE*
|
||||
pub supported_rp_ids_count: u32, // NEW in stable
|
||||
pub supported_rp_ids: *const *const u16, // NEW in stable: array of LPCWSTR
|
||||
pub cbor_authenticator_info: *const u8, // const BYTE*
|
||||
pub supported_rp_ids_count: u32, // NEW in stable
|
||||
pub supported_rp_ids: *const *const u16, // NEW in stable: array of LPCWSTR
|
||||
}
|
||||
|
||||
/// Used as a response type when adding a Windows plugin authenticator (stable API).
|
||||
@@ -56,13 +56,13 @@ pub struct WebAuthnPluginAddAuthenticatorResponse {
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WebAuthnPluginCredentialDetails {
|
||||
pub credential_id_byte_count: u32,
|
||||
pub credential_id_pointer: *const u8, // Changed to const in stable
|
||||
pub rpid: *const u16, // Changed to const (LPCWSTR)
|
||||
pub rp_friendly_name: *const u16, // Changed to const (LPCWSTR)
|
||||
pub credential_id_pointer: *const u8, // Changed to const in stable
|
||||
pub rpid: *const u16, // Changed to const (LPCWSTR)
|
||||
pub rp_friendly_name: *const u16, // Changed to const (LPCWSTR)
|
||||
pub user_id_byte_count: u32,
|
||||
pub user_id_pointer: *const u8, // Changed to const
|
||||
pub user_name: *const u16, // Changed to const (LPCWSTR)
|
||||
pub user_display_name: *const u16, // Changed to const (LPCWSTR)
|
||||
pub user_id_pointer: *const u8, // Changed to const
|
||||
pub user_name: *const u16, // Changed to const (LPCWSTR)
|
||||
pub user_display_name: *const u16, // Changed to const (LPCWSTR)
|
||||
}
|
||||
|
||||
// Keep experimental version for internal use
|
||||
@@ -78,7 +78,8 @@ impl WebAuthnPluginCredentialDetails {
|
||||
user_display_name: String,
|
||||
) -> Self {
|
||||
// Allocate credential_id bytes with COM
|
||||
let (credential_id_pointer, credential_id_byte_count) = ComBuffer::from_buffer(&credential_id);
|
||||
let (credential_id_pointer, credential_id_byte_count) =
|
||||
ComBuffer::from_buffer(&credential_id);
|
||||
|
||||
// Allocate user_id bytes with COM
|
||||
let (user_id_pointer, user_id_byte_count) = ComBuffer::from_buffer(&user_id);
|
||||
@@ -139,23 +140,25 @@ impl ExperimentalWebAuthnPluginCredentialDetailsList {
|
||||
credentials: Vec<ExperimentalWebAuthnPluginCredentialDetails>,
|
||||
) -> Self {
|
||||
// Convert credentials to COM-allocated pointers
|
||||
let credential_pointers: Vec<*mut ExperimentalWebAuthnPluginCredentialDetails> = credentials
|
||||
.into_iter()
|
||||
.map(|cred| {
|
||||
// Use COM allocation for each credential struct
|
||||
ComBuffer::with_object(cred)
|
||||
})
|
||||
.collect();
|
||||
let credential_pointers: Vec<*mut ExperimentalWebAuthnPluginCredentialDetails> =
|
||||
credentials
|
||||
.into_iter()
|
||||
.map(|cred| {
|
||||
// Use COM allocation for each credential struct
|
||||
ComBuffer::with_object(cred)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let credentials_len = credential_pointers.len();
|
||||
|
||||
// Allocate the array of pointers using COM as well
|
||||
let credentials_pointer = if credentials_len > 0 {
|
||||
let pointer_array_bytes = credential_pointers.len() * std::mem::size_of::<*mut ExperimentalWebAuthnPluginCredentialDetails>();
|
||||
let pointer_array_bytes = credential_pointers.len()
|
||||
* std::mem::size_of::<*mut ExperimentalWebAuthnPluginCredentialDetails>();
|
||||
let (ptr, _) = ComBuffer::from_buffer(unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
credential_pointers.as_ptr() as *const u8,
|
||||
pointer_array_bytes
|
||||
pointer_array_bytes,
|
||||
)
|
||||
});
|
||||
ptr as *mut *mut ExperimentalWebAuthnPluginCredentialDetails
|
||||
@@ -177,9 +180,9 @@ impl ExperimentalWebAuthnPluginCredentialDetailsList {
|
||||
// Stable API function signatures - now use REFCLSID and flat arrays
|
||||
pub type WebAuthNPluginAuthenticatorAddCredentialsFnDeclaration =
|
||||
unsafe extern "cdecl" fn(
|
||||
rclsid: *const GUID, // Changed from string to GUID reference
|
||||
rclsid: *const GUID, // Changed from string to GUID reference
|
||||
cCredentialDetails: u32,
|
||||
pCredentialDetails: *const WebAuthnPluginCredentialDetails, // Flat array, not list
|
||||
pCredentialDetails: *const WebAuthnPluginCredentialDetails, // Flat array, not list
|
||||
) -> HRESULT;
|
||||
|
||||
pub type WebAuthNPluginAuthenticatorRemoveCredentialsFnDeclaration =
|
||||
@@ -192,8 +195,8 @@ pub type WebAuthNPluginAuthenticatorRemoveCredentialsFnDeclaration =
|
||||
pub type WebAuthNPluginAuthenticatorGetAllCredentialsFnDeclaration =
|
||||
unsafe extern "cdecl" fn(
|
||||
rclsid: *const GUID,
|
||||
pcCredentialDetails: *mut u32, // Out param for count
|
||||
ppCredentialDetailsArray: *mut *mut WebAuthnPluginCredentialDetails, // Out param for array
|
||||
pcCredentialDetails: *mut u32, // Out param for count
|
||||
ppCredentialDetailsArray: *mut *mut WebAuthnPluginCredentialDetails, // Out param for array
|
||||
) -> HRESULT;
|
||||
|
||||
pub type WebAuthNPluginAuthenticatorFreeCredentialDetailsArrayFnDeclaration =
|
||||
@@ -203,9 +206,7 @@ pub type WebAuthNPluginAuthenticatorFreeCredentialDetailsArrayFnDeclaration =
|
||||
);
|
||||
|
||||
pub type WebAuthNPluginAuthenticatorRemoveAllCredentialsFnDeclaration =
|
||||
unsafe extern "cdecl" fn(
|
||||
rclsid: *const GUID,
|
||||
) -> HRESULT;
|
||||
unsafe extern "cdecl" fn(rclsid: *const GUID) -> HRESULT;
|
||||
|
||||
pub fn add_credentials(
|
||||
clsid_guid: GUID,
|
||||
@@ -245,7 +246,7 @@ pub fn add_credentials(
|
||||
|
||||
debug_log("API call succeeded");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
None => {
|
||||
debug_log("Failed to load WebAuthNPluginAuthenticatorAddCredentials function from webauthn.dll");
|
||||
Err(String::from("Error: Can't complete add_credentials(), as the function WebAuthNPluginAuthenticatorAddCredentials can't be loaded."))
|
||||
@@ -440,9 +441,7 @@ fn free_credential_details_array(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_all_credentials(
|
||||
clsid_guid: GUID,
|
||||
) -> std::result::Result<(), String> {
|
||||
pub fn remove_all_credentials(clsid_guid: GUID) -> std::result::Result<(), String> {
|
||||
debug_log("Loading WebAuthNPluginAuthenticatorRemoveAllCredentials function...");
|
||||
|
||||
let result = unsafe {
|
||||
@@ -470,7 +469,7 @@ pub fn remove_all_credentials(
|
||||
|
||||
debug_log("API call succeeded");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
None => {
|
||||
debug_log("Failed to load WebAuthNPluginAuthenticatorRemoveAllCredentials function from webauthn.dll");
|
||||
Err(String::from("Error: Can't complete remove_all_credentials(), as the function WebAuthNPluginAuthenticatorRemoveAllCredentials can't be loaded."))
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"pack:mac:masdev": "npm run clean:dist && electron-builder --mac mas-dev --universal -p never",
|
||||
"pack:mac:masdev:with-extension": "npm run clean:dist && npm run build:macos-extension:masdev && electron-builder --mac mas-dev --universal -p never",
|
||||
"pack:win": "npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p never -c.win.signtoolOptions.certificateSubjectName=\"8bit Solutions LLC\"",
|
||||
"pack:win:arm64": "npm run clean:dist && electron-builder --win --arm64 -p never -c.win.signtoolOptions.certificateSubjectName=\"8bit Solutions LLC\"",
|
||||
"pack:win:beta": "npm run clean:dist && electron-builder --config electron-builder.beta.json --win --x64 --arm64 --ia32 -p never -c.win.signtoolOptions.certificateSubjectName=\"8bit Solutions LLC\"",
|
||||
"pack:win:ci": "npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p never",
|
||||
"dist:dir": "npm run build && npm run pack:dir",
|
||||
|
||||
@@ -18,5 +18,21 @@ exports.default = async function (configuration) {
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
} else if (process.env.ELECTRON_BUILDER_SIGN_CERT) {
|
||||
const certFile = process.env.ELECTRON_BUILDER_SIGN_CERT
|
||||
const certPw = process.env.ELECTRON_BUILDER_SIGN_CERT_PW
|
||||
console.log(`[*] Signing file: ${configuration.path} with ${certFile}`);
|
||||
require("child_process").execSync(
|
||||
"signtool.exe sign" +
|
||||
" /fd SHA256" +
|
||||
" /a" +
|
||||
` /f "${certFile}"` +
|
||||
` /p "${process.env.ELECTRON_BUILDER_SIGN_CERT_PW}"` +
|
||||
` "${configuration.path}"`,
|
||||
{
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ export default {
|
||||
) => {
|
||||
const { clientId, sequenceNumber, request } = data;
|
||||
fn(clientId, sequenceNumber, request, (error, response) => {
|
||||
console.log("autofill.passkeyRegistration IPC response", error, response)
|
||||
if (error) {
|
||||
ipcRenderer.send("autofill.completeError", {
|
||||
clientId,
|
||||
|
||||
@@ -92,7 +92,7 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
// }
|
||||
|
||||
let fido2Credentials: NativeAutofillFido2Credential[];
|
||||
let passwordCredentials: NativeAutofillPasswordCredential[];
|
||||
let passwordCredentials: NativeAutofillPasswordCredential[] = [];
|
||||
|
||||
fido2Credentials = (await getCredentialsForAutofill(cipherViews)).map((credential) => ({
|
||||
type: "fido2",
|
||||
@@ -229,7 +229,7 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
|
||||
ipc.autofill.listenPasskeyAssertion(async (clientId, sequenceNumber, request, callback) => {
|
||||
this.logService.warning("listenPasskeyAssertion", clientId, sequenceNumber, request);
|
||||
|
||||
// II: Stopped here!
|
||||
const controller = new AbortController();
|
||||
void this.fido2AuthenticatorService
|
||||
.getAssertion(
|
||||
@@ -278,6 +278,7 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
request: autofill.PasskeyRegistrationRequest,
|
||||
response: Fido2AuthenticatorMakeCredentialResult,
|
||||
): autofill.PasskeyRegistrationResponse {
|
||||
this.logService.debug("converting registrationResponse to Windows format")
|
||||
return {
|
||||
rpId: request.rpId,
|
||||
clientDataHash: request.clientDataHash,
|
||||
|
||||
@@ -130,6 +130,8 @@ export class Main {
|
||||
}
|
||||
|
||||
this.logService = new ElectronLogMainService(null, app.getPath("userData"));
|
||||
this.logService.info("IS THIS THING ON?")
|
||||
this.logService.debug("IS THIS THING ON? [debug]")
|
||||
|
||||
const storageDefaults: any = {};
|
||||
this.storageService = new ElectronStorageService(app.getPath("userData"), storageDefaults);
|
||||
@@ -303,8 +305,15 @@ export class Main {
|
||||
|
||||
new ChromiumImporterService();
|
||||
|
||||
|
||||
this.nativeAutofillMain = new NativeAutofillMain(this.logService, this.windowMain);
|
||||
void this.nativeAutofillMain.init();
|
||||
app
|
||||
.whenReady()
|
||||
.then(async () => {
|
||||
this.logService.debug("ATTEMPTING TO INITIALIZE NATIVE AUTOFILL")
|
||||
await this.nativeAutofillMain.init();
|
||||
|
||||
})
|
||||
|
||||
this.mainDesktopAutotypeService = new MainDesktopAutotypeService(
|
||||
this.logService,
|
||||
|
||||
@@ -29,20 +29,33 @@ export class NativeAutofillMain {
|
||||
|
||||
async init() {
|
||||
const enableWindowsPasskeyProvider = true;
|
||||
this.logService.debug("Windows passkey provider enabled: " + enableWindowsPasskeyProvider)
|
||||
if (enableWindowsPasskeyProvider) {
|
||||
this.windowsMain.initWindows();
|
||||
this.windowsMain.setupWindowsRendererIPCHandlers();
|
||||
}
|
||||
let err = this.windowsMain.initWindows();
|
||||
if (err) {
|
||||
this.logService.debug("Error occurred while initialized windows plugin:", err)
|
||||
throw err
|
||||
}
|
||||
this.logService.debug("Windows passkey provider initialized: " + enableWindowsPasskeyProvider)
|
||||
|
||||
this.logService.debug("Setting up windows renderers")
|
||||
this.windowsMain.setupWindowsRendererIPCHandlers();
|
||||
this.logService.debug("Setting up windows renderers done.")
|
||||
}
|
||||
this.logService.debug("Set up autofill IPC handlers: ")
|
||||
|
||||
/*
|
||||
ipcMain.handle(
|
||||
"autofill.runCommand",
|
||||
<C extends CommandDefinition>(
|
||||
_event: any,
|
||||
params: RunCommandParams<C>,
|
||||
): Promise<RunCommandResult<C>> => {
|
||||
this.logService.debug("Received event:", "autofill.runCommand", params)
|
||||
return this.runCommand(params);
|
||||
},
|
||||
);
|
||||
*/
|
||||
|
||||
this.ipcServer = await autofill.IpcServer.listen(
|
||||
"af",
|
||||
@@ -87,6 +100,7 @@ export class NativeAutofillMain {
|
||||
},
|
||||
);
|
||||
|
||||
/*
|
||||
ipcMain.on("autofill.completePasskeyRegistration", (event, data) => {
|
||||
this.logService.warning("autofill.completePasskeyRegistration", data);
|
||||
const { clientId, sequenceNumber, response } = data;
|
||||
@@ -104,6 +118,7 @@ export class NativeAutofillMain {
|
||||
const { clientId, sequenceNumber, error } = data;
|
||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
private async runCommand<C extends CommandDefinition>(
|
||||
|
||||
@@ -18,7 +18,16 @@ export class NativeAutofillWindowsMain {
|
||||
) {}
|
||||
|
||||
initWindows() {
|
||||
passkey_authenticator.register();
|
||||
try {
|
||||
passkey_authenticator.register();
|
||||
}
|
||||
catch (err) {
|
||||
this.logService.error("Failed to register windows passkey plugin:", err)
|
||||
return JSON.stringify({
|
||||
"type": "error",
|
||||
"message": "Failed to register windows passkey plugin"
|
||||
})
|
||||
}
|
||||
void passkey_authenticator.onRequest(async (error, event) => {
|
||||
this.logService.info("Passkey request received:", { error, event });
|
||||
|
||||
@@ -241,6 +250,7 @@ export class NativeAutofillWindowsMain {
|
||||
_event: any,
|
||||
params: RunCommandParams<C>,
|
||||
): Promise<RunCommandResult<C>> => {
|
||||
this.logService.debug("Received event (windows):", "autofill.runCommand", params)
|
||||
return this.runCommand(params);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@ export class ElectronLogMainService extends BaseLogService {
|
||||
return;
|
||||
}
|
||||
|
||||
log.transports.file.level = "info";
|
||||
log.transports.file.level = "debug";
|
||||
if (this.logDir != null) {
|
||||
log.transports.file.resolvePathFn = () => path.join(this.logDir, "app.log");
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ export function parseCredentialId(encodedCredentialId: string): ArrayBuffer {
|
||||
return Fido2Utils.stringToBuffer(encodedCredentialId.slice(4));
|
||||
}
|
||||
|
||||
return guidToRawFormat(encodedCredentialId).buffer;
|
||||
let arr = guidToRawFormat(encodedCredentialId);
|
||||
console.log("guidToRawFormat output:", arr)
|
||||
return arr.buffer;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
@@ -19,12 +21,14 @@ export function parseCredentialId(encodedCredentialId: string): ArrayBuffer {
|
||||
* Compares two credential IDs for equality.
|
||||
*/
|
||||
export function compareCredentialIds(a: ArrayBuffer, b: ArrayBuffer): boolean {
|
||||
console.log("compareCredentialIds:", a, b)
|
||||
if (a.byteLength !== b.byteLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const viewA = new Uint8Array(a);
|
||||
const viewB = new Uint8Array(b);
|
||||
console.log("Comparing credential IDs:", viewA, viewB)
|
||||
|
||||
for (let i = 0; i < viewA.length; i++) {
|
||||
if (viewA[i] !== viewB[i]) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import { compareCredentialIds, parseCredentialId } from "./credential-id-utils";
|
||||
import { p1363ToDer } from "./ecdsa-utils";
|
||||
import { Fido2Utils } from "./fido2-utils";
|
||||
import { guidToStandardFormat } from "./guid-utils";
|
||||
import { PrimarySecondaryStorageService } from "../../storage/primary-secondary-storage.service";
|
||||
|
||||
// AAGUID: d548826e-79b4-db40-a3d8-11116f7e8349
|
||||
export const AAGUID = new Uint8Array([
|
||||
@@ -62,12 +63,15 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
window: ParentWindowReference,
|
||||
abortController?: AbortController,
|
||||
): Promise<Fido2AuthenticatorMakeCredentialResult> {
|
||||
this.logService.debug("[Fido2AuthenticatorService] makeCredential")
|
||||
this.logService.debug("[Fido2AuthenticatorService] create new session")
|
||||
const userInterfaceSession = await this.userInterface.newSession(
|
||||
params.fallbackSupported,
|
||||
window,
|
||||
abortController,
|
||||
);
|
||||
|
||||
this.logService.debug("[Fido2AuthenticatorService] try create new credential")
|
||||
try {
|
||||
if (params.credTypesAndPubKeyAlgs.every((p) => p.alg !== Fido2AlgorithmIdentifier.ES256)) {
|
||||
const requestedAlgorithms = params.credTypesAndPubKeyAlgs.map((p) => p.alg).join(", ");
|
||||
@@ -101,6 +105,7 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
throw new Fido2AuthenticatorError(Fido2AuthenticatorErrorCode.Unknown);
|
||||
}
|
||||
|
||||
this.logService.debug("Ensuring unlocked vault before creating credential")
|
||||
await userInterfaceSession.ensureUnlockedVault();
|
||||
|
||||
// Avoid syncing if we did it reasonably soon as the only reason for syncing is to validate excludeCredentials
|
||||
@@ -128,6 +133,8 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
let userVerified = false;
|
||||
let credentialId: string;
|
||||
let pubKeyDer: ArrayBuffer;
|
||||
|
||||
this.logService.debug("[Fido2AuthenticatorService] Prompting user to confirm creating credential")
|
||||
const response = await userInterfaceSession.confirmNewCredential({
|
||||
credentialName: params.rpEntity.name,
|
||||
userName: params.userEntity.name,
|
||||
@@ -435,15 +442,29 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
credentials: PublicKeyCredentialDescriptor[],
|
||||
rpId: string,
|
||||
): Promise<CipherView[]> {
|
||||
this.logService.debug("[findCredentialsById]:", credentials, rpId)
|
||||
if (credentials.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
const ciphers = await this.cipherService.getAllDecrypted(activeUserId);
|
||||
this.logService.debug("[findCredentialsById] ciphers:", ciphers)
|
||||
return ciphers.filter(
|
||||
(cipher) =>
|
||||
!cipher.isDeleted &&
|
||||
(cipher) => {
|
||||
this.logService.debug(cipher.id,
|
||||
!cipher.isDeleted,
|
||||
cipher.type === CipherType.Login,
|
||||
cipher.login.hasFido2Credentials,
|
||||
cipher.login.fido2Credentials[0].rpId === rpId,
|
||||
credentials.some((credential) => {
|
||||
let credId = cipher.login.fido2Credentials[0].credentialId
|
||||
let parsedCredId = parseCredentialId(credId)
|
||||
this.logService.debug(credential.id, credential.id.byteLength, typeof(credential.id), credId, parsedCredId, parsedCredId.byteLength, typeof(parsedCredId))
|
||||
return compareCredentialIds(credential.id, parsedCredId)
|
||||
}),
|
||||
)
|
||||
return !cipher.isDeleted &&
|
||||
cipher.type === CipherType.Login &&
|
||||
cipher.login.hasFido2Credentials &&
|
||||
cipher.login.fido2Credentials[0].rpId === rpId &&
|
||||
@@ -452,13 +473,16 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
credential.id,
|
||||
parseCredentialId(cipher.login.fido2Credentials[0].credentialId),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async findCredentialsByRp(rpId: string): Promise<CipherView[]> {
|
||||
this.logService.debug("[findCredentialByRp]:", rpId)
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
const ciphers = await this.cipherService.getAllDecrypted(activeUserId);
|
||||
this.logService.debug("[findCredentialsByRp] ciphers:", ciphers)
|
||||
return ciphers.filter(
|
||||
(cipher) =>
|
||||
!cipher.isDeleted &&
|
||||
|
||||
Reference in New Issue
Block a user