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