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