blob: e762657803be59fda83d8863d7dc4d1b1067679e [file] [log] [blame]
Jun Choi4fc8b7812018-04-05 07:39:071// Copyright 2018 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 <memory>
6#include <utility>
7
Balazs Engedy159452d2018-08-16 15:18:188#include "base/bind.h"
9#include "base/bind_helpers.h"
Jun Choi0e56e5dd2018-06-08 21:27:3410#include "base/test/scoped_feature_list.h"
Jun Choi4fc8b7812018-04-05 07:39:0711#include "base/test/scoped_task_environment.h"
Jun Choi0e56e5dd2018-06-08 21:27:3412#include "device/base/features.h"
Jun Choife176682018-08-30 07:49:1413#include "device/bluetooth/bluetooth_adapter_factory.h"
14#include "device/bluetooth/test/mock_bluetooth_adapter.h"
Jun Choi4fc8b7812018-04-05 07:39:0715#include "device/fido/authenticator_get_assertion_response.h"
16#include "device/fido/ctap_get_assertion_request.h"
Balazs Engedy159452d2018-08-16 15:18:1817#include "device/fido/device_response_converter.h"
Jun Choi4fc8b7812018-04-05 07:39:0718#include "device/fido/fake_fido_discovery.h"
19#include "device/fido/fido_constants.h"
Jun Choi19b944e92018-04-23 20:20:2020#include "device/fido/fido_parsing_utils.h"
Jun Choi22af8b372018-04-09 04:29:1821#include "device/fido/fido_test_data.h"
Jun Choib60937e2018-04-12 17:02:3822#include "device/fido/fido_transport_protocol.h"
Jun Choi4fc8b7812018-04-05 07:39:0723#include "device/fido/get_assertion_request_handler.h"
24#include "device/fido/mock_fido_device.h"
25#include "device/fido/test_callback_receiver.h"
Jun Choi4fc8b7812018-04-05 07:39:0726#include "testing/gmock/include/gmock/gmock.h"
27#include "testing/gtest/include/gtest/gtest.h"
28
29namespace device {
30
31namespace {
32
Balazs Engedy159452d2018-08-16 15:18:1833constexpr uint8_t kBogusCredentialId[] = {0x01, 0x02, 0x03, 0x04};
34
Jun Choi1beb4c22018-08-16 03:04:3835using TestGetAssertionRequestCallback = test::StatusAndValuesCallbackReceiver<
Jun Choif7ab0df2018-04-05 21:48:1636 FidoReturnCode,
Jun Choi1beb4c22018-08-16 03:04:3837 base::Optional<AuthenticatorGetAssertionResponse>,
38 FidoTransportProtocol>;
Jun Choi4fc8b7812018-04-05 07:39:0739
40} // namespace
41
42class FidoGetAssertionHandlerTest : public ::testing::Test {
43 public:
Jun Choife176682018-08-30 07:49:1444 FidoGetAssertionHandlerTest() {
45 mock_adapter_ =
46 base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
47 BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
48 }
49
Balazs Engedy159452d2018-08-16 15:18:1850 void ForgeDiscoveries() {
Jun Choi4fc8b7812018-04-05 07:39:0751 discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
Balazs Engedy159452d2018-08-16 15:18:1852 ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
Balazs Engedy5b4891f2018-08-29 23:08:0053 cable_discovery_ = scoped_fake_discovery_factory_.ForgeNextCableDiscovery();
Balazs Engedy159452d2018-08-16 15:18:1854 nfc_discovery_ = scoped_fake_discovery_factory_.ForgeNextNfcDiscovery();
Jun Choi4fc8b7812018-04-05 07:39:0755 }
56
Balazs Engedy5b4891f2018-08-29 23:08:0057 CtapGetAssertionRequest CreateTestRequestWithCableExtension() {
58 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
59 test_data::kClientDataHash);
60 request.SetCableExtension({});
61 return request;
62 }
63
Martin Kreichgauer8987fd02018-07-20 22:42:0364 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerU2f() {
Martin Kreichgauer2b753722018-07-17 01:06:0165 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
66 test_data::kClientDataHash);
67 request.SetAllowList(
Jan Wilken Doerrie726e197e2018-05-14 12:53:2568 {{CredentialType::kPublicKey,
Jun Choi0e56e5dd2018-06-08 21:27:3469 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
Martin Kreichgauer8987fd02018-07-20 22:42:0370 return CreateGetAssertionHandlerWithRequest(std::move(request));
71 }
Jun Choi4fc8b7812018-04-05 07:39:0772
Martin Kreichgauer8987fd02018-07-20 22:42:0373 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerCtap() {
74 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
75 test_data::kClientDataHash);
76 request.SetAllowList({{CredentialType::kPublicKey,
77 fido_parsing_utils::Materialize(
78 test_data::kTestGetAssertionCredentialId)}});
Martin Kreichgauer2b753722018-07-17 01:06:0179 return CreateGetAssertionHandlerWithRequest(std::move(request));
80 }
81
82 std::unique_ptr<GetAssertionRequestHandler>
83 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
Balazs Engedy159452d2018-08-16 15:18:1884 ForgeDiscoveries();
Martin Kreichgauer2b753722018-07-17 01:06:0185
Martin Kreichgauer7cbada432018-08-18 02:30:1486 auto handler = std::make_unique<GetAssertionRequestHandler>(
Balazs Engedy159452d2018-08-16 15:18:1887 nullptr /* connector */, supported_transports_, std::move(request),
Martin Kreichgauer7cbada432018-08-18 02:30:1488 get_assertion_cb_.callback());
89 handler->SetPlatformAuthenticatorOrMarkUnavailable(
90 CreatePlatformAuthenticator());
91 return handler;
Balazs Engedy159452d2018-08-16 15:18:1892 }
93
94 void ExpectAllowedTransportsForRequestAre(
95 GetAssertionRequestHandler* request_handler,
96 base::flat_set<FidoTransportProtocol> transports) {
97 using Transport = FidoTransportProtocol;
98 if (base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
99 discovery()->WaitForCallToStartAndSimulateSuccess();
100 if (base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
101 ble_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy5b4891f2018-08-29 23:08:00102 if (base::ContainsKey(transports,
103 Transport::kCloudAssistedBluetoothLowEnergy))
104 cable_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy159452d2018-08-16 15:18:18105 if (base::ContainsKey(transports, Transport::kNearFieldCommunication))
106 nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy159452d2018-08-16 15:18:18107
108 scoped_task_environment_.FastForwardUntilNoTasksRemain();
109 EXPECT_FALSE(get_assertion_callback().was_called());
110
111 if (!base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
112 EXPECT_FALSE(discovery()->is_start_requested());
113 if (!base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
114 EXPECT_FALSE(ble_discovery()->is_start_requested());
Balazs Engedy5b4891f2018-08-29 23:08:00115 if (!base::ContainsKey(transports,
116 Transport::kCloudAssistedBluetoothLowEnergy))
117 EXPECT_FALSE(cable_discovery()->is_start_requested());
Balazs Engedy159452d2018-08-16 15:18:18118 if (!base::ContainsKey(transports, Transport::kNearFieldCommunication))
119 EXPECT_FALSE(nfc_discovery()->is_start_requested());
Balazs Engedy159452d2018-08-16 15:18:18120
121 // Even with FidoTransportProtocol::kInternal allowed, unless the platform
122 // authenticator factory returns a FidoAuthenticator instance (which it will
123 // not be default), the transport will be marked `unavailable`.
124 transports.erase(Transport::kInternal);
125
126 EXPECT_THAT(
127 request_handler->transport_availability_info().available_transports,
128 ::testing::UnorderedElementsAreArray(transports));
129 }
130
131 void ExpectAllTransportsAreAllowedForRequest(
132 GetAssertionRequestHandler* request_handler) {
133 ExpectAllowedTransportsForRequestAre(request_handler,
Balazs Engedy5b4891f2018-08-29 23:08:00134 GetAllTransportProtocols());
Jun Choi4fc8b7812018-04-05 07:39:07135 }
136
Jun Choid19453d2018-06-21 23:16:39137 void InitFeatureListAndDisableCtapFlag() {
138 scoped_feature_list_.InitAndDisableFeature(kNewCtap2Device);
Jun Choi0e56e5dd2018-06-08 21:27:34139 }
Jun Choi4fc8b7812018-04-05 07:39:07140
Jun Choi0e56e5dd2018-06-08 21:27:34141 test::FakeFidoDiscovery* discovery() const { return discovery_; }
Balazs Engedy159452d2018-08-16 15:18:18142 test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
Balazs Engedy5b4891f2018-08-29 23:08:00143 test::FakeFidoDiscovery* cable_discovery() const { return cable_discovery_; }
Balazs Engedy159452d2018-08-16 15:18:18144 test::FakeFidoDiscovery* nfc_discovery() const { return nfc_discovery_; }
Jun Choi4fc8b7812018-04-05 07:39:07145 TestGetAssertionRequestCallback& get_assertion_callback() {
146 return get_assertion_cb_;
147 }
148
Balazs Engedy159452d2018-08-16 15:18:18149 void set_mock_platform_device(std::unique_ptr<MockFidoDevice> device) {
150 mock_platform_device_ = std::move(device);
151 }
152
Balazs Engedy159452d2018-08-16 15:18:18153 void set_supported_transports(
154 base::flat_set<FidoTransportProtocol> transports) {
155 supported_transports_ = std::move(transports);
156 }
157
Jun Choi4fc8b7812018-04-05 07:39:07158 protected:
Martin Kreichgauere1223082018-08-20 23:17:38159 base::Optional<PlatformAuthenticatorInfo> CreatePlatformAuthenticator() {
Balazs Engedy159452d2018-08-16 15:18:18160 if (!mock_platform_device_)
Martin Kreichgauere1223082018-08-20 23:17:38161 return base::nullopt;
162 return PlatformAuthenticatorInfo(
163 std::make_unique<FidoDeviceAuthenticator>(mock_platform_device_.get()),
164 false /* has_recognized_mac_touch_id_credential_available */);
Balazs Engedy159452d2018-08-16 15:18:18165 }
166
Jun Choi4fc8b7812018-04-05 07:39:07167 base::test::ScopedTaskEnvironment scoped_task_environment_{
168 base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
Jun Choi0e56e5dd2018-06-08 21:27:34169 base::test::ScopedFeatureList scoped_feature_list_;
Jun Choi4fc8b7812018-04-05 07:39:07170 test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
171 test::FakeFidoDiscovery* discovery_;
Balazs Engedy159452d2018-08-16 15:18:18172 test::FakeFidoDiscovery* ble_discovery_;
Balazs Engedy5b4891f2018-08-29 23:08:00173 test::FakeFidoDiscovery* cable_discovery_;
Balazs Engedy159452d2018-08-16 15:18:18174 test::FakeFidoDiscovery* nfc_discovery_;
Jun Choife176682018-08-30 07:49:14175 scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;
Balazs Engedy159452d2018-08-16 15:18:18176 std::unique_ptr<MockFidoDevice> mock_platform_device_;
Jun Choi4fc8b7812018-04-05 07:39:07177 TestGetAssertionRequestCallback get_assertion_cb_;
Balazs Engedy159452d2018-08-16 15:18:18178 base::flat_set<FidoTransportProtocol> supported_transports_ =
Balazs Engedy5b4891f2018-08-29 23:08:00179 GetAllTransportProtocols();
Jun Choi4fc8b7812018-04-05 07:39:07180};
181
Balazs Engedy0c4fdc52018-08-20 19:15:28182TEST_F(FidoGetAssertionHandlerTest, TransportAvailabilityInfo) {
183 auto request_handler =
184 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
185 test_data::kRelyingPartyId, test_data::kClientDataHash));
186
187 EXPECT_EQ(FidoRequestHandlerBase::RequestType::kGetAssertion,
188 request_handler->transport_availability_info().request_type);
189 EXPECT_EQ(test_data::kRelyingPartyId,
190 request_handler->transport_availability_info().rp_id);
191}
192
Martin Kreichgauer8987fd02018-07-20 22:42:03193TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
194 auto request_handler = CreateGetAssertionHandlerCtap();
Jun Choi4fc8b7812018-04-05 07:39:07195 discovery()->WaitForCallToStartAndSimulateSuccess();
Martin Kreichgauer8987fd02018-07-20 22:42:03196 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Jun Choi4fc8b7812018-04-05 07:39:07197 device->ExpectCtap2CommandAndRespondWith(
198 CtapRequestCommand::kAuthenticatorGetAssertion,
199 test_data::kTestGetAssertionResponse);
200
201 discovery()->AddDevice(std::move(device));
202 get_assertion_callback().WaitForCallback();
203
204 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38205 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi4fc8b7812018-04-05 07:39:07206 EXPECT_TRUE(request_handler->is_complete());
207}
208
Jun Choi0e56e5dd2018-06-08 21:27:34209// Test a scenario where the connected authenticator is a U2F device.
210TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
Martin Kreichgauer8987fd02018-07-20 22:42:03211 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi4fc8b7812018-04-05 07:39:07212 discovery()->WaitForCallToStartAndSimulateSuccess();
213
Martin Kreichgauer8987fd02018-07-20 22:42:03214 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
Jun Choi0e56e5dd2018-06-08 21:27:34215 device->ExpectRequestAndRespondWith(
216 test_data::kU2fCheckOnlySignCommandApdu,
217 test_data::kApduEncodedNoErrorSignResponse);
218 device->ExpectRequestAndRespondWith(
219 test_data::kU2fSignCommandApdu,
220 test_data::kApduEncodedNoErrorSignResponse);
Jun Choi4fc8b7812018-04-05 07:39:07221
222 discovery()->AddDevice(std::move(device));
223 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Jun Choi0e56e5dd2018-06-08 21:27:34224 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38225 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi0e56e5dd2018-06-08 21:27:34226 EXPECT_TRUE(request_handler->is_complete());
227}
228
229// Test a scenario where the connected authenticator is a U2F device and
230// "WebAuthenticationCtap2" flag is not enabled.
231TEST_F(FidoGetAssertionHandlerTest, TestU2fSignWithoutCtapFlag) {
Jun Choid19453d2018-06-21 23:16:39232 InitFeatureListAndDisableCtapFlag();
Martin Kreichgauer8987fd02018-07-20 22:42:03233 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi0e56e5dd2018-06-08 21:27:34234 discovery()->WaitForCallToStartAndSimulateSuccess();
235
236 auto device = std::make_unique<MockFidoDevice>();
237 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
238 device->ExpectRequestAndRespondWith(
239 test_data::kU2fCheckOnlySignCommandApdu,
240 test_data::kApduEncodedNoErrorSignResponse);
241 device->ExpectRequestAndRespondWith(
242 test_data::kU2fSignCommandApdu,
243 test_data::kApduEncodedNoErrorSignResponse);
244
245 discovery()->AddDevice(std::move(device));
246 scoped_task_environment_.FastForwardUntilNoTasksRemain();
247 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38248 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi0e56e5dd2018-06-08 21:27:34249 EXPECT_TRUE(request_handler->is_complete());
Jun Choi4fc8b7812018-04-05 07:39:07250}
251
Martin Kreichgauer2b753722018-07-17 01:06:01252TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {
253 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
254 test_data::kClientDataHash);
255 request.SetUserVerification(UserVerificationRequirement::kRequired);
256 auto request_handler =
257 CreateGetAssertionHandlerWithRequest(std::move(request));
258 discovery()->WaitForCallToStartAndSimulateSuccess();
259
Martin Kreichgauer8987fd02018-07-20 22:42:03260 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
Martin Kreichgauer2b753722018-07-17 01:06:01261 test_data::kTestGetInfoResponseWithoutUvSupport);
262
263 discovery()->AddDevice(std::move(device));
264
265 scoped_task_environment_.FastForwardUntilNoTasksRemain();
266 EXPECT_FALSE(get_assertion_callback().was_called());
267}
268
269TEST_F(FidoGetAssertionHandlerTest,
270 TestU2fSignRequestWithUserVerificationRequired) {
271 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
272 test_data::kClientDataHash);
273 request.SetAllowList(
274 {{CredentialType::kPublicKey,
275 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
276 request.SetUserVerification(UserVerificationRequirement::kRequired);
277 auto request_handler =
278 CreateGetAssertionHandlerWithRequest(std::move(request));
279 discovery()->WaitForCallToStartAndSimulateSuccess();
280
Martin Kreichgauer8987fd02018-07-20 22:42:03281 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
282 discovery()->AddDevice(std::move(device));
283
284 scoped_task_environment_.FastForwardUntilNoTasksRemain();
285 EXPECT_FALSE(get_assertion_callback().was_called());
286}
287
288TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) {
289 auto request_handler =
290 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
291 test_data::kRelyingPartyId, test_data::kClientDataHash));
292 discovery()->WaitForCallToStartAndSimulateSuccess();
293 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Martin Kreichgauer2b753722018-07-17 01:06:01294 device->ExpectCtap2CommandAndRespondWith(
Martin Kreichgauer8987fd02018-07-20 22:42:03295 CtapRequestCommand::kAuthenticatorGetAssertion,
296 test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
297
298 discovery()->AddDevice(std::move(device));
299
300 scoped_task_environment_.FastForwardUntilNoTasksRemain();
301 EXPECT_FALSE(get_assertion_callback().was_called());
302}
303
304// Tests a scenario where the authenticator responds with credential ID that
305// is not included in the allowed list.
306TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) {
307 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
308 test_data::kClientDataHash);
309 request.SetAllowList(
310 {{CredentialType::kPublicKey,
311 fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)}});
312 auto request_handler =
313 CreateGetAssertionHandlerWithRequest(std::move(request));
314 discovery()->WaitForCallToStartAndSimulateSuccess();
315 // Resident Keys must be disabled, otherwise allow list check is skipped.
316 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
317 test_data::kTestGetInfoResponseWithoutResidentKeySupport);
318 device->ExpectCtap2CommandAndRespondWith(
319 CtapRequestCommand::kAuthenticatorGetAssertion,
320 test_data::kTestGetAssertionResponse);
321
322 discovery()->AddDevice(std::move(device));
323
324 scoped_task_environment_.FastForwardUntilNoTasksRemain();
325 EXPECT_FALSE(get_assertion_callback().was_called());
326}
327
328// Tests a scenario where authenticator responds without user entity in its
329// response but client is expecting a resident key credential.
330TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) {
331 // Use a GetAssertion request with an empty allow list.
332 auto request_handler =
333 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
334 test_data::kRelyingPartyId, test_data::kClientDataHash));
335 discovery()->WaitForCallToStartAndSimulateSuccess();
336 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
337 device->ExpectCtap2CommandAndRespondWith(
338 CtapRequestCommand::kAuthenticatorGetAssertion,
339 test_data::kTestGetAssertionResponse);
Martin Kreichgauer2b753722018-07-17 01:06:01340
341 discovery()->AddDevice(std::move(device));
342
343 scoped_task_environment_.FastForwardUntilNoTasksRemain();
344 EXPECT_FALSE(get_assertion_callback().was_called());
345}
346
Balazs Engedy159452d2018-08-16 15:18:18347TEST_F(FidoGetAssertionHandlerTest,
348 AllTransportsAllowedIfAllowCredentialsListUndefined) {
Balazs Engedy5b4891f2018-08-29 23:08:00349 auto request = CreateTestRequestWithCableExtension();
Jun Choife176682018-08-30 07:49:14350 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18351 auto request_handler =
Balazs Engedy5b4891f2018-08-29 23:08:00352 CreateGetAssertionHandlerWithRequest(std::move(request));
Balazs Engedy159452d2018-08-16 15:18:18353 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
354}
355
356TEST_F(FidoGetAssertionHandlerTest,
357 AllTransportsAllowedIfAllowCredentialsListIsEmpty) {
Balazs Engedy5b4891f2018-08-29 23:08:00358 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18359 request.SetAllowList({});
Jun Choife176682018-08-30 07:49:14360 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18361 auto request_handler =
362 CreateGetAssertionHandlerWithRequest(std::move(request));
363 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
364}
365
366TEST_F(FidoGetAssertionHandlerTest,
367 AllTransportsAllowedIfHasAllowedCredentialWithEmptyTransportsList) {
Balazs Engedy5b4891f2018-08-29 23:08:00368 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18369 request.SetAllowList({
370 {CredentialType::kPublicKey,
371 fido_parsing_utils::Materialize(
372 test_data::kTestGetAssertionCredentialId),
373 {FidoTransportProtocol::kBluetoothLowEnergy}},
374 {CredentialType::kPublicKey,
375 fido_parsing_utils::Materialize(kBogusCredentialId)},
376 });
377
Jun Choife176682018-08-30 07:49:14378 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18379 auto request_handler =
380 CreateGetAssertionHandlerWithRequest(std::move(request));
381 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
382}
383
384TEST_F(FidoGetAssertionHandlerTest,
385 AllowedTransportsAreUnionOfTransportsLists) {
Balazs Engedy5b4891f2018-08-29 23:08:00386 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18387 request.SetAllowList({
388 {CredentialType::kPublicKey,
389 fido_parsing_utils::Materialize(
390 test_data::kTestGetAssertionCredentialId),
391 {FidoTransportProtocol::kBluetoothLowEnergy}},
392 {CredentialType::kPublicKey,
393 fido_parsing_utils::Materialize(kBogusCredentialId),
394 {FidoTransportProtocol::kInternal,
395 FidoTransportProtocol::kNearFieldCommunication}},
396 });
397
Jun Choife176682018-08-30 07:49:14398 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18399 auto request_handler =
400 CreateGetAssertionHandlerWithRequest(std::move(request));
401 ExpectAllowedTransportsForRequestAre(
402 request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
403 FidoTransportProtocol::kInternal,
404 FidoTransportProtocol::kNearFieldCommunication});
405}
406
Balazs Engedy5b4891f2018-08-29 23:08:00407TEST_F(FidoGetAssertionHandlerTest,
408 CableDisabledIfAllowCredentialsListUndefinedButCableExtensionMissing) {
409 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
410 test_data::kClientDataHash);
411 ASSERT_FALSE(!!request.cable_extension());
Jun Choife176682018-08-30 07:49:14412 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy5b4891f2018-08-29 23:08:00413 auto request_handler =
414 CreateGetAssertionHandlerWithRequest(std::move(request));
415 ExpectAllowedTransportsForRequestAre(
416 request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
417 FidoTransportProtocol::kUsbHumanInterfaceDevice,
418 FidoTransportProtocol::kNearFieldCommunication,
419 FidoTransportProtocol::kInternal});
420}
421
422TEST_F(FidoGetAssertionHandlerTest,
423 CableDisabledIfExplicitlyAllowedButCableExtensionMissing) {
424 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
425 test_data::kClientDataHash);
426 ASSERT_FALSE(!!request.cable_extension());
427 request.SetAllowList({
428 {CredentialType::kPublicKey,
429 fido_parsing_utils::Materialize(
430 test_data::kTestGetAssertionCredentialId),
431 {FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
432 FidoTransportProtocol::kUsbHumanInterfaceDevice}},
433 });
434
435 auto request_handler =
436 CreateGetAssertionHandlerWithRequest(std::move(request));
437 ExpectAllowedTransportsForRequestAre(
438 request_handler.get(), {FidoTransportProtocol::kUsbHumanInterfaceDevice});
439}
440
Balazs Engedy159452d2018-08-16 15:18:18441TEST_F(FidoGetAssertionHandlerTest, SupportedTransportsAreOnlyBleAndNfc) {
442 const base::flat_set<FidoTransportProtocol> kBleAndNfc = {
443 FidoTransportProtocol::kBluetoothLowEnergy,
444 FidoTransportProtocol::kNearFieldCommunication,
445 };
446
447 set_supported_transports(kBleAndNfc);
Jun Choife176682018-08-30 07:49:14448 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy5b4891f2018-08-29 23:08:00449 auto request_handler = CreateGetAssertionHandlerWithRequest(
450 CreateTestRequestWithCableExtension());
Balazs Engedy159452d2018-08-16 15:18:18451 ExpectAllowedTransportsForRequestAre(request_handler.get(), kBleAndNfc);
452}
453
Balazs Engedy5b4891f2018-08-29 23:08:00454TEST_F(FidoGetAssertionHandlerTest,
455 SupportedTransportsAreOnlyCableAndInternal) {
456 const base::flat_set<FidoTransportProtocol> kCableAndInternal = {
Balazs Engedy159452d2018-08-16 15:18:18457 FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
458 FidoTransportProtocol::kInternal,
459 };
460
Jun Choife176682018-08-30 07:49:14461 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy5b4891f2018-08-29 23:08:00462 set_supported_transports(kCableAndInternal);
463 auto request_handler = CreateGetAssertionHandlerWithRequest(
464 CreateTestRequestWithCableExtension());
465 ExpectAllowedTransportsForRequestAre(request_handler.get(),
466 kCableAndInternal);
Balazs Engedy159452d2018-08-16 15:18:18467}
468
469TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyUsbTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00470 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18471 request.SetAllowList({
472 {CredentialType::kPublicKey,
473 fido_parsing_utils::Materialize(
474 test_data::kTestGetAssertionCredentialId),
475 {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
476 });
477
478 set_supported_transports({FidoTransportProtocol::kUsbHumanInterfaceDevice});
479
480 auto request_handler =
481 CreateGetAssertionHandlerWithRequest(std::move(request));
482
483 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
484 device->ExpectCtap2CommandAndRespondWith(
485 CtapRequestCommand::kAuthenticatorGetAssertion,
486 test_data::kTestGetAssertionResponse);
487 discovery()->WaitForCallToStartAndSimulateSuccess();
488 discovery()->AddDevice(std::move(device));
489
490 get_assertion_callback().WaitForCallback();
491
492 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
493 EXPECT_TRUE(get_assertion_callback().value<0>());
494 EXPECT_TRUE(request_handler->is_complete());
495 EXPECT_THAT(
496 request_handler->transport_availability_info().available_transports,
497 ::testing::UnorderedElementsAre(
498 FidoTransportProtocol::kUsbHumanInterfaceDevice));
499}
500
501TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyBleTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00502 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18503 request.SetAllowList({
504 {CredentialType::kPublicKey,
505 fido_parsing_utils::Materialize(
506 test_data::kTestGetAssertionCredentialId),
507 {FidoTransportProtocol::kBluetoothLowEnergy}},
508 });
509
510 set_supported_transports({FidoTransportProtocol::kBluetoothLowEnergy});
Jun Choife176682018-08-30 07:49:14511 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18512 auto request_handler =
513 CreateGetAssertionHandlerWithRequest(std::move(request));
514
515 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
516 device->SetDeviceTransport(FidoTransportProtocol::kBluetoothLowEnergy);
517 device->ExpectCtap2CommandAndRespondWith(
518 CtapRequestCommand::kAuthenticatorGetAssertion,
519 test_data::kTestGetAssertionResponse);
520 ble_discovery()->WaitForCallToStartAndSimulateSuccess();
521 ble_discovery()->AddDevice(std::move(device));
522
523 get_assertion_callback().WaitForCallback();
524
525 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
526 EXPECT_TRUE(get_assertion_callback().value<0>());
527 EXPECT_TRUE(request_handler->is_complete());
528 EXPECT_THAT(
529 request_handler->transport_availability_info().available_transports,
530 ::testing::UnorderedElementsAre(
531 FidoTransportProtocol::kBluetoothLowEnergy));
532}
533
534TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyNfcTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00535 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18536 request.SetAllowList({
537 {CredentialType::kPublicKey,
538 fido_parsing_utils::Materialize(
539 test_data::kTestGetAssertionCredentialId),
540 {FidoTransportProtocol::kNearFieldCommunication}},
541 });
542
543 set_supported_transports({FidoTransportProtocol::kNearFieldCommunication});
544
545 auto request_handler =
546 CreateGetAssertionHandlerWithRequest(std::move(request));
547
548 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
549 device->SetDeviceTransport(FidoTransportProtocol::kNearFieldCommunication);
550 device->ExpectCtap2CommandAndRespondWith(
551 CtapRequestCommand::kAuthenticatorGetAssertion,
552 test_data::kTestGetAssertionResponse);
553 nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
554 nfc_discovery()->AddDevice(std::move(device));
555
556 get_assertion_callback().WaitForCallback();
557
558 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
559 EXPECT_TRUE(get_assertion_callback().value<0>());
560 EXPECT_TRUE(request_handler->is_complete());
561 EXPECT_THAT(
562 request_handler->transport_availability_info().available_transports,
563 ::testing::UnorderedElementsAre(
564 FidoTransportProtocol::kNearFieldCommunication));
565}
566
567TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyInternalTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00568 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18569 request.SetAllowList({
570 {CredentialType::kPublicKey,
571 fido_parsing_utils::Materialize(
572 test_data::kTestGetAssertionCredentialId),
573 {FidoTransportProtocol::kInternal}},
574 });
575
576 set_supported_transports({FidoTransportProtocol::kInternal});
577
578 auto device = MockFidoDevice::MakeCtap(
579 ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
580 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
581 device->SetDeviceTransport(FidoTransportProtocol::kInternal);
582 device->ExpectCtap2CommandAndRespondWith(
Jun Choi6065c1d2018-08-23 19:04:48583 CtapRequestCommand::kAuthenticatorGetInfo,
584 test_data::kTestGetInfoResponsePlatformDevice);
585 device->ExpectCtap2CommandAndRespondWith(
Balazs Engedy159452d2018-08-16 15:18:18586 CtapRequestCommand::kAuthenticatorGetAssertion,
587 test_data::kTestGetAssertionResponse);
588 set_mock_platform_device(std::move(device));
589
590 auto request_handler =
591 CreateGetAssertionHandlerWithRequest(std::move(request));
592 get_assertion_callback().WaitForCallback();
593
594 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
595 EXPECT_TRUE(get_assertion_callback().value<0>());
596 EXPECT_TRUE(request_handler->is_complete());
597 EXPECT_THAT(
598 request_handler->transport_availability_info().available_transports,
599 ::testing::UnorderedElementsAre(FidoTransportProtocol::kInternal));
600}
601
Jun Choie4aee4ff32018-08-13 19:35:07602// Tests a scenario where authenticator of incorrect transport type was used to
603// conduct CTAP GetAssertion call.
Balazs Engedy159452d2018-08-16 15:18:18604//
605// TODO(engedy): This should not happen, instead |allowCredentials| should be
606// filtered to only contain items compatible with the transport actually used to
607// talk to the authenticator.
Jun Choie4aee4ff32018-08-13 19:35:07608TEST_F(FidoGetAssertionHandlerTest, IncorrectTransportType) {
609 // GetAssertion request that expects GetAssertion call for credential
610 // |CredentialType::kPublicKey| to be signed with Cable authenticator.
Balazs Engedy5b4891f2018-08-29 23:08:00611 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18612 request.SetAllowList({
613 {CredentialType::kPublicKey,
614 fido_parsing_utils::Materialize(
615 test_data::kTestGetAssertionCredentialId),
616 {FidoTransportProtocol::kBluetoothLowEnergy}},
617 {CredentialType::kPublicKey,
618 fido_parsing_utils::Materialize(kBogusCredentialId),
619 {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
620 });
Jun Choie4aee4ff32018-08-13 19:35:07621 auto request_handler =
622 CreateGetAssertionHandlerWithRequest(std::move(request));
623 discovery()->WaitForCallToStartAndSimulateSuccess();
624 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
625 // Since transport type of |device| is different from what the relying party
626 // defined in |request| above, this request should fail.
627 device->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
628 device->ExpectCtap2CommandAndRespondWith(
629 CtapRequestCommand::kAuthenticatorGetAssertion,
630 test_data::kTestGetAssertionResponse);
631
632 discovery()->AddDevice(std::move(device));
633
634 scoped_task_environment_.FastForwardUntilNoTasksRemain();
635 EXPECT_FALSE(get_assertion_callback().was_called());
636}
637
Martin Kreichgauer5bac1652018-08-22 16:41:10638// If a device with transport type kInternal returns a
639// CTAP2_ERR_OPERATION_DENIED error, the request should complete with
640// FidoReturnCode::kUserConsentDenied. Pending authenticators should be
641// cancelled.
642TEST_F(FidoGetAssertionHandlerTest,
643 TestRequestWithOperationDeniedErrorPlatform) {
Jun Choi6065c1d2018-08-23 19:04:48644 auto platform_device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
645 test_data::kTestGetInfoResponsePlatformDevice);
Martin Kreichgauer5bac1652018-08-22 16:41:10646 platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
647 platform_device->ExpectCtap2CommandAndRespondWithError(
648 CtapRequestCommand::kAuthenticatorGetAssertion,
649 CtapDeviceResponseCode::kCtap2ErrOperationDenied,
650 base::TimeDelta::FromMicroseconds(10));
Martin Kreichgauer5bac1652018-08-22 16:41:10651 set_mock_platform_device(std::move(platform_device));
652
653 auto other_device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
654 other_device->ExpectCtap2CommandAndDoNotRespond(
655 CtapRequestCommand::kAuthenticatorGetAssertion);
656 EXPECT_CALL(*other_device, Cancel);
657
658 auto request_handler = CreateGetAssertionHandlerCtap();
659 discovery()->WaitForCallToStartAndSimulateSuccess();
660 discovery()->AddDevice(std::move(other_device));
661
662 scoped_task_environment_.FastForwardUntilNoTasksRemain();
663 EXPECT_TRUE(get_assertion_callback().was_called());
664 EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
665 get_assertion_callback().status());
666}
667
668// Like |TestRequestWithOperationDeniedErrorPlatform|, but with a
Martin Kreichgauer8e6f1b32018-08-24 13:42:49669// cross-platform device.
Martin Kreichgauer5bac1652018-08-22 16:41:10670TEST_F(FidoGetAssertionHandlerTest,
671 TestRequestWithOperationDeniedErrorCrossPlatform) {
672 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
673 device->ExpectCtap2CommandAndRespondWithError(
674 CtapRequestCommand::kAuthenticatorGetAssertion,
675 CtapDeviceResponseCode::kCtap2ErrOperationDenied);
676
677 auto request_handler = CreateGetAssertionHandlerCtap();
678 discovery()->WaitForCallToStartAndSimulateSuccess();
679 discovery()->AddDevice(std::move(device));
680
681 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Martin Kreichgauer8e6f1b32018-08-24 13:42:49682 EXPECT_TRUE(get_assertion_callback().was_called());
683 EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
684 get_assertion_callback().status());
Martin Kreichgauer5bac1652018-08-22 16:41:10685}
686
Jun Choi4fc8b7812018-04-05 07:39:07687} // namespace device