fido_hid_device_unittest.cc 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  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_device.h"
  5. #include <array>
  6. #include <memory>
  7. #include <tuple>
  8. #include "base/bind.h"
  9. #include "base/containers/span.h"
  10. #include "base/memory/ptr_util.h"
  11. #include "base/memory/raw_ptr.h"
  12. #include "base/run_loop.h"
  13. #include "base/strings/string_number_conversions.h"
  14. #include "base/test/task_environment.h"
  15. #include "base/threading/thread_task_runner_handle.h"
  16. #include "device/fido/fido_constants.h"
  17. #include "device/fido/fido_parsing_utils.h"
  18. #include "device/fido/fido_test_data.h"
  19. #include "device/fido/hid/fake_hid_impl_for_testing.h"
  20. #include "device/fido/hid/fido_hid_message.h"
  21. #include "device/fido/test_callback_receiver.h"
  22. #include "mojo/public/cpp/bindings/remote.h"
  23. #include "services/device/public/cpp/hid/hid_device_filter.h"
  24. #include "services/device/public/mojom/hid.mojom.h"
  25. #include "testing/gmock/include/gmock/gmock.h"
  26. #include "testing/gtest/include/gtest/gtest.h"
  27. namespace device {
  28. using ::testing::_;
  29. using ::testing::Invoke;
  30. namespace {
  31. // HID_MSG(83), followed by payload length(000b), followed by response data
  32. // "MOCK_DATA", followed by APDU SW_NO_ERROR response code(9000).
  33. constexpr uint8_t kU2fMockResponseMessage[] = {
  34. 0x83, 0x00, 0x0b, 0x4d, 0x4f, 0x43, 0x4b,
  35. 0x5f, 0x44, 0x41, 0x54, 0x41, 0x90, 0x00,
  36. };
  37. // HID_WINK(0x08), followed by payload length(0).
  38. constexpr uint8_t kU2fWinkResponseMessage[] = {0x08, 0x00};
  39. // APDU encoded success response with data "MOCK_DATA" followed by a SW_NO_ERROR
  40. // APDU response code(9000).
  41. constexpr uint8_t kU2fMockResponseData[] = {0x4d, 0x4f, 0x43, 0x4b, 0x5f, 0x44,
  42. 0x41, 0x54, 0x41, 0x90, 0x00};
  43. // HID_ERROR(BF), followed by payload length(0001), followed by
  44. // kInvalidCommand(01).
  45. constexpr uint8_t kHidUnknownCommandError[] = {0xBF, 0x00, 0x01, 0x01};
  46. // HID_KEEP_ALIVE(bb), followed by payload length(0001), followed by
  47. // status processing(01) byte.
  48. constexpr uint8_t kMockKeepAliveResponseSuffix[] = {0xbb, 0x00, 0x01, 0x01};
  49. // 4 byte broadcast channel id(ffffffff), followed by an HID_INIT command(86),
  50. // followed by a fixed size payload length(11). 8 byte nonce and 4 byte channel
  51. // ID must be appended to create a well formed HID_INIT packet.
  52. constexpr uint8_t kInitResponsePrefix[] = {
  53. 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11,
  54. };
  55. // Mock APDU encoded U2F request with empty data and mock P1 parameter(0x04).
  56. constexpr uint8_t kMockU2fRequest[] = {0x00, 0x04, 0x00, 0x00,
  57. 0x00, 0x00, 0x00};
  58. constexpr uint8_t kMockCancelResponse[] = {
  59. // clang-format off
  60. 0x90, // CTAPHID_CBOR
  61. 0, 1, // one byte payload
  62. 0x2d, // CTAP2_ERR_KEEPALIVE_CANCEL
  63. // clang-format on
  64. };
  65. constexpr uint8_t kMockU2fChannelBusyResponse[] = {
  66. // clang-format off
  67. 0xBF, // CTAPHID_ERROR
  68. 0, 1, // one byte payload
  69. 0x06, // CTAP1_ERR_CHANNEL_BUSY
  70. // clang-format on
  71. };
  72. constexpr std::array<uint8_t, 4> kChannelId = {0x01, 0x02, 0x03, 0x04};
  73. // Returns HID_INIT request to send to device with mock connection.
  74. std::vector<uint8_t> CreateMockInitResponse(
  75. base::span<const uint8_t> nonce,
  76. base::span<const uint8_t> channel_id,
  77. base::span<const uint8_t> payload = base::span<const uint8_t>()) {
  78. auto init_response = fido_parsing_utils::Materialize(kInitResponsePrefix);
  79. fido_parsing_utils::Append(&init_response, nonce);
  80. fido_parsing_utils::Append(&init_response, channel_id);
  81. fido_parsing_utils::Append(&init_response, payload);
  82. init_response.resize(64);
  83. return init_response;
  84. }
  85. // Returns HID keep alive message encoded into HID packet format.
  86. std::vector<uint8_t> GetKeepAliveHidMessage(
  87. base::span<const uint8_t> channel_id) {
  88. auto response = fido_parsing_utils::Materialize(channel_id);
  89. fido_parsing_utils::Append(&response, kMockKeepAliveResponseSuffix);
  90. response.resize(64);
  91. return response;
  92. }
  93. // Returns "U2F_v2" as a mock response to version request with given channel id.
  94. std::vector<uint8_t> CreateMockResponseWithChannelId(
  95. base::span<const uint8_t> channel_id,
  96. base::span<const uint8_t> response_buffer) {
  97. auto response = fido_parsing_utils::Materialize(channel_id);
  98. fido_parsing_utils::Append(&response, response_buffer);
  99. response.resize(64);
  100. return response;
  101. }
  102. // Returns a APDU encoded U2F version request for testing.
  103. std::vector<uint8_t> GetMockDeviceRequest() {
  104. return fido_parsing_utils::Materialize(kMockU2fRequest);
  105. }
  106. device::mojom::HidDeviceInfoPtr TestHidDevice() {
  107. auto c_info = device::mojom::HidCollectionInfo::New();
  108. c_info->usage = device::mojom::HidUsageAndPage::New(1, 0xf1d0);
  109. auto hid_device = device::mojom::HidDeviceInfo::New();
  110. hid_device->guid = "A";
  111. hid_device->vendor_id = 0x1234;
  112. hid_device->product_id = 0x5678;
  113. hid_device->product_name = "Test Fido device";
  114. hid_device->serial_number = "123FIDO";
  115. hid_device->bus_type = device::mojom::HidBusType::kHIDBusTypeUSB;
  116. hid_device->collections.push_back(std::move(c_info));
  117. hid_device->max_input_report_size = 64;
  118. hid_device->max_output_report_size = 64;
  119. return hid_device;
  120. }
  121. std::unique_ptr<MockFidoHidConnection>
  122. CreateHidConnectionWithHidInitExpectations(
  123. const std::array<uint8_t, 4>& channel_id,
  124. FakeFidoHidManager* fake_hid_manager,
  125. ::testing::Sequence sequence) {
  126. auto hid_device = TestHidDevice();
  127. mojo::PendingRemote<device::mojom::HidConnection> connection_client;
  128. // Replace device HID connection with custom client connection bound to mock
  129. // server-side mojo connection.
  130. auto mock_connection = std::make_unique<MockFidoHidConnection>(
  131. hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(),
  132. channel_id);
  133. // Initial write for establishing channel ID.
  134. mock_connection->ExpectWriteHidInit(sequence);
  135. EXPECT_CALL(*mock_connection, ReadPtr(_))
  136. .InSequence(sequence)
  137. // Response to HID_INIT request.
  138. .WillOnce(Invoke([mock_connection = mock_connection.get()](
  139. device::mojom::HidConnection::ReadCallback* cb) {
  140. std::move(*cb).Run(
  141. true, 0,
  142. CreateMockInitResponse(mock_connection->nonce(),
  143. mock_connection->connection_channel_id()));
  144. }));
  145. // Add device and set mock connection to fake hid manager.
  146. fake_hid_manager->AddDeviceAndSetConnection(std::move(hid_device),
  147. std::move(connection_client));
  148. return mock_connection;
  149. }
  150. // Set up expectations on mock_connection to read a potentially multi-packet
  151. // response.
  152. void SetupReadExpectation(MockFidoHidConnection* mock_connection,
  153. FidoHidDeviceCommand command_type,
  154. base::span<const uint8_t> payload,
  155. ::testing::Sequence sequence) {
  156. auto channel_id_vector = mock_connection->connection_channel_id();
  157. uint32_t channel_id = channel_id_vector[0] << 24 |
  158. channel_id_vector[1] << 16 | channel_id_vector[2] << 8 |
  159. channel_id_vector[3];
  160. auto message = FidoHidMessage::Create(channel_id, command_type,
  161. kHidMaxPacketSize, payload);
  162. while (message->NumPackets() != 0) {
  163. EXPECT_CALL(*mock_connection, ReadPtr(_))
  164. .InSequence(sequence)
  165. .WillOnce(Invoke([packet = message->PopNextPacket()](
  166. device::mojom::HidConnection::ReadCallback* cb) {
  167. std::move(*cb).Run(true, 0, std::move(packet));
  168. }));
  169. }
  170. }
  171. class FidoDeviceEnumerateCallbackReceiver
  172. : public test::TestCallbackReceiver<std::vector<mojom::HidDeviceInfoPtr>> {
  173. public:
  174. explicit FidoDeviceEnumerateCallbackReceiver(
  175. device::mojom::HidManager* hid_manager)
  176. : hid_manager_(hid_manager) {}
  177. FidoDeviceEnumerateCallbackReceiver(
  178. const FidoDeviceEnumerateCallbackReceiver&) = delete;
  179. FidoDeviceEnumerateCallbackReceiver& operator=(
  180. const FidoDeviceEnumerateCallbackReceiver&) = delete;
  181. ~FidoDeviceEnumerateCallbackReceiver() = default;
  182. std::vector<std::unique_ptr<FidoHidDevice>> TakeReturnedDevicesFiltered() {
  183. std::vector<std::unique_ptr<FidoHidDevice>> filtered_results;
  184. auto [results] = TakeResult();
  185. for (auto& device_info : results) {
  186. HidDeviceFilter filter;
  187. filter.SetUsagePage(0xf1d0);
  188. if (filter.Matches(*device_info)) {
  189. filtered_results.push_back(std::make_unique<FidoHidDevice>(
  190. std::move(device_info), hid_manager_));
  191. }
  192. }
  193. return filtered_results;
  194. }
  195. std::unique_ptr<FidoHidDevice> TakeSingleDevice() {
  196. std::vector<std::unique_ptr<FidoHidDevice>> filtered_results =
  197. TakeReturnedDevicesFiltered();
  198. CHECK_EQ(filtered_results.size(), 1u);
  199. return std::move(filtered_results.front());
  200. }
  201. private:
  202. raw_ptr<device::mojom::HidManager> hid_manager_;
  203. };
  204. using TestDeviceCallbackReceiver =
  205. ::device::test::ValueCallbackReceiver<absl::optional<std::vector<uint8_t>>>;
  206. } // namespace
  207. class FidoHidDeviceTest : public ::testing::Test {
  208. public:
  209. void SetUp() override {
  210. fake_hid_manager_ = std::make_unique<FakeFidoHidManager>();
  211. fake_hid_manager_->AddReceiver(hid_manager_.BindNewPipeAndPassReceiver());
  212. }
  213. protected:
  214. std::unique_ptr<FidoHidDevice> GetMockDevice() {
  215. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  216. auto hid_device = TestHidDevice();
  217. fake_hid_manager_->AddDevice(std::move(hid_device));
  218. hid_manager_->GetDevices(receiver.callback());
  219. receiver.WaitForCallback();
  220. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  221. receiver.TakeReturnedDevicesFiltered();
  222. CHECK_EQ(static_cast<size_t>(1), u2f_devices.size());
  223. return std::move(u2f_devices.front());
  224. }
  225. base::test::TaskEnvironment task_environment_{
  226. base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  227. mojo::Remote<device::mojom::HidManager> hid_manager_;
  228. std::unique_ptr<FakeFidoHidManager> fake_hid_manager_;
  229. };
  230. TEST_F(FidoHidDeviceTest, DisplayName) {
  231. // If this format changes then beware of any configured filters (see filter.h)
  232. // that might be matching against these names.
  233. CHECK_EQ(GetMockDevice()->GetDisplayName(), "usb-1234:5678");
  234. }
  235. TEST_F(FidoHidDeviceTest, TestDeviceError) {
  236. std::unique_ptr<FidoHidDevice> device = GetMockDevice();
  237. // Mock connection where writes always fail.
  238. FakeFidoHidConnection::mock_connection_error_ = true;
  239. TestDeviceCallbackReceiver receiver_0;
  240. device->DeviceTransact(GetMockDeviceRequest(), receiver_0.callback());
  241. receiver_0.WaitForCallback();
  242. EXPECT_FALSE(receiver_0.value());
  243. EXPECT_EQ(FidoDevice::State::kDeviceError, device->state_);
  244. // Add pending transactions manually and ensure they are processed.
  245. TestDeviceCallbackReceiver receiver_1;
  246. device->pending_transactions_.emplace_back(FidoHidDeviceCommand::kMsg,
  247. GetMockDeviceRequest(),
  248. receiver_1.callback(), 0);
  249. TestDeviceCallbackReceiver receiver_2;
  250. device->pending_transactions_.emplace_back(FidoHidDeviceCommand::kMsg,
  251. GetMockDeviceRequest(),
  252. receiver_2.callback(), 0);
  253. TestDeviceCallbackReceiver receiver_3;
  254. device->DeviceTransact(GetMockDeviceRequest(), receiver_3.callback());
  255. FakeFidoHidConnection::mock_connection_error_ = false;
  256. EXPECT_EQ(FidoDevice::State::kDeviceError, device->state_);
  257. EXPECT_FALSE(receiver_1.value());
  258. EXPECT_FALSE(receiver_2.value());
  259. EXPECT_FALSE(receiver_3.value());
  260. }
  261. TEST_F(FidoHidDeviceTest, TestRetryChannelAllocation) {
  262. constexpr uint8_t kIncorrectNonce[] = {0x00, 0x00, 0x00, 0x00,
  263. 0x00, 0x00, 0x00, 0x00};
  264. auto hid_device = TestHidDevice();
  265. // Replace device HID connection with custom client connection bound to mock
  266. // server-side mojo connection.
  267. mojo::PendingRemote<device::mojom::HidConnection> connection_client;
  268. MockFidoHidConnection mock_connection(
  269. hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(),
  270. kChannelId);
  271. // Initial write for establishing a channel ID.
  272. mock_connection.ExpectWriteHidInit();
  273. // HID_MSG request to authenticator for version request.
  274. mock_connection.ExpectHidWriteWithCommand(FidoHidDeviceCommand::kMsg);
  275. EXPECT_CALL(mock_connection, ReadPtr(_))
  276. // First response to HID_INIT request with an incorrect nonce.
  277. .WillOnce(Invoke([kIncorrectNonce, &mock_connection](auto* cb) {
  278. std::move(*cb).Run(
  279. true, 0,
  280. CreateMockInitResponse(kIncorrectNonce,
  281. mock_connection.connection_channel_id()));
  282. }))
  283. // Second response to HID_INIT request with a correct nonce.
  284. .WillOnce(Invoke(
  285. [&mock_connection](device::mojom::HidConnection::ReadCallback* cb) {
  286. std::move(*cb).Run(true, 0,
  287. CreateMockInitResponse(
  288. mock_connection.nonce(),
  289. mock_connection.connection_channel_id()));
  290. }))
  291. // Version response from the authenticator.
  292. .WillOnce(Invoke(
  293. [&mock_connection](device::mojom::HidConnection::ReadCallback* cb) {
  294. std::move(*cb).Run(true, 0,
  295. CreateMockResponseWithChannelId(
  296. mock_connection.connection_channel_id(),
  297. kU2fMockResponseMessage));
  298. }));
  299. // Add device and set mock connection to fake hid manager.
  300. fake_hid_manager_->AddDeviceAndSetConnection(std::move(hid_device),
  301. std::move(connection_client));
  302. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  303. hid_manager_->GetDevices(receiver.callback());
  304. receiver.WaitForCallback();
  305. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  306. receiver.TakeReturnedDevicesFiltered();
  307. ASSERT_EQ(1u, u2f_devices.size());
  308. auto& device = u2f_devices.front();
  309. TestDeviceCallbackReceiver cb;
  310. device->DeviceTransact(GetMockDeviceRequest(), cb.callback());
  311. cb.WaitForCallback();
  312. const auto& value = cb.value();
  313. ASSERT_TRUE(value);
  314. EXPECT_THAT(*value, testing::ElementsAreArray(kU2fMockResponseData));
  315. }
  316. TEST_F(FidoHidDeviceTest, TestKeepAliveMessage) {
  317. ::testing::Sequence sequence;
  318. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  319. kChannelId, fake_hid_manager_.get(), sequence);
  320. // HID_CBOR request to authenticator.
  321. mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
  322. EXPECT_CALL(*mock_connection, ReadPtr(_))
  323. .InSequence(sequence)
  324. // Keep alive message sent from the authenticator.
  325. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  326. std::move(*cb).Run(
  327. true, 0,
  328. GetKeepAliveHidMessage(mock_connection->connection_channel_id()));
  329. }))
  330. // Repeated Read() invocation due to keep alive message. Sends a dummy
  331. // response that corresponds to U2F version response.
  332. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  333. auto almost_time_out = kDeviceTimeout - base::Microseconds(1);
  334. task_environment_.FastForwardBy(almost_time_out);
  335. std::move(*cb).Run(true, 0,
  336. CreateMockResponseWithChannelId(
  337. mock_connection->connection_channel_id(),
  338. kU2fMockResponseMessage));
  339. }));
  340. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  341. hid_manager_->GetDevices(receiver.callback());
  342. receiver.WaitForCallback();
  343. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  344. receiver.TakeReturnedDevicesFiltered();
  345. ASSERT_EQ(1u, u2f_devices.size());
  346. auto& device = u2f_devices.front();
  347. // Keep alive message handling is only supported for CTAP HID device.
  348. device->set_supported_protocol(ProtocolVersion::kCtap2);
  349. TestDeviceCallbackReceiver cb;
  350. device->DeviceTransact(GetMockDeviceRequest(), cb.callback());
  351. cb.WaitForCallback();
  352. const auto& value = cb.value();
  353. ASSERT_TRUE(value);
  354. EXPECT_THAT(*value, testing::ElementsAreArray(kU2fMockResponseData));
  355. }
  356. // InvertChannelID inverts all the bits in the given channel ID. This is used to
  357. // create a channel ID that will not be equal to the expected channel ID.
  358. std::array<uint8_t, 4> InvertChannelID(
  359. const std::array<uint8_t, 4> channel_id) {
  360. std::array<uint8_t, 4> ret;
  361. memcpy(ret.data(), channel_id.data(), ret.size());
  362. for (size_t i = 0; i < ret.size(); i++) {
  363. ret[i] ^= 0xff;
  364. }
  365. return ret;
  366. }
  367. TEST_F(FidoHidDeviceTest, TestMessageOnOtherChannel) {
  368. // Test that a HID message with a different channel ID is ignored.
  369. ::testing::Sequence sequence;
  370. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  371. kChannelId, fake_hid_manager_.get(), sequence);
  372. // HID_CBOR request to authenticator.
  373. mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kMsg);
  374. EXPECT_CALL(*mock_connection, ReadPtr(_))
  375. .InSequence(sequence)
  376. // Message on wrong channel.
  377. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  378. std::move(*cb).Run(
  379. true, 0,
  380. CreateMockResponseWithChannelId(
  381. InvertChannelID(mock_connection->connection_channel_id()),
  382. kHidUnknownCommandError));
  383. }))
  384. // Expected message on the correct channel.
  385. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  386. std::move(*cb).Run(true, 0,
  387. CreateMockResponseWithChannelId(
  388. mock_connection->connection_channel_id(),
  389. kU2fMockResponseMessage));
  390. }));
  391. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  392. hid_manager_->GetDevices(receiver.callback());
  393. receiver.WaitForCallback();
  394. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  395. receiver.TakeReturnedDevicesFiltered();
  396. ASSERT_EQ(1u, u2f_devices.size());
  397. auto& device = u2f_devices.front();
  398. TestDeviceCallbackReceiver cb;
  399. device->DeviceTransact(GetMockDeviceRequest(), cb.callback());
  400. cb.WaitForCallback();
  401. const auto& value = cb.value();
  402. ASSERT_TRUE(value);
  403. EXPECT_THAT(*value, testing::ElementsAreArray(kU2fMockResponseData));
  404. }
  405. TEST_F(FidoHidDeviceTest, TestContinuedMessageOnOtherChannel) {
  406. // Test that a multi-frame HID message with a different channel ID is
  407. // ignored.
  408. ::testing::Sequence sequence;
  409. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  410. kChannelId, fake_hid_manager_.get(), sequence);
  411. // HID_CBOR request to authenticator.
  412. mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kMsg);
  413. constexpr uint8_t kOtherChannelMsgPrefix[64] = {
  414. 0x83,
  415. 0x00,
  416. // Mark reply as being 64 bytes long, which is more than a single USB
  417. // frame can contain.
  418. 0x40,
  419. 0,
  420. };
  421. constexpr uint8_t kOtherChannelMsgSuffix[64] = {
  422. // Continuation packet zero.
  423. 0x00,
  424. // Contents can be anything.
  425. };
  426. EXPECT_CALL(*mock_connection, ReadPtr(_))
  427. .InSequence(sequence)
  428. // Beginning of a message on the wrong channel.
  429. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  430. std::move(*cb).Run(
  431. true, 0,
  432. CreateMockResponseWithChannelId(
  433. InvertChannelID(mock_connection->connection_channel_id()),
  434. kOtherChannelMsgPrefix));
  435. }))
  436. // Continuation of the message on the wrong channel.
  437. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  438. std::move(*cb).Run(
  439. true, 0,
  440. CreateMockResponseWithChannelId(
  441. InvertChannelID(mock_connection->connection_channel_id()),
  442. kOtherChannelMsgSuffix));
  443. }))
  444. // Expected message on the correct channel.
  445. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  446. std::move(*cb).Run(true, 0,
  447. CreateMockResponseWithChannelId(
  448. mock_connection->connection_channel_id(),
  449. kU2fMockResponseMessage));
  450. }));
  451. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  452. hid_manager_->GetDevices(receiver.callback());
  453. receiver.WaitForCallback();
  454. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  455. receiver.TakeReturnedDevicesFiltered();
  456. ASSERT_EQ(1u, u2f_devices.size());
  457. auto& device = u2f_devices.front();
  458. TestDeviceCallbackReceiver cb;
  459. device->DeviceTransact(GetMockDeviceRequest(), cb.callback());
  460. cb.WaitForCallback();
  461. const auto& value = cb.value();
  462. ASSERT_TRUE(value);
  463. EXPECT_THAT(*value, testing::ElementsAreArray(kU2fMockResponseData));
  464. }
  465. TEST_F(FidoHidDeviceTest, TestDeviceTimeoutAfterKeepAliveMessage) {
  466. ::testing::Sequence sequence;
  467. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  468. kChannelId, fake_hid_manager_.get(), sequence);
  469. // HID_CBOR request to authenticator.
  470. mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
  471. EXPECT_CALL(*mock_connection, ReadPtr(_))
  472. .InSequence(sequence)
  473. // Keep alive message sent from the authenticator.
  474. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  475. std::move(*cb).Run(
  476. true, 0,
  477. GetKeepAliveHidMessage(mock_connection->connection_channel_id()));
  478. }))
  479. // Repeated Read() invocation due to keep alive message. The callback
  480. // is invoked only after 3 seconds, which should cause device to timeout.
  481. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  482. task_environment_.FastForwardBy(kDeviceTimeout);
  483. std::move(*cb).Run(true, 0,
  484. CreateMockResponseWithChannelId(
  485. mock_connection->connection_channel_id(),
  486. kU2fMockResponseMessage));
  487. }));
  488. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  489. hid_manager_->GetDevices(receiver.callback());
  490. receiver.WaitForCallback();
  491. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  492. receiver.TakeReturnedDevicesFiltered();
  493. ASSERT_EQ(1u, u2f_devices.size());
  494. auto& device = u2f_devices.front();
  495. // Keep alive message handling is only supported for CTAP HID device.
  496. device->set_supported_protocol(ProtocolVersion::kCtap2);
  497. TestDeviceCallbackReceiver cb;
  498. device->DeviceTransact(GetMockDeviceRequest(), cb.callback());
  499. cb.WaitForCallback();
  500. const auto& value = cb.value();
  501. EXPECT_FALSE(value);
  502. EXPECT_EQ(FidoDevice::State::kDeviceError, device->state_for_testing());
  503. }
  504. TEST_F(FidoHidDeviceTest, TestCancel) {
  505. ::testing::Sequence sequence;
  506. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  507. kChannelId, fake_hid_manager_.get(), sequence);
  508. // HID_CBOR request to authenticator.
  509. mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
  510. // Cancel request to authenticator.
  511. mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCancel);
  512. EXPECT_CALL(*mock_connection, ReadPtr(_))
  513. .InSequence(sequence)
  514. // Device response with a significant delay.
  515. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  516. auto delay = base::Seconds(2);
  517. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  518. FROM_HERE,
  519. base::BindOnce(std::move(*cb), true, 0,
  520. CreateMockResponseWithChannelId(
  521. mock_connection->connection_channel_id(),
  522. kU2fMockResponseMessage)),
  523. delay);
  524. }));
  525. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  526. hid_manager_->GetDevices(receiver.callback());
  527. receiver.WaitForCallback();
  528. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  529. receiver.TakeReturnedDevicesFiltered();
  530. ASSERT_EQ(1u, u2f_devices.size());
  531. auto& device = u2f_devices.front();
  532. // Keep alive message handling is only supported for CTAP HID device.
  533. device->set_supported_protocol(ProtocolVersion::kCtap2);
  534. TestDeviceCallbackReceiver cb;
  535. auto token = device->DeviceTransact(GetMockDeviceRequest(), cb.callback());
  536. auto delay_before_cancel = base::Seconds(1);
  537. auto cancel_callback = base::BindOnce(
  538. &FidoHidDevice::Cancel, device->weak_factory_.GetWeakPtr(), token);
  539. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  540. FROM_HERE, std::move(cancel_callback), delay_before_cancel);
  541. task_environment_.FastForwardUntilNoTasksRemain();
  542. }
  543. TEST_F(FidoHidDeviceTest, TestCancelWhileWriting) {
  544. // Simulate a cancelation request that occurs while the request is being
  545. // written.
  546. ::testing::Sequence sequence;
  547. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  548. kChannelId, fake_hid_manager_.get(), sequence);
  549. FidoDevice::CancelToken token = FidoDevice::kInvalidCancelToken;
  550. FidoDevice* device = nullptr;
  551. EXPECT_CALL(*mock_connection, WritePtr(_, _, _))
  552. .InSequence(sequence)
  553. .WillOnce(Invoke(
  554. [&token, &device](auto&&, const std::vector<uint8_t>& buffer,
  555. device::mojom::HidConnection::WriteCallback* cb) {
  556. device->Cancel(token);
  557. std::move(*cb).Run(true);
  558. }));
  559. EXPECT_CALL(*mock_connection, WritePtr(_, _, _))
  560. .InSequence(sequence)
  561. .WillOnce(Invoke([](auto&&, const std::vector<uint8_t>& buffer,
  562. device::mojom::HidConnection::WriteCallback* cb) {
  563. std::move(*cb).Run(true);
  564. }));
  565. EXPECT_CALL(*mock_connection, WritePtr(_, _, _))
  566. .InSequence(sequence)
  567. .WillOnce(Invoke([](auto&&, const std::vector<uint8_t>& buffer,
  568. device::mojom::HidConnection::WriteCallback* cb) {
  569. CHECK_LE(5u, buffer.size());
  570. CHECK_EQ(static_cast<uint8_t>(FidoHidDeviceCommand::kCancel) | 0x80,
  571. buffer[4]);
  572. std::move(*cb).Run(true);
  573. }));
  574. EXPECT_CALL(*mock_connection, ReadPtr(_))
  575. .InSequence(sequence)
  576. .WillOnce(Invoke(
  577. [&mock_connection](device::mojom::HidConnection::ReadCallback* cb) {
  578. std::move(*cb).Run(true, 0,
  579. CreateMockResponseWithChannelId(
  580. mock_connection->connection_channel_id(),
  581. kMockCancelResponse));
  582. }));
  583. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  584. hid_manager_->GetDevices(receiver.callback());
  585. receiver.WaitForCallback();
  586. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  587. receiver.TakeReturnedDevicesFiltered();
  588. ASSERT_EQ(1u, u2f_devices.size());
  589. device = u2f_devices.front().get();
  590. // Keep alive message handling is only supported for CTAP HID device.
  591. device->set_supported_protocol(ProtocolVersion::kCtap2);
  592. TestDeviceCallbackReceiver cb;
  593. // The size of |dummy_request| needs only to make the request need two USB
  594. // frames.
  595. std::vector<uint8_t> dummy_request(100);
  596. token = device->DeviceTransact(std::move(dummy_request), cb.callback());
  597. cb.WaitForCallback();
  598. ASSERT_TRUE(cb.value());
  599. ASSERT_EQ(1u, cb.value()->size());
  600. ASSERT_EQ(0x2d /* CTAP2_ERR_KEEPALIVE_CANCEL */, cb.value().value()[0]);
  601. }
  602. TEST_F(FidoHidDeviceTest, TestCancelAfterWriting) {
  603. // Simulate a cancelation request that occurs while waiting for a response.
  604. ::testing::Sequence sequence;
  605. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  606. kChannelId, fake_hid_manager_.get(), sequence);
  607. FidoDevice::CancelToken token = FidoDevice::kInvalidCancelToken;
  608. FidoDevice* device = nullptr;
  609. device::mojom::HidConnection::ReadCallback read_callback;
  610. EXPECT_CALL(*mock_connection, WritePtr(_, _, _))
  611. .InSequence(sequence)
  612. .WillOnce(Invoke([](auto&&, const std::vector<uint8_t>& buffer,
  613. device::mojom::HidConnection::WriteCallback* cb) {
  614. std::move(*cb).Run(true);
  615. }));
  616. EXPECT_CALL(*mock_connection, ReadPtr(_))
  617. .InSequence(sequence)
  618. .WillOnce(Invoke([&read_callback, &device, &token](
  619. device::mojom::HidConnection::ReadCallback* cb) {
  620. read_callback = std::move(*cb);
  621. base::ThreadTaskRunnerHandle::Get()->PostTask(
  622. FROM_HERE,
  623. base::BindOnce(
  624. [](FidoDevice* device, FidoDevice::CancelToken token) {
  625. device->Cancel(token);
  626. },
  627. device, token));
  628. }));
  629. EXPECT_CALL(*mock_connection, WritePtr(_, _, _))
  630. .InSequence(sequence)
  631. .WillOnce(Invoke([&mock_connection, &read_callback](
  632. auto&&, const std::vector<uint8_t>& buffer,
  633. device::mojom::HidConnection::WriteCallback* cb) {
  634. CHECK_LE(5u, buffer.size());
  635. CHECK_EQ(static_cast<uint8_t>(FidoHidDeviceCommand::kCancel) | 0x80,
  636. buffer[4]);
  637. std::move(*cb).Run(true);
  638. std::move(read_callback)
  639. .Run(true, 0,
  640. CreateMockResponseWithChannelId(
  641. mock_connection->connection_channel_id(),
  642. kMockCancelResponse));
  643. }));
  644. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  645. hid_manager_->GetDevices(receiver.callback());
  646. receiver.WaitForCallback();
  647. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  648. receiver.TakeReturnedDevicesFiltered();
  649. ASSERT_EQ(1u, u2f_devices.size());
  650. device = u2f_devices.front().get();
  651. // Cancelation is only supported for CTAP HID device.
  652. device->set_supported_protocol(ProtocolVersion::kCtap2);
  653. TestDeviceCallbackReceiver cb;
  654. std::vector<uint8_t> dummy_request(1);
  655. token = device->DeviceTransact(std::move(dummy_request), cb.callback());
  656. cb.WaitForCallback();
  657. ASSERT_TRUE(cb.value());
  658. ASSERT_EQ(1u, cb.value()->size());
  659. ASSERT_EQ(0x2d /* CTAP2_ERR_KEEPALIVE_CANCEL */, cb.value().value()[0]);
  660. }
  661. TEST_F(FidoHidDeviceTest, TestCancelAfterReading) {
  662. // Simulate a cancelation request that occurs after the first frame of the
  663. // response has been received.
  664. ::testing::Sequence sequence;
  665. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  666. kChannelId, fake_hid_manager_.get(), sequence);
  667. FidoDevice::CancelToken token = FidoDevice::kInvalidCancelToken;
  668. FidoDevice* device = nullptr;
  669. device::mojom::HidConnection::ReadCallback read_callback;
  670. EXPECT_CALL(*mock_connection, WritePtr(_, _, _))
  671. .InSequence(sequence)
  672. .WillOnce(Invoke([](auto&&, const std::vector<uint8_t>& buffer,
  673. device::mojom::HidConnection::WriteCallback* cb) {
  674. std::move(*cb).Run(true);
  675. }));
  676. EXPECT_CALL(*mock_connection, ReadPtr(_))
  677. .InSequence(sequence)
  678. .WillOnce(Invoke(
  679. [&mock_connection](device::mojom::HidConnection::ReadCallback* cb) {
  680. std::vector<uint8_t> frame = {0x90, 0, 64};
  681. frame.resize(64, 0);
  682. std::move(*cb).Run(true, 0,
  683. CreateMockResponseWithChannelId(
  684. mock_connection->connection_channel_id(),
  685. std::move(frame)));
  686. }));
  687. EXPECT_CALL(*mock_connection, ReadPtr(_))
  688. .InSequence(sequence)
  689. .WillOnce(Invoke([&device, &token, &mock_connection](
  690. device::mojom::HidConnection::ReadCallback* cb) {
  691. // This |Cancel| call should be a no-op because the response has already
  692. // started to be received.
  693. device->Cancel(token);
  694. std::vector<uint8_t> frame;
  695. frame.resize(64, 0);
  696. std::move(*cb).Run(
  697. true, 0,
  698. CreateMockResponseWithChannelId(
  699. mock_connection->connection_channel_id(), std::move(frame)));
  700. }));
  701. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  702. hid_manager_->GetDevices(receiver.callback());
  703. receiver.WaitForCallback();
  704. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  705. receiver.TakeReturnedDevicesFiltered();
  706. ASSERT_EQ(1u, u2f_devices.size());
  707. device = u2f_devices.front().get();
  708. // Cancelation is only supported for CTAP HID device.
  709. device->set_supported_protocol(ProtocolVersion::kCtap2);
  710. TestDeviceCallbackReceiver cb;
  711. std::vector<uint8_t> dummy_request(1);
  712. token = device->DeviceTransact(std::move(dummy_request), cb.callback());
  713. cb.WaitForCallback();
  714. ASSERT_TRUE(cb.value());
  715. ASSERT_EQ(64u, cb.value()->size());
  716. }
  717. TEST_F(FidoHidDeviceTest, TestGetInfoFailsOnDeviceError) {
  718. // HID_ERROR(7F), followed by payload length(0001), followed by kUnknown(7F).
  719. constexpr uint8_t kHidUnknownTransportError[] = {0x7F, 0x00, 0x01, 0x7F};
  720. ::testing::Sequence sequence;
  721. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  722. kChannelId, fake_hid_manager_.get(), sequence);
  723. // HID_CBOR request to authenticator.
  724. mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
  725. EXPECT_CALL(*mock_connection, ReadPtr(_))
  726. .InSequence(sequence)
  727. // Device response with a significant delay.
  728. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  729. auto delay = base::Seconds(2);
  730. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  731. FROM_HERE,
  732. base::BindOnce(std::move(*cb), true, 0,
  733. CreateMockResponseWithChannelId(
  734. mock_connection->connection_channel_id(),
  735. kHidUnknownTransportError)),
  736. delay);
  737. }));
  738. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  739. hid_manager_->GetDevices(receiver.callback());
  740. receiver.WaitForCallback();
  741. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  742. receiver.TakeReturnedDevicesFiltered();
  743. ASSERT_EQ(1u, u2f_devices.size());
  744. auto& device = u2f_devices.front();
  745. device::test::TestCallbackReceiver<> get_info_callback;
  746. device->DiscoverSupportedProtocolAndDeviceInfo(get_info_callback.callback());
  747. task_environment_.FastForwardUntilNoTasksRemain();
  748. EXPECT_FALSE(get_info_callback.was_called());
  749. EXPECT_EQ(FidoDevice::State::kDeviceError, device->state_for_testing());
  750. }
  751. // Test that FidoHidDevice::DiscoverSupportedProtocolAndDeviceInfo() invokes
  752. // callback when device error outs with kMsgError state.
  753. TEST_F(FidoHidDeviceTest, TestDeviceMessageError) {
  754. ::testing::Sequence sequence;
  755. auto mock_connection = CreateHidConnectionWithHidInitExpectations(
  756. kChannelId, fake_hid_manager_.get(), sequence);
  757. // HID_CBOR request to authenticator.
  758. mock_connection->ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
  759. EXPECT_CALL(*mock_connection, ReadPtr(_))
  760. .InSequence(sequence)
  761. // Device response with a significant delay.
  762. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  763. auto delay = base::Seconds(2);
  764. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  765. FROM_HERE,
  766. base::BindOnce(std::move(*cb), true, 0,
  767. CreateMockResponseWithChannelId(
  768. mock_connection->connection_channel_id(),
  769. kHidUnknownCommandError)),
  770. delay);
  771. }));
  772. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  773. hid_manager_->GetDevices(receiver.callback());
  774. receiver.WaitForCallback();
  775. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  776. receiver.TakeReturnedDevicesFiltered();
  777. ASSERT_EQ(1u, u2f_devices.size());
  778. auto& device = u2f_devices.front();
  779. device::test::TestCallbackReceiver<> get_info_callback;
  780. device->DiscoverSupportedProtocolAndDeviceInfo(get_info_callback.callback());
  781. task_environment_.FastForwardUntilNoTasksRemain();
  782. EXPECT_TRUE(get_info_callback.was_called());
  783. }
  784. // Test that the wink command does not get sent if the device does not support
  785. // it.
  786. TEST_F(FidoHidDeviceTest, TestWinkNotSupported) {
  787. constexpr uint8_t kWinkNotSupportedPayload[] = {0x00, 0x00, 0x00, 0x00, 0x00};
  788. auto hid_device = TestHidDevice();
  789. // Replace device HID connection with custom client connection bound to mock
  790. // server-side mojo connection.
  791. mojo::PendingRemote<device::mojom::HidConnection> connection_client;
  792. MockFidoHidConnection mock_connection(
  793. hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(),
  794. kChannelId);
  795. // Initial write for establishing a channel ID.
  796. mock_connection.ExpectWriteHidInit();
  797. // GetInfo command.
  798. mock_connection.ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
  799. EXPECT_CALL(mock_connection, ReadPtr(_))
  800. // Respond to HID_INIT indicating the device does not support winking.
  801. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  802. std::move(*cb).Run(
  803. true, 0,
  804. CreateMockInitResponse(mock_connection.nonce(),
  805. mock_connection.connection_channel_id(),
  806. kWinkNotSupportedPayload));
  807. }))
  808. // Respond to GetInfo with kHidUnknownCommandError to signal this is a
  809. // U2F device.
  810. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  811. std::move(*cb).Run(true, 0,
  812. CreateMockResponseWithChannelId(
  813. mock_connection.connection_channel_id(),
  814. kHidUnknownCommandError));
  815. }));
  816. // Add device and set mock connection to fake hid manager.
  817. fake_hid_manager_->AddDeviceAndSetConnection(std::move(hid_device),
  818. std::move(connection_client));
  819. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  820. hid_manager_->GetDevices(receiver.callback());
  821. receiver.WaitForCallback();
  822. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  823. receiver.TakeReturnedDevicesFiltered();
  824. ASSERT_EQ(1u, u2f_devices.size());
  825. auto& device = u2f_devices.front();
  826. device::test::TestCallbackReceiver<> callback_receiver;
  827. device->DiscoverSupportedProtocolAndDeviceInfo(callback_receiver.callback());
  828. task_environment_.FastForwardUntilNoTasksRemain();
  829. EXPECT_TRUE(callback_receiver.was_called());
  830. device->TryWink(callback_receiver.callback());
  831. task_environment_.FastForwardUntilNoTasksRemain();
  832. EXPECT_TRUE(callback_receiver.was_called());
  833. }
  834. // Test that the wink command does not get sent for CTAP2 devices, even if they
  835. // support it.
  836. // This is a workaround for crbug.com/994867
  837. TEST_F(FidoHidDeviceTest, TestCtap2DeviceShouldNotBlink) {
  838. constexpr uint8_t kWinkSupportedPayload[] = {0x00, 0x00, 0x00, 0x00, 0x01};
  839. auto hid_device = TestHidDevice();
  840. // Replace device HID connection with custom client connection bound to mock
  841. // server-side mojo connection.
  842. mojo::PendingRemote<device::mojom::HidConnection> connection_client;
  843. MockFidoHidConnection mock_connection(
  844. hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(),
  845. kChannelId);
  846. // Initial write for establishing a channel ID.
  847. mock_connection.ExpectWriteHidInit();
  848. // Write for the GetInfo command.
  849. mock_connection.ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
  850. ::testing::Sequence sequence;
  851. EXPECT_CALL(mock_connection, ReadPtr(_))
  852. // Respond to HID_INIT indicating the device supports winking.
  853. .InSequence(sequence)
  854. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  855. std::move(*cb).Run(
  856. true, 0,
  857. CreateMockInitResponse(mock_connection.nonce(),
  858. mock_connection.connection_channel_id(),
  859. kWinkSupportedPayload));
  860. }));
  861. SetupReadExpectation(&mock_connection, FidoHidDeviceCommand::kCbor,
  862. test_data::kTestAuthenticatorGetInfoResponse, sequence);
  863. // Add device and set mock connection to fake hid manager.
  864. fake_hid_manager_->AddDeviceAndSetConnection(std::move(hid_device),
  865. std::move(connection_client));
  866. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  867. hid_manager_->GetDevices(receiver.callback());
  868. receiver.WaitForCallback();
  869. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  870. receiver.TakeReturnedDevicesFiltered();
  871. ASSERT_EQ(1u, u2f_devices.size());
  872. auto& device = u2f_devices.front();
  873. device::test::TestCallbackReceiver<> callback_receiver;
  874. device->DiscoverSupportedProtocolAndDeviceInfo(callback_receiver.callback());
  875. task_environment_.FastForwardUntilNoTasksRemain();
  876. EXPECT_TRUE(callback_receiver.was_called());
  877. device->TryWink(callback_receiver.callback());
  878. task_environment_.FastForwardUntilNoTasksRemain();
  879. EXPECT_TRUE(callback_receiver.was_called());
  880. }
  881. // Test that the wink command is sent to a device that supports it.
  882. TEST_F(FidoHidDeviceTest, TestSuccessfulWink) {
  883. constexpr uint8_t kWinkSupportedPayload[] = {0x00, 0x00, 0x00, 0x00, 0x01};
  884. auto hid_device = TestHidDevice();
  885. // Replace device HID connection with custom client connection bound to mock
  886. // server-side mojo connection.
  887. mojo::PendingRemote<device::mojom::HidConnection> connection_client;
  888. MockFidoHidConnection mock_connection(
  889. hid_device.Clone(), connection_client.InitWithNewPipeAndPassReceiver(),
  890. kChannelId);
  891. // Initial write for establishing a channel ID.
  892. mock_connection.ExpectWriteHidInit();
  893. // GetInfo write.
  894. mock_connection.ExpectHidWriteWithCommand(FidoHidDeviceCommand::kCbor);
  895. mock_connection.ExpectHidWriteWithCommand(FidoHidDeviceCommand::kWink);
  896. EXPECT_CALL(mock_connection, ReadPtr(_))
  897. // Respond to HID_INIT indicating the device supports winking.
  898. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  899. std::move(*cb).Run(
  900. true, 0,
  901. CreateMockInitResponse(mock_connection.nonce(),
  902. mock_connection.connection_channel_id(),
  903. kWinkSupportedPayload));
  904. }))
  905. // Respond to GetInfo with kHidUnknownCommandError to signal this is a
  906. // U2F device.
  907. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  908. std::move(*cb).Run(true, 0,
  909. CreateMockResponseWithChannelId(
  910. mock_connection.connection_channel_id(),
  911. kHidUnknownCommandError));
  912. }))
  913. // Response to HID_WINK.
  914. .WillOnce(Invoke([&](device::mojom::HidConnection::ReadCallback* cb) {
  915. std::move(*cb).Run(true, 0,
  916. CreateMockResponseWithChannelId(
  917. mock_connection.connection_channel_id(),
  918. kU2fWinkResponseMessage));
  919. }));
  920. // Add device and set mock connection to fake hid manager.
  921. fake_hid_manager_->AddDeviceAndSetConnection(std::move(hid_device),
  922. std::move(connection_client));
  923. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  924. hid_manager_->GetDevices(receiver.callback());
  925. receiver.WaitForCallback();
  926. std::vector<std::unique_ptr<FidoHidDevice>> u2f_devices =
  927. receiver.TakeReturnedDevicesFiltered();
  928. ASSERT_EQ(1u, u2f_devices.size());
  929. auto& device = u2f_devices.front();
  930. device::test::TestCallbackReceiver<> callback_receiver;
  931. device->DiscoverSupportedProtocolAndDeviceInfo(callback_receiver.callback());
  932. task_environment_.FastForwardUntilNoTasksRemain();
  933. EXPECT_TRUE(callback_receiver.was_called());
  934. device->TryWink(callback_receiver.callback());
  935. task_environment_.FastForwardUntilNoTasksRemain();
  936. EXPECT_TRUE(callback_receiver.was_called());
  937. }
  938. TEST_F(FidoHidDeviceTest, RetryAfterU2fChannelBusy) {
  939. testing::Sequence sequence;
  940. std::unique_ptr<MockFidoHidConnection> mock_connection =
  941. CreateHidConnectionWithHidInitExpectations(
  942. kChannelId, fake_hid_manager_.get(), sequence);
  943. // Expect the original HID_MSG and reply with a ERR_CHANNEL_BUSY. Then expect
  944. // the message to be resent and reply.
  945. mock_connection->ExpectHidWriteWithCommand(sequence,
  946. FidoHidDeviceCommand::kMsg);
  947. mock_connection->ExpectReadAndReplyWith(
  948. sequence,
  949. CreateMockResponseWithChannelId(mock_connection->connection_channel_id(),
  950. kMockU2fChannelBusyResponse));
  951. mock_connection->ExpectHidWriteWithCommand(sequence,
  952. FidoHidDeviceCommand::kMsg);
  953. mock_connection->ExpectReadAndReplyWith(
  954. sequence,
  955. CreateMockResponseWithChannelId(mock_connection->connection_channel_id(),
  956. kU2fMockResponseMessage));
  957. FidoDeviceEnumerateCallbackReceiver receiver(hid_manager_.get());
  958. hid_manager_->GetDevices(receiver.callback());
  959. receiver.WaitForCallback();
  960. std::unique_ptr<FidoHidDevice> device = receiver.TakeSingleDevice();
  961. TestDeviceCallbackReceiver cb;
  962. device->DeviceTransact(GetMockDeviceRequest(), cb.callback());
  963. cb.WaitForCallback();
  964. const auto& value = cb.value();
  965. ASSERT_TRUE(value);
  966. EXPECT_THAT(*value, testing::ElementsAreArray(kU2fMockResponseData));
  967. }
  968. } // namespace device