blob: 6e072877a949eec7585933e4bbeb0c226b3ed9fb [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 Choi4fc8b7812018-04-05 07:39:0713#include "device/fido/authenticator_get_assertion_response.h"
14#include "device/fido/ctap_get_assertion_request.h"
Balazs Engedy159452d2018-08-16 15:18:1815#include "device/fido/device_response_converter.h"
Jun Choi4fc8b7812018-04-05 07:39:0716#include "device/fido/fake_fido_discovery.h"
17#include "device/fido/fido_constants.h"
Jun Choi19b944e92018-04-23 20:20:2018#include "device/fido/fido_parsing_utils.h"
Jun Choi22af8b372018-04-09 04:29:1819#include "device/fido/fido_test_data.h"
Jun Choib60937e2018-04-12 17:02:3820#include "device/fido/fido_transport_protocol.h"
Jun Choi4fc8b7812018-04-05 07:39:0721#include "device/fido/get_assertion_request_handler.h"
22#include "device/fido/mock_fido_device.h"
23#include "device/fido/test_callback_receiver.h"
Jun Choi4fc8b7812018-04-05 07:39:0724#include "testing/gmock/include/gmock/gmock.h"
25#include "testing/gtest/include/gtest/gtest.h"
26
27namespace device {
28
29namespace {
30
Balazs Engedy159452d2018-08-16 15:18:1831constexpr uint8_t kBogusCredentialId[] = {0x01, 0x02, 0x03, 0x04};
32
Jun Choi1beb4c22018-08-16 03:04:3833using TestGetAssertionRequestCallback = test::StatusAndValuesCallbackReceiver<
Jun Choif7ab0df2018-04-05 21:48:1634 FidoReturnCode,
Jun Choi1beb4c22018-08-16 03:04:3835 base::Optional<AuthenticatorGetAssertionResponse>,
36 FidoTransportProtocol>;
Jun Choi4fc8b7812018-04-05 07:39:0737
Balazs Engedy159452d2018-08-16 15:18:1838// Returns the set of transport protocols that are safe to test with. Because
39// FidoCableDiscovery cannot be faked out, and attempting to start the real
40// thing would flakily work/fail depending on the environment, avoid testing.
41base::flat_set<FidoTransportProtocol> GetTestableTransportProtocols() {
42 auto transports = GetAllTransportProtocols();
43 transports.erase(FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy);
44 return transports;
45}
46
Jun Choi4fc8b7812018-04-05 07:39:0747} // namespace
48
49class FidoGetAssertionHandlerTest : public ::testing::Test {
50 public:
Balazs Engedy159452d2018-08-16 15:18:1851 void ForgeDiscoveries() {
Jun Choi4fc8b7812018-04-05 07:39:0752 discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
Balazs Engedy159452d2018-08-16 15:18:1853 ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
54 nfc_discovery_ = scoped_fake_discovery_factory_.ForgeNextNfcDiscovery();
Jun Choi4fc8b7812018-04-05 07:39:0755 }
56
Martin Kreichgauer8987fd02018-07-20 22:42:0357 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerU2f() {
Martin Kreichgauer2b753722018-07-17 01:06:0158 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
59 test_data::kClientDataHash);
60 request.SetAllowList(
Jan Wilken Doerrie726e197e2018-05-14 12:53:2561 {{CredentialType::kPublicKey,
Jun Choi0e56e5dd2018-06-08 21:27:3462 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
Martin Kreichgauer8987fd02018-07-20 22:42:0363 return CreateGetAssertionHandlerWithRequest(std::move(request));
64 }
Jun Choi4fc8b7812018-04-05 07:39:0765
Martin Kreichgauer8987fd02018-07-20 22:42:0366 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerCtap() {
67 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
68 test_data::kClientDataHash);
69 request.SetAllowList({{CredentialType::kPublicKey,
70 fido_parsing_utils::Materialize(
71 test_data::kTestGetAssertionCredentialId)}});
Martin Kreichgauer2b753722018-07-17 01:06:0172 return CreateGetAssertionHandlerWithRequest(std::move(request));
73 }
74
75 std::unique_ptr<GetAssertionRequestHandler>
76 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
Balazs Engedy159452d2018-08-16 15:18:1877 ForgeDiscoveries();
Martin Kreichgauer2b753722018-07-17 01:06:0178
Martin Kreichgauer7cbada432018-08-18 02:30:1479 auto handler = std::make_unique<GetAssertionRequestHandler>(
Balazs Engedy159452d2018-08-16 15:18:1880 nullptr /* connector */, supported_transports_, std::move(request),
Martin Kreichgauer7cbada432018-08-18 02:30:1481 get_assertion_cb_.callback());
82 handler->SetPlatformAuthenticatorOrMarkUnavailable(
83 CreatePlatformAuthenticator());
84 return handler;
Balazs Engedy159452d2018-08-16 15:18:1885 }
86
87 void ExpectAllowedTransportsForRequestAre(
88 GetAssertionRequestHandler* request_handler,
89 base::flat_set<FidoTransportProtocol> transports) {
90 using Transport = FidoTransportProtocol;
91 if (base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
92 discovery()->WaitForCallToStartAndSimulateSuccess();
93 if (base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
94 ble_discovery()->WaitForCallToStartAndSimulateSuccess();
95 if (base::ContainsKey(transports, Transport::kNearFieldCommunication))
96 nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy159452d2018-08-16 15:18:1897
98 scoped_task_environment_.FastForwardUntilNoTasksRemain();
99 EXPECT_FALSE(get_assertion_callback().was_called());
100
101 if (!base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
102 EXPECT_FALSE(discovery()->is_start_requested());
103 if (!base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
104 EXPECT_FALSE(ble_discovery()->is_start_requested());
105 if (!base::ContainsKey(transports, Transport::kNearFieldCommunication))
106 EXPECT_FALSE(nfc_discovery()->is_start_requested());
Balazs Engedy159452d2018-08-16 15:18:18107
108 // Even with FidoTransportProtocol::kInternal allowed, unless the platform
109 // authenticator factory returns a FidoAuthenticator instance (which it will
110 // not be default), the transport will be marked `unavailable`.
111 transports.erase(Transport::kInternal);
112
113 EXPECT_THAT(
114 request_handler->transport_availability_info().available_transports,
115 ::testing::UnorderedElementsAreArray(transports));
116 }
117
118 void ExpectAllTransportsAreAllowedForRequest(
119 GetAssertionRequestHandler* request_handler) {
120 ExpectAllowedTransportsForRequestAre(request_handler,
121 GetTestableTransportProtocols());
Jun Choi4fc8b7812018-04-05 07:39:07122 }
123
Jun Choid19453d2018-06-21 23:16:39124 void InitFeatureListAndDisableCtapFlag() {
125 scoped_feature_list_.InitAndDisableFeature(kNewCtap2Device);
Jun Choi0e56e5dd2018-06-08 21:27:34126 }
Jun Choi4fc8b7812018-04-05 07:39:07127
Jun Choi0e56e5dd2018-06-08 21:27:34128 test::FakeFidoDiscovery* discovery() const { return discovery_; }
Balazs Engedy159452d2018-08-16 15:18:18129 test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
130 test::FakeFidoDiscovery* nfc_discovery() const { return nfc_discovery_; }
Jun Choi4fc8b7812018-04-05 07:39:07131 TestGetAssertionRequestCallback& get_assertion_callback() {
132 return get_assertion_cb_;
133 }
134
Balazs Engedy159452d2018-08-16 15:18:18135 void set_mock_platform_device(std::unique_ptr<MockFidoDevice> device) {
136 mock_platform_device_ = std::move(device);
137 }
138
Balazs Engedy159452d2018-08-16 15:18:18139 void set_supported_transports(
140 base::flat_set<FidoTransportProtocol> transports) {
141 supported_transports_ = std::move(transports);
142 }
143
Jun Choi4fc8b7812018-04-05 07:39:07144 protected:
Martin Kreichgauere1223082018-08-20 23:17:38145 base::Optional<PlatformAuthenticatorInfo> CreatePlatformAuthenticator() {
Balazs Engedy159452d2018-08-16 15:18:18146 if (!mock_platform_device_)
Martin Kreichgauere1223082018-08-20 23:17:38147 return base::nullopt;
148 return PlatformAuthenticatorInfo(
149 std::make_unique<FidoDeviceAuthenticator>(mock_platform_device_.get()),
150 false /* has_recognized_mac_touch_id_credential_available */);
Balazs Engedy159452d2018-08-16 15:18:18151 }
152
Jun Choi4fc8b7812018-04-05 07:39:07153 base::test::ScopedTaskEnvironment scoped_task_environment_{
154 base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
Jun Choi0e56e5dd2018-06-08 21:27:34155 base::test::ScopedFeatureList scoped_feature_list_;
Jun Choi4fc8b7812018-04-05 07:39:07156 test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
157 test::FakeFidoDiscovery* discovery_;
Balazs Engedy159452d2018-08-16 15:18:18158 test::FakeFidoDiscovery* ble_discovery_;
159 test::FakeFidoDiscovery* nfc_discovery_;
160 std::unique_ptr<MockFidoDevice> mock_platform_device_;
Jun Choi4fc8b7812018-04-05 07:39:07161 TestGetAssertionRequestCallback get_assertion_cb_;
Balazs Engedy159452d2018-08-16 15:18:18162 base::flat_set<FidoTransportProtocol> supported_transports_ =
163 GetTestableTransportProtocols();
Jun Choi4fc8b7812018-04-05 07:39:07164};
165
Balazs Engedy0c4fdc52018-08-20 19:15:28166TEST_F(FidoGetAssertionHandlerTest, TransportAvailabilityInfo) {
167 auto request_handler =
168 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
169 test_data::kRelyingPartyId, test_data::kClientDataHash));
170
171 EXPECT_EQ(FidoRequestHandlerBase::RequestType::kGetAssertion,
172 request_handler->transport_availability_info().request_type);
173 EXPECT_EQ(test_data::kRelyingPartyId,
174 request_handler->transport_availability_info().rp_id);
175}
176
Martin Kreichgauer8987fd02018-07-20 22:42:03177TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
178 auto request_handler = CreateGetAssertionHandlerCtap();
Jun Choi4fc8b7812018-04-05 07:39:07179 discovery()->WaitForCallToStartAndSimulateSuccess();
Martin Kreichgauer8987fd02018-07-20 22:42:03180 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Jun Choi4fc8b7812018-04-05 07:39:07181 device->ExpectCtap2CommandAndRespondWith(
182 CtapRequestCommand::kAuthenticatorGetAssertion,
183 test_data::kTestGetAssertionResponse);
184
185 discovery()->AddDevice(std::move(device));
186 get_assertion_callback().WaitForCallback();
187
188 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38189 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi4fc8b7812018-04-05 07:39:07190 EXPECT_TRUE(request_handler->is_complete());
191}
192
Jun Choi0e56e5dd2018-06-08 21:27:34193// Test a scenario where the connected authenticator is a U2F device.
194TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
Martin Kreichgauer8987fd02018-07-20 22:42:03195 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi4fc8b7812018-04-05 07:39:07196 discovery()->WaitForCallToStartAndSimulateSuccess();
197
Martin Kreichgauer8987fd02018-07-20 22:42:03198 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
Jun Choi0e56e5dd2018-06-08 21:27:34199 device->ExpectRequestAndRespondWith(
200 test_data::kU2fCheckOnlySignCommandApdu,
201 test_data::kApduEncodedNoErrorSignResponse);
202 device->ExpectRequestAndRespondWith(
203 test_data::kU2fSignCommandApdu,
204 test_data::kApduEncodedNoErrorSignResponse);
Jun Choi4fc8b7812018-04-05 07:39:07205
206 discovery()->AddDevice(std::move(device));
207 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Jun Choi0e56e5dd2018-06-08 21:27:34208 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38209 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi0e56e5dd2018-06-08 21:27:34210 EXPECT_TRUE(request_handler->is_complete());
211}
212
213// Test a scenario where the connected authenticator is a U2F device and
214// "WebAuthenticationCtap2" flag is not enabled.
215TEST_F(FidoGetAssertionHandlerTest, TestU2fSignWithoutCtapFlag) {
Jun Choid19453d2018-06-21 23:16:39216 InitFeatureListAndDisableCtapFlag();
Martin Kreichgauer8987fd02018-07-20 22:42:03217 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi0e56e5dd2018-06-08 21:27:34218 discovery()->WaitForCallToStartAndSimulateSuccess();
219
220 auto device = std::make_unique<MockFidoDevice>();
221 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
222 device->ExpectRequestAndRespondWith(
223 test_data::kU2fCheckOnlySignCommandApdu,
224 test_data::kApduEncodedNoErrorSignResponse);
225 device->ExpectRequestAndRespondWith(
226 test_data::kU2fSignCommandApdu,
227 test_data::kApduEncodedNoErrorSignResponse);
228
229 discovery()->AddDevice(std::move(device));
230 scoped_task_environment_.FastForwardUntilNoTasksRemain();
231 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38232 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi0e56e5dd2018-06-08 21:27:34233 EXPECT_TRUE(request_handler->is_complete());
Jun Choi4fc8b7812018-04-05 07:39:07234}
235
Martin Kreichgauer2b753722018-07-17 01:06:01236TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {
237 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
238 test_data::kClientDataHash);
239 request.SetUserVerification(UserVerificationRequirement::kRequired);
240 auto request_handler =
241 CreateGetAssertionHandlerWithRequest(std::move(request));
242 discovery()->WaitForCallToStartAndSimulateSuccess();
243
Martin Kreichgauer8987fd02018-07-20 22:42:03244 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
Martin Kreichgauer2b753722018-07-17 01:06:01245 test_data::kTestGetInfoResponseWithoutUvSupport);
246
247 discovery()->AddDevice(std::move(device));
248
249 scoped_task_environment_.FastForwardUntilNoTasksRemain();
250 EXPECT_FALSE(get_assertion_callback().was_called());
251}
252
253TEST_F(FidoGetAssertionHandlerTest,
254 TestU2fSignRequestWithUserVerificationRequired) {
255 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
256 test_data::kClientDataHash);
257 request.SetAllowList(
258 {{CredentialType::kPublicKey,
259 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
260 request.SetUserVerification(UserVerificationRequirement::kRequired);
261 auto request_handler =
262 CreateGetAssertionHandlerWithRequest(std::move(request));
263 discovery()->WaitForCallToStartAndSimulateSuccess();
264
Martin Kreichgauer8987fd02018-07-20 22:42:03265 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
266 discovery()->AddDevice(std::move(device));
267
268 scoped_task_environment_.FastForwardUntilNoTasksRemain();
269 EXPECT_FALSE(get_assertion_callback().was_called());
270}
271
272TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) {
273 auto request_handler =
274 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
275 test_data::kRelyingPartyId, test_data::kClientDataHash));
276 discovery()->WaitForCallToStartAndSimulateSuccess();
277 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Martin Kreichgauer2b753722018-07-17 01:06:01278 device->ExpectCtap2CommandAndRespondWith(
Martin Kreichgauer8987fd02018-07-20 22:42:03279 CtapRequestCommand::kAuthenticatorGetAssertion,
280 test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
281
282 discovery()->AddDevice(std::move(device));
283
284 scoped_task_environment_.FastForwardUntilNoTasksRemain();
285 EXPECT_FALSE(get_assertion_callback().was_called());
286}
287
288// Tests a scenario where the authenticator responds with credential ID that
289// is not included in the allowed list.
290TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) {
291 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
292 test_data::kClientDataHash);
293 request.SetAllowList(
294 {{CredentialType::kPublicKey,
295 fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)}});
296 auto request_handler =
297 CreateGetAssertionHandlerWithRequest(std::move(request));
298 discovery()->WaitForCallToStartAndSimulateSuccess();
299 // Resident Keys must be disabled, otherwise allow list check is skipped.
300 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
301 test_data::kTestGetInfoResponseWithoutResidentKeySupport);
302 device->ExpectCtap2CommandAndRespondWith(
303 CtapRequestCommand::kAuthenticatorGetAssertion,
304 test_data::kTestGetAssertionResponse);
305
306 discovery()->AddDevice(std::move(device));
307
308 scoped_task_environment_.FastForwardUntilNoTasksRemain();
309 EXPECT_FALSE(get_assertion_callback().was_called());
310}
311
312// Tests a scenario where authenticator responds without user entity in its
313// response but client is expecting a resident key credential.
314TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) {
315 // Use a GetAssertion request with an empty allow list.
316 auto request_handler =
317 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
318 test_data::kRelyingPartyId, test_data::kClientDataHash));
319 discovery()->WaitForCallToStartAndSimulateSuccess();
320 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
321 device->ExpectCtap2CommandAndRespondWith(
322 CtapRequestCommand::kAuthenticatorGetAssertion,
323 test_data::kTestGetAssertionResponse);
Martin Kreichgauer2b753722018-07-17 01:06:01324
325 discovery()->AddDevice(std::move(device));
326
327 scoped_task_environment_.FastForwardUntilNoTasksRemain();
328 EXPECT_FALSE(get_assertion_callback().was_called());
329}
330
Balazs Engedy159452d2018-08-16 15:18:18331TEST_F(FidoGetAssertionHandlerTest,
332 AllTransportsAllowedIfAllowCredentialsListUndefined) {
333 auto request_handler =
334 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
335 test_data::kRelyingPartyId, test_data::kClientDataHash));
336 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
337}
338
339TEST_F(FidoGetAssertionHandlerTest,
340 AllTransportsAllowedIfAllowCredentialsListIsEmpty) {
341 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
342 test_data::kClientDataHash);
343 request.SetAllowList({});
344
345 auto request_handler =
346 CreateGetAssertionHandlerWithRequest(std::move(request));
347 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
348}
349
350TEST_F(FidoGetAssertionHandlerTest,
351 AllTransportsAllowedIfHasAllowedCredentialWithEmptyTransportsList) {
352 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
353 test_data::kClientDataHash);
354 request.SetAllowList({
355 {CredentialType::kPublicKey,
356 fido_parsing_utils::Materialize(
357 test_data::kTestGetAssertionCredentialId),
358 {FidoTransportProtocol::kBluetoothLowEnergy}},
359 {CredentialType::kPublicKey,
360 fido_parsing_utils::Materialize(kBogusCredentialId)},
361 });
362
363 auto request_handler =
364 CreateGetAssertionHandlerWithRequest(std::move(request));
365 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
366}
367
368TEST_F(FidoGetAssertionHandlerTest,
369 AllowedTransportsAreUnionOfTransportsLists) {
370 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
371 test_data::kClientDataHash);
372 request.SetAllowList({
373 {CredentialType::kPublicKey,
374 fido_parsing_utils::Materialize(
375 test_data::kTestGetAssertionCredentialId),
376 {FidoTransportProtocol::kBluetoothLowEnergy}},
377 {CredentialType::kPublicKey,
378 fido_parsing_utils::Materialize(kBogusCredentialId),
379 {FidoTransportProtocol::kInternal,
380 FidoTransportProtocol::kNearFieldCommunication}},
381 });
382
383 auto request_handler =
384 CreateGetAssertionHandlerWithRequest(std::move(request));
385 ExpectAllowedTransportsForRequestAre(
386 request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
387 FidoTransportProtocol::kInternal,
388 FidoTransportProtocol::kNearFieldCommunication});
389}
390
391TEST_F(FidoGetAssertionHandlerTest, SupportedTransportsAreOnlyBleAndNfc) {
392 const base::flat_set<FidoTransportProtocol> kBleAndNfc = {
393 FidoTransportProtocol::kBluetoothLowEnergy,
394 FidoTransportProtocol::kNearFieldCommunication,
395 };
396
397 set_supported_transports(kBleAndNfc);
398
399 auto request_handler =
400 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
401 test_data::kRelyingPartyId, test_data::kClientDataHash));
402 ExpectAllowedTransportsForRequestAre(request_handler.get(), kBleAndNfc);
403}
404
405TEST_F(FidoGetAssertionHandlerTest, SupportedTransportsAreOnlyUsbAndInternal) {
406 const base::flat_set<FidoTransportProtocol> kUsbAndInternal = {
407 FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
408 FidoTransportProtocol::kInternal,
409 };
410
411 set_supported_transports(kUsbAndInternal);
412 auto request_handler =
413 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
414 test_data::kRelyingPartyId, test_data::kClientDataHash));
415 ExpectAllowedTransportsForRequestAre(request_handler.get(), kUsbAndInternal);
416}
417
418TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyUsbTransportAllowed) {
419 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
420 test_data::kClientDataHash);
421 request.SetAllowList({
422 {CredentialType::kPublicKey,
423 fido_parsing_utils::Materialize(
424 test_data::kTestGetAssertionCredentialId),
425 {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
426 });
427
428 set_supported_transports({FidoTransportProtocol::kUsbHumanInterfaceDevice});
429
430 auto request_handler =
431 CreateGetAssertionHandlerWithRequest(std::move(request));
432
433 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
434 device->ExpectCtap2CommandAndRespondWith(
435 CtapRequestCommand::kAuthenticatorGetAssertion,
436 test_data::kTestGetAssertionResponse);
437 discovery()->WaitForCallToStartAndSimulateSuccess();
438 discovery()->AddDevice(std::move(device));
439
440 get_assertion_callback().WaitForCallback();
441
442 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
443 EXPECT_TRUE(get_assertion_callback().value<0>());
444 EXPECT_TRUE(request_handler->is_complete());
445 EXPECT_THAT(
446 request_handler->transport_availability_info().available_transports,
447 ::testing::UnorderedElementsAre(
448 FidoTransportProtocol::kUsbHumanInterfaceDevice));
449}
450
451TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyBleTransportAllowed) {
452 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
453 test_data::kClientDataHash);
454 request.SetAllowList({
455 {CredentialType::kPublicKey,
456 fido_parsing_utils::Materialize(
457 test_data::kTestGetAssertionCredentialId),
458 {FidoTransportProtocol::kBluetoothLowEnergy}},
459 });
460
461 set_supported_transports({FidoTransportProtocol::kBluetoothLowEnergy});
462
463 auto request_handler =
464 CreateGetAssertionHandlerWithRequest(std::move(request));
465
466 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
467 device->SetDeviceTransport(FidoTransportProtocol::kBluetoothLowEnergy);
468 device->ExpectCtap2CommandAndRespondWith(
469 CtapRequestCommand::kAuthenticatorGetAssertion,
470 test_data::kTestGetAssertionResponse);
471 ble_discovery()->WaitForCallToStartAndSimulateSuccess();
472 ble_discovery()->AddDevice(std::move(device));
473
474 get_assertion_callback().WaitForCallback();
475
476 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
477 EXPECT_TRUE(get_assertion_callback().value<0>());
478 EXPECT_TRUE(request_handler->is_complete());
479 EXPECT_THAT(
480 request_handler->transport_availability_info().available_transports,
481 ::testing::UnorderedElementsAre(
482 FidoTransportProtocol::kBluetoothLowEnergy));
483}
484
485TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyNfcTransportAllowed) {
486 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
487 test_data::kClientDataHash);
488 request.SetAllowList({
489 {CredentialType::kPublicKey,
490 fido_parsing_utils::Materialize(
491 test_data::kTestGetAssertionCredentialId),
492 {FidoTransportProtocol::kNearFieldCommunication}},
493 });
494
495 set_supported_transports({FidoTransportProtocol::kNearFieldCommunication});
496
497 auto request_handler =
498 CreateGetAssertionHandlerWithRequest(std::move(request));
499
500 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
501 device->SetDeviceTransport(FidoTransportProtocol::kNearFieldCommunication);
502 device->ExpectCtap2CommandAndRespondWith(
503 CtapRequestCommand::kAuthenticatorGetAssertion,
504 test_data::kTestGetAssertionResponse);
505 nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
506 nfc_discovery()->AddDevice(std::move(device));
507
508 get_assertion_callback().WaitForCallback();
509
510 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
511 EXPECT_TRUE(get_assertion_callback().value<0>());
512 EXPECT_TRUE(request_handler->is_complete());
513 EXPECT_THAT(
514 request_handler->transport_availability_info().available_transports,
515 ::testing::UnorderedElementsAre(
516 FidoTransportProtocol::kNearFieldCommunication));
517}
518
519TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyInternalTransportAllowed) {
520 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
521 test_data::kClientDataHash);
522 request.SetAllowList({
523 {CredentialType::kPublicKey,
524 fido_parsing_utils::Materialize(
525 test_data::kTestGetAssertionCredentialId),
526 {FidoTransportProtocol::kInternal}},
527 });
528
529 set_supported_transports({FidoTransportProtocol::kInternal});
530
531 auto device = MockFidoDevice::MakeCtap(
532 ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
533 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
534 device->SetDeviceTransport(FidoTransportProtocol::kInternal);
535 device->ExpectCtap2CommandAndRespondWith(
Jun Choi6065c1d2018-08-23 19:04:48536 CtapRequestCommand::kAuthenticatorGetInfo,
537 test_data::kTestGetInfoResponsePlatformDevice);
538 device->ExpectCtap2CommandAndRespondWith(
Balazs Engedy159452d2018-08-16 15:18:18539 CtapRequestCommand::kAuthenticatorGetAssertion,
540 test_data::kTestGetAssertionResponse);
541 set_mock_platform_device(std::move(device));
542
543 auto request_handler =
544 CreateGetAssertionHandlerWithRequest(std::move(request));
545 get_assertion_callback().WaitForCallback();
546
547 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
548 EXPECT_TRUE(get_assertion_callback().value<0>());
549 EXPECT_TRUE(request_handler->is_complete());
550 EXPECT_THAT(
551 request_handler->transport_availability_info().available_transports,
552 ::testing::UnorderedElementsAre(FidoTransportProtocol::kInternal));
553}
554
Jun Choie4aee4ff32018-08-13 19:35:07555// Tests a scenario where authenticator of incorrect transport type was used to
556// conduct CTAP GetAssertion call.
Balazs Engedy159452d2018-08-16 15:18:18557//
558// TODO(engedy): This should not happen, instead |allowCredentials| should be
559// filtered to only contain items compatible with the transport actually used to
560// talk to the authenticator.
Jun Choie4aee4ff32018-08-13 19:35:07561TEST_F(FidoGetAssertionHandlerTest, IncorrectTransportType) {
562 // GetAssertion request that expects GetAssertion call for credential
563 // |CredentialType::kPublicKey| to be signed with Cable authenticator.
564 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
565 test_data::kClientDataHash);
Balazs Engedy159452d2018-08-16 15:18:18566 request.SetAllowList({
567 {CredentialType::kPublicKey,
568 fido_parsing_utils::Materialize(
569 test_data::kTestGetAssertionCredentialId),
570 {FidoTransportProtocol::kBluetoothLowEnergy}},
571 {CredentialType::kPublicKey,
572 fido_parsing_utils::Materialize(kBogusCredentialId),
573 {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
574 });
Jun Choie4aee4ff32018-08-13 19:35:07575 auto request_handler =
576 CreateGetAssertionHandlerWithRequest(std::move(request));
577 discovery()->WaitForCallToStartAndSimulateSuccess();
578 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
579 // Since transport type of |device| is different from what the relying party
580 // defined in |request| above, this request should fail.
581 device->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
582 device->ExpectCtap2CommandAndRespondWith(
583 CtapRequestCommand::kAuthenticatorGetAssertion,
584 test_data::kTestGetAssertionResponse);
585
586 discovery()->AddDevice(std::move(device));
587
588 scoped_task_environment_.FastForwardUntilNoTasksRemain();
589 EXPECT_FALSE(get_assertion_callback().was_called());
590}
591
Martin Kreichgauer5bac1652018-08-22 16:41:10592// If a device with transport type kInternal returns a
593// CTAP2_ERR_OPERATION_DENIED error, the request should complete with
594// FidoReturnCode::kUserConsentDenied. Pending authenticators should be
595// cancelled.
596TEST_F(FidoGetAssertionHandlerTest,
597 TestRequestWithOperationDeniedErrorPlatform) {
Jun Choi6065c1d2018-08-23 19:04:48598 auto platform_device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
599 test_data::kTestGetInfoResponsePlatformDevice);
Martin Kreichgauer5bac1652018-08-22 16:41:10600 platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
601 platform_device->ExpectCtap2CommandAndRespondWithError(
602 CtapRequestCommand::kAuthenticatorGetAssertion,
603 CtapDeviceResponseCode::kCtap2ErrOperationDenied,
604 base::TimeDelta::FromMicroseconds(10));
Martin Kreichgauer5bac1652018-08-22 16:41:10605 set_mock_platform_device(std::move(platform_device));
606
607 auto other_device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
608 other_device->ExpectCtap2CommandAndDoNotRespond(
609 CtapRequestCommand::kAuthenticatorGetAssertion);
610 EXPECT_CALL(*other_device, Cancel);
611
612 auto request_handler = CreateGetAssertionHandlerCtap();
613 discovery()->WaitForCallToStartAndSimulateSuccess();
614 discovery()->AddDevice(std::move(other_device));
615
616 scoped_task_environment_.FastForwardUntilNoTasksRemain();
617 EXPECT_TRUE(get_assertion_callback().was_called());
618 EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
619 get_assertion_callback().status());
620}
621
622// Like |TestRequestWithOperationDeniedErrorPlatform|, but with a
Martin Kreichgauer8e6f1b32018-08-24 13:42:49623// cross-platform device.
Martin Kreichgauer5bac1652018-08-22 16:41:10624TEST_F(FidoGetAssertionHandlerTest,
625 TestRequestWithOperationDeniedErrorCrossPlatform) {
626 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
627 device->ExpectCtap2CommandAndRespondWithError(
628 CtapRequestCommand::kAuthenticatorGetAssertion,
629 CtapDeviceResponseCode::kCtap2ErrOperationDenied);
630
631 auto request_handler = CreateGetAssertionHandlerCtap();
632 discovery()->WaitForCallToStartAndSimulateSuccess();
633 discovery()->AddDevice(std::move(device));
634
635 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Martin Kreichgauer8e6f1b32018-08-24 13:42:49636 EXPECT_TRUE(get_assertion_callback().was_called());
637 EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
638 get_assertion_callback().status());
Martin Kreichgauer5bac1652018-08-22 16:41:10639}
640
Jun Choi4fc8b7812018-04-05 07:39:07641} // namespace device