blob: bf1cda064cd197c366dc884e5d51bedb94d3d4ee [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"
Martin Kreichgauerdf06312d2018-11-08 00:43:3610#include "base/stl_util.h"
Jun Choi4fc8b7812018-04-05 07:39:0711#include "base/test/scoped_task_environment.h"
Martin Kreichgauerdf06312d2018-11-08 00:43:3612#include "build/build_config.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"
Jun Choi4fc8b7812018-04-05 07:39:0719#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"
Martin Kreichgauerdf06312d2018-11-08 00:43:3624#include "device/fido/hid/fake_hid_impl_for_testing.h"
Jun Choi4fc8b7812018-04-05 07:39:0725#include "device/fido/mock_fido_device.h"
26#include "device/fido/test_callback_receiver.h"
Jun Choi4fc8b7812018-04-05 07:39:0727#include "testing/gmock/include/gmock/gmock.h"
28#include "testing/gtest/include/gtest/gtest.h"
29
Martin Kreichgauer75377812018-11-09 18:58:1330#if defined(OS_WIN)
Martin Kreichgauerb129ae52018-11-14 21:26:3331#include "device/fido/win/authenticator.h"
Martin Kreichgauerdf06312d2018-11-08 00:43:3632#include "device/fido/win/fake_webauthn_api.h"
Martin Kreichgauer75377812018-11-09 18:58:1333#endif // defined(OS_WIN)
Martin Kreichgauerdf06312d2018-11-08 00:43:3634
Jun Choi4fc8b7812018-04-05 07:39:0735namespace device {
36
37namespace {
38
Balazs Engedy159452d2018-08-16 15:18:1839constexpr uint8_t kBogusCredentialId[] = {0x01, 0x02, 0x03, 0x04};
40
Jun Choi1beb4c22018-08-16 03:04:3841using TestGetAssertionRequestCallback = test::StatusAndValuesCallbackReceiver<
Jun Choif7ab0df2018-04-05 21:48:1642 FidoReturnCode,
Jun Choi1beb4c22018-08-16 03:04:3843 base::Optional<AuthenticatorGetAssertionResponse>,
Martin Kreichgauerc6de3bc2018-11-13 03:14:0044 base::Optional<FidoTransportProtocol>>;
Jun Choi4fc8b7812018-04-05 07:39:0745
46} // namespace
47
48class FidoGetAssertionHandlerTest : public ::testing::Test {
49 public:
Jun Choife176682018-08-30 07:49:1450 FidoGetAssertionHandlerTest() {
51 mock_adapter_ =
52 base::MakeRefCounted<::testing::NiceMock<MockBluetoothAdapter>>();
53 BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
54 }
55
Balazs Engedy159452d2018-08-16 15:18:1856 void ForgeDiscoveries() {
Jun Choi4fc8b7812018-04-05 07:39:0757 discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
Balazs Engedy159452d2018-08-16 15:18:1858 ble_discovery_ = scoped_fake_discovery_factory_.ForgeNextBleDiscovery();
Balazs Engedy5b4891f2018-08-29 23:08:0059 cable_discovery_ = scoped_fake_discovery_factory_.ForgeNextCableDiscovery();
Balazs Engedy159452d2018-08-16 15:18:1860 nfc_discovery_ = scoped_fake_discovery_factory_.ForgeNextNfcDiscovery();
Jun Choi4fc8b7812018-04-05 07:39:0761 }
62
Balazs Engedy5b4891f2018-08-29 23:08:0063 CtapGetAssertionRequest CreateTestRequestWithCableExtension() {
64 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
Martin Kreichgauer90c625742018-10-31 04:24:1565 test_data::kClientDataJson);
Balazs Engedy5b4891f2018-08-29 23:08:0066 request.SetCableExtension({});
67 return request;
68 }
69
Martin Kreichgauer8987fd02018-07-20 22:42:0370 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerU2f() {
Martin Kreichgauer2b753722018-07-17 01:06:0171 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
Martin Kreichgauer90c625742018-10-31 04:24:1572 test_data::kClientDataJson);
Martin Kreichgauer2b753722018-07-17 01:06:0173 request.SetAllowList(
Jan Wilken Doerrie726e197e2018-05-14 12:53:2574 {{CredentialType::kPublicKey,
Jun Choi0e56e5dd2018-06-08 21:27:3475 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
Martin Kreichgauer8987fd02018-07-20 22:42:0376 return CreateGetAssertionHandlerWithRequest(std::move(request));
77 }
Jun Choi4fc8b7812018-04-05 07:39:0778
Martin Kreichgauer8987fd02018-07-20 22:42:0379 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerCtap() {
80 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
Martin Kreichgauer90c625742018-10-31 04:24:1581 test_data::kClientDataJson);
Martin Kreichgauer8987fd02018-07-20 22:42:0382 request.SetAllowList({{CredentialType::kPublicKey,
83 fido_parsing_utils::Materialize(
84 test_data::kTestGetAssertionCredentialId)}});
Martin Kreichgauer2b753722018-07-17 01:06:0185 return CreateGetAssertionHandlerWithRequest(std::move(request));
86 }
87
88 std::unique_ptr<GetAssertionRequestHandler>
89 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
Balazs Engedy159452d2018-08-16 15:18:1890 ForgeDiscoveries();
Martin Kreichgauer2b753722018-07-17 01:06:0191
Martin Kreichgauer7cbada432018-08-18 02:30:1492 auto handler = std::make_unique<GetAssertionRequestHandler>(
Balazs Engedy159452d2018-08-16 15:18:1893 nullptr /* connector */, supported_transports_, std::move(request),
Martin Kreichgauer7cbada432018-08-18 02:30:1494 get_assertion_cb_.callback());
95 handler->SetPlatformAuthenticatorOrMarkUnavailable(
96 CreatePlatformAuthenticator());
97 return handler;
Balazs Engedy159452d2018-08-16 15:18:1898 }
99
100 void ExpectAllowedTransportsForRequestAre(
101 GetAssertionRequestHandler* request_handler,
102 base::flat_set<FidoTransportProtocol> transports) {
103 using Transport = FidoTransportProtocol;
104 if (base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
105 discovery()->WaitForCallToStartAndSimulateSuccess();
106 if (base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
107 ble_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy5b4891f2018-08-29 23:08:00108 if (base::ContainsKey(transports,
109 Transport::kCloudAssistedBluetoothLowEnergy))
110 cable_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy159452d2018-08-16 15:18:18111 if (base::ContainsKey(transports, Transport::kNearFieldCommunication))
112 nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
Balazs Engedy159452d2018-08-16 15:18:18113
114 scoped_task_environment_.FastForwardUntilNoTasksRemain();
115 EXPECT_FALSE(get_assertion_callback().was_called());
116
117 if (!base::ContainsKey(transports, Transport::kUsbHumanInterfaceDevice))
118 EXPECT_FALSE(discovery()->is_start_requested());
119 if (!base::ContainsKey(transports, Transport::kBluetoothLowEnergy))
120 EXPECT_FALSE(ble_discovery()->is_start_requested());
Balazs Engedy5b4891f2018-08-29 23:08:00121 if (!base::ContainsKey(transports,
122 Transport::kCloudAssistedBluetoothLowEnergy))
123 EXPECT_FALSE(cable_discovery()->is_start_requested());
Balazs Engedy159452d2018-08-16 15:18:18124 if (!base::ContainsKey(transports, Transport::kNearFieldCommunication))
125 EXPECT_FALSE(nfc_discovery()->is_start_requested());
Balazs Engedy159452d2018-08-16 15:18:18126
127 // Even with FidoTransportProtocol::kInternal allowed, unless the platform
128 // authenticator factory returns a FidoAuthenticator instance (which it will
129 // not be default), the transport will be marked `unavailable`.
130 transports.erase(Transport::kInternal);
131
132 EXPECT_THAT(
133 request_handler->transport_availability_info().available_transports,
134 ::testing::UnorderedElementsAreArray(transports));
135 }
136
137 void ExpectAllTransportsAreAllowedForRequest(
138 GetAssertionRequestHandler* request_handler) {
139 ExpectAllowedTransportsForRequestAre(request_handler,
Balazs Engedy5b4891f2018-08-29 23:08:00140 GetAllTransportProtocols());
Jun Choi4fc8b7812018-04-05 07:39:07141 }
142
Jun Choi0e56e5dd2018-06-08 21:27:34143 test::FakeFidoDiscovery* discovery() const { return discovery_; }
Balazs Engedy159452d2018-08-16 15:18:18144 test::FakeFidoDiscovery* ble_discovery() const { return ble_discovery_; }
Balazs Engedy5b4891f2018-08-29 23:08:00145 test::FakeFidoDiscovery* cable_discovery() const { return cable_discovery_; }
Balazs Engedy159452d2018-08-16 15:18:18146 test::FakeFidoDiscovery* nfc_discovery() const { return nfc_discovery_; }
Jun Choi4fc8b7812018-04-05 07:39:07147 TestGetAssertionRequestCallback& get_assertion_callback() {
148 return get_assertion_cb_;
149 }
150
Balazs Engedy159452d2018-08-16 15:18:18151 void set_mock_platform_device(std::unique_ptr<MockFidoDevice> device) {
Martin Kreichgauer71a36322018-10-09 02:11:38152 pending_mock_platform_device_ = std::move(device);
Balazs Engedy159452d2018-08-16 15:18:18153 }
154
Balazs Engedy159452d2018-08-16 15:18:18155 void set_supported_transports(
156 base::flat_set<FidoTransportProtocol> transports) {
157 supported_transports_ = std::move(transports);
158 }
159
Jun Choi4fc8b7812018-04-05 07:39:07160 protected:
Martin Kreichgauere1223082018-08-20 23:17:38161 base::Optional<PlatformAuthenticatorInfo> CreatePlatformAuthenticator() {
Martin Kreichgauer71a36322018-10-09 02:11:38162 if (!pending_mock_platform_device_)
Martin Kreichgauere1223082018-08-20 23:17:38163 return base::nullopt;
164 return PlatformAuthenticatorInfo(
Martin Kreichgauer71a36322018-10-09 02:11:38165 std::make_unique<FidoDeviceAuthenticator>(
166 std::move(pending_mock_platform_device_)),
Martin Kreichgauere1223082018-08-20 23:17:38167 false /* has_recognized_mac_touch_id_credential_available */);
Balazs Engedy159452d2018-08-16 15:18:18168 }
169
Jun Choi4fc8b7812018-04-05 07:39:07170 base::test::ScopedTaskEnvironment scoped_task_environment_{
171 base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
Jun Choi4fc8b7812018-04-05 07:39:07172 test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
173 test::FakeFidoDiscovery* discovery_;
Balazs Engedy159452d2018-08-16 15:18:18174 test::FakeFidoDiscovery* ble_discovery_;
Balazs Engedy5b4891f2018-08-29 23:08:00175 test::FakeFidoDiscovery* cable_discovery_;
Balazs Engedy159452d2018-08-16 15:18:18176 test::FakeFidoDiscovery* nfc_discovery_;
Jun Choife176682018-08-30 07:49:14177 scoped_refptr<::testing::NiceMock<MockBluetoothAdapter>> mock_adapter_;
Martin Kreichgauer71a36322018-10-09 02:11:38178 std::unique_ptr<MockFidoDevice> pending_mock_platform_device_;
Jun Choi4fc8b7812018-04-05 07:39:07179 TestGetAssertionRequestCallback get_assertion_cb_;
Balazs Engedy159452d2018-08-16 15:18:18180 base::flat_set<FidoTransportProtocol> supported_transports_ =
Balazs Engedy5b4891f2018-08-29 23:08:00181 GetAllTransportProtocols();
Jun Choi4fc8b7812018-04-05 07:39:07182};
183
Balazs Engedy0c4fdc52018-08-20 19:15:28184TEST_F(FidoGetAssertionHandlerTest, TransportAvailabilityInfo) {
185 auto request_handler =
186 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
Martin Kreichgauer90c625742018-10-31 04:24:15187 test_data::kRelyingPartyId, test_data::kClientDataJson));
Balazs Engedy0c4fdc52018-08-20 19:15:28188
189 EXPECT_EQ(FidoRequestHandlerBase::RequestType::kGetAssertion,
190 request_handler->transport_availability_info().request_type);
191 EXPECT_EQ(test_data::kRelyingPartyId,
192 request_handler->transport_availability_info().rp_id);
193}
194
Martin Kreichgauer8987fd02018-07-20 22:42:03195TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
196 auto request_handler = CreateGetAssertionHandlerCtap();
Jun Choi4fc8b7812018-04-05 07:39:07197 discovery()->WaitForCallToStartAndSimulateSuccess();
Martin Kreichgauer8987fd02018-07-20 22:42:03198 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Jun Choi4fc8b7812018-04-05 07:39:07199 device->ExpectCtap2CommandAndRespondWith(
200 CtapRequestCommand::kAuthenticatorGetAssertion,
201 test_data::kTestGetAssertionResponse);
202
203 discovery()->AddDevice(std::move(device));
204 get_assertion_callback().WaitForCallback();
205
206 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38207 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi4fc8b7812018-04-05 07:39:07208 EXPECT_TRUE(request_handler->is_complete());
209}
210
Jun Choi0e56e5dd2018-06-08 21:27:34211// Test a scenario where the connected authenticator is a U2F device.
212TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
Martin Kreichgauer8987fd02018-07-20 22:42:03213 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi4fc8b7812018-04-05 07:39:07214 discovery()->WaitForCallToStartAndSimulateSuccess();
215
Martin Kreichgauer8987fd02018-07-20 22:42:03216 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
Jun Choi0e56e5dd2018-06-08 21:27:34217 device->ExpectRequestAndRespondWith(
218 test_data::kU2fCheckOnlySignCommandApdu,
219 test_data::kApduEncodedNoErrorSignResponse);
220 device->ExpectRequestAndRespondWith(
221 test_data::kU2fSignCommandApdu,
222 test_data::kApduEncodedNoErrorSignResponse);
Jun Choi4fc8b7812018-04-05 07:39:07223
224 discovery()->AddDevice(std::move(device));
225 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Jun Choi0e56e5dd2018-06-08 21:27:34226 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38227 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi0e56e5dd2018-06-08 21:27:34228 EXPECT_TRUE(request_handler->is_complete());
229}
230
Martin Kreichgauer2b753722018-07-17 01:06:01231TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {
232 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
Martin Kreichgauer90c625742018-10-31 04:24:15233 test_data::kClientDataJson);
Martin Kreichgauer2b753722018-07-17 01:06:01234 request.SetUserVerification(UserVerificationRequirement::kRequired);
235 auto request_handler =
236 CreateGetAssertionHandlerWithRequest(std::move(request));
237 discovery()->WaitForCallToStartAndSimulateSuccess();
238
Martin Kreichgauer8987fd02018-07-20 22:42:03239 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
Martin Kreichgauer2b753722018-07-17 01:06:01240 test_data::kTestGetInfoResponseWithoutUvSupport);
241
242 discovery()->AddDevice(std::move(device));
243
244 scoped_task_environment_.FastForwardUntilNoTasksRemain();
245 EXPECT_FALSE(get_assertion_callback().was_called());
246}
247
248TEST_F(FidoGetAssertionHandlerTest,
249 TestU2fSignRequestWithUserVerificationRequired) {
250 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
Martin Kreichgauer90c625742018-10-31 04:24:15251 test_data::kClientDataJson);
Martin Kreichgauer2b753722018-07-17 01:06:01252 request.SetAllowList(
253 {{CredentialType::kPublicKey,
254 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
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::MakeU2fWithGetInfoExpectation();
261 discovery()->AddDevice(std::move(device));
262
263 scoped_task_environment_.FastForwardUntilNoTasksRemain();
264 EXPECT_FALSE(get_assertion_callback().was_called());
265}
266
267TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) {
268 auto request_handler =
269 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
Martin Kreichgauer90c625742018-10-31 04:24:15270 test_data::kRelyingPartyId, test_data::kClientDataJson));
Martin Kreichgauer8987fd02018-07-20 22:42:03271 discovery()->WaitForCallToStartAndSimulateSuccess();
272 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Martin Kreichgauer2b753722018-07-17 01:06:01273 device->ExpectCtap2CommandAndRespondWith(
Martin Kreichgauer8987fd02018-07-20 22:42:03274 CtapRequestCommand::kAuthenticatorGetAssertion,
275 test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
276
277 discovery()->AddDevice(std::move(device));
278
279 scoped_task_environment_.FastForwardUntilNoTasksRemain();
280 EXPECT_FALSE(get_assertion_callback().was_called());
281}
282
283// Tests a scenario where the authenticator responds with credential ID that
284// is not included in the allowed list.
285TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) {
286 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
Martin Kreichgauer90c625742018-10-31 04:24:15287 test_data::kClientDataJson);
Martin Kreichgauer8987fd02018-07-20 22:42:03288 request.SetAllowList(
289 {{CredentialType::kPublicKey,
290 fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)}});
291 auto request_handler =
292 CreateGetAssertionHandlerWithRequest(std::move(request));
293 discovery()->WaitForCallToStartAndSimulateSuccess();
294 // Resident Keys must be disabled, otherwise allow list check is skipped.
295 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
296 test_data::kTestGetInfoResponseWithoutResidentKeySupport);
297 device->ExpectCtap2CommandAndRespondWith(
298 CtapRequestCommand::kAuthenticatorGetAssertion,
299 test_data::kTestGetAssertionResponse);
300
301 discovery()->AddDevice(std::move(device));
302
303 scoped_task_environment_.FastForwardUntilNoTasksRemain();
304 EXPECT_FALSE(get_assertion_callback().was_called());
305}
306
Jun Choib9499af2018-11-07 07:43:51307// Tests a scenario where the authenticator responds with an empty credential.
308// When GetAssertion request only has a single credential in the allow list,
309// this is a valid response. Check that credential is set by the client before
310// the response is returned to the relying party.
311TEST_F(FidoGetAssertionHandlerTest, ValidEmptyCredential) {
312 auto request_handler = CreateGetAssertionHandlerCtap();
313 discovery()->WaitForCallToStartAndSimulateSuccess();
314 // Resident Keys must be disabled, otherwise allow list check is skipped.
315 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
316 test_data::kTestGetInfoResponseWithoutResidentKeySupport);
317 device->ExpectCtap2CommandAndRespondWith(
318 CtapRequestCommand::kAuthenticatorGetAssertion,
319 test_data::kTestGetAssertionResponseWithEmptyCredential);
320 discovery()->AddDevice(std::move(device));
321
322 get_assertion_callback().WaitForCallback();
323 const auto& response = get_assertion_callback().value<0>();
324 EXPECT_TRUE(request_handler->is_complete());
325 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
326 ASSERT_TRUE(response);
327 EXPECT_TRUE(response->credential());
328 EXPECT_THAT(
329 response->raw_credential_id(),
330 ::testing::ElementsAreArray(test_data::kTestGetAssertionCredentialId));
331}
332
Martin Kreichgauer8987fd02018-07-20 22:42:03333// Tests a scenario where authenticator responds without user entity in its
334// response but client is expecting a resident key credential.
335TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) {
336 // Use a GetAssertion request with an empty allow list.
337 auto request_handler =
338 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
Martin Kreichgauer90c625742018-10-31 04:24:15339 test_data::kRelyingPartyId, test_data::kClientDataJson));
Martin Kreichgauer8987fd02018-07-20 22:42:03340 discovery()->WaitForCallToStartAndSimulateSuccess();
341 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
342 device->ExpectCtap2CommandAndRespondWith(
343 CtapRequestCommand::kAuthenticatorGetAssertion,
344 test_data::kTestGetAssertionResponse);
Martin Kreichgauer2b753722018-07-17 01:06:01345
346 discovery()->AddDevice(std::move(device));
347
348 scoped_task_environment_.FastForwardUntilNoTasksRemain();
349 EXPECT_FALSE(get_assertion_callback().was_called());
350}
351
Balazs Engedy159452d2018-08-16 15:18:18352TEST_F(FidoGetAssertionHandlerTest,
353 AllTransportsAllowedIfAllowCredentialsListUndefined) {
Balazs Engedy5b4891f2018-08-29 23:08:00354 auto request = CreateTestRequestWithCableExtension();
Jun Choife176682018-08-30 07:49:14355 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18356 auto request_handler =
Balazs Engedy5b4891f2018-08-29 23:08:00357 CreateGetAssertionHandlerWithRequest(std::move(request));
Balazs Engedy159452d2018-08-16 15:18:18358 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
359}
360
361TEST_F(FidoGetAssertionHandlerTest,
362 AllTransportsAllowedIfAllowCredentialsListIsEmpty) {
Balazs Engedy5b4891f2018-08-29 23:08:00363 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18364 request.SetAllowList({});
Jun Choife176682018-08-30 07:49:14365 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18366 auto request_handler =
367 CreateGetAssertionHandlerWithRequest(std::move(request));
368 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
369}
370
371TEST_F(FidoGetAssertionHandlerTest,
372 AllTransportsAllowedIfHasAllowedCredentialWithEmptyTransportsList) {
Balazs Engedy5b4891f2018-08-29 23:08:00373 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18374 request.SetAllowList({
375 {CredentialType::kPublicKey,
376 fido_parsing_utils::Materialize(
377 test_data::kTestGetAssertionCredentialId),
378 {FidoTransportProtocol::kBluetoothLowEnergy}},
379 {CredentialType::kPublicKey,
380 fido_parsing_utils::Materialize(kBogusCredentialId)},
381 });
382
Jun Choife176682018-08-30 07:49:14383 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18384 auto request_handler =
385 CreateGetAssertionHandlerWithRequest(std::move(request));
386 ExpectAllTransportsAreAllowedForRequest(request_handler.get());
387}
388
389TEST_F(FidoGetAssertionHandlerTest,
390 AllowedTransportsAreUnionOfTransportsLists) {
Balazs Engedy5b4891f2018-08-29 23:08:00391 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18392 request.SetAllowList({
393 {CredentialType::kPublicKey,
394 fido_parsing_utils::Materialize(
395 test_data::kTestGetAssertionCredentialId),
396 {FidoTransportProtocol::kBluetoothLowEnergy}},
397 {CredentialType::kPublicKey,
398 fido_parsing_utils::Materialize(kBogusCredentialId),
399 {FidoTransportProtocol::kInternal,
400 FidoTransportProtocol::kNearFieldCommunication}},
401 });
402
Jun Choife176682018-08-30 07:49:14403 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18404 auto request_handler =
405 CreateGetAssertionHandlerWithRequest(std::move(request));
406 ExpectAllowedTransportsForRequestAre(
407 request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
408 FidoTransportProtocol::kInternal,
409 FidoTransportProtocol::kNearFieldCommunication});
410}
411
Balazs Engedy5b4891f2018-08-29 23:08:00412TEST_F(FidoGetAssertionHandlerTest,
413 CableDisabledIfAllowCredentialsListUndefinedButCableExtensionMissing) {
414 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
Martin Kreichgauer90c625742018-10-31 04:24:15415 test_data::kClientDataJson);
Balazs Engedy5b4891f2018-08-29 23:08:00416 ASSERT_FALSE(!!request.cable_extension());
Jun Choife176682018-08-30 07:49:14417 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy5b4891f2018-08-29 23:08:00418 auto request_handler =
419 CreateGetAssertionHandlerWithRequest(std::move(request));
420 ExpectAllowedTransportsForRequestAre(
421 request_handler.get(), {FidoTransportProtocol::kBluetoothLowEnergy,
422 FidoTransportProtocol::kUsbHumanInterfaceDevice,
423 FidoTransportProtocol::kNearFieldCommunication,
424 FidoTransportProtocol::kInternal});
425}
426
427TEST_F(FidoGetAssertionHandlerTest,
428 CableDisabledIfExplicitlyAllowedButCableExtensionMissing) {
429 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
Martin Kreichgauer90c625742018-10-31 04:24:15430 test_data::kClientDataJson);
Balazs Engedy5b4891f2018-08-29 23:08:00431 ASSERT_FALSE(!!request.cable_extension());
432 request.SetAllowList({
433 {CredentialType::kPublicKey,
434 fido_parsing_utils::Materialize(
435 test_data::kTestGetAssertionCredentialId),
436 {FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
437 FidoTransportProtocol::kUsbHumanInterfaceDevice}},
438 });
439
440 auto request_handler =
441 CreateGetAssertionHandlerWithRequest(std::move(request));
442 ExpectAllowedTransportsForRequestAre(
443 request_handler.get(), {FidoTransportProtocol::kUsbHumanInterfaceDevice});
444}
445
Balazs Engedy159452d2018-08-16 15:18:18446TEST_F(FidoGetAssertionHandlerTest, SupportedTransportsAreOnlyBleAndNfc) {
447 const base::flat_set<FidoTransportProtocol> kBleAndNfc = {
448 FidoTransportProtocol::kBluetoothLowEnergy,
449 FidoTransportProtocol::kNearFieldCommunication,
450 };
451
452 set_supported_transports(kBleAndNfc);
Jun Choife176682018-08-30 07:49:14453 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy5b4891f2018-08-29 23:08:00454 auto request_handler = CreateGetAssertionHandlerWithRequest(
455 CreateTestRequestWithCableExtension());
Balazs Engedy159452d2018-08-16 15:18:18456 ExpectAllowedTransportsForRequestAre(request_handler.get(), kBleAndNfc);
457}
458
Balazs Engedy5b4891f2018-08-29 23:08:00459TEST_F(FidoGetAssertionHandlerTest,
460 SupportedTransportsAreOnlyCableAndInternal) {
461 const base::flat_set<FidoTransportProtocol> kCableAndInternal = {
Balazs Engedy159452d2018-08-16 15:18:18462 FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy,
463 FidoTransportProtocol::kInternal,
464 };
465
Jun Choife176682018-08-30 07:49:14466 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy5b4891f2018-08-29 23:08:00467 set_supported_transports(kCableAndInternal);
468 auto request_handler = CreateGetAssertionHandlerWithRequest(
469 CreateTestRequestWithCableExtension());
470 ExpectAllowedTransportsForRequestAre(request_handler.get(),
471 kCableAndInternal);
Balazs Engedy159452d2018-08-16 15:18:18472}
473
474TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyUsbTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00475 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18476 request.SetAllowList({
477 {CredentialType::kPublicKey,
478 fido_parsing_utils::Materialize(
479 test_data::kTestGetAssertionCredentialId),
480 {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
481 });
482
483 set_supported_transports({FidoTransportProtocol::kUsbHumanInterfaceDevice});
484
485 auto request_handler =
486 CreateGetAssertionHandlerWithRequest(std::move(request));
487
488 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
489 device->ExpectCtap2CommandAndRespondWith(
490 CtapRequestCommand::kAuthenticatorGetAssertion,
491 test_data::kTestGetAssertionResponse);
492 discovery()->WaitForCallToStartAndSimulateSuccess();
493 discovery()->AddDevice(std::move(device));
494
495 get_assertion_callback().WaitForCallback();
496
497 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
498 EXPECT_TRUE(get_assertion_callback().value<0>());
499 EXPECT_TRUE(request_handler->is_complete());
500 EXPECT_THAT(
501 request_handler->transport_availability_info().available_transports,
502 ::testing::UnorderedElementsAre(
503 FidoTransportProtocol::kUsbHumanInterfaceDevice));
504}
505
506TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyBleTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00507 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18508 request.SetAllowList({
509 {CredentialType::kPublicKey,
510 fido_parsing_utils::Materialize(
511 test_data::kTestGetAssertionCredentialId),
512 {FidoTransportProtocol::kBluetoothLowEnergy}},
513 });
514
515 set_supported_transports({FidoTransportProtocol::kBluetoothLowEnergy});
Jun Choife176682018-08-30 07:49:14516 EXPECT_CALL(*mock_adapter_, IsPresent()).WillOnce(::testing::Return(true));
Balazs Engedy159452d2018-08-16 15:18:18517 auto request_handler =
518 CreateGetAssertionHandlerWithRequest(std::move(request));
519
520 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
521 device->SetDeviceTransport(FidoTransportProtocol::kBluetoothLowEnergy);
522 device->ExpectCtap2CommandAndRespondWith(
523 CtapRequestCommand::kAuthenticatorGetAssertion,
524 test_data::kTestGetAssertionResponse);
525 ble_discovery()->WaitForCallToStartAndSimulateSuccess();
526 ble_discovery()->AddDevice(std::move(device));
527
528 get_assertion_callback().WaitForCallback();
529
530 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
531 EXPECT_TRUE(get_assertion_callback().value<0>());
532 EXPECT_TRUE(request_handler->is_complete());
533 EXPECT_THAT(
534 request_handler->transport_availability_info().available_transports,
535 ::testing::UnorderedElementsAre(
536 FidoTransportProtocol::kBluetoothLowEnergy));
537}
538
539TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyNfcTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00540 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18541 request.SetAllowList({
542 {CredentialType::kPublicKey,
543 fido_parsing_utils::Materialize(
544 test_data::kTestGetAssertionCredentialId),
545 {FidoTransportProtocol::kNearFieldCommunication}},
546 });
547
548 set_supported_transports({FidoTransportProtocol::kNearFieldCommunication});
549
550 auto request_handler =
551 CreateGetAssertionHandlerWithRequest(std::move(request));
552
553 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
554 device->SetDeviceTransport(FidoTransportProtocol::kNearFieldCommunication);
555 device->ExpectCtap2CommandAndRespondWith(
556 CtapRequestCommand::kAuthenticatorGetAssertion,
557 test_data::kTestGetAssertionResponse);
558 nfc_discovery()->WaitForCallToStartAndSimulateSuccess();
559 nfc_discovery()->AddDevice(std::move(device));
560
561 get_assertion_callback().WaitForCallback();
562
563 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
564 EXPECT_TRUE(get_assertion_callback().value<0>());
565 EXPECT_TRUE(request_handler->is_complete());
566 EXPECT_THAT(
567 request_handler->transport_availability_info().available_transports,
568 ::testing::UnorderedElementsAre(
569 FidoTransportProtocol::kNearFieldCommunication));
570}
571
572TEST_F(FidoGetAssertionHandlerTest, SuccessWithOnlyInternalTransportAllowed) {
Balazs Engedy5b4891f2018-08-29 23:08:00573 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18574 request.SetAllowList({
575 {CredentialType::kPublicKey,
576 fido_parsing_utils::Materialize(
577 test_data::kTestGetAssertionCredentialId),
578 {FidoTransportProtocol::kInternal}},
579 });
580
581 set_supported_transports({FidoTransportProtocol::kInternal});
582
583 auto device = MockFidoDevice::MakeCtap(
584 ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
585 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
586 device->SetDeviceTransport(FidoTransportProtocol::kInternal);
587 device->ExpectCtap2CommandAndRespondWith(
Jun Choi6065c1d2018-08-23 19:04:48588 CtapRequestCommand::kAuthenticatorGetInfo,
589 test_data::kTestGetInfoResponsePlatformDevice);
590 device->ExpectCtap2CommandAndRespondWith(
Balazs Engedy159452d2018-08-16 15:18:18591 CtapRequestCommand::kAuthenticatorGetAssertion,
592 test_data::kTestGetAssertionResponse);
593 set_mock_platform_device(std::move(device));
594
595 auto request_handler =
596 CreateGetAssertionHandlerWithRequest(std::move(request));
597 get_assertion_callback().WaitForCallback();
598
599 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
600 EXPECT_TRUE(get_assertion_callback().value<0>());
601 EXPECT_TRUE(request_handler->is_complete());
602 EXPECT_THAT(
603 request_handler->transport_availability_info().available_transports,
604 ::testing::UnorderedElementsAre(FidoTransportProtocol::kInternal));
605}
606
Jun Choie4aee4ff32018-08-13 19:35:07607// Tests a scenario where authenticator of incorrect transport type was used to
608// conduct CTAP GetAssertion call.
Balazs Engedy159452d2018-08-16 15:18:18609//
610// TODO(engedy): This should not happen, instead |allowCredentials| should be
611// filtered to only contain items compatible with the transport actually used to
612// talk to the authenticator.
Jun Choie4aee4ff32018-08-13 19:35:07613TEST_F(FidoGetAssertionHandlerTest, IncorrectTransportType) {
614 // GetAssertion request that expects GetAssertion call for credential
615 // |CredentialType::kPublicKey| to be signed with Cable authenticator.
Balazs Engedy5b4891f2018-08-29 23:08:00616 auto request = CreateTestRequestWithCableExtension();
Balazs Engedy159452d2018-08-16 15:18:18617 request.SetAllowList({
618 {CredentialType::kPublicKey,
619 fido_parsing_utils::Materialize(
620 test_data::kTestGetAssertionCredentialId),
621 {FidoTransportProtocol::kBluetoothLowEnergy}},
622 {CredentialType::kPublicKey,
623 fido_parsing_utils::Materialize(kBogusCredentialId),
624 {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
625 });
Jun Choie4aee4ff32018-08-13 19:35:07626 auto request_handler =
627 CreateGetAssertionHandlerWithRequest(std::move(request));
628 discovery()->WaitForCallToStartAndSimulateSuccess();
629 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
630 // Since transport type of |device| is different from what the relying party
631 // defined in |request| above, this request should fail.
632 device->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
633 device->ExpectCtap2CommandAndRespondWith(
634 CtapRequestCommand::kAuthenticatorGetAssertion,
635 test_data::kTestGetAssertionResponse);
636
637 discovery()->AddDevice(std::move(device));
638
639 scoped_task_environment_.FastForwardUntilNoTasksRemain();
640 EXPECT_FALSE(get_assertion_callback().was_called());
641}
642
Martin Kreichgauer5bac1652018-08-22 16:41:10643// If a device with transport type kInternal returns a
644// CTAP2_ERR_OPERATION_DENIED error, the request should complete with
645// FidoReturnCode::kUserConsentDenied. Pending authenticators should be
646// cancelled.
647TEST_F(FidoGetAssertionHandlerTest,
648 TestRequestWithOperationDeniedErrorPlatform) {
Jun Choi6065c1d2018-08-23 19:04:48649 auto platform_device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
650 test_data::kTestGetInfoResponsePlatformDevice);
Martin Kreichgauer5bac1652018-08-22 16:41:10651 platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
652 platform_device->ExpectCtap2CommandAndRespondWithError(
653 CtapRequestCommand::kAuthenticatorGetAssertion,
654 CtapDeviceResponseCode::kCtap2ErrOperationDenied,
655 base::TimeDelta::FromMicroseconds(10));
Martin Kreichgauer5bac1652018-08-22 16:41:10656 set_mock_platform_device(std::move(platform_device));
657
658 auto other_device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
659 other_device->ExpectCtap2CommandAndDoNotRespond(
660 CtapRequestCommand::kAuthenticatorGetAssertion);
661 EXPECT_CALL(*other_device, Cancel);
662
663 auto request_handler = CreateGetAssertionHandlerCtap();
664 discovery()->WaitForCallToStartAndSimulateSuccess();
665 discovery()->AddDevice(std::move(other_device));
666
667 scoped_task_environment_.FastForwardUntilNoTasksRemain();
668 EXPECT_TRUE(get_assertion_callback().was_called());
669 EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
670 get_assertion_callback().status());
671}
672
673// Like |TestRequestWithOperationDeniedErrorPlatform|, but with a
Martin Kreichgauer8e6f1b32018-08-24 13:42:49674// cross-platform device.
Martin Kreichgauer5bac1652018-08-22 16:41:10675TEST_F(FidoGetAssertionHandlerTest,
676 TestRequestWithOperationDeniedErrorCrossPlatform) {
677 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
678 device->ExpectCtap2CommandAndRespondWithError(
679 CtapRequestCommand::kAuthenticatorGetAssertion,
680 CtapDeviceResponseCode::kCtap2ErrOperationDenied);
681
682 auto request_handler = CreateGetAssertionHandlerCtap();
683 discovery()->WaitForCallToStartAndSimulateSuccess();
684 discovery()->AddDevice(std::move(device));
685
686 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Martin Kreichgauer8e6f1b32018-08-24 13:42:49687 EXPECT_TRUE(get_assertion_callback().was_called());
688 EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
689 get_assertion_callback().status());
Martin Kreichgauer5bac1652018-08-22 16:41:10690}
691
Martin Kreichgauer75377812018-11-09 18:58:13692#if defined(OS_WIN)
Martin Kreichgauerdf06312d2018-11-08 00:43:36693// Verify that the request handler instantiates a HID device backed
694// FidoDeviceAuthenticator or a WinNativeCrossPlatformAuthenticator, depending
Martin Kreichgauerfd071e52019-01-26 00:38:55695// on API availability.
696TEST(GetAssertionRequestHandlerWinTest, TestWinUsbDiscovery) {
697 base::test::ScopedTaskEnvironment scoped_task_environment;
698 ScopedFakeWinWebAuthnApi scoped_fake_win_webauthn_api;
699 for (const bool enable_api : {false, true}) {
700 SCOPED_TRACE(::testing::Message() << "enable_api=" << enable_api);
701 scoped_fake_win_webauthn_api.set_available(enable_api);
Martin Kreichgauerdf06312d2018-11-08 00:43:36702
Martin Kreichgauerb129ae52018-11-14 21:26:33703 // Simulate a connected HID device.
704 ScopedFakeHidManager fake_hid_manager;
705 fake_hid_manager.AddFidoHidDevice("guid");
706
Martin Kreichgauerdf06312d2018-11-08 00:43:36707 TestGetAssertionRequestCallback cb;
Martin Kreichgauerdf06312d2018-11-08 00:43:36708 auto handler = std::make_unique<GetAssertionRequestHandler>(
Martin Kreichgauerb129ae52018-11-14 21:26:33709 fake_hid_manager.service_manager_connector(),
Martin Kreichgauerdf06312d2018-11-08 00:43:36710 base::flat_set<FidoTransportProtocol>(
711 {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
712 CtapGetAssertionRequest(test_data::kRelyingPartyId,
713 test_data::kClientDataJson),
714
715 cb.callback());
Martin Kreichgauerfd071e52019-01-26 00:38:55716 scoped_task_environment.RunUntilIdle();
Martin Kreichgauerdf06312d2018-11-08 00:43:36717
Martin Kreichgauerdf06312d2018-11-08 00:43:36718 EXPECT_EQ(1u, handler->AuthenticatorsForTesting().size());
719 // Crudely distinguish authenticator type by FidoAuthenticator::GetId.
Martin Kreichgauerfd071e52019-01-26 00:38:55720 EXPECT_EQ(
721 enable_api ? WinWebAuthnApiAuthenticator::kAuthenticatorId : "hid:guid",
722 handler->AuthenticatorsForTesting().begin()->second->GetId());
Martin Kreichgauerdf06312d2018-11-08 00:43:36723 }
724}
Martin Kreichgauer75377812018-11-09 18:58:13725#endif // defined(OS_WIN)
Martin Kreichgauerdf06312d2018-11-08 00:43:36726
Jun Choi4fc8b7812018-04-05 07:39:07727} // namespace device