Commit 977c0047 authored by Martin Kreichgauer's avatar Martin Kreichgauer Committed by Commit Bot

fido: implement IsUVPAA for Windows

This wires the IsUserVerifyingPlatformAuthenticator API call up to its
implementation in the native Windows API, where available.

Bug: 898718
Change-Id: I40307ff39f8dc8e02197debc48041aad62d253ac
Reviewed-on: https://chromium-review.googlesource.com/c/1351900
Commit-Queue: Adam Langley <agl@chromium.org>
Reviewed-by: default avatarAdam Langley <agl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611948}
parent 99ffaaeb
......@@ -61,6 +61,10 @@
#include "device/fido/mac/authenticator.h"
#endif
#if defined(OS_WIN)
#include "device/fido/win/authenticator.h"
#endif
namespace content {
namespace client_data {
......@@ -232,7 +236,8 @@ bool IsAppIdAllowedForOrigin(const GURL& appid, const url::Origin& origin) {
device::CtapMakeCredentialRequest CreateCtapMakeCredentialRequest(
const std::string& client_data_json,
const blink::mojom::PublicKeyCredentialCreationOptionsPtr& options,
bool is_individual_attestation) {
bool is_individual_attestation,
bool is_incognito) {
auto credential_params = mojo::ConvertTo<
std::vector<device::PublicKeyCredentialParams::CredentialInfo>>(
options->public_key_parameters);
......@@ -251,6 +256,7 @@ device::CtapMakeCredentialRequest CreateCtapMakeCredentialRequest(
make_credential_param.SetExcludeList(std::move(exclude_list));
make_credential_param.SetIsIndividualAttestation(is_individual_attestation);
make_credential_param.SetHmacSecret(options->hmac_create_secret);
make_credential_param.set_is_incognito_mode(is_incognito);
return make_credential_param;
}
......@@ -258,7 +264,8 @@ device::CtapGetAssertionRequest CreateCtapGetAssertionRequest(
const std::string& client_data_json,
const blink::mojom::PublicKeyCredentialRequestOptionsPtr& options,
base::Optional<base::span<const uint8_t, device::kRpIdHashLength>>
alternative_application_parameter) {
alternative_application_parameter,
bool is_incognito) {
device::CtapGetAssertionRequest request_parameter(options->relying_party_id,
client_data_json);
......@@ -281,6 +288,7 @@ device::CtapGetAssertionRequest CreateCtapGetAssertionRequest(
mojo::ConvertTo<std::vector<device::CableDiscoveryData>>(
options->cable_authentication_data));
}
request_parameter.set_is_incognito_mode(is_incognito);
return request_parameter;
}
......@@ -482,12 +490,11 @@ base::flat_set<device::FidoTransportProtocol> GetTransportsEnabledByFlags() {
transports.insert(device::FidoTransportProtocol::kBluetoothLowEnergy);
}
if (
#if defined(OS_WIN)
if (base::FeatureList::IsEnabled(features::kWebAuthCable) &&
base::FeatureList::IsEnabled(features::kWebAuthCableWin)) {
#else
if (base::FeatureList::IsEnabled(features::kWebAuthCable)) {
#endif // defined(OS_WIN)
base::FeatureList::IsEnabled(features::kWebAuthCableWin) &&
#endif
base::FeatureList::IsEnabled(features::kWebAuthCable)) {
transports.insert(
device::FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy);
}
......@@ -669,7 +676,8 @@ void AuthenticatorImpl::MakeCredential(
: device::AuthenticatorSelectionCriteria();
auto ctap_request = CreateCtapMakeCredentialRequest(
client_data_json_, options, individual_attestation);
client_data_json_, options, individual_attestation,
browser_context()->IsOffTheRecord());
ctap_request.set_is_u2f_only(OriginIsCryptoTokenExtension(caller_origin_));
request_ = std::make_unique<device::MakeCredentialRequestHandler>(
......@@ -778,9 +786,9 @@ void AuthenticatorImpl::GetAssertion(
if (!connector_)
connector_ = ServiceManagerConnection::GetForProcess()->GetConnector();
auto ctap_request =
CreateCtapGetAssertionRequest(client_data_json_, std::move(options),
alternative_application_parameter_);
auto ctap_request = CreateCtapGetAssertionRequest(
client_data_json_, std::move(options), alternative_application_parameter_,
browser_context()->IsOffTheRecord());
auto opt_platform_authenticator_info =
CreatePlatformAuthenticatorIfAvailableAndCheckIfCredentialExists(
ctap_request);
......@@ -815,18 +823,25 @@ void AuthenticatorImpl::IsUserVerifyingPlatformAuthenticatorAvailable(
}
bool AuthenticatorImpl::IsUserVerifyingPlatformAuthenticatorAvailableImpl() {
// N.B. request_delegate_ may be nullptr at this point.
// All platform authenticators are disabled in incognito mode.
// TODO(martinkr): Revisit incognito handling (crbug/908622).
if (browser_context()->IsOffTheRecord())
return false;
#if defined(OS_MACOSX)
// Touch ID is disabled, regardless of hardware support, if the embedder
// doesn't support it or if this is an Incognito session. N.B.
// request_delegate_ may be nullptr at this point.
// doesn't support it.
if (!GetContentClient()
->browser()
->IsWebAuthenticationTouchIdAuthenticatorSupported() ||
browser_context()->IsOffTheRecord()) {
->IsWebAuthenticationTouchIdAuthenticatorSupported())
return false;
}
return device::fido::mac::TouchIdAuthenticator::IsAvailable();
#elif defined(OS_WIN)
return base::FeatureList::IsEnabled(device::kWebAuthUseNativeWinApi) &&
device::WinWebAuthnApiAuthenticator::
IsUserVerifyingPlatformAuthenticatorAvailable();
#else
return false;
#endif
......
......@@ -48,6 +48,10 @@
#include "device/fido/mac/scoped_touch_id_test_environment.h"
#endif
#if defined(OS_WIN)
#include "device/fido/win/fake_webauthn_api.h"
#endif
namespace content {
using ::testing::_;
......@@ -1947,9 +1951,42 @@ TEST_F(AuthenticatorContentBrowserClientTest, IsUVPAATrueIfTouchIdAvailable) {
}
#endif // defined(OS_MACOSX)
#if !defined(OS_MACOSX)
#if defined(OS_WIN)
TEST_F(AuthenticatorContentBrowserClientTest, WinIsUVPAA) {
for (const bool enable_feature_flag : {false, true}) {
SCOPED_TRACE(enable_feature_flag ? "enable_feature_flag"
: "!enable_feature_flag");
for (const bool enable_win_webauthn_api : {false, true}) {
SCOPED_TRACE(enable_win_webauthn_api ? "enable_win_webauthn_api"
: "!enable_win_webauthn_api");
for (const bool is_uvpaa : {false, true}) {
SCOPED_TRACE(is_uvpaa ? "is_uvpaa" : "!is_uvpaa");
base::test::ScopedFeatureList scoped_feature_list;
if (enable_feature_flag)
scoped_feature_list.InitAndEnableFeature(
device::kWebAuthUseNativeWinApi);
device::ScopedFakeWinWebAuthnApi fake_api;
fake_api.set_available(enable_win_webauthn_api);
fake_api.set_is_uvpaa(is_uvpaa);
AuthenticatorPtr authenticator = ConnectToAuthenticator();
TestIsUvpaaCallback cb;
authenticator->IsUserVerifyingPlatformAuthenticatorAvailable(
cb.callback());
cb.WaitForCallback();
EXPECT_EQ(enable_feature_flag && enable_win_webauthn_api && is_uvpaa,
cb.value());
}
}
}
}
#endif // defined(OS_WIN)
#if !defined(OS_MACOSX) && !defined(OS_WIN)
TEST_F(AuthenticatorContentBrowserClientTest, IsUVPAAFalse) {
// No platform authenticator on non-macOS platforms.
// There are no platform authenticators other than Windows Hello and macOS
// Touch ID.
NavigateAndCommit(GURL(kTestOrigin1));
AuthenticatorPtr authenticator = ConnectToAuthenticator();
......@@ -1958,7 +1995,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, IsUVPAAFalse) {
cb.WaitForCallback();
EXPECT_FALSE(cb.value());
}
#endif // !defined(OS_MACOSX)
#endif // !defined(OS_MACOSX) && !defined(OS_WIN)
TEST_F(AuthenticatorContentBrowserClientTest,
CryptotokenBypassesAttestationConsentPrompt) {
......
......@@ -88,6 +88,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest {
return alternative_application_parameter_;
}
bool is_incognito_mode() const { return is_incognito_mode_; }
void set_is_incognito_mode(bool is_incognito_mode) {
is_incognito_mode_ = is_incognito_mode;
}
private:
std::string rp_id_;
std::string client_data_json_;
......@@ -102,6 +107,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapGetAssertionRequest {
base::Optional<std::vector<CableDiscoveryData>> cable_extension_;
base::Optional<std::array<uint8_t, kRpIdHashLength>>
alternative_application_parameter_;
bool is_incognito_mode_ = false;
};
} // namespace device
......
......@@ -88,6 +88,11 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest {
void set_is_u2f_only(bool is_u2f_only) { is_u2f_only_ = is_u2f_only; }
bool is_u2f_only() { return is_u2f_only_; }
bool is_incognito_mode() const { return is_incognito_mode_; }
void set_is_incognito_mode(bool is_incognito_mode) {
is_incognito_mode_ = is_incognito_mode;
}
private:
std::string client_data_json_;
std::array<uint8_t, kClientDataHashLength> client_data_hash_;
......@@ -107,6 +112,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) CtapMakeCredentialRequest {
// If true, instruct the request handler only to dispatch this request via
// U2F.
bool is_u2f_only_ = false;
bool is_incognito_mode_ = false;
base::Optional<std::vector<PublicKeyCredentialDescriptor>> exclude_list_;
base::Optional<std::vector<uint8_t>> pin_auth_;
......
......@@ -43,6 +43,17 @@ base::string16 OptionalGURLToUTF16(const base::Optional<GURL>& in) {
const char WinWebAuthnApiAuthenticator::kAuthenticatorId[] =
"WinWebAuthnApiAuthenticator";
// static
bool WinWebAuthnApiAuthenticator::
IsUserVerifyingPlatformAuthenticatorAvailable() {
BOOL result;
return WinWebAuthnApi::GetDefault()->IsAvailable() &&
WinWebAuthnApi::GetDefault()
->IsUserVerifyingPlatformAuthenticatorAvailable(&result) ==
S_OK &&
result == TRUE;
}
WinWebAuthnApiAuthenticator::WinWebAuthnApiAuthenticator(
WinWebAuthnApi* win_api,
HWND current_window)
......@@ -185,15 +196,26 @@ void WinWebAuthnApiAuthenticator::MakeCredentialBlocking(
_WEBAUTHN_CREDENTIAL_LIST exclude_credential_list{exclude_list.size(),
&exclude_list_ptr};
uint32_t authenticator_attachment;
if (request.is_u2f_only()) {
authenticator_attachment =
WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2;
} else if (request.is_incognito_mode()) {
// Disable all platform authenticators in incognito mode. We are going to
// revisit this in crbug/908622.
authenticator_attachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
} else {
authenticator_attachment =
ToWinAuthenticatorAttachment(request.authenticator_attachment());
}
WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS make_credential_options{
WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_3,
kWinWebAuthnTimeoutMilliseconds,
WEBAUTHN_CREDENTIALS{
0, nullptr}, // Ignored because pExcludeCredentialList is set.
WEBAUTHN_EXTENSIONS{extensions.size(), extensions.data()},
request.is_u2f_only()
? WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2
: ToWinAuthenticatorAttachment(request.authenticator_attachment()),
authenticator_attachment,
request.resident_key_required(),
ToWinUserVerificationRequirement(request.user_verification()),
WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT,
......@@ -301,19 +323,31 @@ void WinWebAuthnApiAuthenticator::GetAssertionBlocking(
_WEBAUTHN_CREDENTIAL_LIST allow_credential_list{allow_list.size(),
&allow_list_ptr};
uint32_t authenticator_attachment;
if (opt_app_id16) {
authenticator_attachment =
WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2;
} else if (request.is_incognito_mode()) {
// Disable all platform authenticators in incognito mode. We are going to
// revisit this in crbug/908622.
authenticator_attachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM;
} else {
authenticator_attachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY;
}
WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS get_assertion_options{
WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_4,
kWinWebAuthnTimeoutMilliseconds,
WEBAUTHN_CREDENTIALS{
0, nullptr}, // Ignored because pAllowCredentialList is set.
WEBAUTHN_EXTENSIONS{0, nullptr},
// Note that attachment is effectively restricted via |allow_list|.
WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY,
authenticator_attachment,
ToWinUserVerificationRequirement(request.user_verification()),
0, // flags
opt_app_id16 ? opt_app_id16->c_str() : nullptr, // pwszU2fAppId
opt_app_id16 ? &kUseAppIdTrue : nullptr, // pbU2fAppId
&cancellation_id_, &allow_credential_list,
&cancellation_id_,
&allow_credential_list,
};
// |assertion| must not not outlive |win_api_|.
......
......@@ -29,6 +29,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApiAuthenticator
// The return value of |GetId|.
static const char kAuthenticatorId[];
static bool IsUserVerifyingPlatformAuthenticatorAvailable();
WinWebAuthnApiAuthenticator(WinWebAuthnApi* win_api, HWND current_window);
~WinWebAuthnApiAuthenticator() override;
......
......@@ -17,9 +17,9 @@ bool FakeWinWebAuthnApi::IsAvailable() const {
HRESULT FakeWinWebAuthnApi::IsUserVerifyingPlatformAuthenticatorAvailable(
BOOL* result) {
*result = false;
DCHECK(is_available_);
return E_NOTIMPL;
*result = is_uvpaa_;
return S_OK;
}
HRESULT FakeWinWebAuthnApi::AuthenticatorMakeCredential(
......
......@@ -16,6 +16,9 @@ class FakeWinWebAuthnApi : public WinWebAuthnApi {
// Inject the return value for WinWebAuthnApi::IsAvailable().
void set_available(bool available) { is_available_ = available; }
// Inject the return value for
// WinWebAuthnApi::IsUserverifyingPlatformAuthenticatorAvailable().
void set_is_uvpaa(bool is_uvpaa) { is_uvpaa_ = is_uvpaa; }
// WinWebAuthnApi:
bool IsAvailable() const override;
......@@ -42,6 +45,7 @@ class FakeWinWebAuthnApi : public WinWebAuthnApi {
private:
bool is_available_ = true;
bool is_uvpaa_ = false;
};
// ScopedFakeWinWebAuthnApi overrides the value returned
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment