#include "device/fido/win/webauthn_api.h"
#include <string>
#include <vector>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_conversions.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_piece_forward.h"
#include "base/strings/string_util_win.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/scoped_thread_priority.h"
#include "components/device_event_log/device_event_log.h"
#include "device/fido/features.h"
#include "device/fido/fido_types.h"
#include "device/fido/win/logging.h"
#include "device/fido/win/type_conversions.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/microsoft_webauthn/webauthn.h"
namespace device {
namespace {
constexpr uint32_t kWinWebAuthnTimeoutMilliseconds = 1000 * 60 * 5;
std::string HresultToHex(HRESULT hr) {
return base::StringPrintf("0x%0lX", hr);
}
WEBAUTHN_HMAC_SECRET_SALT* FillHMACSalts(
std::vector<WEBAUTHN_HMAC_SECRET_SALT>* salts_storage,
const PRFInput& input) {
const WEBAUTHN_HMAC_SECRET_SALT salts{
base::checked_cast<DWORD>(input.salt1.size()),
const_cast<PBYTE>(input.salt1.data()),
input.salt2.has_value() ? base::checked_cast<DWORD>(input.salt2->size())
: 0,
input.salt2.has_value() ? const_cast<PBYTE>(input.salt2->data())
: nullptr,
};
salts_storage->push_back(salts);
return &salts_storage->back();
}
WEBAUTHN_HMAC_SECRET_SALT_VALUES* FillHMACSaltValues(
WEBAUTHN_HMAC_SECRET_SALT_VALUES* values_storage,
std::vector<WEBAUTHN_HMAC_SECRET_SALT>* salts_storage,
std::vector<WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT>* cred_salts_storage,
const std::vector<PRFInput>& inputs) {
if (inputs.empty()) {
return nullptr;
}
memset(values_storage, 0, sizeof(*values_storage));
salts_storage->reserve(inputs.size());
cred_salts_storage->reserve(inputs.size());
for (const auto& input : inputs) {
if (!input.credential_id.has_value()) {
DCHECK(cred_salts_storage->empty());
values_storage->pGlobalHmacSalt = FillHMACSalts(salts_storage, input);
} else {
const WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT cred_salt{
base::checked_cast<DWORD>(input.credential_id->size()),
const_cast<PBYTE>(input.credential_id->data()),
FillHMACSalts(salts_storage, input),
};
cred_salts_storage->push_back(cred_salt);
}
}
if (!cred_salts_storage->empty()) {
values_storage->cCredWithHmacSecretSaltList =
base::checked_cast<DWORD>(cred_salts_storage->size());
values_storage->pCredWithHmacSecretSaltList = cred_salts_storage->data();
}
return values_storage;
}
}
class WinWebAuthnApiImpl : public WinWebAuthnApi {
public:
WinWebAuthnApiImpl() {
if (!base::FeatureList::IsEnabled(device::kWebAuthUseNativeWinApi)) {
FIDO_LOG(DEBUG) << "Windows WebAuthn API deactivated via feature flag";
return;
}
{
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
webauthn_dll_ =
LoadLibraryExA("webauthn.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
}
if (!webauthn_dll_) {
FIDO_LOG(ERROR) << "Windows WebAuthn API failed to load";
return;
}
#define BIND_FN(fn_pointer, lib_handle, fn_name) \
DCHECK(!fn_pointer); \
fn_pointer = reinterpret_cast<decltype(fn_pointer)>( \
GetProcAddress(lib_handle, fn_name));
#define BIND_FN_OR_RETURN(fn_pointer, lib_handle, fn_name) \
BIND_FN(fn_pointer, lib_handle, fn_name); \
if (!fn_pointer) { \
DLOG(ERROR) << "failed to bind " << fn_name; \
return; \
}
BIND_FN_OR_RETURN(is_user_verifying_platform_authenticator_available_,
webauthn_dll_,
"WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable");
BIND_FN_OR_RETURN(authenticator_make_credential_, webauthn_dll_,
"WebAuthNAuthenticatorMakeCredential");
BIND_FN_OR_RETURN(authenticator_get_assertion_, webauthn_dll_,
"WebAuthNAuthenticatorGetAssertion");
BIND_FN_OR_RETURN(cancel_current_operation_, webauthn_dll_,
"WebAuthNCancelCurrentOperation");
BIND_FN_OR_RETURN(get_error_name_, webauthn_dll_, "WebAuthNGetErrorName");
BIND_FN_OR_RETURN(free_credential_attestation_, webauthn_dll_,
"WebAuthNFreeCredentialAttestation");
BIND_FN_OR_RETURN(free_assertion_, webauthn_dll_, "WebAuthNFreeAssertion");
BIND_FN(get_platform_credential_list_, webauthn_dll_,
"WebAuthNGetPlatformCredentialList");
if (get_platform_credential_list_) {
BIND_FN_OR_RETURN(free_platform_credential_list_, webauthn_dll_,
"WebAuthNFreePlatformCredentialList");
BIND_FN_OR_RETURN(delete_platform_credential_, webauthn_dll_,
"WebAuthNDeletePlatformCredential");
}
is_bound_ = true;
BIND_FN(get_api_version_number_, webauthn_dll_,
"WebAuthNGetApiVersionNumber");
api_version_ = get_api_version_number_ ? get_api_version_number_() : 0;
FIDO_LOG(DEBUG) << "webauthn.dll version " << api_version_;
}
~WinWebAuthnApiImpl() override = default;
bool IsAvailable() const override {
return is_bound_ && (api_version_ >= WEBAUTHN_API_VERSION_1);
}
bool SupportsSilentDiscovery() const override {
return get_platform_credential_list_;
}
HRESULT IsUserVerifyingPlatformAuthenticatorAvailable(
BOOL* available) override {
DCHECK(is_bound_);
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
return is_user_verifying_platform_authenticator_available_(available);
}
HRESULT AuthenticatorMakeCredential(
HWND h_wnd,
PCWEBAUTHN_RP_ENTITY_INFORMATION rp,
PCWEBAUTHN_USER_ENTITY_INFORMATION user,
PCWEBAUTHN_COSE_CREDENTIAL_PARAMETERS cose_credential_parameters,
PCWEBAUTHN_CLIENT_DATA client_data,
PCWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS options,
PWEBAUTHN_CREDENTIAL_ATTESTATION* credential_attestation_ptr) override {
DCHECK(is_bound_);
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
return authenticator_make_credential_(
h_wnd, rp, user, cose_credential_parameters, client_data, options,
credential_attestation_ptr);
}
HRESULT AuthenticatorGetAssertion(
HWND h_wnd,
LPCWSTR rp_id,
PCWEBAUTHN_CLIENT_DATA client_data,
PCWEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS options,
PWEBAUTHN_ASSERTION* assertion_ptr) override {
DCHECK(is_bound_);
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
return authenticator_get_assertion_(h_wnd, rp_id, client_data, options,
assertion_ptr);
}
HRESULT CancelCurrentOperation(GUID* cancellation_id) override {
DCHECK(is_bound_);
return cancel_current_operation_(cancellation_id);
}
HRESULT GetPlatformCredentialList(
PCWEBAUTHN_GET_CREDENTIALS_OPTIONS options,
PWEBAUTHN_CREDENTIAL_DETAILS_LIST* credentials) override {
DCHECK(is_bound_ && get_platform_credential_list_);
return get_platform_credential_list_(options, credentials);
}
HRESULT DeletePlatformCredential(
base::span<const uint8_t> credential_id) override {
return delete_platform_credential_(credential_id.size(),
credential_id.data());
}
PCWSTR GetErrorName(HRESULT hr) override {
DCHECK(is_bound_);
return get_error_name_(hr);
}
void FreeCredentialAttestation(
PWEBAUTHN_CREDENTIAL_ATTESTATION attestation_ptr) override {
DCHECK(is_bound_);
return free_credential_attestation_(attestation_ptr);
}
void FreeAssertion(PWEBAUTHN_ASSERTION assertion_ptr) override {
DCHECK(is_bound_);
return free_assertion_(assertion_ptr);
}
void FreePlatformCredentialList(
PWEBAUTHN_CREDENTIAL_DETAILS_LIST credentials) override {
DCHECK(is_bound_ && free_platform_credential_list_);
free_platform_credential_list_(credentials);
}
int Version() override { return api_version_; }
private:
bool is_bound_ = false;
uint32_t api_version_ = 0;
HMODULE webauthn_dll_;
decltype(&WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable)
is_user_verifying_platform_authenticator_available_ = nullptr;
decltype(&WebAuthNAuthenticatorMakeCredential)
authenticator_make_credential_ = nullptr;
decltype(&WebAuthNAuthenticatorGetAssertion) authenticator_get_assertion_ =
nullptr;
decltype(&WebAuthNCancelCurrentOperation) cancel_current_operation_ = nullptr;
decltype(&WebAuthNGetPlatformCredentialList) get_platform_credential_list_ =
nullptr;
decltype(&WebAuthNDeletePlatformCredential) delete_platform_credential_ =
nullptr;
decltype(&WebAuthNGetErrorName) get_error_name_ = nullptr;
decltype(&WebAuthNFreeCredentialAttestation) free_credential_attestation_ =
nullptr;
decltype(&WebAuthNFreeAssertion) free_assertion_ = nullptr;
decltype(&WebAuthNFreePlatformCredentialList) free_platform_credential_list_ =
nullptr;
decltype(&WebAuthNGetApiVersionNumber) get_api_version_number_ = nullptr;
};
WinWebAuthnApi* WinWebAuthnApi::GetDefault() {
static base::NoDestructor<WinWebAuthnApiImpl> api;
return api.get();
}
WinWebAuthnApi::WinWebAuthnApi() = default;
WinWebAuthnApi::~WinWebAuthnApi() = default;
std::pair<CtapDeviceResponseCode,
absl::optional<AuthenticatorMakeCredentialResponse>>
AuthenticatorMakeCredentialBlocking(WinWebAuthnApi* webauthn_api,
HWND h_wnd,
GUID cancellation_id,
CtapMakeCredentialRequest request,
MakeCredentialOptions request_options) {
DCHECK(webauthn_api->IsAvailable());
const int api_version = webauthn_api->Version();
DCHECK(
request_options.large_blob_support != LargeBlobSupport::kRequired ||
(api_version >= WEBAUTHN_API_VERSION_3 &&
request.authenticator_attachment != AuthenticatorAttachment::kPlatform));
std::u16string rp_id = base::UTF8ToUTF16(request.rp.id);
std::u16string rp_name = base::UTF8ToUTF16(request.rp.name.value_or(""));
WEBAUTHN_RP_ENTITY_INFORMATION rp_info{
WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, base::as_wcstr(rp_id),
base::as_wcstr(rp_name),
base::as_wcstr(base::EmptyString16())};
std::u16string user_name = base::UTF8ToUTF16(request.user.name.value_or(""));
std::u16string user_display_name =
base::UTF8ToUTF16(request.user.display_name.value_or(""));
std::vector<uint8_t> user_id = request.user.id;
WEBAUTHN_USER_ENTITY_INFORMATION user_info{
WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION,
base::checked_cast<DWORD>(user_id.size()),
const_cast<unsigned char*>(user_id.data()),
base::as_wcstr(user_name),
base::as_wcstr(base::EmptyString16()),
base::as_wcstr(user_display_name),
};
std::vector<WEBAUTHN_COSE_CREDENTIAL_PARAMETER>
cose_credential_parameter_values;
for (const PublicKeyCredentialParams::CredentialInfo& credential_info :
request.public_key_credential_params.public_key_credential_params()) {
if (credential_info.type != CredentialType::kPublicKey) {
continue;
}
cose_credential_parameter_values.push_back(
{WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION,
WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, credential_info.algorithm});
}
WEBAUTHN_COSE_CREDENTIAL_PARAMETERS cose_credential_parameters{
base::checked_cast<DWORD>(cose_credential_parameter_values.size()),
cose_credential_parameter_values.data()};
std::string client_data_json = request.client_data_json;
WEBAUTHN_CLIENT_DATA client_data{
WEBAUTHN_CLIENT_DATA_CURRENT_VERSION,
base::checked_cast<DWORD>(client_data_json.size()),
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(client_data_json.data())),
WEBAUTHN_HASH_ALGORITHM_SHA_256};
std::vector<WEBAUTHN_EXTENSION> extensions;
if (request.hmac_secret) {
static BOOL kHMACSecretTrue = TRUE;
extensions.emplace_back(
WEBAUTHN_EXTENSION{WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET,
sizeof(BOOL), static_cast<void*>(&kHMACSecretTrue)});
}
WEBAUTHN_CRED_PROTECT_EXTENSION_IN maybe_cred_protect_extension;
if (request.cred_protect) {
if (request.cred_protect_enforce && api_version < WEBAUTHN_API_VERSION_2) {
NOTREACHED();
return {CtapDeviceResponseCode::kCtap2ErrNotAllowed, absl::nullopt};
}
maybe_cred_protect_extension = WEBAUTHN_CRED_PROTECT_EXTENSION_IN{
static_cast<uint8_t>(*request.cred_protect),
request.cred_protect_enforce,
};
extensions.emplace_back(WEBAUTHN_EXTENSION{
WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT,
sizeof(WEBAUTHN_CRED_PROTECT_EXTENSION_IN),
&maybe_cred_protect_extension,
});
}
uint32_t authenticator_attachment;
if (request_options.is_off_the_record_context &&
api_version < WEBAUTHN_API_VERSION_4) {
authenticator_attachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
} else if (request_options.large_blob_support ==
LargeBlobSupport::kRequired) {
authenticator_attachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
} else {
authenticator_attachment =
ToWinAuthenticatorAttachment(request.authenticator_attachment);
}
WEBAUTHN_CRED_BLOB_EXTENSION cred_blob_ext;
if (request.cred_blob && api_version >= WEBAUTHN_API_VERSION_3 &&
request.cred_blob->size() <=
std::numeric_limits<decltype(cred_blob_ext.cbCredBlob)>::max()) {
cred_blob_ext = {
base::checked_cast<decltype(cred_blob_ext.cbCredBlob)>(
request.cred_blob->size()),
request.cred_blob->data(),
};
extensions.emplace_back(WEBAUTHN_EXTENSION{
WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_BLOB,
sizeof(cred_blob_ext),
&cred_blob_ext,
});
}
if (request.min_pin_length_requested &&
api_version >= WEBAUTHN_API_VERSION_3) {
static const BOOL kRequestMinPINLength = TRUE;
extensions.emplace_back(WEBAUTHN_EXTENSION{
WEBAUTHN_EXTENSIONS_IDENTIFIER_MIN_PIN_LENGTH,
sizeof(kRequestMinPINLength),
const_cast<BOOL*>(&kRequestMinPINLength),
});
}
DWORD enterprise_attestation = WEBAUTHN_ENTERPRISE_ATTESTATION_NONE;
switch (request.attestation_preference) {
case AttestationConveyancePreference::kEnterpriseIfRPListedOnAuthenticator:
enterprise_attestation =
WEBAUTHN_ENTERPRISE_ATTESTATION_VENDOR_FACILITATED;
break;
case AttestationConveyancePreference::kEnterpriseApprovedByBrowser:
enterprise_attestation = WEBAUTHN_ENTERPRISE_ATTESTATION_PLATFORM_MANAGED;
break;
default:
break;
}
std::vector<WEBAUTHN_CREDENTIAL_EX> exclude_list_credentials =
ToWinCredentialExVector(&request.exclude_list);
std::vector<WEBAUTHN_CREDENTIAL_EX*> exclude_list_ptrs;
base::ranges::transform(exclude_list_credentials,
std::back_inserter(exclude_list_ptrs),
[](auto& cred) { return &cred; });
WEBAUTHN_CREDENTIAL_LIST exclude_credential_list{
base::checked_cast<DWORD>(exclude_list_ptrs.size()),
exclude_list_ptrs.data()};
WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS options{
WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_5,
kWinWebAuthnTimeoutMilliseconds,
WEBAUTHN_CREDENTIALS{
0, nullptr},
WEBAUTHN_EXTENSIONS{base::checked_cast<DWORD>(extensions.size()),
extensions.data()},
authenticator_attachment,
request.resident_key_required,
ToWinUserVerificationRequirement(request.user_verification),
ToWinAttestationConveyancePreference(request.attestation_preference,
api_version),
0,
&cancellation_id,
&exclude_credential_list,
enterprise_attestation,
ToWinLargeBlobSupport(request_options.large_blob_support),
request_options.resident_key ==
ResidentKeyRequirement::kPreferred,
request_options.is_off_the_record_context,
};
WEBAUTHN_CREDENTIAL_ATTESTATION* credential_attestation = nullptr;
FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorMakeCredential("
<< "rp=" << rp_info << ", user=" << user_info
<< ", cose_credential_parameters="
<< cose_credential_parameters
<< ", client_data=" << client_data << ", options=" << options
<< ")";
HRESULT hresult = webauthn_api->AuthenticatorMakeCredential(
h_wnd, &rp_info, &user_info, &cose_credential_parameters, &client_data,
&options, &credential_attestation);
std::unique_ptr<WEBAUTHN_CREDENTIAL_ATTESTATION,
std::function<void(PWEBAUTHN_CREDENTIAL_ATTESTATION)>>
credential_attestation_deleter(
credential_attestation,
[webauthn_api](PWEBAUTHN_CREDENTIAL_ATTESTATION ptr) {
webauthn_api->FreeCredentialAttestation(ptr);
});
if (hresult != S_OK) {
FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorMakeCredential()="
<< HresultToHex(hresult) << " ("
<< webauthn_api->GetErrorName(hresult) << ")";
return {WinErrorNameToCtapDeviceResponseCode(
base::as_u16cstr(webauthn_api->GetErrorName(hresult))),
absl::nullopt};
}
FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorMakeCredential()="
<< *credential_attestation;
return {CtapDeviceResponseCode::kSuccess,
ToAuthenticatorMakeCredentialResponse(*credential_attestation)};
}
std::pair<CtapDeviceResponseCode,
absl::optional<AuthenticatorGetAssertionResponse>>
AuthenticatorGetAssertionBlocking(WinWebAuthnApi* webauthn_api,
HWND h_wnd,
GUID cancellation_id,
CtapGetAssertionRequest request,
CtapGetAssertionOptions request_options) {
DCHECK(webauthn_api->IsAvailable());
const int api_version = webauthn_api->Version();
std::u16string rp_id16 = base::UTF8ToUTF16(request.rp_id);
std::string client_data_json = request.client_data_json;
WEBAUTHN_CLIENT_DATA client_data{
WEBAUTHN_CLIENT_DATA_CURRENT_VERSION,
base::checked_cast<DWORD>(client_data_json.size()),
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(client_data_json.data())),
WEBAUTHN_HASH_ALGORITHM_SHA_256};
absl::optional<std::u16string> opt_app_id16 = absl::nullopt;
if (request.app_id) {
opt_app_id16 = base::UTF8ToUTF16(
base::StringPiece(reinterpret_cast<const char*>(request.app_id->data()),
request.app_id->size()));
}
std::vector<WEBAUTHN_CREDENTIAL_EX> allow_list_credentials =
ToWinCredentialExVector(&request.allow_list);
std::vector<WEBAUTHN_CREDENTIAL_EX*> allow_list_ptrs;
base::ranges::transform(allow_list_credentials,
std::back_inserter(allow_list_ptrs),
[](auto& cred) { return &cred; });
WEBAUTHN_CREDENTIAL_LIST allow_credential_list{
base::checked_cast<DWORD>(allow_list_ptrs.size()),
allow_list_ptrs.data()};
auto legacy_credentials = ToWinCredentialVector(&request.allow_list);
std::vector<WEBAUTHN_EXTENSION> extensions;
if (api_version >= WEBAUTHN_API_VERSION_3 && request.get_cred_blob) {
static const BOOL kCredBlobTrue = TRUE;
extensions.emplace_back(WEBAUTHN_EXTENSION{
WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_BLOB,
sizeof(kCredBlobTrue),
const_cast<BOOL*>(&kCredBlobTrue),
});
}
WEBAUTHN_HMAC_SECRET_SALT_VALUES hmac_salt_values_storage;
std::vector<WEBAUTHN_HMAC_SECRET_SALT> salts_storage;
std::vector<WEBAUTHN_CRED_WITH_HMAC_SECRET_SALT> cred_salts_storage;
WEBAUTHN_HMAC_SECRET_SALT_VALUES* const hmac_salt_values =
FillHMACSaltValues(&hmac_salt_values_storage, &salts_storage,
&cred_salts_storage, request_options.prf_inputs);
DWORD flags = 0;
if (hmac_salt_values) {
flags |= WEBAUTHN_AUTHENTICATOR_HMAC_SECRET_VALUES_FLAG;
}
DWORD large_blob_operation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_NONE;
base::span<uint8_t> large_blob;
if (api_version >= WEBAUTHN_API_VERSION_3) {
if (request_options.large_blob_read) {
large_blob_operation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_GET;
} else if (request_options.large_blob_write) {
large_blob_operation = WEBAUTHN_CRED_LARGE_BLOB_OPERATION_SET;
large_blob = *request_options.large_blob_write;
}
}
static BOOL kUseAppIdTrue = TRUE;
static BOOL kUseAppIdFalse = FALSE;
WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS options{
WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6,
kWinWebAuthnTimeoutMilliseconds,
WEBAUTHN_CREDENTIALS{base::checked_cast<DWORD>(legacy_credentials.size()),
legacy_credentials.data()},
WEBAUTHN_EXTENSIONS{base::checked_cast<DWORD>(extensions.size()),
extensions.data()},
WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY,
ToWinUserVerificationRequirement(request.user_verification),
flags,
opt_app_id16 ? base::as_wcstr(*opt_app_id16) : nullptr,
opt_app_id16 ? &kUseAppIdTrue : &kUseAppIdFalse,
&cancellation_id,
&allow_credential_list,
large_blob_operation,
base::checked_cast<DWORD>(large_blob.size()),
large_blob.data(),
hmac_salt_values,
request_options.is_off_the_record_context,
};
WEBAUTHN_ASSERTION* assertion = nullptr;
FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorGetAssertion("
<< "rp_id=\"" << rp_id16 << "\", client_data=" << client_data
<< ", options=" << options << ")";
HRESULT hresult = webauthn_api->AuthenticatorGetAssertion(
h_wnd, base::as_wcstr(rp_id16), &client_data, &options, &assertion);
std::unique_ptr<WEBAUTHN_ASSERTION, std::function<void(PWEBAUTHN_ASSERTION)>>
assertion_deleter(assertion, [webauthn_api](PWEBAUTHN_ASSERTION ptr) {
webauthn_api->FreeAssertion(ptr);
});
if (hresult != S_OK) {
FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorGetAssertion()="
<< HresultToHex(hresult) << " ("
<< webauthn_api->GetErrorName(hresult) << ")";
return {WinErrorNameToCtapDeviceResponseCode(
base::as_u16cstr(webauthn_api->GetErrorName(hresult))),
absl::nullopt};
}
FIDO_LOG(DEBUG) << "WebAuthNAuthenticatorGetAssertion()=" << *assertion;
absl::optional<AuthenticatorGetAssertionResponse> response =
ToAuthenticatorGetAssertionResponse(*assertion, request_options);
if (response && !request_options.prf_inputs.empty() &&
webauthn_api->Version() < WEBAUTHN_API_VERSION_4) {
response->hmac_secret_not_evaluated = true;
}
return {response ? CtapDeviceResponseCode::kSuccess
: CtapDeviceResponseCode::kCtap2ErrOther,
std::move(response)};
}
}