blob: d02eeba1de4b9e4cc50d2722baf5d29dfbc799b9 [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
38} // namespace
39
40class FidoGetAssertionHandlerTest : public ::testing::Test {
41 public:
Balazs Engedy159452d2018-08-16 15:18:1842 void ForgeDiscoveries() {
Jun Choi4fc8b7812018-04-05 07:39:0743 discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
Balazs Engedy159452d2018-08-16 15:18:1844 ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
Balazs Engedy5b4891f2018-08-29 23:08:0045 cable_discovery_ = scoped_fake_discovery_factory_.ForgeNextCableDiscovery();
Balazs Engedy159452d2018-08-16 15:18:1846 nfc_discovery_ = scoped_fake_discovery_factory_.ForgeNextNfcDiscovery();
Jun Choi4fc8b7812018-04-05 07:39:0747 }
48
Balazs Engedy5b4891f2018-08-29 23:08:0049 CtapGetAssertionRequest CreateTestRequestWithCableExtension() {
50 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
51 test_data::kClientDataHash);
52 request.SetCableExtension({});
53 return request;
54 }
55
Martin Kreichgauer8987fd02018-07-20 22:42:0356 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerU2f() {
Martin Kreichgauer2b753722018-07-17 01:06:0157 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
58 test_data::kClientDataHash);
59 request.SetAllowList(
Jan Wilken Doerrie726e197e2018-05-14 12:53:2560 {{CredentialType::kPublicKey,
Jun Choi0e56e5dd2018-06-08 21:27:3461 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
Martin Kreichgauer8987fd02018-07-20 22:42:0362 return CreateGetAssertionHandlerWithRequest(std::move(request));
63 }
Jun Choi4fc8b7812018-04-05 07:39:0764
Martin Kreichgauer8987fd02018-07-20 22:42:0365 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerCtap() {
66 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
67 test_data::kClientDataHash);
68 request.SetAllowList({{CredentialType::kPublicKey,
69 fido_parsing_utils::Materialize(
70 test_data::kTestGetAssertionCredentialId)}});
Martin Kreichgauer2b753722018-07-17 01:06:0171 return CreateGetAssertionHandlerWithRequest(std::move(request));
72 }
73
74 std::unique_ptr<GetAssertionRequestHandler>
75 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
Balazs Engedy159452d2018-08-16 15:18:1876 ForgeDiscoveries();
Martin Kreichgauer2b753722018-07-17 01:06:0177
Martin Kreichgauer7cbada432018-08-18 02:30:1478 auto handler = std::make_unique<GetAssertionRequestHandler>(
Balazs Engedy159452d2018-08-16 15:18:1879 nullptr /* connector */, supported_transports_, std::move(request),
Martin Kreichgauer7cbada432018-08-18 02:30:1480 get_assertion_cb_.callback());
81 handler->SetPlatformAuthenticatorOrMarkUnavailable(
82 CreatePlatformAuthenticator());
83 return handler;
Balazs Engedy159452d2018-08-16 15:18:1884 }
85
86 void ExpectAllowedTransportsForRequestAre(
87 GetAssertionRequestHandler* request_handler,
88 base::flat_set<FidoTransportProtocol> transports) {
89 using Transport = FidoTransportProtocol;
90 if (base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
91 discovery()->WaitForCallToStartAndSimulateSuccess();
92 if (base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
93 ble_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy5b4891f2018-08-29 23:08:0094 if (base::ContainsKey(transports,
95 Transport::kCloudAssistedBluetoothLowEnergy))
96 cable_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy159452d2018-08-16 15:18:1897 if (base::ContainsKey(transports, Transport::kNearFieldCommunication))
98 nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy159452d2018-08-16 15:18:1899
100 scoped_task_environment_.FastForwardUntilNoTasksRemain();
101 EXPECT_FALSE(get_assertion_callback().was_called());
102
103 if (!base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
104 EXPECT_FALSE(discovery()->is_start_requested());
105 if (!base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
106 EXPECT_FALSE(ble_discovery()->is_start_requested());
Balazs Engedy5b4891f2018-08-29 23:08:00107 if (!base::ContainsKey(transports,
108 Transport::kCloudAssistedBluetoothLowEnergy))
109 EXPECT_FALSE(cable_discovery()->is_start_requested());
Balazs Engedy159452d2018-08-16 15:18:18110 if (!base::ContainsKey(transports, Transport::kNearFieldCommunication))
111 EXPECT_FALSE(nfc_discovery()->is_start_requested());
Balazs Engedy159452d2018-08-16 15:18:18112
113 // Even with FidoTransportProtocol::kInternal allowed, unless the platform
114 // authenticator factory returns a FidoAuthenticator instance (which it will
115 // not be default), the transport will be marked `unavailable`.
116 transports.erase(Transport::kInternal);
117
118 EXPECT_THAT(
119 request_handler->transport_availability_info().available_transports,
120 ::testing::UnorderedElementsAreArray(transports));
121 }
122
123 void ExpectAllTransportsAreAllowedForRequest(
124 GetAssertionRequestHandler* request_handler) {
125 ExpectAllowedTransportsForRequestAre(request_handler,
Balazs Engedy5b4891f2018-08-29 23:08:00126 GetAllTransportProtocols());
Jun Choi4fc8b7812018-04-05 07:39:07127 }
128
Jun Choid19453d2018-06-21 23:16:39129 void InitFeatureListAndDisableCtapFlag() {
130 scoped_feature_list_.InitAndDisableFeature(kNewCtap2Device);
Jun Choi0e56e5dd2018-06-08 21:27:34131 }
Jun Choi4fc8b7812018-04-05 07:39:07132
Jun Choi0e56e5dd2018-06-08 21:27:34133 test::FakeFidoDiscovery* discovery() const { return discovery_; }
Balazs Engedy159452d2018-08-16 15:18:18134 test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
Balazs Engedy5b4891f2018-08-29 23:08:00135 test::FakeFidoDiscovery* cable_discovery() const { return cable_discovery_; }
Balazs Engedy159452d2018-08-16 15:18:18136 test::FakeFidoDiscovery* nfc_discovery() const { return nfc_discovery_; }
Jun Choi4fc8b7812018-04-05 07:39:07137 TestGetAssertionRequestCallback& get_assertion_callback() {
138 return get_assertion_cb_;
139 }
140
Balazs Engedy159452d2018-08-16 15:18:18141 void set_mock_platform_device(std::unique_ptr<MockFidoDevice> device) {
142 mock_platform_device_ = std::move(device);
143 }
144
Balazs Engedy159452d2018-08-16 15:18:18145 void set_supported_transports(
146 base::flat_set<FidoTransportProtocol> transports) {
147 supported_transports_ = std::move(transports);
148 }
149
Jun Choi4fc8b7812018-04-05 07:39:07150 protected:
Martin Kreichgauere1223082018-08-20 23:17:38151 base::Optional<PlatformAuthenticatorInfo> CreatePlatformAuthenticator() {
Balazs Engedy159452d2018-08-16 15:18:18152 if (!mock_platform_device_)
Martin Kreichgauere1223082018-08-20 23:17:38153 return base::nullopt;
154 return PlatformAuthenticatorInfo(
155 std::make_unique<FidoDeviceAuthenticator>(mock_platform_device_.get()),
156 false /* has_recognized_mac_touch_id_credential_available */);
Balazs Engedy159452d2018-08-16 15:18:18157 }
158
Jun Choi4fc8b7812018-04-05 07:39:07159 base::test::ScopedTaskEnvironment scoped_task_environment_{
160 base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
Jun Choi0e56e5dd2018-06-08 21:27:34161 base::test::ScopedFeatureList scoped_feature_list_;
Jun Choi4fc8b7812018-04-05 07:39:07162 test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
163 test::FakeFidoDiscovery* discovery_;
Balazs Engedy159452d2018-08-16 15:18:18164 test::FakeFidoDiscovery* ble_discovery_;
Balazs Engedy5b4891f2018-08-29 23:08:00165 test::FakeFidoDiscovery* cable_discovery_;
Balazs Engedy159452d2018-08-16 15:18:18166 test::FakeFidoDiscovery* nfc_discovery_;
167 std::unique_ptr<MockFidoDevice> mock_platform_device_;
Jun Choi4fc8b7812018-04-05 07:39:07168 TestGetAssertionRequestCallback get_assertion_cb_;
Balazs Engedy159452d2018-08-16 15:18:18169 base::flat_set<FidoTransportProtocol> supported_transports_ =
Balazs Engedy5b4891f2018-08-29 23:08:00170 GetAllTransportProtocols();
Jun Choi4fc8b7812018-04-05 07:39:07171};
172
Balazs Engedy0c4fdc52018-08-20 19:15:28173TEST_F(FidoGetAssertionHandlerTest, TransportAvailabilityInfo) {
174 auto request_handler =
175 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
176 test_data::kRelyingPartyId, test_data::kClientDataHash));
177
178 EXPECT_EQ(FidoRequestHandlerBase::RequestType::kGetAssertion,
179 request_handler->transport_availability_info().request_type);
180 EXPECT_EQ(test_data::kRelyingPartyId,
181 request_handler->transport_availability_info().rp_id);
182}
183
Martin Kreichgauer8987fd02018-07-20 22:42:03184TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
185 auto request_handler = CreateGetAssertionHandlerCtap();
Jun Choi4fc8b7812018-04-05 07:39:07186 discovery()->WaitForCallToStartAndSimulateSuccess();
Martin Kreichgauer8987fd02018-07-20 22:42:03187 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Jun Choi4fc8b7812018-04-05 07:39:07188 device->ExpectCtap2CommandAndRespondWith(
189 CtapRequestCommand::kAuthenticatorGetAssertion,
190 test_data::kTestGetAssertionResponse);
191
192 discovery()->AddDevice(std::move(device));
193 get_assertion_callback().WaitForCallback();
194
195 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38196 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi4fc8b7812018-04-05 07:39:07197 EXPECT_TRUE(request_handler->is_complete());
198}
199
Jun Choi0e56e5dd2018-06-08 21:27:34200// Test a scenario where the connected authenticator is a U2F device.
201TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
Martin Kreichgauer8987fd02018-07-20 22:42:03202 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi4fc8b7812018-04-05 07:39:07203 discovery()->WaitForCallToStartAndSimulateSuccess();
204
Martin Kreichgauer8987fd02018-07-20 22:42:03205 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
Jun Choi0e56e5dd2018-06-08 21:27:34206 device->ExpectRequestAndRespondWith(
207 test_data::kU2fCheckOnlySignCommandApdu,
208 test_data::kApduEncodedNoErrorSignResponse);
209 device->ExpectRequestAndRespondWith(
210 test_data::kU2fSignCommandApdu,
211 test_data::kApduEncodedNoErrorSignResponse);
Jun Choi4fc8b7812018-04-05 07:39:07212
213 discovery()->AddDevice(std::move(device));
214 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Jun Choi0e56e5dd2018-06-08 21:27:34215 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38216 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi0e56e5dd2018-06-08 21:27:34217 EXPECT_TRUE(request_handler->is_complete());
218}
219
220// Test a scenario where the connected authenticator is a U2F device and
221// "WebAuthenticationCtap2" flag is not enabled.
222TEST_F(FidoGetAssertionHandlerTest, TestU2fSignWithoutCtapFlag) {
Jun Choid19453d2018-06-21 23:16:39223 InitFeatureListAndDisableCtapFlag();
Martin Kreichgauer8987fd02018-07-20 22:42:03224 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi0e56e5dd2018-06-08 21:27:34225 discovery()->WaitForCallToStartAndSimulateSuccess();
226
227 auto device = std::make_unique<MockFidoDevice>();
228 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
229 device->ExpectRequestAndRespondWith(
230 test_data::kU2fCheckOnlySignCommandApdu,
231 test_data::kApduEncodedNoErrorSignResponse);
232 device->ExpectRequestAndRespondWith(
233 test_data::kU2fSignCommandApdu,
234 test_data::kApduEncodedNoErrorSignResponse);
235
236 discovery()->AddDevice(std::move(device));
237 scoped_task_environment_.FastForwardUntilNoTasksRemain();
238 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38239 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi0e56e5dd2018-06-08 21:27:34240 EXPECT_TRUE(request_handler->is_complete());
Jun Choi4fc8b7812018-04-05 07:39:07241}
242
Martin Kreichgauer2b753722018-07-17 01:06:01243TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {
244 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
245 test_data::kClientDataHash);
246 request.SetUserVerification(UserVerificationRequirement::kRequired);
247 auto request_handler =
248 CreateGetAssertionHandlerWithRequest(std::move(request));
249 discovery()->WaitForCallToStartAndSimulateSuccess();
250
Martin Kreichgauer8987fd02018-07-20 22:42:03251 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
Martin Kreichgauer2b753722018-07-17 01:06:01252 test_data::kTestGetInfoResponseWithoutUvSupport);
253
254 discovery()->AddDevice(std::move(device));
255
256 scoped_task_environment_.FastForwardUntilNoTasksRemain();
257 EXPECT_FALSE(get_assertion_callback().was_called());
258}
259
260TEST_F(FidoGetAssertionHandlerTest,
261 TestU2fSignRequestWithUserVerificationRequired) {
262 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
263 test_data::kClientDataHash);
264 request.SetAllowList(
265 {{CredentialType::kPublicKey,
266 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
267 request.SetUserVerification(UserVerificationRequirement::kRequired);
268 auto request_handler =
269 CreateGetAssertionHandlerWithRequest(std::move(request));
270 discovery()->WaitForCallToStartAndSimulateSuccess();
271
Martin Kreichgauer8987fd02018-07-20 22:42:03272 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
273 discovery()->AddDevice(std::move(device));
274
275 scoped_task_environment_.FastForwardUntilNoTasksRemain();
276 EXPECT_FALSE(get_assertion_callback().was_called());
277}
278
279TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) {
280 auto request_handler =
281 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
282 test_data::kRelyingPartyId, test_data::kClientDataHash));
283 discovery()->WaitForCallToStartAndSimulateSuccess();
284 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Martin Kreichgauer2b753722018-07-17 01:06:01285 device->ExpectCtap2CommandAndRespondWith(
Martin Kreichgauer8987fd02018-07-20 22:42:03286 CtapRequestCommand::kAuthenticatorGetAssertion,
287 test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
288
289 discovery()->AddDevice(std::move(device));
290
291 scoped_task_environment_.FastForwardUntilNoTasksRemain();
292 EXPECT_FALSE(get_assertion_callback().was_called());
293}
294
295// Tests a scenario where the authenticator responds with credential ID that
296// is not included in the allowed list.
297TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) {
298 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
299 test_data::kClientDataHash);
300 request.SetAllowList(
301 {{CredentialType::kPublicKey,
302 fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)}});
303 auto request_handler =
304 CreateGetAssertionHandlerWithRequest(std::move(request));
305 discovery()->WaitForCallToStartAndSimulateSuccess();
306 // Resident Keys must be disabled, otherwise allow list check is skipped.
307 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
308 test_data::kTestGetInfoResponseWithoutResidentKeySupport);
309 device->ExpectCtap2CommandAndRespondWith(
310 CtapRequestCommand::kAuthenticatorGetAssertion,
311 test_data::kTestGetAssertionResponse);
312
313 discovery()->AddDevice(std::move(device));
314
315 scoped_task_environment_.FastForwardUntilNoTasksRemain();
316 EXPECT_FALSE(get_assertion_callback().was_called());
317}
318
319// Tests a scenario where authenticator responds without user entity in its
320// response but client is expecting a resident key credential.
321TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) {
322 // Use a GetAssertion request with an empty allow list.
323 auto request_handler =
324 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
325 test_data::kRelyingPartyId, test_data::kClientDataHash));
326 discovery()->WaitForCallToStartAndSimulateSuccess();
327 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
328 device->ExpectCtap2CommandAndRespondWith(
329 CtapRequestCommand::kAuthenticatorGetAssertion,
330 test_data::kTestGetAssertionResponse);
Martin Kreichgauer2b753722018-07-17 01:06:01331
332 discovery()->AddDevice(std::move(device));
333
334 scoped_task_environment_.FastForwardUntilNoTasksRemain();
335 EXPECT_FALSE(get_assertion_callback().was_called());
336}
337
Balazs Engedy159452d2018-08-16 15:18:18338TEST_F(FidoGetAssertionHandlerTest,
339 AllTransportsAllowedIfAllowCredentialsListUndefined) {
Balazs Engedy5b4891f2018-08-29 23:08:00340 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18341 auto request_handler =
Balazs Engedy5b4891f2018-08-29 23:08:00342 CreateGetAssertionHandlerWithRequest(std::move(request));
Balazs Engedy159452d2018-08-16 15:18:18343 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
344}
345
346TEST_F(FidoGetAssertionHandlerTest,
347 AllTransportsAllowedIfAllowCredentialsListIsEmpty) {
Balazs Engedy5b4891f2018-08-29 23:08:00348 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18349 request.SetAllowList({});
350
351 auto request_handler =
352 CreateGetAssertionHandlerWithRequest(std::move(request));
353 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
354}
355
356TEST_F(FidoGetAssertionHandlerTest,
357 AllTransportsAllowedIfHasAllowedCredentialWithEmptyTransportsList) {
Balazs Engedy5b4891f2018-08-29 23:08:00358 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18359 request.SetAllowList({
360 {CredentialType::kPublicKey,
361 fido_parsing_utils::Materialize(
362 test_data::kTestGetAssertionCredentialId),
363 {FidoTransportProtocol::kBluetoothLowEnergy}},
364 {CredentialType::kPublicKey,
365 fido_parsing_utils::Materialize(kBogusCredentialId)},
366 });
367
368 auto request_handler =
369 CreateGetAssertionHandlerWithRequest(std::move(request));
370 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
371}
372
373TEST_F(FidoGetAssertionHandlerTest,
374 AllowedTransportsAreUnionOfTransportsLists) {
Balazs Engedy5b4891f2018-08-29 23:08:00375 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18376 request.SetAllowList({
377 {CredentialType::kPublicKey,
378 fido_parsing_utils::Materialize(
379 test_data::kTestGetAssertionCredentialId),
380 {FidoTransportProtocol::kBluetoothLowEnergy}},
381 {CredentialType::kPublicKey,
382 fido_parsing_utils::Materialize(kBogusCredentialId),
383 {FidoTransportProtocol::kInternal,
384 FidoTransportProtocol::kNearFieldCommunication}},
385 });
386
387 auto request_handler =
388 CreateGetAssertionHandlerWithRequest(std::move(request));
389 ExpectAllowedTransportsForRequestAre(
390 request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
391 FidoTransportProtocol::kInternal,
392 FidoTransportProtocol::kNearFieldCommunication});
393}
394
Balazs Engedy5b4891f2018-08-29 23:08:00395TEST_F(FidoGetAssertionHandlerTest,
396 CableDisabledIfAllowCredentialsListUndefinedButCableExtensionMissing) {
397 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
398 test_data::kClientDataHash);
399 ASSERT_FALSE(!!request.cable_extension());
400
401 auto request_handler =
402 CreateGetAssertionHandlerWithRequest(std::move(request));
403 ExpectAllowedTransportsForRequestAre(
404 request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
405 FidoTransportProtocol::kUsbHumanInterfaceDevice,
406 FidoTransportProtocol::kNearFieldCommunication,
407 FidoTransportProtocol::kInternal});
408}
409
410TEST_F(FidoGetAssertionHandlerTest,
411 CableDisabledIfExplicitlyAllowedButCableExtensionMissing) {
412 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
413 test_data::kClientDataHash);
414 ASSERT_FALSE(!!request.cable_extension());
415 request.SetAllowList({
416 {CredentialType::kPublicKey,
417 fido_parsing_utils::Materialize(
418 test_data::kTestGetAssertionCredentialId),
419 {FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
420 FidoTransportProtocol::kUsbHumanInterfaceDevice}},
421 });
422
423 auto request_handler =
424 CreateGetAssertionHandlerWithRequest(std::move(request));
425 ExpectAllowedTransportsForRequestAre(
426 request_handler.get(), {FidoTransportProtocol::kUsbHumanInterfaceDevice});
427}
428
Balazs Engedy159452d2018-08-16 15:18:18429TEST_F(FidoGetAssertionHandlerTest, SupportedTransportsAreOnlyBleAndNfc) {
430 const base::flat_set<FidoTransportProtocol> kBleAndNfc = {
431 FidoTransportProtocol::kBluetoothLowEnergy,
432 FidoTransportProtocol::kNearFieldCommunication,
433 };
434
435 set_supported_transports(kBleAndNfc);
436
Balazs Engedy5b4891f2018-08-29 23:08:00437 auto request_handler = CreateGetAssertionHandlerWithRequest(
438 CreateTestRequestWithCableExtension());
Balazs Engedy159452d2018-08-16 15:18:18439 ExpectAllowedTransportsForRequestAre(request_handler.get(), kBleAndNfc);
440}
441
Balazs Engedy5b4891f2018-08-29 23:08:00442TEST_F(FidoGetAssertionHandlerTest,
443 SupportedTransportsAreOnlyCableAndInternal) {
444 const base::flat_set<FidoTransportProtocol> kCableAndInternal = {
Balazs Engedy159452d2018-08-16 15:18:18445 FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
446 FidoTransportProtocol::kInternal,
447 };
448
Balazs Engedy5b4891f2018-08-29 23:08:00449 set_supported_transports(kCableAndInternal);
450 auto request_handler = CreateGetAssertionHandlerWithRequest(
451 CreateTestRequestWithCableExtension());
452 ExpectAllowedTransportsForRequestAre(request_handler.get(),
453 kCableAndInternal);
Balazs Engedy159452d2018-08-16 15:18:18454}
455
456TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyUsbTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00457 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18458 request.SetAllowList({
459 {CredentialType::kPublicKey,
460 fido_parsing_utils::Materialize(
461 test_data::kTestGetAssertionCredentialId),
462 {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
463 });
464
465 set_supported_transports({FidoTransportProtocol::kUsbHumanInterfaceDevice});
466
467 auto request_handler =
468 CreateGetAssertionHandlerWithRequest(std::move(request));
469
470 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
471 device->ExpectCtap2CommandAndRespondWith(
472 CtapRequestCommand::kAuthenticatorGetAssertion,
473 test_data::kTestGetAssertionResponse);
474 discovery()->WaitForCallToStartAndSimulateSuccess();
475 discovery()->AddDevice(std::move(device));
476
477 get_assertion_callback().WaitForCallback();
478
479 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
480 EXPECT_TRUE(get_assertion_callback().value<0>());
481 EXPECT_TRUE(request_handler->is_complete());
482 EXPECT_THAT(
483 request_handler->transport_availability_info().available_transports,
484 ::testing::UnorderedElementsAre(
485 FidoTransportProtocol::kUsbHumanInterfaceDevice));
486}
487
488TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyBleTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00489 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18490 request.SetAllowList({
491 {CredentialType::kPublicKey,
492 fido_parsing_utils::Materialize(
493 test_data::kTestGetAssertionCredentialId),
494 {FidoTransportProtocol::kBluetoothLowEnergy}},
495 });
496
497 set_supported_transports({FidoTransportProtocol::kBluetoothLowEnergy});
498
499 auto request_handler =
500 CreateGetAssertionHandlerWithRequest(std::move(request));
501
502 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
503 device->SetDeviceTransport(FidoTransportProtocol::kBluetoothLowEnergy);
504 device->ExpectCtap2CommandAndRespondWith(
505 CtapRequestCommand::kAuthenticatorGetAssertion,
506 test_data::kTestGetAssertionResponse);
507 ble_discovery()->WaitForCallToStartAndSimulateSuccess();
508 ble_discovery()->AddDevice(std::move(device));
509
510 get_assertion_callback().WaitForCallback();
511
512 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
513 EXPECT_TRUE(get_assertion_callback().value<0>());
514 EXPECT_TRUE(request_handler->is_complete());
515 EXPECT_THAT(
516 request_handler->transport_availability_info().available_transports,
517 ::testing::UnorderedElementsAre(
518 FidoTransportProtocol::kBluetoothLowEnergy));
519}
520
521TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyNfcTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00522 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18523 request.SetAllowList({
524 {CredentialType::kPublicKey,
525 fido_parsing_utils::Materialize(
526 test_data::kTestGetAssertionCredentialId),
527 {FidoTransportProtocol::kNearFieldCommunication}},
528 });
529
530 set_supported_transports({FidoTransportProtocol::kNearFieldCommunication});
531
532 auto request_handler =
533 CreateGetAssertionHandlerWithRequest(std::move(request));
534
535 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
536 device->SetDeviceTransport(FidoTransportProtocol::kNearFieldCommunication);
537 device->ExpectCtap2CommandAndRespondWith(
538 CtapRequestCommand::kAuthenticatorGetAssertion,
539 test_data::kTestGetAssertionResponse);
540 nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
541 nfc_discovery()->AddDevice(std::move(device));
542
543 get_assertion_callback().WaitForCallback();
544
545 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
546 EXPECT_TRUE(get_assertion_callback().value<0>());
547 EXPECT_TRUE(request_handler->is_complete());
548 EXPECT_THAT(
549 request_handler->transport_availability_info().available_transports,
550 ::testing::UnorderedElementsAre(
551 FidoTransportProtocol::kNearFieldCommunication));
552}
553
554TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyInternalTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00555 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18556 request.SetAllowList({
557 {CredentialType::kPublicKey,
558 fido_parsing_utils::Materialize(
559 test_data::kTestGetAssertionCredentialId),
560 {FidoTransportProtocol::kInternal}},
561 });
562
563 set_supported_transports({FidoTransportProtocol::kInternal});
564
565 auto device = MockFidoDevice::MakeCtap(
566 ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
567 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
568 device->SetDeviceTransport(FidoTransportProtocol::kInternal);
569 device->ExpectCtap2CommandAndRespondWith(
Jun Choi6065c1d2018-08-23 19:04:48570 CtapRequestCommand::kAuthenticatorGetInfo,
571 test_data::kTestGetInfoResponsePlatformDevice);
572 device->ExpectCtap2CommandAndRespondWith(
Balazs Engedy159452d2018-08-16 15:18:18573 CtapRequestCommand::kAuthenticatorGetAssertion,
574 test_data::kTestGetAssertionResponse);
575 set_mock_platform_device(std::move(device));
576
577 auto request_handler =
578 CreateGetAssertionHandlerWithRequest(std::move(request));
579 get_assertion_callback().WaitForCallback();
580
581 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
582 EXPECT_TRUE(get_assertion_callback().value<0>());
583 EXPECT_TRUE(request_handler->is_complete());
584 EXPECT_THAT(
585 request_handler->transport_availability_info().available_transports,
586 ::testing::UnorderedElementsAre(FidoTransportProtocol::kInternal));
587}
588
Jun Choie4aee4ff32018-08-13 19:35:07589// Tests a scenario where authenticator of incorrect transport type was used to
590// conduct CTAP GetAssertion call.
Balazs Engedy159452d2018-08-16 15:18:18591//
592// TODO(engedy): This should not happen, instead |allowCredentials| should be
593// filtered to only contain items compatible with the transport actually used to
594// talk to the authenticator.
Jun Choie4aee4ff32018-08-13 19:35:07595TEST_F(FidoGetAssertionHandlerTest, IncorrectTransportType) {
596 // GetAssertion request that expects GetAssertion call for credential
597 // |CredentialType::kPublicKey| to be signed with Cable authenticator.
Balazs Engedy5b4891f2018-08-29 23:08:00598 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18599 request.SetAllowList({
600 {CredentialType::kPublicKey,
601 fido_parsing_utils::Materialize(
602 test_data::kTestGetAssertionCredentialId),
603 {FidoTransportProtocol::kBluetoothLowEnergy}},
604 {CredentialType::kPublicKey,
605 fido_parsing_utils::Materialize(kBogusCredentialId),
606 {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
607 });
Jun Choie4aee4ff32018-08-13 19:35:07608 auto request_handler =
609 CreateGetAssertionHandlerWithRequest(std::move(request));
610 discovery()->WaitForCallToStartAndSimulateSuccess();
611 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
612 // Since transport type of |device| is different from what the relying party
613 // defined in |request| above, this request should fail.
614 device->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
615 device->ExpectCtap2CommandAndRespondWith(
616 CtapRequestCommand::kAuthenticatorGetAssertion,
617 test_data::kTestGetAssertionResponse);
618
619 discovery()->AddDevice(std::move(device));
620
621 scoped_task_environment_.FastForwardUntilNoTasksRemain();
622 EXPECT_FALSE(get_assertion_callback().was_called());
623}
624
Martin Kreichgauer5bac1652018-08-22 16:41:10625// If a device with transport type kInternal returns a
626// CTAP2_ERR_OPERATION_DENIED error, the request should complete with
627// FidoReturnCode::kUserConsentDenied. Pending authenticators should be
628// cancelled.
629TEST_F(FidoGetAssertionHandlerTest,
630 TestRequestWithOperationDeniedErrorPlatform) {
Jun Choi6065c1d2018-08-23 19:04:48631 auto platform_device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
632 test_data::kTestGetInfoResponsePlatformDevice);
Martin Kreichgauer5bac1652018-08-22 16:41:10633 platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
634 platform_device->ExpectCtap2CommandAndRespondWithError(
635 CtapRequestCommand::kAuthenticatorGetAssertion,
636 CtapDeviceResponseCode::kCtap2ErrOperationDenied,
637 base::TimeDelta::FromMicroseconds(10));
Martin Kreichgauer5bac1652018-08-22 16:41:10638 set_mock_platform_device(std::move(platform_device));
639
640 auto other_device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
641 other_device->ExpectCtap2CommandAndDoNotRespond(
642 CtapRequestCommand::kAuthenticatorGetAssertion);
643 EXPECT_CALL(*other_device, Cancel);
644
645 auto request_handler = CreateGetAssertionHandlerCtap();
646 discovery()->WaitForCallToStartAndSimulateSuccess();
647 discovery()->AddDevice(std::move(other_device));
648
649 scoped_task_environment_.FastForwardUntilNoTasksRemain();
650 EXPECT_TRUE(get_assertion_callback().was_called());
651 EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
652 get_assertion_callback().status());
653}
654
655// Like |TestRequestWithOperationDeniedErrorPlatform|, but with a
Martin Kreichgauer8e6f1b32018-08-24 13:42:49656// cross-platform device.
Martin Kreichgauer5bac1652018-08-22 16:41:10657TEST_F(FidoGetAssertionHandlerTest,
658 TestRequestWithOperationDeniedErrorCrossPlatform) {
659 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
660 device->ExpectCtap2CommandAndRespondWithError(
661 CtapRequestCommand::kAuthenticatorGetAssertion,
662 CtapDeviceResponseCode::kCtap2ErrOperationDenied);
663
664 auto request_handler = CreateGetAssertionHandlerCtap();
665 discovery()->WaitForCallToStartAndSimulateSuccess();
666 discovery()->AddDevice(std::move(device));
667
668 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Martin Kreichgauer8e6f1b32018-08-24 13:42:49669 EXPECT_TRUE(get_assertion_callback().was_called());
670 EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
671 get_assertion_callback().status());
Martin Kreichgauer5bac1652018-08-22 16:41:10672}
673
Jun Choi4fc8b7812018-04-05 07:39:07674} // namespace device