fido_request_handler_unittest.cc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <memory>
  5. #include <string>
  6. #include <utility>
  7. #include <vector>
  8. #include "base/bind.h"
  9. #include "base/memory/raw_ptr.h"
  10. #include "base/numerics/safe_conversions.h"
  11. #include "base/strings/utf_string_conversions.h"
  12. #include "base/test/task_environment.h"
  13. #include "build/build_config.h"
  14. #include "device/bluetooth/bluetooth_adapter_factory.h"
  15. #include "device/bluetooth/test/mock_bluetooth_adapter.h"
  16. #include "device/fido/fake_fido_discovery.h"
  17. #include "device/fido/fido_constants.h"
  18. #include "device/fido/fido_device.h"
  19. #include "device/fido/fido_device_authenticator.h"
  20. #include "device/fido/fido_request_handler_base.h"
  21. #include "device/fido/fido_task.h"
  22. #include "device/fido/fido_test_data.h"
  23. #include "device/fido/fido_transport_protocol.h"
  24. #include "device/fido/mock_fido_device.h"
  25. #include "device/fido/test_callback_receiver.h"
  26. #include "testing/gmock/include/gmock/gmock.h"
  27. #include "testing/gtest/include/gtest/gtest.h"
  28. #if BUILDFLAG(IS_WIN)
  29. #include "device/fido/win/fake_webauthn_api.h"
  30. #endif // BUILDFLAG(IS_WIN)
  31. using ::testing::_;
  32. namespace device {
  33. namespace {
  34. using FakeTaskCallback =
  35. base::OnceCallback<void(CtapDeviceResponseCode status_code,
  36. absl::optional<std::vector<uint8_t>>)>;
  37. using FakeHandlerCallbackReceiver =
  38. test::StatusAndValuesCallbackReceiver<bool,
  39. absl::optional<std::vector<uint8_t>>,
  40. const FidoAuthenticator*>;
  41. enum class FakeTaskResponse : uint8_t {
  42. kSuccess = 0x00,
  43. kErrorReceivedAfterObtainingUserPresence = 0x01,
  44. kProcessingError = 0x02,
  45. kOperationDenied = 0x03,
  46. };
  47. // FidoRequestHandler that automatically starts discovery but does nothing on
  48. // DispatchRequest().
  49. class EmptyRequestHandler : public FidoRequestHandlerBase {
  50. public:
  51. EmptyRequestHandler(const base::flat_set<FidoTransportProtocol>& protocols,
  52. test::FakeFidoDiscoveryFactory* fake_discovery_factory)
  53. : FidoRequestHandlerBase(fake_discovery_factory, protocols) {
  54. Start();
  55. }
  56. ~EmptyRequestHandler() override = default;
  57. void DispatchRequest(FidoAuthenticator* authenticator) override {}
  58. };
  59. class TestObserver : public FidoRequestHandlerBase::Observer {
  60. public:
  61. using TransportAvailabilityNotificationReceiver = test::TestCallbackReceiver<
  62. FidoRequestHandlerBase::TransportAvailabilityInfo>;
  63. TestObserver() = default;
  64. TestObserver(const TestObserver&) = delete;
  65. TestObserver& operator=(const TestObserver&) = delete;
  66. ~TestObserver() override = default;
  67. FidoRequestHandlerBase::TransportAvailabilityInfo
  68. WaitForTransportAvailabilityInfo() {
  69. transport_availability_notification_receiver_.WaitForCallback();
  70. return std::get<0>(*transport_availability_notification_receiver_.result());
  71. }
  72. void WaitForAndExpectAvailableTransportsAre(
  73. base::flat_set<FidoTransportProtocol> expected_transports,
  74. FidoRequestHandlerBase::RecognizedCredential
  75. has_platform_authenticator_credential =
  76. FidoRequestHandlerBase::RecognizedCredential::kUnknown) {
  77. auto result = WaitForTransportAvailabilityInfo();
  78. EXPECT_THAT(result.available_transports,
  79. ::testing::UnorderedElementsAreArray(expected_transports));
  80. EXPECT_EQ(result.has_platform_authenticator_credential,
  81. has_platform_authenticator_credential);
  82. }
  83. protected:
  84. // FidoRequestHandlerBase::Observer:
  85. void OnTransportAvailabilityEnumerated(
  86. FidoRequestHandlerBase::TransportAvailabilityInfo data) override {
  87. transport_availability_notification_receiver_.callback().Run(
  88. std::move(data));
  89. }
  90. bool EmbedderControlsAuthenticatorDispatch(
  91. const FidoAuthenticator&) override {
  92. return false;
  93. }
  94. void BluetoothAdapterPowerChanged(bool is_powered_on) override {}
  95. void FidoAuthenticatorAdded(const FidoAuthenticator& authenticator) override {
  96. }
  97. void FidoAuthenticatorRemoved(base::StringPiece device_id) override {}
  98. bool SupportsPIN() const override { return false; }
  99. void CollectPIN(
  100. CollectPINOptions options,
  101. base::OnceCallback<void(std::u16string)> provide_pin_cb) override {
  102. NOTREACHED();
  103. }
  104. void OnRetryUserVerification(int attempts) override {}
  105. void StartBioEnrollment(base::OnceClosure next_callback) override {}
  106. void OnSampleCollected(int remaining_samples) override {}
  107. void FinishCollectToken() override { NOTREACHED(); }
  108. private:
  109. TransportAvailabilityNotificationReceiver
  110. transport_availability_notification_receiver_;
  111. };
  112. // Fake FidoTask implementation that sends an empty byte array to the device
  113. // when StartTask() is invoked.
  114. class FakeFidoTask : public FidoTask {
  115. public:
  116. FakeFidoTask(FidoDevice* device, FakeTaskCallback callback)
  117. : FidoTask(device), callback_(std::move(callback)) {}
  118. ~FakeFidoTask() override = default;
  119. void Cancel() override {
  120. if (token_) {
  121. device()->Cancel(*token_);
  122. token_.reset();
  123. }
  124. }
  125. void StartTask() override {
  126. token_ = device()->DeviceTransact(
  127. std::vector<uint8_t>(),
  128. base::BindOnce(&FakeFidoTask::CompletionCallback,
  129. weak_factory_.GetWeakPtr()));
  130. }
  131. void CompletionCallback(
  132. absl::optional<std::vector<uint8_t>> device_response) {
  133. DCHECK(device_response && device_response->size() == 1);
  134. switch (static_cast<FakeTaskResponse>(device_response->front())) {
  135. case FakeTaskResponse::kSuccess:
  136. std::move(callback_).Run(CtapDeviceResponseCode::kSuccess,
  137. std::vector<uint8_t>());
  138. return;
  139. case FakeTaskResponse::kErrorReceivedAfterObtainingUserPresence:
  140. std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
  141. std::vector<uint8_t>());
  142. return;
  143. case FakeTaskResponse::kOperationDenied:
  144. std::move(callback_).Run(
  145. CtapDeviceResponseCode::kCtap2ErrOperationDenied, absl::nullopt);
  146. return;
  147. case FakeTaskResponse::kProcessingError:
  148. default:
  149. std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
  150. absl::nullopt);
  151. return;
  152. }
  153. }
  154. private:
  155. absl::optional<FidoDevice::CancelToken> token_;
  156. FakeTaskCallback callback_;
  157. base::WeakPtrFactory<FakeFidoTask> weak_factory_{this};
  158. };
  159. class FakeFidoRequestHandler : public FidoRequestHandlerBase {
  160. public:
  161. using CompletionCallback =
  162. base::OnceCallback<void(bool,
  163. absl::optional<std::vector<uint8_t>>,
  164. const FidoAuthenticator*)>;
  165. FakeFidoRequestHandler(test::FakeFidoDiscoveryFactory* fake_discovery_factory,
  166. const base::flat_set<FidoTransportProtocol>& protocols,
  167. CompletionCallback callback)
  168. : FidoRequestHandlerBase(fake_discovery_factory, protocols),
  169. completion_callback_(std::move(callback)) {
  170. Start();
  171. }
  172. ~FakeFidoRequestHandler() override = default;
  173. void set_has_platform_credential(
  174. RecognizedCredential has_platform_credential) {
  175. has_platform_credential_ = has_platform_credential;
  176. }
  177. private:
  178. void DispatchRequest(FidoAuthenticator* authenticator) override {
  179. // FidoRequestHandlerTest uses FakeDiscovery to inject mock devices
  180. // that get wrapped in a FidoDeviceAuthenticator, so we can safely cast
  181. // here.
  182. auto* device_authenticator =
  183. static_cast<FidoDeviceAuthenticator*>(authenticator);
  184. // Instead of sending a real CTAP request, send an empty byte array. Note
  185. // that during discovery, the device already has received a GetInfo command
  186. // at this point.
  187. device_authenticator->SetTaskForTesting(std::make_unique<FakeFidoTask>(
  188. device_authenticator->device(),
  189. base::BindOnce(&FakeFidoRequestHandler::HandleResponse,
  190. weak_factory_.GetWeakPtr(), authenticator)));
  191. }
  192. void AuthenticatorAdded(FidoDiscoveryBase* discovery,
  193. FidoAuthenticator* authenticator) override {
  194. if (authenticator->AuthenticatorTransport() ==
  195. FidoTransportProtocol::kInternal) {
  196. transport_availability_info().has_platform_authenticator_credential =
  197. has_platform_credential_;
  198. }
  199. FidoRequestHandlerBase::AuthenticatorAdded(discovery, authenticator);
  200. }
  201. void HandleResponse(FidoAuthenticator* authenticator,
  202. CtapDeviceResponseCode status,
  203. absl::optional<std::vector<uint8_t>> response) {
  204. auto* device_authenticator =
  205. static_cast<FidoDeviceAuthenticator*>(authenticator);
  206. device_authenticator->SetTaskForTesting(nullptr);
  207. if (status == CtapDeviceResponseCode::kCtap2ErrOther) {
  208. // Simulates an error that is sent without the user touching the
  209. // authenticator (FakeTaskResponse::kProcessingError). Don't resolve
  210. // the request for this response.
  211. return;
  212. }
  213. if (!completion_callback_) {
  214. return;
  215. }
  216. CancelActiveAuthenticators(authenticator->GetId());
  217. std::move(completion_callback_)
  218. .Run(status == CtapDeviceResponseCode::kSuccess, std::move(response),
  219. authenticator);
  220. }
  221. CompletionCallback completion_callback_;
  222. RecognizedCredential has_platform_credential_ =
  223. RecognizedCredential::kNoRecognizedCredential;
  224. base::WeakPtrFactory<FakeFidoRequestHandler> weak_factory_{this};
  225. };
  226. std::vector<uint8_t> CreateFakeSuccessDeviceResponse() {
  227. return {base::strict_cast<uint8_t>(FakeTaskResponse::kSuccess)};
  228. }
  229. std::vector<uint8_t> CreateFakeUserPresenceVerifiedError() {
  230. return {base::strict_cast<uint8_t>(
  231. FakeTaskResponse::kErrorReceivedAfterObtainingUserPresence)};
  232. }
  233. std::vector<uint8_t> CreateFakeDeviceProcesssingError() {
  234. return {base::strict_cast<uint8_t>(FakeTaskResponse::kProcessingError)};
  235. }
  236. std::vector<uint8_t> CreateFakeOperationDeniedError() {
  237. return {base::strict_cast<uint8_t>(FakeTaskResponse::kOperationDenied)};
  238. }
  239. } // namespace
  240. class FidoRequestHandlerTest : public ::testing::Test {
  241. public:
  242. FidoRequestHandlerTest() {
  243. mock_adapter_ =
  244. base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
  245. BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
  246. }
  247. void ForgeNextHidDiscovery() {
  248. discovery_ = fake_discovery_factory_.ForgeNextHidDiscovery();
  249. }
  250. std::unique_ptr<FakeFidoRequestHandler> CreateFakeHandler() {
  251. ForgeNextHidDiscovery();
  252. auto handler = std::make_unique<FakeFidoRequestHandler>(
  253. &fake_discovery_factory_,
  254. base::flat_set<FidoTransportProtocol>(
  255. {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
  256. cb_.callback());
  257. return handler;
  258. }
  259. test::FakeFidoDiscovery* discovery() const { return discovery_; }
  260. scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> adapter() {
  261. return mock_adapter_;
  262. }
  263. FakeHandlerCallbackReceiver& callback() { return cb_; }
  264. protected:
  265. base::test::TaskEnvironment task_environment_{
  266. base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  267. test::FakeFidoDiscoveryFactory fake_discovery_factory_;
  268. scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;
  269. raw_ptr<test::FakeFidoDiscovery> discovery_;
  270. FakeHandlerCallbackReceiver cb_;
  271. };
  272. TEST_F(FidoRequestHandlerTest, TestSingleDeviceSuccess) {
  273. auto request_handler = CreateFakeHandler();
  274. discovery()->WaitForCallToStartAndSimulateSuccess();
  275. auto device = std::make_unique<MockFidoDevice>();
  276. device->ExpectCtap2CommandAndRespondWith(
  277. CtapRequestCommand::kAuthenticatorGetInfo, absl::nullopt);
  278. EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
  279. // Device returns success response.
  280. device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  281. CreateFakeSuccessDeviceResponse());
  282. discovery()->AddDevice(std::move(device));
  283. callback().WaitForCallback();
  284. EXPECT_TRUE(callback().status());
  285. }
  286. // Tests a scenario where two unresponsive authenticators are connected and
  287. // cancel request has been sent either from the user or from the relying party
  288. // (i.e. FidoRequestHandler object is destroyed.) Upon destruction, cancel
  289. // command must be invoked to all connected authenticators.
  290. TEST_F(FidoRequestHandlerTest, TestAuthenticatorHandlerReset) {
  291. auto request_handler = CreateFakeHandler();
  292. discovery()->WaitForCallToStartAndSimulateSuccess();
  293. auto device0 = std::make_unique<MockFidoDevice>();
  294. device0->ExpectCtap2CommandAndRespondWith(
  295. CtapRequestCommand::kAuthenticatorGetInfo,
  296. test_data::kTestAuthenticatorGetInfoResponse);
  297. EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
  298. device0->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
  299. EXPECT_CALL(*device0, Cancel(_));
  300. auto device1 = std::make_unique<MockFidoDevice>();
  301. device1->ExpectCtap2CommandAndRespondWith(
  302. CtapRequestCommand::kAuthenticatorGetInfo,
  303. test_data::kTestAuthenticatorGetInfoResponse);
  304. EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
  305. device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
  306. EXPECT_CALL(*device1, Cancel(_));
  307. discovery()->AddDevice(std::move(device0));
  308. discovery()->AddDevice(std::move(device1));
  309. task_environment_.FastForwardUntilNoTasksRemain();
  310. request_handler.reset();
  311. }
  312. // Test a scenario where 2 devices are connected and a response is received
  313. // from only a single device(device1) and the remaining device hangs.
  314. TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleDevices) {
  315. auto request_handler = CreateFakeHandler();
  316. discovery()->WaitForCallToStartAndSimulateSuccess();
  317. // Represents a connected device that hangs without a response.
  318. auto device0 = std::make_unique<MockFidoDevice>();
  319. device0->ExpectCtap2CommandAndRespondWith(
  320. CtapRequestCommand::kAuthenticatorGetInfo,
  321. test_data::kTestAuthenticatorGetInfoResponse);
  322. EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
  323. // Device is unresponsive and cancel command is invoked afterwards.
  324. device0->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
  325. EXPECT_CALL(*device0, Cancel(_));
  326. // Represents a connected device that response successfully.
  327. auto device1 = std::make_unique<MockFidoDevice>();
  328. device1->ExpectCtap2CommandAndRespondWith(
  329. CtapRequestCommand::kAuthenticatorGetInfo,
  330. test_data::kTestAuthenticatorGetInfoResponse);
  331. EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
  332. device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  333. CreateFakeSuccessDeviceResponse());
  334. discovery()->AddDevice(std::move(device0));
  335. discovery()->AddDevice(std::move(device1));
  336. callback().WaitForCallback();
  337. EXPECT_TRUE(callback().status());
  338. }
  339. // Test a scenario where 2 devices respond successfully with small time
  340. // delay. Only the first received response should be passed on to the relying
  341. // party, and cancel request should be sent to the other authenticator.
  342. TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleSuccessResponses) {
  343. auto request_handler = CreateFakeHandler();
  344. discovery()->WaitForCallToStartAndSimulateSuccess();
  345. // Represents a connected device that responds successfully after small time
  346. // delay.
  347. auto device0 = std::make_unique<MockFidoDevice>();
  348. device0->ExpectCtap2CommandAndRespondWith(
  349. CtapRequestCommand::kAuthenticatorGetInfo,
  350. test_data::kTestAuthenticatorGetInfoResponse);
  351. EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
  352. device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  353. CreateFakeSuccessDeviceResponse(),
  354. base::Microseconds(1));
  355. // Represents a device that returns a success response after a longer time
  356. // delay.
  357. auto device1 = std::make_unique<MockFidoDevice>();
  358. device1->ExpectCtap2CommandAndRespondWith(
  359. CtapRequestCommand::kAuthenticatorGetInfo,
  360. test_data::kTestAuthenticatorGetInfoResponse);
  361. EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
  362. device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  363. CreateFakeSuccessDeviceResponse(),
  364. base::Microseconds(10));
  365. // Cancel command is invoked after receiving response from |device0|.
  366. EXPECT_CALL(*device1, Cancel(_));
  367. discovery()->AddDevice(std::move(device0));
  368. discovery()->AddDevice(std::move(device1));
  369. task_environment_.FastForwardUntilNoTasksRemain();
  370. callback().WaitForCallback();
  371. EXPECT_TRUE(callback().status());
  372. }
  373. // Test a scenario where 3 devices respond with a processing error, an UP(user
  374. // presence) verified failure response with small time delay, and an UP
  375. // verified failure response with big time delay, respectively. Request for
  376. // device with processing error should be immediately dropped. Also, for UP
  377. // verified failures, the first received response should be passed on to the
  378. // relying party and cancel command should be sent to the remaining device.
  379. TEST_F(FidoRequestHandlerTest, TestRequestWithMultipleFailureResponses) {
  380. auto request_handler = CreateFakeHandler();
  381. discovery()->WaitForCallToStartAndSimulateSuccess();
  382. // Represents a connected device that immediately responds with a processing
  383. // error.
  384. auto device0 = std::make_unique<MockFidoDevice>();
  385. device0->ExpectCtap2CommandAndRespondWith(
  386. CtapRequestCommand::kAuthenticatorGetInfo,
  387. test_data::kTestAuthenticatorGetInfoResponse);
  388. EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
  389. EXPECT_CALL(*device0, GetDisplayName())
  390. .WillRepeatedly(testing::Return(std::string()));
  391. device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  392. CreateFakeDeviceProcesssingError());
  393. // Represents a device that returns an UP verified failure response after a
  394. // small time delay.
  395. auto device1 = std::make_unique<MockFidoDevice>();
  396. device1->ExpectCtap2CommandAndRespondWith(
  397. CtapRequestCommand::kAuthenticatorGetInfo,
  398. test_data::kTestAuthenticatorGetInfoResponse);
  399. EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
  400. EXPECT_CALL(*device1, GetDisplayName())
  401. .WillRepeatedly(testing::Return(std::string()));
  402. device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  403. CreateFakeUserPresenceVerifiedError(),
  404. base::Microseconds(1));
  405. // Represents a device that returns an UP verified failure response after a
  406. // big time delay.
  407. auto device2 = std::make_unique<MockFidoDevice>();
  408. device2->ExpectCtap2CommandAndRespondWith(
  409. CtapRequestCommand::kAuthenticatorGetInfo,
  410. test_data::kTestAuthenticatorGetInfoResponse);
  411. EXPECT_CALL(*device2, GetId()).WillRepeatedly(testing::Return("device2"));
  412. EXPECT_CALL(*device2, GetDisplayName())
  413. .WillRepeatedly(testing::Return(std::string()));
  414. device2->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  415. CreateFakeDeviceProcesssingError(),
  416. base::Microseconds(10));
  417. EXPECT_CALL(*device2, Cancel(_));
  418. discovery()->AddDevice(std::move(device0));
  419. discovery()->AddDevice(std::move(device1));
  420. discovery()->AddDevice(std::move(device2));
  421. task_environment_.FastForwardUntilNoTasksRemain();
  422. callback().WaitForCallback();
  423. EXPECT_FALSE(callback().status());
  424. }
  425. // If a device with transport type kInternal returns a
  426. // CTAP2_ERR_OPERATION_DENIED error, the request should be cancelled on all
  427. // pending authenticators.
  428. TEST_F(FidoRequestHandlerTest,
  429. TestRequestWithOperationDeniedErrorInternalTransport) {
  430. TestObserver observer;
  431. // Device will send CTAP2_ERR_OPERATION_DENIED.
  432. auto device0 = MockFidoDevice::MakeCtapWithGetInfoExpectation(
  433. test_data::kTestGetInfoResponsePlatformDevice);
  434. device0->SetDeviceTransport(FidoTransportProtocol::kInternal);
  435. device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  436. CreateFakeOperationDeniedError(),
  437. base::Microseconds(10));
  438. ForgeNextHidDiscovery();
  439. auto* platform_discovery =
  440. fake_discovery_factory_.ForgeNextPlatformDiscovery();
  441. auto request_handler = std::make_unique<FakeFidoRequestHandler>(
  442. &fake_discovery_factory_,
  443. base::flat_set<FidoTransportProtocol>(
  444. {FidoTransportProtocol::kInternal,
  445. FidoTransportProtocol::kUsbHumanInterfaceDevice}),
  446. callback().callback());
  447. request_handler->set_observer(&observer);
  448. auto device1 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
  449. device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
  450. EXPECT_CALL(*device1, Cancel(_));
  451. discovery()->AddDevice(std::move(device0));
  452. platform_discovery->AddDevice(std::move(device1));
  453. discovery()->WaitForCallToStartAndSimulateSuccess();
  454. platform_discovery->WaitForCallToStartAndSimulateSuccess();
  455. task_environment_.FastForwardUntilNoTasksRemain();
  456. callback().WaitForCallback();
  457. EXPECT_FALSE(callback().status());
  458. }
  459. // Like |TestRequestWithOperationDeniedErrorInternalTransport|, but with a
  460. // cross-platform authenticator.
  461. TEST_F(FidoRequestHandlerTest,
  462. TestRequestWithOperationDeniedErrorCrossPlatform) {
  463. auto request_handler = CreateFakeHandler();
  464. discovery()->WaitForCallToStartAndSimulateSuccess();
  465. // Device will send CTAP2_ERR_OPERATION_DENIED.
  466. auto device0 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
  467. device0->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
  468. device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  469. CreateFakeOperationDeniedError());
  470. auto device1 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
  471. device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
  472. EXPECT_CALL(*device1, Cancel(_));
  473. discovery()->AddDevice(std::move(device0));
  474. discovery()->AddDevice(std::move(device1));
  475. task_environment_.FastForwardUntilNoTasksRemain();
  476. callback().WaitForCallback();
  477. EXPECT_FALSE(callback().status());
  478. }
  479. // Requests should be dispatched to the platform authenticator.
  480. TEST_F(FidoRequestHandlerTest, TestWithPlatformAuthenticator) {
  481. // A platform authenticator usually wouldn't usually use a FidoDevice, but
  482. // that's not the point of the test here. The test is only trying to ensure
  483. // the authenticator gets injected and used.
  484. auto device = MockFidoDevice::MakeCtap();
  485. EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
  486. device->ExpectCtap2CommandAndRespondWith(
  487. CtapRequestCommand::kAuthenticatorGetInfo,
  488. test_data::kTestGetInfoResponsePlatformDevice);
  489. // Device returns success response.
  490. device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
  491. CreateFakeSuccessDeviceResponse());
  492. device->SetDeviceTransport(FidoTransportProtocol::kInternal);
  493. auto* fake_discovery = fake_discovery_factory_.ForgeNextPlatformDiscovery(
  494. test::FakeFidoDiscovery::StartMode::kAutomatic);
  495. TestObserver observer;
  496. auto request_handler = std::make_unique<FakeFidoRequestHandler>(
  497. &fake_discovery_factory_,
  498. base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
  499. callback().callback());
  500. request_handler->set_has_platform_credential(
  501. FidoRequestHandlerBase::RecognizedCredential::kHasRecognizedCredential);
  502. request_handler->set_observer(&observer);
  503. fake_discovery->AddDevice(std::move(device));
  504. observer.WaitForAndExpectAvailableTransportsAre(
  505. {FidoTransportProtocol::kInternal},
  506. FidoRequestHandlerBase::RecognizedCredential::kHasRecognizedCredential);
  507. callback().WaitForCallback();
  508. EXPECT_TRUE(callback().status());
  509. }
  510. TEST_F(FidoRequestHandlerTest, InternalTransportDisallowedIfMarkedUnavailable) {
  511. TestObserver observer;
  512. auto request_handler = std::make_unique<FakeFidoRequestHandler>(
  513. &fake_discovery_factory_,
  514. base::flat_set<FidoTransportProtocol>({FidoTransportProtocol::kInternal}),
  515. callback().callback());
  516. request_handler->set_observer(&observer);
  517. observer.WaitForAndExpectAvailableTransportsAre({});
  518. }
  519. TEST_F(FidoRequestHandlerTest,
  520. TransportAvailabilityNotificationOnObserverSetLate) {
  521. TestObserver observer;
  522. auto request_handler = CreateFakeHandler();
  523. discovery()->WaitForCallToStartAndSimulateSuccess();
  524. task_environment_.FastForwardUntilNoTasksRemain();
  525. request_handler->set_observer(&observer);
  526. observer.WaitForAndExpectAvailableTransportsAre(
  527. {FidoTransportProtocol::kUsbHumanInterfaceDevice});
  528. }
  529. #if BUILDFLAG(IS_WIN)
  530. TEST_F(FidoRequestHandlerTest, TransportAvailabilityOfWindowsAuthenticator) {
  531. FakeWinWebAuthnApi api;
  532. fake_discovery_factory_.set_win_webauthn_api(&api);
  533. for (const bool api_available : {false, true}) {
  534. SCOPED_TRACE(::testing::Message() << "api_available=" << api_available);
  535. api.set_available(api_available);
  536. TestObserver observer;
  537. ForgeNextHidDiscovery();
  538. EmptyRequestHandler request_handler(
  539. {FidoTransportProtocol::kUsbHumanInterfaceDevice},
  540. &fake_discovery_factory_);
  541. request_handler.set_observer(&observer);
  542. // If the windows API is not enabled, the request is dispatched to the USB
  543. // discovery. Simulate a success to fill the transport availability info.
  544. if (!api_available)
  545. discovery()->WaitForCallToStartAndSimulateSuccess();
  546. task_environment_.FastForwardUntilNoTasksRemain();
  547. auto transport_availability_info =
  548. observer.WaitForTransportAvailabilityInfo();
  549. EXPECT_EQ(transport_availability_info.available_transports.empty(),
  550. api_available);
  551. EXPECT_EQ(transport_availability_info.has_win_native_api_authenticator,
  552. api_available);
  553. EXPECT_EQ(transport_availability_info.win_native_api_authenticator_id,
  554. api_available ? "WinWebAuthnApiAuthenticator" : "");
  555. }
  556. }
  557. #endif // BUILDFLAG(IS_WIN)
  558. } // namespace device