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