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/mock_fido_device.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/strings/strcat.h"
12 #include "base/threading/thread_task_runner_handle.h"
13 #include "components/apdu/apdu_response.h"
14 #include "components/cbor/writer.h"
15 #include "device/fido/device_response_converter.h"
16 #include "device/fido/fido_constants.h"
17 #include "device/fido/fido_parsing_utils.h"
18 #include "device/fido/fido_test_data.h"
19 
20 namespace device {
21 
22 namespace {
DefaultAuthenticatorInfo()23 AuthenticatorGetInfoResponse DefaultAuthenticatorInfo() {
24   return *ReadCTAPGetInfoResponse(test_data::kTestAuthenticatorGetInfoResponse);
25 }
26 }  // namespace
27 
28 // static
MakeU2f()29 std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeU2f() {
30   return std::make_unique<MockFidoDevice>(ProtocolVersion::kU2f, base::nullopt);
31 }
32 
33 // static
MakeCtap(base::Optional<AuthenticatorGetInfoResponse> device_info)34 std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeCtap(
35     base::Optional<AuthenticatorGetInfoResponse> device_info) {
36   if (!device_info) {
37     device_info = DefaultAuthenticatorInfo();
38   }
39   auto device = std::make_unique<MockFidoDevice>(ProtocolVersion::kCtap2,
40                                                  std::move(*device_info));
41   device->StubGetDisplayName();
42   return device;
43 }
44 
45 // static
46 std::unique_ptr<MockFidoDevice>
MakeU2fWithGetInfoExpectation()47 MockFidoDevice::MakeU2fWithGetInfoExpectation() {
48   auto device = std::make_unique<MockFidoDevice>();
49   device->StubGetId();
50   device->StubGetDisplayName();
51   device->ExpectWinkedAtLeastOnce();
52   device->ExpectCtap2CommandAndRespondWith(
53       CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
54   return device;
55 }
56 
57 // static
MakeCtapWithGetInfoExpectation(base::Optional<base::span<const uint8_t>> get_info_response)58 std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeCtapWithGetInfoExpectation(
59     base::Optional<base::span<const uint8_t>> get_info_response) {
60   if (!get_info_response) {
61     get_info_response = test_data::kTestAuthenticatorGetInfoResponse;
62   }
63 
64   auto get_info = ReadCTAPGetInfoResponse(*get_info_response);
65   CHECK(get_info);
66   auto device = MockFidoDevice::MakeCtap(std::move(*get_info));
67   device->StubGetId();
68   device->StubGetDisplayName();
69   device->ExpectCtap2CommandAndRespondWith(
70       CtapRequestCommand::kAuthenticatorGetInfo, std::move(get_info_response));
71   return device;
72 }
73 
EncodeCBORRequest(std::pair<CtapRequestCommand,base::Optional<cbor::Value>> request)74 std::vector<uint8_t> MockFidoDevice::EncodeCBORRequest(
75     std::pair<CtapRequestCommand, base::Optional<cbor::Value>> request) {
76   std::vector<uint8_t> request_bytes;
77 
78   if (request.second) {
79     base::Optional<std::vector<uint8_t>> cbor_bytes =
80         cbor::Writer::Write(*request.second);
81     DCHECK(cbor_bytes);
82     request_bytes = std::move(*cbor_bytes);
83   }
84   request_bytes.insert(request_bytes.begin(),
85                        static_cast<uint8_t>(request.first));
86   return request_bytes;
87 }
88 
89 // Matcher to compare the fist byte of the incoming requests.
90 MATCHER_P(IsCtap2Command, expected_command, "") {
91   return !arg.empty() && arg[0] == base::strict_cast<uint8_t>(expected_command);
92 }
93 
MockFidoDevice()94 MockFidoDevice::MockFidoDevice() {}
MockFidoDevice(ProtocolVersion protocol_version,base::Optional<AuthenticatorGetInfoResponse> device_info)95 MockFidoDevice::MockFidoDevice(
96     ProtocolVersion protocol_version,
97     base::Optional<AuthenticatorGetInfoResponse> device_info)
98     : MockFidoDevice() {
99   set_supported_protocol(protocol_version);
100   if (device_info) {
101     SetDeviceInfo(std::move(*device_info));
102   }
103 }
104 MockFidoDevice::~MockFidoDevice() = default;
105 
DeviceTransact(std::vector<uint8_t> command,DeviceCallback cb)106 FidoDevice::CancelToken MockFidoDevice::DeviceTransact(
107     std::vector<uint8_t> command,
108     DeviceCallback cb) {
109   return DeviceTransactPtr(command, cb);
110 }
111 
TryWink(base::OnceClosure cb)112 void MockFidoDevice::TryWink(base::OnceClosure cb) {
113   TryWinkRef(cb);
114 }
115 
DeviceTransport() const116 FidoTransportProtocol MockFidoDevice::DeviceTransport() const {
117   return transport_protocol_;
118 }
119 
GetWeakPtr()120 base::WeakPtr<FidoDevice> MockFidoDevice::GetWeakPtr() {
121   return weak_factory_.GetWeakPtr();
122 }
123 
ExpectWinkedAtLeastOnce()124 void MockFidoDevice::ExpectWinkedAtLeastOnce() {
125   EXPECT_CALL(*this, TryWinkRef(::testing::_))
126       .Times(::testing::AtLeast(1))
127       .WillRepeatedly([](base::OnceClosure& cb) { std::move(cb).Run(); });
128 }
129 
StubGetId()130 void MockFidoDevice::StubGetId() {
131   // Use a counter to keep the device ID unique.
132   static size_t i = 0;
133   EXPECT_CALL(*this, GetId())
134       .WillRepeatedly(
135           testing::Return(base::StrCat({"mockdevice", std::to_string(i++)})));
136 }
137 
ExpectCtap2CommandAndRespondWith(CtapRequestCommand command,base::Optional<base::span<const uint8_t>> response,base::TimeDelta delay)138 void MockFidoDevice::ExpectCtap2CommandAndRespondWith(
139     CtapRequestCommand command,
140     base::Optional<base::span<const uint8_t>> response,
141     base::TimeDelta delay) {
142   auto data = fido_parsing_utils::MaterializeOrNull(response);
143   auto send_response = [ data(std::move(data)), delay ](DeviceCallback & cb) {
144     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
145         FROM_HERE, base::BindOnce(std::move(cb), std::move(data)), delay);
146   };
147 
148   EXPECT_CALL(*this, DeviceTransactPtr(IsCtap2Command(command), ::testing::_))
149       .WillOnce(::testing::DoAll(
150           ::testing::WithArg<1>(::testing::Invoke(send_response)),
151           ::testing::Return(0)));
152 }
153 
ExpectCtap2CommandAndRespondWithError(CtapRequestCommand command,CtapDeviceResponseCode response_code,base::TimeDelta delay)154 void MockFidoDevice::ExpectCtap2CommandAndRespondWithError(
155     CtapRequestCommand command,
156     CtapDeviceResponseCode response_code,
157     base::TimeDelta delay) {
158   std::array<uint8_t, 1> data{base::strict_cast<uint8_t>(response_code)};
159   return ExpectCtap2CommandAndRespondWith(std::move(command), data, delay);
160 }
161 
ExpectRequestAndRespondWith(base::span<const uint8_t> request,base::Optional<base::span<const uint8_t>> response,base::TimeDelta delay)162 void MockFidoDevice::ExpectRequestAndRespondWith(
163     base::span<const uint8_t> request,
164     base::Optional<base::span<const uint8_t>> response,
165     base::TimeDelta delay) {
166   auto data = fido_parsing_utils::MaterializeOrNull(response);
167   auto send_response = [ data(std::move(data)), delay ](DeviceCallback & cb) {
168     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
169         FROM_HERE, base::BindOnce(std::move(cb), std::move(data)), delay);
170   };
171 
172   auto request_as_vector = fido_parsing_utils::Materialize(request);
173   EXPECT_CALL(*this,
174               DeviceTransactPtr(std::move(request_as_vector), ::testing::_))
175       .WillOnce(::testing::DoAll(
176           ::testing::WithArg<1>(::testing::Invoke(send_response)),
177           ::testing::Return(0)));
178 }
179 
ExpectCtap2CommandAndDoNotRespond(CtapRequestCommand command)180 void MockFidoDevice::ExpectCtap2CommandAndDoNotRespond(
181     CtapRequestCommand command) {
182   EXPECT_CALL(*this, DeviceTransactPtr(IsCtap2Command(command), ::testing::_))
183       .WillOnce(::testing::Return(0));
184 }
185 
ExpectRequestAndDoNotRespond(base::span<const uint8_t> request)186 void MockFidoDevice::ExpectRequestAndDoNotRespond(
187     base::span<const uint8_t> request) {
188   auto request_as_vector = fido_parsing_utils::Materialize(request);
189   EXPECT_CALL(*this,
190               DeviceTransactPtr(std::move(request_as_vector), ::testing::_))
191       .WillOnce(::testing::Return(0));
192 }
193 
SetDeviceTransport(FidoTransportProtocol transport_protocol)194 void MockFidoDevice::SetDeviceTransport(
195     FidoTransportProtocol transport_protocol) {
196   transport_protocol_ = transport_protocol;
197 }
198 
StubGetDisplayName()199 void MockFidoDevice::StubGetDisplayName() {
200   EXPECT_CALL(*this, GetDisplayName())
201       .WillRepeatedly(testing::Return(base::string16()));
202 }
203 
204 }  // namespace device
205