Commit 17bbafc4 authored by Jun Choi's avatar Jun Choi Committed by Commit Bot

Implement flag enabled CTAP authentication

Add support for register/sign with CTAP2.0 tokens behind a feature
flag.

Bug: 780078
Change-Id: I3b0fe7ce2eaf48304300e4fed5d722cd5f87c6da
Reviewed-on: https://chromium-review.googlesource.com/974796
Commit-Queue: Jun Choi <hongjunchoi@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Reviewed-by: default avatarAntoine Labour <piman@chromium.org>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#549827}
parent 97fd0360
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/rand_util.h" #include "base/rand_util.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "content/browser/bad_message.h" #include "content/browser/bad_message.h"
#include "content/browser/webauth/authenticator_type_converters.h"
#include "content/public/browser/content_browser_client.h" #include "content/public/browser/content_browser_client.h"
#include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
...@@ -24,6 +25,13 @@ ...@@ -24,6 +25,13 @@
#include "content/public/common/origin_util.h" #include "content/public/common/origin_util.h"
#include "content/public/common/service_manager_connection.h" #include "content/public/common/service_manager_connection.h"
#include "crypto/sha2.h" #include "crypto/sha2.h"
#include "device/fido/authenticator_selection_criteria.h"
#include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/ctap_make_credential_request.h"
#include "device/fido/get_assertion_request_handler.h"
#include "device/fido/make_credential_request_handler.h"
#include "device/fido/public_key_credential_descriptor.h"
#include "device/fido/public_key_credential_params.h"
#include "device/fido/u2f_register.h" #include "device/fido/u2f_register.h"
#include "device/fido/u2f_request.h" #include "device/fido/u2f_request.h"
#include "device/fido/u2f_sign.h" #include "device/fido/u2f_sign.h"
...@@ -195,6 +203,45 @@ std::vector<std::vector<uint8_t>> FilterCredentialList( ...@@ -195,6 +203,45 @@ std::vector<std::vector<uint8_t>> FilterCredentialList(
return handles; return handles;
} }
device::CtapMakeCredentialRequest CreateCtapMakeCredentialRequest(
std::vector<uint8_t> client_data_hash,
const webauth::mojom::PublicKeyCredentialCreationOptionsPtr& options) {
auto credential_params = mojo::ConvertTo<
std::vector<device::PublicKeyCredentialParams::CredentialInfo>>(
options->public_key_parameters);
device::CtapMakeCredentialRequest make_credential_param(
std::move(client_data_hash),
mojo::ConvertTo<device::PublicKeyCredentialRpEntity>(
options->relying_party),
mojo::ConvertTo<device::PublicKeyCredentialUserEntity>(options->user),
device::PublicKeyCredentialParams(std::move(credential_params)));
auto exclude_list =
mojo::ConvertTo<std::vector<device::PublicKeyCredentialDescriptor>>(
options->exclude_credentials);
make_credential_param.SetExcludeList(std::move(exclude_list));
return make_credential_param;
}
device::CtapGetAssertionRequest CreateCtapGetAssertionRequest(
std::vector<uint8_t> client_data_hash,
const webauth::mojom::PublicKeyCredentialRequestOptionsPtr& options) {
device::CtapGetAssertionRequest request_parameter(
options->relying_party_id, std::move(client_data_hash));
auto allowed_list =
mojo::ConvertTo<std::vector<device::PublicKeyCredentialDescriptor>>(
options->allow_credentials);
request_parameter.SetAllowList(std::move(allowed_list));
request_parameter.SetUserVerificationRequired(
options->user_verification ==
webauth::mojom::UserVerificationRequirement::REQUIRED);
return request_parameter;
}
std::vector<uint8_t> ConstructClientDataHash(const std::string& client_data) { std::vector<uint8_t> ConstructClientDataHash(const std::string& client_data) {
// SHA-256 hash of the JSON data structure. // SHA-256 hash of the JSON data structure.
std::vector<uint8_t> client_data_hash(crypto::kSHA256Length); std::vector<uint8_t> client_data_hash(crypto::kSHA256Length);
...@@ -373,7 +420,7 @@ std::string AuthenticatorImpl::SerializeCollectedClientDataToJson( ...@@ -373,7 +420,7 @@ std::string AuthenticatorImpl::SerializeCollectedClientDataToJson(
void AuthenticatorImpl::MakeCredential( void AuthenticatorImpl::MakeCredential(
webauth::mojom::PublicKeyCredentialCreationOptionsPtr options, webauth::mojom::PublicKeyCredentialCreationOptionsPtr options,
MakeCredentialCallback callback) { MakeCredentialCallback callback) {
if (u2f_request_) { if (u2f_request_ || ctap_request_) {
std::move(callback).Run( std::move(callback).Run(
webauth::mojom::AuthenticatorStatus::PENDING_REQUEST, nullptr); webauth::mojom::AuthenticatorStatus::PENDING_REQUEST, nullptr);
return; return;
...@@ -403,7 +450,8 @@ void AuthenticatorImpl::MakeCredential( ...@@ -403,7 +450,8 @@ void AuthenticatorImpl::MakeCredential(
// Verify that the request doesn't contain parameters that U2F authenticators // Verify that the request doesn't contain parameters that U2F authenticators
// cannot fulfill. // cannot fulfill.
// TODO(crbug.com/819256): Improve messages for "Not Allowed" errors. // TODO(crbug.com/819256): Improve messages for "Not Allowed" errors.
if (!AreOptionsSupportedByU2fAuthenticators(options)) { if (!base::FeatureList::IsEnabled(features::kWebAuthCtap2) &&
!AreOptionsSupportedByU2fAuthenticators(options)) {
InvokeCallbackAndCleanup( InvokeCallbackAndCleanup(
std::move(callback), std::move(callback),
webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr); webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
...@@ -411,7 +459,8 @@ void AuthenticatorImpl::MakeCredential( ...@@ -411,7 +459,8 @@ void AuthenticatorImpl::MakeCredential(
} }
// TODO(crbug.com/819256): Improve messages for "Not Supported" errors. // TODO(crbug.com/819256): Improve messages for "Not Supported" errors.
if (!IsAlgorithmSupportedByU2fAuthenticators( if (!base::FeatureList::IsEnabled(features::kWebAuthCtap2) &&
!IsAlgorithmSupportedByU2fAuthenticators(
options->public_key_parameters)) { options->public_key_parameters)) {
InvokeCallbackAndCleanup( InvokeCallbackAndCleanup(
std::move(callback), std::move(callback),
...@@ -450,25 +499,41 @@ void AuthenticatorImpl::MakeCredential( ...@@ -450,25 +499,41 @@ void AuthenticatorImpl::MakeCredential(
attestation_preference_ = options->attestation; attestation_preference_ = options->attestation;
// TODO(kpaulhamus): Mock U2fRegister for unit tests. if (base::FeatureList::IsEnabled(features::kWebAuthCtap2)) {
// http://crbug.com/785955. auto authenticator_selection_criteria =
// Per fido-u2f-raw-message-formats: options->authenticator_selection
// The challenge parameter is the SHA-256 hash of the Client Data, ? mojo::ConvertTo<device::AuthenticatorSelectionCriteria>(
// Among other things, the Client Data contains the challenge from the options->authenticator_selection)
// relying party (hence the name of the parameter). : device::AuthenticatorSelectionCriteria();
u2f_request_ = device::U2fRegister::TryRegistration(
connector_, protocols_, registered_keys, ctap_request_ = std::make_unique<device::MakeCredentialRequestHandler>(
ConstructClientDataHash(client_data_json_), connector_, protocols_,
CreateApplicationParameter(relying_party_id_), individual_attestation, CreateCtapMakeCredentialRequest(
base::BindOnce(&AuthenticatorImpl::OnRegisterResponse, ConstructClientDataHash(client_data_json_), options),
weak_factory_.GetWeakPtr())); std::move(authenticator_selection_criteria),
base::BindOnce(&AuthenticatorImpl::OnRegisterResponse,
weak_factory_.GetWeakPtr()));
} else {
// TODO(kpaulhamus): Mock U2fRegister for unit tests.
// http://crbug.com/785955.
// Per fido-u2f-raw-message-formats:
// The challenge parameter is the SHA-256 hash of the Client Data,
// Among other things, the Client Data contains the challenge from the
// relying party (hence the name of the parameter).
u2f_request_ = device::U2fRegister::TryRegistration(
connector_, protocols_, registered_keys,
ConstructClientDataHash(client_data_json_),
CreateApplicationParameter(relying_party_id_), individual_attestation,
base::BindOnce(&AuthenticatorImpl::OnRegisterResponse,
weak_factory_.GetWeakPtr()));
}
} }
// mojom:Authenticator // mojom:Authenticator
void AuthenticatorImpl::GetAssertion( void AuthenticatorImpl::GetAssertion(
webauth::mojom::PublicKeyCredentialRequestOptionsPtr options, webauth::mojom::PublicKeyCredentialRequestOptionsPtr options,
GetAssertionCallback callback) { GetAssertionCallback callback) {
if (u2f_request_) { if (u2f_request_ || ctap_request_) {
std::move(callback).Run( std::move(callback).Run(
webauth::mojom::AuthenticatorStatus::PENDING_REQUEST, nullptr); webauth::mojom::AuthenticatorStatus::PENDING_REQUEST, nullptr);
return; return;
...@@ -495,8 +560,9 @@ void AuthenticatorImpl::GetAssertion( ...@@ -495,8 +560,9 @@ void AuthenticatorImpl::GetAssertion(
} }
// To use U2F, the relying party must not require user verification. // To use U2F, the relying party must not require user verification.
if (options->user_verification == if (!base::FeatureList::IsEnabled(features::kWebAuthCtap2) &&
webauth::mojom::UserVerificationRequirement::REQUIRED) { options->user_verification ==
webauth::mojom::UserVerificationRequirement::REQUIRED) {
InvokeCallbackAndCleanup( InvokeCallbackAndCleanup(
std::move(callback), std::move(callback),
webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr); webauth::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
...@@ -541,12 +607,21 @@ void AuthenticatorImpl::GetAssertion( ...@@ -541,12 +607,21 @@ void AuthenticatorImpl::GetAssertion(
client_data::kGetType, caller_origin, std::move(options->challenge), client_data::kGetType, caller_origin, std::move(options->challenge),
base::nullopt); base::nullopt);
u2f_request_ = device::U2fSign::TrySign( if (base::FeatureList::IsEnabled(features::kWebAuthCtap2)) {
connector_, protocols_, handles, ctap_request_ = std::make_unique<device::GetAssertionRequestHandler>(
ConstructClientDataHash(client_data_json_), application_parameter, connector_, protocols_,
alternative_application_parameter, CreateCtapGetAssertionRequest(
base::BindOnce(&AuthenticatorImpl::OnSignResponse, ConstructClientDataHash(client_data_json_), options),
weak_factory_.GetWeakPtr())); base::BindOnce(&AuthenticatorImpl::OnSignResponse,
weak_factory_.GetWeakPtr()));
} else {
u2f_request_ = device::U2fSign::TrySign(
connector_, protocols_, handles,
ConstructClientDataHash(client_data_json_), application_parameter,
alternative_application_parameter,
base::BindOnce(&AuthenticatorImpl::OnSignResponse,
weak_factory_.GetWeakPtr()));
}
} }
void AuthenticatorImpl::DidFinishNavigation( void AuthenticatorImpl::DidFinishNavigation(
...@@ -566,8 +641,8 @@ void AuthenticatorImpl::OnRegisterResponse( ...@@ -566,8 +641,8 @@ void AuthenticatorImpl::OnRegisterResponse(
device::FidoReturnCode status_code, device::FidoReturnCode status_code,
base::Optional<device::AuthenticatorMakeCredentialResponse> response_data) { base::Optional<device::AuthenticatorMakeCredentialResponse> response_data) {
// If callback is called immediately, this code will call |Cleanup| before // If callback is called immediately, this code will call |Cleanup| before
// |u2f_request_| has been assigned – violating invariants. // |u2f_request_| or |ctap_request_| has been assigned – violating invariants.
DCHECK(u2f_request_) << "unsupported callback hairpin"; DCHECK(u2f_request_ || ctap_request_);
switch (status_code) { switch (status_code) {
case device::FidoReturnCode::kUserConsentButCredentialExcluded: case device::FidoReturnCode::kUserConsentButCredentialExcluded:
...@@ -661,8 +736,8 @@ void AuthenticatorImpl::OnSignResponse( ...@@ -661,8 +736,8 @@ void AuthenticatorImpl::OnSignResponse(
device::FidoReturnCode status_code, device::FidoReturnCode status_code,
base::Optional<device::AuthenticatorGetAssertionResponse> response_data) { base::Optional<device::AuthenticatorGetAssertionResponse> response_data) {
// If callback is called immediately, this code will call |Cleanup| before // If callback is called immediately, this code will call |Cleanup| before
// |u2f_request_| has been assigned – violating invariants. // |u2f_request_| or |ctap_request_| has been assigned – violating invariants.
DCHECK(u2f_request_) << "unsupported callback hairpin"; DCHECK(u2f_request_ || ctap_request_);
switch (status_code) { switch (status_code) {
case device::FidoReturnCode::kUserConsentButCredentialNotRecognized: case device::FidoReturnCode::kUserConsentButCredentialNotRecognized:
...@@ -728,6 +803,7 @@ void AuthenticatorImpl::InvokeCallbackAndCleanup( ...@@ -728,6 +803,7 @@ void AuthenticatorImpl::InvokeCallbackAndCleanup(
void AuthenticatorImpl::Cleanup() { void AuthenticatorImpl::Cleanup() {
timer_->Stop(); timer_->Stop();
u2f_request_.reset(); u2f_request_.reset();
ctap_request_.reset();
make_credential_response_callback_.Reset(); make_credential_response_callback_.Reset();
get_assertion_response_callback_.Reset(); get_assertion_response_callback_.Reset();
client_data_json_.clear(); client_data_json_.clear();
......
...@@ -29,8 +29,12 @@ class OneShotTimer; ...@@ -29,8 +29,12 @@ class OneShotTimer;
} }
namespace device { namespace device {
class U2fRequest; class U2fRequest;
class FidoRequestHandlerBase;
enum class FidoReturnCode : uint8_t; enum class FidoReturnCode : uint8_t;
} // namespace device } // namespace device
namespace service_manager { namespace service_manager {
...@@ -132,6 +136,7 @@ class CONTENT_EXPORT AuthenticatorImpl : public webauth::mojom::Authenticator, ...@@ -132,6 +136,7 @@ class CONTENT_EXPORT AuthenticatorImpl : public webauth::mojom::Authenticator,
base::flat_set<device::U2fTransportProtocol> protocols_; base::flat_set<device::U2fTransportProtocol> protocols_;
std::unique_ptr<device::U2fRequest> u2f_request_; std::unique_ptr<device::U2fRequest> u2f_request_;
std::unique_ptr<device::FidoRequestHandlerBase> ctap_request_;
MakeCredentialCallback make_credential_response_callback_; MakeCredentialCallback make_credential_response_callback_;
GetAssertionCallback get_assertion_response_callback_; GetAssertionCallback get_assertion_response_callback_;
std::string client_data_json_; std::string client_data_json_;
......
...@@ -13,13 +13,14 @@ ...@@ -13,13 +13,14 @@
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/gtest_util.h" #include "base/test/gtest_util.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h" #include "base/test/test_mock_time_task_runner.h"
#include "base/time/tick_clock.h" #include "base/time/tick_clock.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "components/cbor/cbor_reader.h" #include "components/cbor/cbor_reader.h"
#include "components/cbor/cbor_values.h" #include "components/cbor/cbor_values.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_features.h"
#include "content/public/test/test_service_manager_context.h" #include "content/public/test/test_service_manager_context.h"
#include "content/test/test_render_frame_host.h" #include "content/test/test_render_frame_host.h"
#include "device/fido/fake_hid_impl_for_testing.h" #include "device/fido/fake_hid_impl_for_testing.h"
...@@ -706,6 +707,72 @@ TEST_F(AuthenticatorImplTest, OversizedCredentialId) { ...@@ -706,6 +707,72 @@ TEST_F(AuthenticatorImplTest, OversizedCredentialId) {
} }
} }
TEST_F(AuthenticatorImplTest, TestU2fDeviceDoesNotSupportMakeCredential) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kWebAuthCtap2);
SimulateNavigation(GURL(kTestOrigin1));
PublicKeyCredentialCreationOptionsPtr options =
GetTestPublicKeyCredentialCreationOptions();
TestMakeCredentialCallback cb;
// Set up service_manager::Connector for tests.
auto fake_hid_manager = std::make_unique<device::FakeHidManager>();
service_manager::mojom::ConnectorRequest request;
auto connector = service_manager::Connector::Create(&request);
// Set up a timer for testing.
auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
base::Time::Now(), base::TimeTicks::Now());
auto timer =
std::make_unique<base::OneShotTimer>(task_runner->GetMockTickClock());
timer->SetTaskRunner(task_runner);
AuthenticatorPtr authenticator =
ConnectToAuthenticator(connector.get(), std::move(timer));
device::test::ScopedVirtualFidoDevice virtual_device;
authenticator->MakeCredential(std::move(options), cb.callback());
// Trigger timer.
base::RunLoop().RunUntilIdle();
task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
cb.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, cb.status());
}
TEST_F(AuthenticatorImplTest, TestU2fDeviceDoesNotSupportGetAssertion) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kWebAuthCtap2);
SimulateNavigation(GURL(kTestOrigin1));
PublicKeyCredentialRequestOptionsPtr options =
GetTestPublicKeyCredentialRequestOptions();
TestGetAssertionCallback cb;
// Set up service_manager::Connector for tests.
auto fake_hid_manager = std::make_unique<device::FakeHidManager>();
service_manager::mojom::ConnectorRequest request;
auto connector = service_manager::Connector::Create(&request);
// Set up a timer for testing.
auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
base::Time::Now(), base::TimeTicks::Now());
auto timer =
std::make_unique<base::OneShotTimer>(task_runner->GetMockTickClock());
timer->SetTaskRunner(task_runner);
AuthenticatorPtr authenticator =
ConnectToAuthenticator(connector.get(), std::move(timer));
device::test::ScopedVirtualFidoDevice virtual_device;
authenticator->GetAssertion(std::move(options), cb.callback());
// Trigger timer.
base::RunLoop().RunUntilIdle();
task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1));
cb.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, cb.status());
}
enum class IndividualAttestation { enum class IndividualAttestation {
REQUESTED, REQUESTED,
NOT_REQUESTED, NOT_REQUESTED,
......
...@@ -4,25 +4,166 @@ ...@@ -4,25 +4,166 @@
#include "content/browser/webauth/authenticator_type_converters.h" #include "content/browser/webauth/authenticator_type_converters.h"
#include "base/logging.h" #include <algorithm>
#include "device/fido/fido_constants.h"
namespace mojo { namespace mojo {
using ::webauth::mojom::PublicKeyCredentialUserEntityPtr;
using ::webauth::mojom::PublicKeyCredentialRpEntityPtr;
using ::webauth::mojom::AuthenticatorTransport;
using ::webauth::mojom::PublicKeyCredentialType;
using ::webauth::mojom::PublicKeyCredentialParametersPtr;
using ::webauth::mojom::PublicKeyCredentialDescriptorPtr;
using ::webauth::mojom::AuthenticatorSelectionCriteriaPtr;
using ::webauth::mojom::AuthenticatorAttachment;
using ::webauth::mojom::UserVerificationRequirement;
// static // static
::device::U2fTransportProtocol ::device::U2fTransportProtocol
TypeConverter<::device::U2fTransportProtocol, TypeConverter<::device::U2fTransportProtocol, AuthenticatorTransport>::Convert(
::webauth::mojom::AuthenticatorTransport>:: const AuthenticatorTransport& input) {
Convert(const ::webauth::mojom::AuthenticatorTransport& input) {
switch (input) { switch (input) {
case ::webauth::mojom::AuthenticatorTransport::USB: case AuthenticatorTransport::USB:
return ::device::U2fTransportProtocol::kUsbHumanInterfaceDevice; return ::device::U2fTransportProtocol::kUsbHumanInterfaceDevice;
case ::webauth::mojom::AuthenticatorTransport::NFC: case AuthenticatorTransport::NFC:
return ::device::U2fTransportProtocol::kNearFieldCommunication; return ::device::U2fTransportProtocol::kNearFieldCommunication;
case ::webauth::mojom::AuthenticatorTransport::BLE: case AuthenticatorTransport::BLE:
return ::device::U2fTransportProtocol::kBluetoothLowEnergy; return ::device::U2fTransportProtocol::kBluetoothLowEnergy;
} }
NOTREACHED(); NOTREACHED();
return ::device::U2fTransportProtocol::kUsbHumanInterfaceDevice; return ::device::U2fTransportProtocol::kUsbHumanInterfaceDevice;
} }
// static
::device::CredentialType
TypeConverter<::device::CredentialType, PublicKeyCredentialType>::Convert(
const PublicKeyCredentialType& input) {
switch (input) {
case PublicKeyCredentialType::PUBLIC_KEY:
return ::device::CredentialType::kPublicKey;
}
NOTREACHED();
return ::device::CredentialType::kPublicKey;
}
// static
std::vector<::device::PublicKeyCredentialParams::CredentialInfo>
TypeConverter<std::vector<::device::PublicKeyCredentialParams::CredentialInfo>,
std::vector<PublicKeyCredentialParametersPtr>>::
Convert(const std::vector<PublicKeyCredentialParametersPtr>& input) {
std::vector<::device::PublicKeyCredentialParams::CredentialInfo>
credential_params;
credential_params.reserve(input.size());
for (const auto& credential : input) {
credential_params.emplace_back(
::device::PublicKeyCredentialParams::CredentialInfo{
ConvertTo<::device::CredentialType>(credential->type),
credential->algorithm_identifier});
}
return credential_params;
}
// static
std::vector<::device::PublicKeyCredentialDescriptor>
TypeConverter<std::vector<::device::PublicKeyCredentialDescriptor>,
std::vector<PublicKeyCredentialDescriptorPtr>>::
Convert(const std::vector<PublicKeyCredentialDescriptorPtr>& input) {
std::vector<::device::PublicKeyCredentialDescriptor> credential_descriptors;
credential_descriptors.reserve(input.size());
for (const auto& credential : input) {
credential_descriptors.emplace_back(::device::PublicKeyCredentialDescriptor(
device::to_string(
ConvertTo<::device::CredentialType>(credential->type)),
credential->id));
}
return credential_descriptors;
}
// static
::device::AuthenticatorSelectionCriteria::UserVerificationRequirement
TypeConverter<
::device::AuthenticatorSelectionCriteria::UserVerificationRequirement,
UserVerificationRequirement>::
Convert(const ::webauth::mojom::UserVerificationRequirement& input) {
switch (input) {
case UserVerificationRequirement::PREFERRED:
return ::device::AuthenticatorSelectionCriteria::
UserVerificationRequirement::kPreferred;
case UserVerificationRequirement::REQUIRED:
return ::device::AuthenticatorSelectionCriteria::
UserVerificationRequirement::kRequired;
case UserVerificationRequirement::DISCOURAGED:
return ::device::AuthenticatorSelectionCriteria::
UserVerificationRequirement::kDiscouraged;
}
NOTREACHED();
return ::device::AuthenticatorSelectionCriteria::UserVerificationRequirement::
kPreferred;
}
// static
::device::AuthenticatorSelectionCriteria::AuthenticatorAttachment TypeConverter<
::device::AuthenticatorSelectionCriteria::AuthenticatorAttachment,
AuthenticatorAttachment>::Convert(const AuthenticatorAttachment& input) {
switch (input) {
case AuthenticatorAttachment::NO_PREFERENCE:
return ::device::AuthenticatorSelectionCriteria::AuthenticatorAttachment::
kAny;
case AuthenticatorAttachment::PLATFORM:
return ::device::AuthenticatorSelectionCriteria::AuthenticatorAttachment::
kPlatform;
case AuthenticatorAttachment::CROSS_PLATFORM:
return ::device::AuthenticatorSelectionCriteria::AuthenticatorAttachment::
kCrossPlatform;
}
NOTREACHED();
return ::device::AuthenticatorSelectionCriteria::AuthenticatorAttachment::
kAny;
}
// static
::device::AuthenticatorSelectionCriteria
TypeConverter<::device::AuthenticatorSelectionCriteria,
AuthenticatorSelectionCriteriaPtr>::
Convert(const AuthenticatorSelectionCriteriaPtr& input) {
return device::AuthenticatorSelectionCriteria(
ConvertTo<
::device::AuthenticatorSelectionCriteria::AuthenticatorAttachment>(
input->authenticator_attachment),
input->require_resident_key,
ConvertTo<::device::AuthenticatorSelectionCriteria::
UserVerificationRequirement>(input->user_verification));
}
// static
::device::PublicKeyCredentialRpEntity
TypeConverter<::device::PublicKeyCredentialRpEntity,
PublicKeyCredentialRpEntityPtr>::
Convert(const PublicKeyCredentialRpEntityPtr& input) {
device::PublicKeyCredentialRpEntity rp_entity(input->id);
rp_entity.SetRpName(input->name);
if (input->icon)
rp_entity.SetRpIconUrl(*input->icon);
return rp_entity;
}
// static
::device::PublicKeyCredentialUserEntity
TypeConverter<::device::PublicKeyCredentialUserEntity,
PublicKeyCredentialUserEntityPtr>::
Convert(const PublicKeyCredentialUserEntityPtr& input) {
device::PublicKeyCredentialUserEntity user_entity(input->id);
user_entity.SetUserName(input->name).SetDisplayName(input->display_name);
if (input->icon)
user_entity.SetIconUrl(*input->icon);
return user_entity;
}
} // namespace mojo } // namespace mojo
...@@ -5,10 +5,20 @@ ...@@ -5,10 +5,20 @@
#ifndef CONTENT_BROWSER_WEBAUTH_AUTHENTICATOR_TYPE_CONVERTERS_H_ #ifndef CONTENT_BROWSER_WEBAUTH_AUTHENTICATOR_TYPE_CONVERTERS_H_
#define CONTENT_BROWSER_WEBAUTH_AUTHENTICATOR_TYPE_CONVERTERS_H_ #define CONTENT_BROWSER_WEBAUTH_AUTHENTICATOR_TYPE_CONVERTERS_H_
#include <vector>
#include "device/fido/authenticator_selection_criteria.h"
#include "device/fido/public_key_credential_descriptor.h"
#include "device/fido/public_key_credential_params.h"
#include "device/fido/public_key_credential_rp_entity.h"
#include "device/fido/public_key_credential_user_entity.h"
#include "device/fido/u2f_transport_protocol.h" #include "device/fido/u2f_transport_protocol.h"
#include "mojo/public/cpp/bindings/type_converter.h" #include "mojo/public/cpp/bindings/type_converter.h"
#include "third_party/blink/public/platform/modules/webauth/authenticator.mojom.h" #include "third_party/blink/public/platform/modules/webauth/authenticator.mojom.h"
// TODO(hongjunchoi): Remove type converters and instead expose mojo interface
// directly from device/fido service.
// See: https://crbug.com/831209
namespace mojo { namespace mojo {
template <> template <>
...@@ -18,6 +28,68 @@ struct TypeConverter<::device::U2fTransportProtocol, ...@@ -18,6 +28,68 @@ struct TypeConverter<::device::U2fTransportProtocol,
const ::webauth::mojom::AuthenticatorTransport& input); const ::webauth::mojom::AuthenticatorTransport& input);
}; };
template <>
struct TypeConverter<::device::CredentialType,
::webauth::mojom::PublicKeyCredentialType> {
static ::device::CredentialType Convert(
const ::webauth::mojom::PublicKeyCredentialType& input);
};
template <>
struct TypeConverter<
std::vector<::device::PublicKeyCredentialParams::CredentialInfo>,
std::vector<::webauth::mojom::PublicKeyCredentialParametersPtr>> {
static std::vector<::device::PublicKeyCredentialParams::CredentialInfo>
Convert(const std::vector<::webauth::mojom::PublicKeyCredentialParametersPtr>&
input);
};
template <>
struct TypeConverter<
std::vector<::device::PublicKeyCredentialDescriptor>,
std::vector<::webauth::mojom::PublicKeyCredentialDescriptorPtr>> {
static std::vector<::device::PublicKeyCredentialDescriptor> Convert(
const std::vector<::webauth::mojom::PublicKeyCredentialDescriptorPtr>&
input);
};
template <>
struct TypeConverter<
::device::AuthenticatorSelectionCriteria::AuthenticatorAttachment,
::webauth::mojom::AuthenticatorAttachment> {
static ::device::AuthenticatorSelectionCriteria::AuthenticatorAttachment
Convert(const ::webauth::mojom::AuthenticatorAttachment& input);
};
template <>
struct TypeConverter<
::device::AuthenticatorSelectionCriteria::UserVerificationRequirement,
::webauth::mojom::UserVerificationRequirement> {
static ::device::AuthenticatorSelectionCriteria::UserVerificationRequirement
Convert(const ::webauth::mojom::UserVerificationRequirement& input);
};
template <>
struct TypeConverter<::device::AuthenticatorSelectionCriteria,
::webauth::mojom::AuthenticatorSelectionCriteriaPtr> {
static ::device::AuthenticatorSelectionCriteria Convert(
const ::webauth::mojom::AuthenticatorSelectionCriteriaPtr& input);
};
template <>
struct TypeConverter<::device::PublicKeyCredentialRpEntity,
::webauth::mojom::PublicKeyCredentialRpEntityPtr> {
static ::device::PublicKeyCredentialRpEntity Convert(
const ::webauth::mojom::PublicKeyCredentialRpEntityPtr& input);
};
template <>
struct TypeConverter<::device::PublicKeyCredentialUserEntity,
::webauth::mojom::PublicKeyCredentialUserEntityPtr> {
static ::device::PublicKeyCredentialUserEntity Convert(
const ::webauth::mojom::PublicKeyCredentialUserEntityPtr& input);
};
} // namespace mojo } // namespace mojo
#endif // CONTENT_BROWSER_WEBAUTH_AUTHENTICATOR_TYPE_CONVERTERS_H_ #endif // CONTENT_BROWSER_WEBAUTH_AUTHENTICATOR_TYPE_CONVERTERS_H_
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include "content/test/did_commit_provisional_load_interceptor.h" #include "content/test/did_commit_provisional_load_interceptor.h"
#include "device/fido/fake_fido_discovery.h" #include "device/fido/fake_fido_discovery.h"
#include "device/fido/fake_hid_impl_for_testing.h" #include "device/fido/fake_hid_impl_for_testing.h"
#include "device/fido/fido_test_data.h"
#include "device/fido/mock_fido_device.h"
#include "device/fido/test_callback_receiver.h" #include "device/fido/test_callback_receiver.h"
#include "net/dns/mock_host_resolver.h" #include "net/dns/mock_host_resolver.h"
#include "services/device/public/mojom/constants.mojom.h" #include "services/device/public/mojom/constants.mojom.h"
...@@ -287,9 +289,11 @@ class WebAuthLocalClientBrowserTest : public WebAuthBrowserTestBase { ...@@ -287,9 +289,11 @@ class WebAuthLocalClientBrowserTest : public WebAuthBrowserTestBase {
std::vector<webauth::mojom::AuthenticatorTransport> transports; std::vector<webauth::mojom::AuthenticatorTransport> transports;
transports.push_back(webauth::mojom::AuthenticatorTransport::USB); transports.push_back(webauth::mojom::AuthenticatorTransport::USB);
std::vector<uint8_t> kCredentialId{0, 0, 0};
auto descriptor = webauth::mojom::PublicKeyCredentialDescriptor::New( auto descriptor = webauth::mojom::PublicKeyCredentialDescriptor::New(
webauth::mojom::PublicKeyCredentialType::PUBLIC_KEY, kCredentialId, webauth::mojom::PublicKeyCredentialType::PUBLIC_KEY,
std::vector<uint8_t>(
std::begin(device::test_data::kTestGetAssertionCredentialId),
std::end(device::test_data::kTestGetAssertionCredentialId)),
transports); transports);
credentials.push_back(std::move(descriptor)); credentials.push_back(std::move(descriptor));
...@@ -612,4 +616,70 @@ IN_PROC_BROWSER_TEST_F(WebAuthBrowserBleDisabledTest, CheckBleDisabled) { ...@@ -612,4 +616,70 @@ IN_PROC_BROWSER_TEST_F(WebAuthBrowserBleDisabledTest, CheckBleDisabled) {
EXPECT_FALSE(fake_ble_discovery->is_start_requested()); EXPECT_FALSE(fake_ble_discovery->is_start_requested());
} }
// WebAuthBrowserCtapTest ----------------------------------------------
// A test fixture that enables CTAP only flag.
class WebAuthBrowserCtapTest : public WebAuthLocalClientBrowserTest {
public:
WebAuthBrowserCtapTest() = default;
protected:
std::vector<base::Feature> GetFeaturesToEnable() override {
return {features::kWebAuth, features::kWebAuthCtap2};
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(WebAuthBrowserCtapTest);
};
// TODO(hongjunchoi): Implement VirtualCtap2Device to replace mocking.
// See: https://crbugs.com/829413
IN_PROC_BROWSER_TEST_F(WebAuthBrowserCtapTest, TestCtapMakeCredential) {
auto* fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
TestCreateCallbackReceiver create_callback_receiver;
authenticator()->MakeCredential(BuildBasicCreateOptions(),
create_callback_receiver.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
auto device = std::make_unique<device::MockFidoDevice>();
EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
device->ExpectCtap2CommandAndRespondWith(
device::CtapRequestCommand::kAuthenticatorGetInfo,
device::test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
device::CtapRequestCommand::kAuthenticatorMakeCredential,
device::test_data::kTestMakeCredentialResponse);
fake_hid_discovery->AddDevice(std::move(device));
create_callback_receiver.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::SUCCESS, create_callback_receiver.status());
}
IN_PROC_BROWSER_TEST_F(WebAuthBrowserCtapTest, TestCtapGetAssertion) {
auto* fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
TestGetCallbackReceiver get_callback_receiver;
auto get_assertion_request_params = BuildBasicGetOptions();
authenticator()->GetAssertion(std::move(get_assertion_request_params),
get_callback_receiver.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
auto device = std::make_unique<device::MockFidoDevice>();
EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
device->ExpectCtap2CommandAndRespondWith(
device::CtapRequestCommand::kAuthenticatorGetInfo,
device::test_data::kTestAuthenticatorGetInfoResponse);
device->ExpectCtap2CommandAndRespondWith(
device::CtapRequestCommand::kAuthenticatorGetAssertion,
device::test_data::kTestGetAssertionResponse);
fake_hid_discovery->AddDevice(std::move(device));
get_callback_receiver.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::SUCCESS, get_callback_receiver.status());
}
} // namespace content } // namespace content
...@@ -486,6 +486,11 @@ const base::Feature kWebAuth { ...@@ -486,6 +486,11 @@ const base::Feature kWebAuth {
const base::Feature kWebAuthBle{"WebAuthenticationBle", const base::Feature kWebAuthBle{"WebAuthenticationBle",
base::FEATURE_DISABLED_BY_DEFAULT}; base::FEATURE_DISABLED_BY_DEFAULT};
// Controls whether CTAP2 authenticators can be used via the WebAuthentication
// API. https://w3c.github.io/webauthn
const base::Feature kWebAuthCtap2{"WebAuthenticationCtap2",
base::FEATURE_DISABLED_BY_DEFAULT};
// If WebGL Image Chromium is allowed, this feature controls whether it is // If WebGL Image Chromium is allowed, this feature controls whether it is
// enabled. // enabled.
const base::Feature kWebGLImageChromium{"WebGLImageChromium", const base::Feature kWebGLImageChromium{"WebGLImageChromium",
......
...@@ -113,6 +113,7 @@ CONTENT_EXPORT extern const base::Feature kWebAssemblyTrapHandler; ...@@ -113,6 +113,7 @@ CONTENT_EXPORT extern const base::Feature kWebAssemblyTrapHandler;
CONTENT_EXPORT extern const base::Feature kWebAuth; CONTENT_EXPORT extern const base::Feature kWebAuth;
CONTENT_EXPORT extern const base::Feature kWebAuthBle; CONTENT_EXPORT extern const base::Feature kWebAuthBle;
CONTENT_EXPORT extern const base::Feature kWebContentsOcclusion; CONTENT_EXPORT extern const base::Feature kWebContentsOcclusion;
CONTENT_EXPORT extern const base::Feature kWebAuthCtap2;
CONTENT_EXPORT extern const base::Feature kWebGLImageChromium; CONTENT_EXPORT extern const base::Feature kWebGLImageChromium;
CONTENT_EXPORT extern const base::Feature kWebPayments; CONTENT_EXPORT extern const base::Feature kWebPayments;
CONTENT_EXPORT extern const base::Feature kWebRtcAecBoundedErlSetup; CONTENT_EXPORT extern const base::Feature kWebRtcAecBoundedErlSetup;
......
...@@ -1056,7 +1056,10 @@ test("content_browsertests") { ...@@ -1056,7 +1056,10 @@ test("content_browsertests") {
is_linux_without_udev = is_linux && !use_udev is_linux_without_udev = is_linux && !use_udev
if (!is_linux_without_udev && !is_android) { if (!is_linux_without_udev && !is_android) {
sources += [ "../browser/webauth/webauth_browsertest.cc" ] sources += [ "../browser/webauth/webauth_browsertest.cc" ]
deps += [ "//device/fido:test_support" ] deps += [
"//device/fido:mocks",
"//device/fido:test_support",
]
} }
if (is_mac) { if (is_mac) {
......
...@@ -141,6 +141,16 @@ void VirtualFidoDevice::DeviceTransact(std::vector<uint8_t> command, ...@@ -141,6 +141,16 @@ void VirtualFidoDevice::DeviceTransact(std::vector<uint8_t> command,
// Note, here we are using the code-under-test in this fake. // Note, here we are using the code-under-test in this fake.
auto parsed_command = apdu::ApduCommand::CreateFromMessage(command); auto parsed_command = apdu::ApduCommand::CreateFromMessage(command);
// If malformed U2F request is received, respond with error immediately.
if (!parsed_command) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
std::move(cb),
ErrorStatus(apdu::ApduResponse::Status::SW_INS_NOT_SUPPORTED)));
return;
}
base::Optional<std::vector<uint8_t>> response; base::Optional<std::vector<uint8_t>> response;
switch (parsed_command->ins()) { switch (parsed_command->ins()) {
......
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