fido_hid_discovery_unittest.cc 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // Copyright 2017 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 "device/fido/hid/fido_hid_discovery.h"
  5. #include <algorithm>
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. #include "base/test/gmock_callback_support.h"
  10. #include "base/test/task_environment.h"
  11. #include "device/fido/fido_authenticator.h"
  12. #include "device/fido/hid/fake_hid_impl_for_testing.h"
  13. #include "device/fido/hid/fido_hid_device.h"
  14. #include "device/fido/mock_fido_discovery_observer.h"
  15. #include "services/device/public/mojom/hid.mojom.h"
  16. #include "testing/gmock/include/gmock/gmock.h"
  17. #include "testing/gtest/include/gtest/gtest.h"
  18. namespace device {
  19. using ::base::test::RunClosure;
  20. using ::testing::ElementsAre;
  21. namespace {
  22. device::mojom::HidDeviceInfoPtr MakeOtherDevice(std::string guid) {
  23. auto other_device = device::mojom::HidDeviceInfo::New();
  24. other_device->guid = std::move(guid);
  25. other_device->product_name = "Other Device";
  26. other_device->serial_number = "OtherDevice";
  27. other_device->bus_type = device::mojom::HidBusType::kHIDBusTypeUSB;
  28. return other_device;
  29. }
  30. device::mojom::HidDeviceInfoPtr MakeDeviceWithOneCollection(
  31. const std::string& guid,
  32. uint16_t usage_page) {
  33. auto collection = device::mojom::HidCollectionInfo::New();
  34. collection->usage = device::mojom::HidUsageAndPage::New(1, usage_page);
  35. auto device = device::mojom::HidDeviceInfo::New();
  36. device->guid = guid;
  37. device->physical_device_id = "physical-device-id";
  38. device->vendor_id = 0x1234;
  39. device->product_id = 0xabcd;
  40. device->product_name = "product-name";
  41. device->serial_number = "serial-number";
  42. device->bus_type = device::mojom::HidBusType::kHIDBusTypeUSB;
  43. device->collections.push_back(std::move(collection));
  44. device->has_report_id = true;
  45. device->max_input_report_size = 64;
  46. device->max_output_report_size = 64;
  47. device->max_feature_report_size = 64;
  48. device->device_node = "device-node";
  49. return device;
  50. }
  51. device::mojom::HidDeviceInfoPtr MakeDeviceWithTwoCollections(
  52. const std::string& guid,
  53. uint16_t usage_page1,
  54. uint16_t usage_page2) {
  55. // Start with the single-collection device info.
  56. auto device = MakeDeviceWithOneCollection(guid, usage_page1);
  57. // Add a second collection with |usage_page2|.
  58. auto collection = device::mojom::HidCollectionInfo::New();
  59. collection->usage = device::mojom::HidUsageAndPage::New(1, usage_page2);
  60. device->collections.push_back(std::move(collection));
  61. return device;
  62. }
  63. MATCHER_P(IdMatches, id, "") {
  64. return arg->GetId() == std::string("hid:") + id;
  65. }
  66. } // namespace
  67. class FidoHidDiscoveryTest : public ::testing::Test {
  68. protected:
  69. base::test::TaskEnvironment task_environment_;
  70. ScopedFakeFidoHidManager fake_hid_manager_;
  71. };
  72. TEST_F(FidoHidDiscoveryTest, TestAddRemoveDevice) {
  73. FidoHidDiscovery discovery;
  74. MockFidoDiscoveryObserver observer;
  75. fake_hid_manager_.AddFidoHidDevice("known");
  76. // Devices initially known to the service before discovery started should be
  77. // reported as KNOWN.
  78. EXPECT_CALL(observer, DiscoveryStarted(&discovery, true,
  79. ElementsAre(IdMatches("known"))));
  80. discovery.set_observer(&observer);
  81. discovery.Start();
  82. task_environment_.RunUntilIdle();
  83. // Devices added during the discovery should be reported as ADDED.
  84. EXPECT_CALL(observer, AuthenticatorAdded(&discovery, IdMatches("added")));
  85. fake_hid_manager_.AddFidoHidDevice("added");
  86. task_environment_.RunUntilIdle();
  87. // Added non-U2F devices should not be reported at all.
  88. EXPECT_CALL(observer, AuthenticatorAdded).Times(0);
  89. fake_hid_manager_.AddDevice(MakeOtherDevice("other"));
  90. // Removed non-U2F devices should not be reported at all.
  91. EXPECT_CALL(observer, AuthenticatorRemoved).Times(0);
  92. fake_hid_manager_.RemoveDevice("other");
  93. task_environment_.RunUntilIdle();
  94. // Removed U2F devices should be reported as REMOVED.
  95. EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, IdMatches("known")));
  96. EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, IdMatches("added")));
  97. fake_hid_manager_.RemoveDevice("known");
  98. fake_hid_manager_.RemoveDevice("added");
  99. task_environment_.RunUntilIdle();
  100. }
  101. TEST_F(FidoHidDiscoveryTest, AlreadyConnectedNonFidoDeviceBecomesFido) {
  102. constexpr char kTestGuid[] = "guid";
  103. FidoHidDiscovery discovery;
  104. MockFidoDiscoveryObserver observer;
  105. // Add a partially-initialized device. To start, the device only has a single
  106. // top-level collection with a vendor-defined usage and should not be
  107. // recognized as a FIDO device.
  108. fake_hid_manager_.AddDevice(
  109. MakeDeviceWithOneCollection(kTestGuid, device::mojom::kPageVendor));
  110. // Start discovery. No U2F devices should be discovered.
  111. base::RunLoop discovery_started_loop;
  112. EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, ElementsAre()))
  113. .WillOnce(RunClosure(discovery_started_loop.QuitClosure()));
  114. discovery.set_observer(&observer);
  115. discovery.Start();
  116. discovery_started_loop.Run();
  117. // Update the device with a second top-level collection with FIDO usage. It
  118. // should be recognized as a U2F device and dispatch AuthenticatorAdded.
  119. base::RunLoop authenticator_added_loop;
  120. EXPECT_CALL(observer, AuthenticatorAdded(&discovery, IdMatches(kTestGuid)))
  121. .WillOnce(RunClosure(authenticator_added_loop.QuitClosure()));
  122. fake_hid_manager_.ChangeDevice(MakeDeviceWithTwoCollections(
  123. kTestGuid, device::mojom::kPageVendor, device::mojom::kPageFido));
  124. authenticator_added_loop.Run();
  125. // Remove the device.
  126. base::RunLoop authenticator_removed_loop;
  127. EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, IdMatches(kTestGuid)))
  128. .WillOnce(RunClosure(authenticator_removed_loop.QuitClosure()));
  129. fake_hid_manager_.RemoveDevice(kTestGuid);
  130. authenticator_removed_loop.Run();
  131. }
  132. TEST_F(FidoHidDiscoveryTest, NewlyAddedNonFidoDeviceBecomesFido) {
  133. constexpr char kTestGuid[] = "guid";
  134. FidoHidDiscovery discovery;
  135. MockFidoDiscoveryObserver observer;
  136. // Start discovery. No U2F devices should be discovered.
  137. base::RunLoop discovery_started_loop;
  138. EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, ElementsAre()))
  139. .WillOnce(RunClosure(discovery_started_loop.QuitClosure()));
  140. discovery.set_observer(&observer);
  141. discovery.Start();
  142. discovery_started_loop.Run();
  143. // Add a partially-initialized device. To start, the device only has a single
  144. // top-level collection with a vendor-defined usage and should not be
  145. // recognized as a FIDO device.
  146. EXPECT_CALL(observer, AuthenticatorAdded).Times(0);
  147. fake_hid_manager_.AddDevice(
  148. MakeDeviceWithOneCollection(kTestGuid, device::mojom::kPageVendor));
  149. task_environment_.RunUntilIdle();
  150. // Update the device with a second top-level collection with FIDO usage. It
  151. // should be recognized as a U2F device and dispatch AuthenticatorAdded.
  152. base::RunLoop authenticator_added_loop;
  153. EXPECT_CALL(observer, AuthenticatorAdded(&discovery, IdMatches(kTestGuid)))
  154. .WillOnce(RunClosure(authenticator_added_loop.QuitClosure()));
  155. fake_hid_manager_.ChangeDevice(MakeDeviceWithTwoCollections(
  156. kTestGuid, device::mojom::kPageVendor, device::mojom::kPageFido));
  157. authenticator_added_loop.Run();
  158. // Remove the device.
  159. base::RunLoop authenticator_removed_loop;
  160. EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, IdMatches(kTestGuid)))
  161. .WillOnce(RunClosure(authenticator_removed_loop.QuitClosure()));
  162. fake_hid_manager_.RemoveDevice(kTestGuid);
  163. authenticator_removed_loop.Run();
  164. }
  165. TEST_F(FidoHidDiscoveryTest, NewlyAddedFidoDeviceChanged) {
  166. constexpr char kTestGuid[] = "guid";
  167. FidoHidDiscovery discovery;
  168. MockFidoDiscoveryObserver observer;
  169. // Start discovery. No U2F devices should be discovered.
  170. base::RunLoop discovery_started_loop;
  171. EXPECT_CALL(observer, DiscoveryStarted(&discovery, true, ElementsAre()))
  172. .WillOnce(RunClosure(discovery_started_loop.QuitClosure()));
  173. discovery.set_observer(&observer);
  174. discovery.Start();
  175. discovery_started_loop.Run();
  176. // Add a partially-initialized device. To start, the device only has a single
  177. // top-level collection with a FIDO usage and should be recognized as a U2F
  178. // device.
  179. base::RunLoop authenticator_added_loop;
  180. EXPECT_CALL(observer, AuthenticatorAdded(&discovery, IdMatches(kTestGuid)))
  181. .WillOnce(RunClosure(authenticator_added_loop.QuitClosure()));
  182. fake_hid_manager_.AddDevice(
  183. MakeDeviceWithOneCollection(kTestGuid, device::mojom::kPageFido));
  184. authenticator_added_loop.Run();
  185. // Update the device with a second top-level collection with vendor-defined
  186. // usage. The update should be ignored since the device was already added.
  187. EXPECT_CALL(observer, AuthenticatorAdded).Times(0);
  188. fake_hid_manager_.ChangeDevice(MakeDeviceWithTwoCollections(
  189. kTestGuid, device::mojom::kPageFido, device::mojom::kPageVendor));
  190. task_environment_.RunUntilIdle();
  191. // Remove the device.
  192. base::RunLoop authenticator_removed_loop;
  193. EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, IdMatches(kTestGuid)))
  194. .WillOnce(RunClosure(authenticator_removed_loop.QuitClosure()));
  195. fake_hid_manager_.RemoveDevice(kTestGuid);
  196. authenticator_removed_loop.Run();
  197. }
  198. } // namespace device