123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include <memory>
- #include <string>
- #include <utility>
- #include <vector>
- #include "base/bind.h"
- #include "base/memory/raw_ptr.h"
- #include "base/numerics/safe_conversions.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/test/task_environment.h"
- #include "build/build_config.h"
- #include "device/bluetooth/bluetooth_adapter_factory.h"
- #include "device/bluetooth/test/mock_bluetooth_adapter.h"
- #include "device/fido/fake_fido_discovery.h"
- #include "device/fido/fido_constants.h"
- #include "device/fido/fido_device.h"
- #include "device/fido/fido_device_authenticator.h"
- #include "device/fido/fido_request_handler_base.h"
- #include "device/fido/fido_task.h"
- #include "device/fido/fido_test_data.h"
- #include "device/fido/fido_transport_protocol.h"
- #include "device/fido/mock_fido_device.h"
- #include "device/fido/test_callback_receiver.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #if BUILDFLAG(IS_WIN)
- #include "device/fido/win/fake_webauthn_api.h"
- #endif // BUILDFLAG(IS_WIN)
- using ::testing::_;
- namespace device {
- namespace {
- using FakeTaskCallback =
- base::OnceCallback<void(CtapDeviceResponseCode status_code,
- absl::optional<std::vector<uint8_t>>)>;
- using FakeHandlerCallbackReceiver =
- test::StatusAndValuesCallbackReceiver<bool,
- absl::optional<std::vector<uint8_t>>,
- const FidoAuthenticator*>;
- enum class FakeTaskResponse : uint8_t {
- kSuccess = 0x00,
- kErrorReceivedAfterObtainingUserPresence = 0x01,
- kProcessingError = 0x02,
- kOperationDenied = 0x03,
- };
- // FidoRequestHandler that automatically starts discovery but does nothing on
- // DispatchRequest().
- class EmptyRequestHandler : public FidoRequestHandlerBase {
- public:
- EmptyRequestHandler(const base::flat_set<FidoTransportProtocol>& protocols,
- test::FakeFidoDiscoveryFactory* fake_discovery_factory)
- : FidoRequestHandlerBase(fake_discovery_factory, protocols) {
- Start();
- }
- ~EmptyRequestHandler() override = default;
- void DispatchRequest(FidoAuthenticator* authenticator) override {}
- };
- class TestObserver : public FidoRequestHandlerBase::Observer {
- public:
- using TransportAvailabilityNotificationReceiver = test::TestCallbackReceiver<
- FidoRequestHandlerBase::TransportAvailabilityInfo>;
- TestObserver() = default;
- TestObserver(const TestObserver&) = delete;
- TestObserver& operator=(const TestObserver&) = delete;
- ~TestObserver() override = default;
- FidoRequestHandlerBase::TransportAvailabilityInfo
- WaitForTransportAvailabilityInfo() {
- transport_availability_notification_receiver_.WaitForCallback();
- return std::get<0>(*transport_availability_notification_receiver_.result());
- }
- void WaitForAndExpectAvailableTransportsAre(
- base::flat_set<FidoTransportProtocol> expected_transports,
- FidoRequestHandlerBase::RecognizedCredential
- has_platform_authenticator_credential =
- FidoRequestHandlerBase::RecognizedCredential::kUnknown) {
- auto result = WaitForTransportAvailabilityInfo();
- EXPECT_THAT(result.available_transports,
- ::testing::UnorderedElementsAreArray(expected_transports));
- EXPECT_EQ(result.has_platform_authenticator_credential,
- has_platform_authenticator_credential);
- }
- protected:
- // FidoRequestHandlerBase::Observer:
- void OnTransportAvailabilityEnumerated(
- FidoRequestHandlerBase::TransportAvailabilityInfo data) override {
- transport_availability_notification_receiver_.callback().Run(
- std::move(data));
- }
- bool EmbedderControlsAuthenticatorDispatch(
- const FidoAuthenticator&) override {
- return false;
- }
- void BluetoothAdapterPowerChanged(bool is_powered_on) override {}
- void FidoAuthenticatorAdded(const FidoAuthenticator& authenticator) override {
- }
- void FidoAuthenticatorRemoved(base::StringPiece device_id) override {}
- bool SupportsPIN() const override { return false; }
- void CollectPIN(
- CollectPINOptions options,
- base::OnceCallback<void(std::u16string)> provide_pin_cb) override {
- NOTREACHED();
- }
- void OnRetryUserVerification(int attempts) override {}
- void StartBioEnrollment(base::OnceClosure next_callback) override {}
- void OnSampleCollected(int remaining_samples) override {}
- void FinishCollectToken() override { NOTREACHED(); }
- private:
- TransportAvailabilityNotificationReceiver
- transport_availability_notification_receiver_;
- };
- // Fake FidoTask implementation that sends an empty byte array to the device
- // when StartTask() is invoked.
- class FakeFidoTask : public FidoTask {
- public:
- FakeFidoTask(FidoDevice* device, FakeTaskCallback callback)
- : FidoTask(device), callback_(std::move(callback)) {}
- ~FakeFidoTask() override = default;
- void Cancel() override {
- if (token_) {
- device()->Cancel(*token_);
- token_.reset();
- }
- }
- void StartTask() override {
- token_ = device()->DeviceTransact(
- std::vector<uint8_t>(),
- base::BindOnce(&FakeFidoTask::CompletionCallback,
- weak_factory_.GetWeakPtr()));
- }
- void CompletionCallback(
- absl::optional<std::vector<uint8_t>> device_response) {
- DCHECK(device_response && device_response->size() == 1);
- switch (static_cast<FakeTaskResponse>(device_response->front())) {
- case FakeTaskResponse::kSuccess:
- std::move(callback_).Run(CtapDeviceResponseCode::kSuccess,
- std::vector<uint8_t>());
- return;
- case FakeTaskResponse::kErrorReceivedAfterObtainingUserPresence:
- std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
- std::vector<uint8_t>());
- return;
- case FakeTaskResponse::kOperationDenied:
- std::move(callback_).Run(
- CtapDeviceResponseCode::kCtap2ErrOperationDenied, absl::nullopt);
- return;
- case FakeTaskResponse::kProcessingError:
- default:
- std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
- absl::nullopt);
- return;
- }
- }
- private:
- absl::optional<FidoDevice::CancelToken> token_;
- FakeTaskCallback callback_;
- base::WeakPtrFactory<FakeFidoTask> weak_factory_{this};
- };
- class FakeFidoRequestHandler : public FidoRequestHandlerBase {
- public:
- using CompletionCallback =
- base::OnceCallback<void(bool,
- absl::optional<std::vector<uint8_t>>,
- const FidoAuthenticator*)>;
- FakeFidoRequestHandler(test::FakeFidoDiscoveryFactory* fake_discovery_factory,
- const base::flat_set<FidoTransportProtocol>& protocols,
- CompletionCallback callback)
- : FidoRequestHandlerBase(fake_discovery_factory, protocols),
- completion_callback_(std::move(callback)) {
- Start();
- }
- ~FakeFidoRequestHandler() override = default;
- void set_has_platform_credential(
- RecognizedCredential has_platform_credential) {
- has_platform_credential_ = has_platform_credential;
- }
- private:
- void DispatchRequest(FidoAuthenticator* authenticator) override {
- // FidoRequestHandlerTest uses FakeDiscovery to inject mock devices
- // that get wrapped in a FidoDeviceAuthenticator, so we can safely cast
- // here.
- auto* device_authenticator =
- static_cast<FidoDeviceAuthenticator*>(authenticator);
- // Instead of sending a real CTAP request, send an empty byte array. Note
- // that during discovery, the device already has received a GetInfo command
- // at this point.
- device_authenticator->SetTaskForTesting(std::make_unique<FakeFidoTask>(
- device_authenticator->device(),
- base::BindOnce(&FakeFidoRequestHandler::HandleResponse,
- weak_factory_.GetWeakPtr(), authenticator)));
- }
- void AuthenticatorAdded(FidoDiscoveryBase* discovery,
- FidoAuthenticator* authenticator) override {
- if (authenticator->AuthenticatorTransport() ==
- FidoTransportProtocol::kInternal) {
- transport_availability_info().has_platform_authenticator_credential =
- has_platform_credential_;
- }
- FidoRequestHandlerBase::AuthenticatorAdded(discovery, authenticator);
- }
- void HandleResponse(FidoAuthenticator* authenticator,
- CtapDeviceResponseCode status,
- absl::optional<std::vector<uint8_t>> response) {
- auto* device_authenticator =
- static_cast<FidoDeviceAuthenticator*>(authenticator);
- device_authenticator->SetTaskForTesting(nullptr);
- if (status == CtapDeviceResponseCode::kCtap2ErrOther) {
- // Simulates an error that is sent without the user touching the
- // authenticator (FakeTaskResponse::kProcessingError). Don't resolve
- // the request for this response.
- return;
- }
- if (!completion_callback_) {
- return;
- }
- CancelActiveAuthenticators(authenticator->GetId());
- std::move(completion_callback_)
- .Run(status == CtapDeviceResponseCode::kSuccess, std::move(response),
- authenticator);
- }
- CompletionCallback completion_callback_;
- RecognizedCredential has_platform_credential_ =
- RecognizedCredential::kNoRecognizedCredential;
- base::WeakPtrFactory<FakeFidoRequestHandler> weak_factory_{this};
- };
- std::vector<uint8_t> CreateFakeSuccessDeviceResponse() {
- return {base::strict_cast<uint8_t>(FakeTaskResponse::kSuccess)};
- }
- std::vector<uint8_t> CreateFakeUserPresenceVerifiedError() {
- return {base::strict_cast<uint8_t>(
- FakeTaskResponse::kErrorReceivedAfterObtainingUserPresence)};
- }
- std::vector<uint8_t> CreateFakeDeviceProcesssingError() {
- return {base::strict_cast<uint8_t>(FakeTaskResponse::kProcessingError)};
- }
- std::vector<uint8_t> CreateFakeOperationDeniedError() {
- return {base::strict_cast<uint8_t>(FakeTaskResponse::kOperationDenied)};
- }
- } // namespace
- class FidoRequestHandlerTest : public ::testing::Test {
- public:
- FidoRequestHandlerTest() {
- mock_adapter_ =
- base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
- BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
- }
- void ForgeNextHidDiscovery() {
- discovery_ = fake_discovery_factory_.ForgeNextHidDiscovery();
- }
- std::unique_ptr<FakeFidoRequestHandler> CreateFakeHandler() {
- ForgeNextHidDiscovery();
- auto handler = std::make_unique<FakeFidoRequestHandler>(
- &fake_discovery_factory_,
- base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
- cb_.callback());
- return handler;
- }
- test::FakeFidoDiscovery* discovery() const { return discovery_; }
- scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> adapter() {
- return mock_adapter_;
- }
- FakeHandlerCallbackReceiver& callback() { return cb_; }
- protected:
- base::test::TaskEnvironment task_environment_{
- base::test::TaskEnvironment::TimeSource::MOCK_TIME};
- test::FakeFidoDiscoveryFactory fake_discovery_factory_;
- scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;
- raw_ptr<test::FakeFidoDiscovery> discovery_;
- FakeHandlerCallbackReceiver cb_;
- };
- TEST_F(FidoRequestHandlerTest, TestSingleDeviceSuccess) {
- auto request_handler = CreateFakeHandler();
- discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device = std::make_unique<MockFidoDevice>();
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo, absl::nullopt);
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- // Device returns success response.
- device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeSuccessDeviceResponse());
- discovery()->AddDevice(std::move(device));
- callback().WaitForCallback();
- EXPECT_TRUE(callback().status());
- }
- // Tests a scenario where two unresponsive authenticators are connected and
- // cancel request has been sent either from the user or from the relying party
- // (i.e. FidoRequestHandler object is destroyed.) Upon destruction, cancel
- // command must be invoked to all connected authenticators.
- TEST_F(FidoRequestHandlerTest, TestAuthenticatorHandlerReset) {
- auto request_handler = CreateFakeHandler();
- discovery()->WaitForCallToStartAndSimulateSuccess();
- auto device0 = std::make_unique<MockFidoDevice>();
- device0->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
- device0->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
- EXPECT_CALL(*device0, Cancel(_));
- auto device1 = std::make_unique<MockFidoDevice>();
- device1->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
- device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
- EXPECT_CALL(*device1, Cancel(_));
- discovery()->AddDevice(std::move(device0));
- discovery()->AddDevice(std::move(device1));
- task_environment_.FastForwardUntilNoTasksRemain();
- request_handler.reset();
- }
- // Test a scenario where 2 devices are connected and a response is received
- // from only a single device(device1) and the remaining device hangs.
- TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleDevices) {
- auto request_handler = CreateFakeHandler();
- discovery()->WaitForCallToStartAndSimulateSuccess();
- // Represents a connected device that hangs without a response.
- auto device0 = std::make_unique<MockFidoDevice>();
- device0->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
- // Device is unresponsive and cancel command is invoked afterwards.
- device0->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
- EXPECT_CALL(*device0, Cancel(_));
- // Represents a connected device that response successfully.
- auto device1 = std::make_unique<MockFidoDevice>();
- device1->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
- device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeSuccessDeviceResponse());
- discovery()->AddDevice(std::move(device0));
- discovery()->AddDevice(std::move(device1));
- callback().WaitForCallback();
- EXPECT_TRUE(callback().status());
- }
- // Test a scenario where 2 devices respond successfully with small time
- // delay. Only the first received response should be passed on to the relying
- // party, and cancel request should be sent to the other authenticator.
- TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleSuccessResponses) {
- auto request_handler = CreateFakeHandler();
- discovery()->WaitForCallToStartAndSimulateSuccess();
- // Represents a connected device that responds successfully after small time
- // delay.
- auto device0 = std::make_unique<MockFidoDevice>();
- device0->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
- device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeSuccessDeviceResponse(),
- base::Microseconds(1));
- // Represents a device that returns a success response after a longer time
- // delay.
- auto device1 = std::make_unique<MockFidoDevice>();
- device1->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
- device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeSuccessDeviceResponse(),
- base::Microseconds(10));
- // Cancel command is invoked after receiving response from |device0|.
- EXPECT_CALL(*device1, Cancel(_));
- discovery()->AddDevice(std::move(device0));
- discovery()->AddDevice(std::move(device1));
- task_environment_.FastForwardUntilNoTasksRemain();
- callback().WaitForCallback();
- EXPECT_TRUE(callback().status());
- }
- // Test a scenario where 3 devices respond with a processing error, an UP(user
- // presence) verified failure response with small time delay, and an UP
- // verified failure response with big time delay, respectively. Request for
- // device with processing error should be immediately dropped. Also, for UP
- // verified failures, the first received response should be passed on to the
- // relying party and cancel command should be sent to the remaining device.
- TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleFailureResponses) {
- auto request_handler = CreateFakeHandler();
- discovery()->WaitForCallToStartAndSimulateSuccess();
- // Represents a connected device that immediately responds with a processing
- // error.
- auto device0 = std::make_unique<MockFidoDevice>();
- device0->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
- EXPECT_CALL(*device0, GetDisplayName())
- .WillRepeatedly(testing::Return(std::string()));
- device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeDeviceProcesssingError());
- // Represents a device that returns an UP verified failure response after a
- // small time delay.
- auto device1 = std::make_unique<MockFidoDevice>();
- device1->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
- EXPECT_CALL(*device1, GetDisplayName())
- .WillRepeatedly(testing::Return(std::string()));
- device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeUserPresenceVerifiedError(),
- base::Microseconds(1));
- // Represents a device that returns an UP verified failure response after a
- // big time delay.
- auto device2 = std::make_unique<MockFidoDevice>();
- device2->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestAuthenticatorGetInfoResponse);
- EXPECT_CALL(*device2, GetId()).WillRepeatedly(testing::Return("device2"));
- EXPECT_CALL(*device2, GetDisplayName())
- .WillRepeatedly(testing::Return(std::string()));
- device2->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeDeviceProcesssingError(),
- base::Microseconds(10));
- EXPECT_CALL(*device2, Cancel(_));
- discovery()->AddDevice(std::move(device0));
- discovery()->AddDevice(std::move(device1));
- discovery()->AddDevice(std::move(device2));
- task_environment_.FastForwardUntilNoTasksRemain();
- callback().WaitForCallback();
- EXPECT_FALSE(callback().status());
- }
- // If a device with transport type kInternal returns a
- // CTAP2_ERR_OPERATION_DENIED error, the request should be cancelled on all
- // pending authenticators.
- TEST_F(FidoRequestHandlerTest,
- TestRequestWithOperationDeniedErrorInternalTransport) {
- TestObserver observer;
- // Device will send CTAP2_ERR_OPERATION_DENIED.
- auto device0 = MockFidoDevice::MakeCtapWithGetInfoExpectation(
- test_data::kTestGetInfoResponsePlatformDevice);
- device0->SetDeviceTransport(FidoTransportProtocol::kInternal);
- device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeOperationDeniedError(),
- base::Microseconds(10));
- ForgeNextHidDiscovery();
- auto* platform_discovery =
- fake_discovery_factory_.ForgeNextPlatformDiscovery();
- auto request_handler = std::make_unique<FakeFidoRequestHandler>(
- &fake_discovery_factory_,
- base::flat_set<FidoTransportProtocol>(
- {FidoTransportProtocol::kInternal,
- FidoTransportProtocol::kUsbHumanInterfaceDevice}),
- callback().callback());
- request_handler->set_observer(&observer);
- auto device1 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
- device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
- EXPECT_CALL(*device1, Cancel(_));
- discovery()->AddDevice(std::move(device0));
- platform_discovery->AddDevice(std::move(device1));
- discovery()->WaitForCallToStartAndSimulateSuccess();
- platform_discovery->WaitForCallToStartAndSimulateSuccess();
- task_environment_.FastForwardUntilNoTasksRemain();
- callback().WaitForCallback();
- EXPECT_FALSE(callback().status());
- }
- // Like |TestRequestWithOperationDeniedErrorInternalTransport|, but with a
- // cross-platform authenticator.
- TEST_F(FidoRequestHandlerTest,
- TestRequestWithOperationDeniedErrorCrossPlatform) {
- auto request_handler = CreateFakeHandler();
- discovery()->WaitForCallToStartAndSimulateSuccess();
- // Device will send CTAP2_ERR_OPERATION_DENIED.
- auto device0 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
- device0->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
- device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeOperationDeniedError());
- auto device1 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
- device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
- EXPECT_CALL(*device1, Cancel(_));
- discovery()->AddDevice(std::move(device0));
- discovery()->AddDevice(std::move(device1));
- task_environment_.FastForwardUntilNoTasksRemain();
- callback().WaitForCallback();
- EXPECT_FALSE(callback().status());
- }
- // Requests should be dispatched to the platform authenticator.
- TEST_F(FidoRequestHandlerTest, TestWithPlatformAuthenticator) {
- // A platform authenticator usually wouldn't usually use a FidoDevice, but
- // that's not the point of the test here. The test is only trying to ensure
- // the authenticator gets injected and used.
- auto device = MockFidoDevice::MakeCtap();
- EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
- device->ExpectCtap2CommandAndRespondWith(
- CtapRequestCommand::kAuthenticatorGetInfo,
- test_data::kTestGetInfoResponsePlatformDevice);
- // Device returns success response.
- device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
- CreateFakeSuccessDeviceResponse());
- device->SetDeviceTransport(FidoTransportProtocol::kInternal);
- auto* fake_discovery = fake_discovery_factory_.ForgeNextPlatformDiscovery(
- test::FakeFidoDiscovery::StartMode::kAutomatic);
- TestObserver observer;
- auto request_handler = std::make_unique<FakeFidoRequestHandler>(
- &fake_discovery_factory_,
- base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
- callback().callback());
- request_handler->set_has_platform_credential(
- FidoRequestHandlerBase::RecognizedCredential::kHasRecognizedCredential);
- request_handler->set_observer(&observer);
- fake_discovery->AddDevice(std::move(device));
- observer.WaitForAndExpectAvailableTransportsAre(
- {FidoTransportProtocol::kInternal},
- FidoRequestHandlerBase::RecognizedCredential::kHasRecognizedCredential);
- callback().WaitForCallback();
- EXPECT_TRUE(callback().status());
- }
- TEST_F(FidoRequestHandlerTest, InternalTransportDisallowedIfMarkedUnavailable) {
- TestObserver observer;
- auto request_handler = std::make_unique<FakeFidoRequestHandler>(
- &fake_discovery_factory_,
- base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
- callback().callback());
- request_handler->set_observer(&observer);
- observer.WaitForAndExpectAvailableTransportsAre({});
- }
- TEST_F(FidoRequestHandlerTest,
- TransportAvailabilityNotificationOnObserverSetLate) {
- TestObserver observer;
- auto request_handler = CreateFakeHandler();
- discovery()->WaitForCallToStartAndSimulateSuccess();
- task_environment_.FastForwardUntilNoTasksRemain();
- request_handler->set_observer(&observer);
- observer.WaitForAndExpectAvailableTransportsAre(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice});
- }
- #if BUILDFLAG(IS_WIN)
- TEST_F(FidoRequestHandlerTest, TransportAvailabilityOfWindowsAuthenticator) {
- FakeWinWebAuthnApi api;
- fake_discovery_factory_.set_win_webauthn_api(&api);
- for (const bool api_available : {false, true}) {
- SCOPED_TRACE(::testing::Message() << "api_available=" << api_available);
- api.set_available(api_available);
- TestObserver observer;
- ForgeNextHidDiscovery();
- EmptyRequestHandler request_handler(
- {FidoTransportProtocol::kUsbHumanInterfaceDevice},
- &fake_discovery_factory_);
- request_handler.set_observer(&observer);
- // If the windows API is not enabled, the request is dispatched to the USB
- // discovery. Simulate a success to fill the transport availability info.
- if (!api_available)
- discovery()->WaitForCallToStartAndSimulateSuccess();
- task_environment_.FastForwardUntilNoTasksRemain();
- auto transport_availability_info =
- observer.WaitForTransportAvailabilityInfo();
- EXPECT_EQ(transport_availability_info.available_transports.empty(),
- api_available);
- EXPECT_EQ(transport_availability_info.has_win_native_api_authenticator,
- api_available);
- EXPECT_EQ(transport_availability_info.win_native_api_authenticator_id,
- api_available ? "WinWebAuthnApiAuthenticator" : "");
- }
- }
- #endif // BUILDFLAG(IS_WIN)
- } // namespace device
|