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/fake_hid_impl_for_testing.h"
6 
7 #include <utility>
8 
9 #include "base/callback_helpers.h"
10 #include "device/fido/fido_parsing_utils.h"
11 #include "device/fido/hid/fido_hid_discovery.h"
12 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
13 #include "mojo/public/cpp/bindings/pending_receiver.h"
14 #include "services/device/public/mojom/hid.mojom.h"
15 
16 namespace device {
17 
18 namespace {
19 
20 MATCHER_P(IsCtapHidCommand, expected_command, "") {
21   return arg.size() >= 5 &&
22          arg[4] == (0x80 | static_cast<uint8_t>(expected_command));
23 }
24 
25 }  // namespace
26 
MockFidoHidConnection(device::mojom::HidDeviceInfoPtr device,mojo::PendingReceiver<device::mojom::HidConnection> receiver,std::array<uint8_t,4> connection_channel_id)27 MockFidoHidConnection::MockFidoHidConnection(
28     device::mojom::HidDeviceInfoPtr device,
29     mojo::PendingReceiver<device::mojom::HidConnection> receiver,
30     std::array<uint8_t, 4> connection_channel_id)
31     : receiver_(this, std::move(receiver)),
32       device_(std::move(device)),
33       connection_channel_id_(connection_channel_id) {}
34 
~MockFidoHidConnection()35 MockFidoHidConnection::~MockFidoHidConnection() {}
36 
Read(ReadCallback callback)37 void MockFidoHidConnection::Read(ReadCallback callback) {
38   return ReadPtr(&callback);
39 }
40 
Write(uint8_t report_id,const std::vector<uint8_t> & buffer,WriteCallback callback)41 void MockFidoHidConnection::Write(uint8_t report_id,
42                                   const std::vector<uint8_t>& buffer,
43                                   WriteCallback callback) {
44   return WritePtr(report_id, buffer, &callback);
45 }
46 
GetFeatureReport(uint8_t report_id,GetFeatureReportCallback callback)47 void MockFidoHidConnection::GetFeatureReport(
48     uint8_t report_id,
49     GetFeatureReportCallback callback) {
50   NOTREACHED();
51 }
52 
SendFeatureReport(uint8_t report_id,const std::vector<uint8_t> & buffer,SendFeatureReportCallback callback)53 void MockFidoHidConnection::SendFeatureReport(
54     uint8_t report_id,
55     const std::vector<uint8_t>& buffer,
56     SendFeatureReportCallback callback) {
57   NOTREACHED();
58 }
59 
SetNonce(base::span<uint8_t const> nonce)60 void MockFidoHidConnection::SetNonce(base::span<uint8_t const> nonce) {
61   nonce_ = std::vector<uint8_t>(nonce.begin(), nonce.end());
62 }
63 
ExpectWriteHidInit()64 void MockFidoHidConnection::ExpectWriteHidInit() {
65   EXPECT_CALL(*this, WritePtr(::testing::_,
66                               IsCtapHidCommand(FidoHidDeviceCommand::kInit),
67                               ::testing::_))
68       .WillOnce(::testing::Invoke(
69           [&](auto&&, const std::vector<uint8_t>& buffer,
70               device::mojom::HidConnection::WriteCallback* cb) {
71             ASSERT_EQ(64u, buffer.size());
72             // First 7 bytes are 4 bytes of channel id, one byte representing
73             // HID command, 2 bytes for payload length.
74             SetNonce(base::make_span(buffer).subspan(7, 8));
75             std::move(*cb).Run(true);
76           }));
77 }
78 
ExpectHidWriteWithCommand(FidoHidDeviceCommand cmd)79 void MockFidoHidConnection::ExpectHidWriteWithCommand(
80     FidoHidDeviceCommand cmd) {
81   EXPECT_CALL(*this,
82               WritePtr(::testing::_, IsCtapHidCommand(cmd), ::testing::_))
83       .WillOnce(::testing::Invoke(
84           [&](auto&&, const std::vector<uint8_t>& buffer,
85               device::mojom::HidConnection::WriteCallback* cb) {
86             std::move(*cb).Run(true);
87           }));
88 }
89 
90 bool FakeFidoHidConnection::mock_connection_error_ = false;
91 
FakeFidoHidConnection(device::mojom::HidDeviceInfoPtr device)92 FakeFidoHidConnection::FakeFidoHidConnection(
93     device::mojom::HidDeviceInfoPtr device)
94     : device_(std::move(device)) {}
95 
96 FakeFidoHidConnection::~FakeFidoHidConnection() = default;
97 
Read(ReadCallback callback)98 void FakeFidoHidConnection::Read(ReadCallback callback) {
99   std::vector<uint8_t> buffer = {'F', 'a', 'k', 'e', ' ', 'H', 'i', 'd'};
100   std::move(callback).Run(true, 0, buffer);
101 }
102 
Write(uint8_t report_id,const std::vector<uint8_t> & buffer,WriteCallback callback)103 void FakeFidoHidConnection::Write(uint8_t report_id,
104                                   const std::vector<uint8_t>& buffer,
105                                   WriteCallback callback) {
106   if (mock_connection_error_) {
107     std::move(callback).Run(false);
108     return;
109   }
110 
111   std::move(callback).Run(true);
112 }
113 
GetFeatureReport(uint8_t report_id,GetFeatureReportCallback callback)114 void FakeFidoHidConnection::GetFeatureReport(
115     uint8_t report_id,
116     GetFeatureReportCallback callback) {
117   NOTREACHED();
118 }
119 
SendFeatureReport(uint8_t report_id,const std::vector<uint8_t> & buffer,SendFeatureReportCallback callback)120 void FakeFidoHidConnection::SendFeatureReport(
121     uint8_t report_id,
122     const std::vector<uint8_t>& buffer,
123     SendFeatureReportCallback callback) {
124   NOTREACHED();
125 }
126 
127 FakeFidoHidManager::FakeFidoHidManager() = default;
128 
129 FakeFidoHidManager::~FakeFidoHidManager() = default;
130 
AddReceiver(mojo::PendingReceiver<device::mojom::HidManager> receiver)131 void FakeFidoHidManager::AddReceiver(
132     mojo::PendingReceiver<device::mojom::HidManager> receiver) {
133   receivers_.Add(this, std::move(receiver));
134 }
135 
AddFidoHidDevice(std::string guid)136 void FakeFidoHidManager::AddFidoHidDevice(std::string guid) {
137   auto c_info = device::mojom::HidCollectionInfo::New();
138   c_info->usage = device::mojom::HidUsageAndPage::New(1, 0xf1d0);
139   auto device = device::mojom::HidDeviceInfo::New();
140   device->guid = std::move(guid);
141   device->product_name = "Test Fido Device";
142   device->serial_number = "123FIDO";
143   device->bus_type = device::mojom::HidBusType::kHIDBusTypeUSB;
144   device->collections.push_back(std::move(c_info));
145   device->max_input_report_size = 64;
146   device->max_output_report_size = 64;
147   AddDevice(std::move(device));
148 }
149 
GetDevicesAndSetClient(mojo::PendingAssociatedRemote<device::mojom::HidManagerClient> client,GetDevicesCallback callback)150 void FakeFidoHidManager::GetDevicesAndSetClient(
151     mojo::PendingAssociatedRemote<device::mojom::HidManagerClient> client,
152     GetDevicesCallback callback) {
153   GetDevices(std::move(callback));
154 
155   clients_.Add(std::move(client));
156 }
157 
GetDevices(GetDevicesCallback callback)158 void FakeFidoHidManager::GetDevices(GetDevicesCallback callback) {
159   std::vector<device::mojom::HidDeviceInfoPtr> device_list;
160   for (auto& map_entry : devices_)
161     device_list.push_back(map_entry.second->Clone());
162 
163   std::move(callback).Run(std::move(device_list));
164 }
165 
Connect(const std::string & device_guid,mojo::PendingRemote<mojom::HidConnectionClient> connection_client,mojo::PendingRemote<mojom::HidConnectionWatcher> watcher,ConnectCallback callback)166 void FakeFidoHidManager::Connect(
167     const std::string& device_guid,
168     mojo::PendingRemote<mojom::HidConnectionClient> connection_client,
169     mojo::PendingRemote<mojom::HidConnectionWatcher> watcher,
170     ConnectCallback callback) {
171   auto device_it = devices_.find(device_guid);
172   auto connection_it = connections_.find(device_guid);
173   if (device_it == devices_.end() || connection_it == connections_.end()) {
174     std::move(callback).Run(mojo::NullRemote());
175     return;
176   }
177 
178   std::move(callback).Run(std::move(connection_it->second));
179 }
180 
AddDevice(device::mojom::HidDeviceInfoPtr device)181 void FakeFidoHidManager::AddDevice(device::mojom::HidDeviceInfoPtr device) {
182   device::mojom::HidDeviceInfo* device_info = device.get();
183   for (auto& client : clients_)
184     client->DeviceAdded(device_info->Clone());
185 
186   devices_[device->guid] = std::move(device);
187 }
188 
AddDeviceAndSetConnection(device::mojom::HidDeviceInfoPtr device,mojo::PendingRemote<device::mojom::HidConnection> connection)189 void FakeFidoHidManager::AddDeviceAndSetConnection(
190     device::mojom::HidDeviceInfoPtr device,
191     mojo::PendingRemote<device::mojom::HidConnection> connection) {
192   connections_[device->guid] = std::move(connection);
193   AddDevice(std::move(device));
194 }
195 
RemoveDevice(const std::string device_guid)196 void FakeFidoHidManager::RemoveDevice(const std::string device_guid) {
197   auto it = devices_.find(device_guid);
198   if (it == devices_.end())
199     return;
200 
201   device::mojom::HidDeviceInfo* device_info = it->second.get();
202   for (auto& client : clients_)
203     client->DeviceRemoved(device_info->Clone());
204   devices_.erase(it);
205 }
206 
ScopedFakeFidoHidManager()207 ScopedFakeFidoHidManager::ScopedFakeFidoHidManager() {
208   FidoHidDiscovery::SetHidManagerBinder(base::BindRepeating(
209       &FakeFidoHidManager::AddReceiver, base::Unretained(this)));
210 }
211 
~ScopedFakeFidoHidManager()212 ScopedFakeFidoHidManager::~ScopedFakeFidoHidManager() {
213   FidoHidDiscovery::SetHidManagerBinder(base::NullCallback());
214 }
215 
216 }  // namespace device
217