| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "device/fido/hid/fake_hid_impl_for_testing.h" |
| |
| #include <utility> |
| |
| #include "device/fido/fido_parsing_utils.h" |
| #include "services/device/public/mojom/constants.mojom.h" |
| #include "services/device/public/mojom/hid.mojom.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "services/service_manager/public/mojom/connector.mojom.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| MATCHER_P(IsCtapHidCommand, expected_command, "") { |
| return arg.size() >= 5 && |
| arg[4] == (0x80 | static_cast<uint8_t>(expected_command)); |
| } |
| |
| } // namespace |
| |
| MockFidoHidConnection::MockFidoHidConnection( |
| device::mojom::HidDeviceInfoPtr device, |
| device::mojom::HidConnectionRequest request, |
| std::vector<uint8_t> connection_channel_id) |
| : binding_(this, std::move(request)), |
| device_(std::move(device)), |
| connection_channel_id_(connection_channel_id) {} |
| |
| MockFidoHidConnection::~MockFidoHidConnection() {} |
| |
| void MockFidoHidConnection::Read(ReadCallback callback) { |
| return ReadPtr(&callback); |
| } |
| |
| void MockFidoHidConnection::Write(uint8_t report_id, |
| const std::vector<uint8_t>& buffer, |
| WriteCallback callback) { |
| return WritePtr(report_id, buffer, &callback); |
| } |
| |
| void MockFidoHidConnection::GetFeatureReport( |
| uint8_t report_id, |
| GetFeatureReportCallback callback) { |
| NOTREACHED(); |
| } |
| |
| void MockFidoHidConnection::SendFeatureReport( |
| uint8_t report_id, |
| const std::vector<uint8_t>& buffer, |
| SendFeatureReportCallback callback) { |
| NOTREACHED(); |
| } |
| |
| void MockFidoHidConnection::SetNonce(base::span<uint8_t const> nonce) { |
| nonce_ = std::vector<uint8_t>(nonce.begin(), nonce.end()); |
| } |
| |
| void MockFidoHidConnection::ExpectWriteHidInit() { |
| EXPECT_CALL(*this, WritePtr(::testing::_, |
| IsCtapHidCommand(FidoHidDeviceCommand::kInit), |
| ::testing::_)) |
| .WillOnce(::testing::Invoke( |
| [&](auto&&, const std::vector<uint8_t>& buffer, |
| device::mojom::HidConnection::WriteCallback* cb) { |
| ASSERT_EQ(64u, buffer.size()); |
| // First 7 bytes are 4 bytes of channel id, one byte representing |
| // HID command, 2 bytes for payload length. |
| SetNonce(base::make_span(buffer).subspan(7, 8)); |
| std::move(*cb).Run(true); |
| })); |
| } |
| |
| void MockFidoHidConnection::ExpectHidWriteWithCommand( |
| FidoHidDeviceCommand cmd) { |
| EXPECT_CALL(*this, |
| WritePtr(::testing::_, IsCtapHidCommand(cmd), ::testing::_)) |
| .WillOnce(::testing::Invoke( |
| [&](auto&&, const std::vector<uint8_t>& buffer, |
| device::mojom::HidConnection::WriteCallback* cb) { |
| std::move(*cb).Run(true); |
| })); |
| } |
| |
| bool FakeHidConnection::mock_connection_error_ = false; |
| |
| FakeHidConnection::FakeHidConnection(device::mojom::HidDeviceInfoPtr device) |
| : device_(std::move(device)) {} |
| |
| FakeHidConnection::~FakeHidConnection() = default; |
| |
| void FakeHidConnection::Read(ReadCallback callback) { |
| std::vector<uint8_t> buffer = {'F', 'a', 'k', 'e', ' ', 'H', 'i', 'd'}; |
| std::move(callback).Run(true, 0, buffer); |
| } |
| |
| void FakeHidConnection::Write(uint8_t report_id, |
| const std::vector<uint8_t>& buffer, |
| WriteCallback callback) { |
| if (mock_connection_error_) { |
| std::move(callback).Run(false); |
| return; |
| } |
| |
| std::move(callback).Run(true); |
| } |
| |
| void FakeHidConnection::GetFeatureReport(uint8_t report_id, |
| GetFeatureReportCallback callback) { |
| NOTREACHED(); |
| } |
| |
| void FakeHidConnection::SendFeatureReport(uint8_t report_id, |
| const std::vector<uint8_t>& buffer, |
| SendFeatureReportCallback callback) { |
| NOTREACHED(); |
| } |
| |
| FakeHidManager::FakeHidManager() = default; |
| |
| FakeHidManager::~FakeHidManager() = default; |
| |
| void FakeHidManager::AddBinding(mojo::ScopedMessagePipeHandle handle) { |
| bindings_.AddBinding(this, |
| device::mojom::HidManagerRequest(std::move(handle))); |
| } |
| |
| void FakeHidManager::AddBinding2(device::mojom::HidManagerRequest request) { |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| void FakeHidManager::AddFidoHidDevice(std::string guid) { |
| auto c_info = device::mojom::HidCollectionInfo::New(); |
| c_info->usage = device::mojom::HidUsageAndPage::New(1, 0xf1d0); |
| auto device = device::mojom::HidDeviceInfo::New(); |
| device->guid = std::move(guid); |
| device->product_name = "Test Fido Device"; |
| device->serial_number = "123FIDO"; |
| device->bus_type = device::mojom::HidBusType::kHIDBusTypeUSB; |
| device->collections.push_back(std::move(c_info)); |
| device->max_input_report_size = 64; |
| device->max_output_report_size = 64; |
| AddDevice(std::move(device)); |
| } |
| |
| void FakeHidManager::GetDevicesAndSetClient( |
| device::mojom::HidManagerClientAssociatedPtrInfo client, |
| GetDevicesCallback callback) { |
| GetDevices(std::move(callback)); |
| |
| device::mojom::HidManagerClientAssociatedPtr client_ptr; |
| client_ptr.Bind(std::move(client)); |
| clients_.AddPtr(std::move(client_ptr)); |
| } |
| |
| void FakeHidManager::GetDevices(GetDevicesCallback callback) { |
| std::vector<device::mojom::HidDeviceInfoPtr> device_list; |
| for (auto& map_entry : devices_) |
| device_list.push_back(map_entry.second->Clone()); |
| |
| std::move(callback).Run(std::move(device_list)); |
| } |
| |
| void FakeHidManager::Connect(const std::string& device_guid, |
| mojom::HidConnectionClientPtr connection_client, |
| ConnectCallback callback) { |
| auto device_it = devices_.find(device_guid); |
| auto connection_it = connections_.find(device_guid); |
| if (device_it == devices_.end() || connection_it == connections_.end()) { |
| std::move(callback).Run(nullptr); |
| return; |
| } |
| |
| std::move(callback).Run(std::move(connection_it->second)); |
| } |
| |
| void FakeHidManager::AddDevice(device::mojom::HidDeviceInfoPtr device) { |
| device::mojom::HidDeviceInfo* device_info = device.get(); |
| clients_.ForAllPtrs([device_info](device::mojom::HidManagerClient* client) { |
| client->DeviceAdded(device_info->Clone()); |
| }); |
| |
| devices_[device->guid] = std::move(device); |
| } |
| |
| void FakeHidManager::AddDeviceAndSetConnection( |
| device::mojom::HidDeviceInfoPtr device, |
| device::mojom::HidConnectionPtr connection) { |
| connections_[device->guid] = std::move(connection); |
| AddDevice(std::move(device)); |
| } |
| |
| void FakeHidManager::RemoveDevice(const std::string device_guid) { |
| auto it = devices_.find(device_guid); |
| if (it == devices_.end()) |
| return; |
| |
| device::mojom::HidDeviceInfo* device_info = it->second.get(); |
| clients_.ForAllPtrs([device_info](device::mojom::HidManagerClient* client) { |
| client->DeviceRemoved(device_info->Clone()); |
| }); |
| devices_.erase(it); |
| } |
| |
| ScopedFakeHidManager::ScopedFakeHidManager() { |
| service_manager::mojom::ConnectorRequest request; |
| connector_ = service_manager::Connector::Create(&request); |
| connector_->OverrideBinderForTesting( |
| service_manager::ServiceFilter::ByName(device::mojom::kServiceName), |
| device::mojom::HidManager::Name_, |
| base::BindRepeating(&FakeHidManager::AddBinding, base::Unretained(this))); |
| } |
| |
| ScopedFakeHidManager::~ScopedFakeHidManager() = default; |
| |
| } // namespace device |