blob: 14430c2d1a8f8c55bfe4341383904aeac2c18506 [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
Jun Choi0e56e5dd2018-06-08 21:27:348#include "base/test/scoped_feature_list.h"
Jun Choi4fc8b7812018-04-05 07:39:079#include "base/test/scoped_task_environment.h"
Jun Choi0e56e5dd2018-06-08 21:27:3410#include "device/base/features.h"
Jun Choi4fc8b7812018-04-05 07:39:0711#include "device/fido/authenticator_get_assertion_response.h"
12#include "device/fido/ctap_get_assertion_request.h"
13#include "device/fido/fake_fido_discovery.h"
14#include "device/fido/fido_constants.h"
Jun Choi19b944e92018-04-23 20:20:2015#include "device/fido/fido_parsing_utils.h"
Jun Choi22af8b372018-04-09 04:29:1816#include "device/fido/fido_test_data.h"
Jun Choib60937e2018-04-12 17:02:3817#include "device/fido/fido_transport_protocol.h"
Jun Choi4fc8b7812018-04-05 07:39:0718#include "device/fido/get_assertion_request_handler.h"
19#include "device/fido/mock_fido_device.h"
20#include "device/fido/test_callback_receiver.h"
Jun Choi4fc8b7812018-04-05 07:39:0721#include "testing/gmock/include/gmock/gmock.h"
22#include "testing/gtest/include/gtest/gtest.h"
23
24namespace device {
25
26namespace {
27
Jun Choi1beb4c22018-08-16 03:04:3828using TestGetAssertionRequestCallback = test::StatusAndValuesCallbackReceiver<
Jun Choif7ab0df2018-04-05 21:48:1629 FidoReturnCode,
Jun Choi1beb4c22018-08-16 03:04:3830 base::Optional<AuthenticatorGetAssertionResponse>,
31 FidoTransportProtocol>;
Jun Choi4fc8b7812018-04-05 07:39:0732
33} // namespace
34
35class FidoGetAssertionHandlerTest : public ::testing::Test {
36 public:
37 void ForgeNextHidDiscovery() {
38 discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
39 }
40
Martin Kreichgauer8987fd02018-07-20 22:42:0341 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerU2f() {
Martin Kreichgauer2b753722018-07-17 01:06:0142 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
43 test_data::kClientDataHash);
44 request.SetAllowList(
Jan Wilken Doerrie726e197e2018-05-14 12:53:2545 {{CredentialType::kPublicKey,
Jun Choi0e56e5dd2018-06-08 21:27:3446 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
Martin Kreichgauer8987fd02018-07-20 22:42:0347 return CreateGetAssertionHandlerWithRequest(std::move(request));
48 }
Jun Choi4fc8b7812018-04-05 07:39:0749
Martin Kreichgauer8987fd02018-07-20 22:42:0350 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerCtap() {
51 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
52 test_data::kClientDataHash);
53 request.SetAllowList({{CredentialType::kPublicKey,
54 fido_parsing_utils::Materialize(
55 test_data::kTestGetAssertionCredentialId)}});
Martin Kreichgauer2b753722018-07-17 01:06:0156 return CreateGetAssertionHandlerWithRequest(std::move(request));
57 }
58
59 std::unique_ptr<GetAssertionRequestHandler>
60 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
61 ForgeNextHidDiscovery();
62
Jun Choi4fc8b7812018-04-05 07:39:0763 return std::make_unique<GetAssertionRequestHandler>(
64 nullptr /* connector */,
Jun Choib60937e2018-04-12 17:02:3865 base::flat_set<FidoTransportProtocol>(
66 {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
Martin Kreichgauer2b753722018-07-17 01:06:0167 std::move(request), get_assertion_cb_.callback());
Jun Choi4fc8b7812018-04-05 07:39:0768 }
69
Jun Choid19453d2018-06-21 23:16:3970 void InitFeatureListAndDisableCtapFlag() {
71 scoped_feature_list_.InitAndDisableFeature(kNewCtap2Device);
Jun Choi0e56e5dd2018-06-08 21:27:3472 }
Jun Choi4fc8b7812018-04-05 07:39:0773
Jun Choi0e56e5dd2018-06-08 21:27:3474 test::FakeFidoDiscovery* discovery() const { return discovery_; }
Jun Choi4fc8b7812018-04-05 07:39:0775 TestGetAssertionRequestCallback& get_assertion_callback() {
76 return get_assertion_cb_;
77 }
78
79 protected:
80 base::test::ScopedTaskEnvironment scoped_task_environment_{
81 base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
Jun Choi0e56e5dd2018-06-08 21:27:3482 base::test::ScopedFeatureList scoped_feature_list_;
Jun Choi4fc8b7812018-04-05 07:39:0783 test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
84 test::FakeFidoDiscovery* discovery_;
85 TestGetAssertionRequestCallback get_assertion_cb_;
86};
87
Martin Kreichgauer8987fd02018-07-20 22:42:0388TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
89 auto request_handler = CreateGetAssertionHandlerCtap();
Jun Choi4fc8b7812018-04-05 07:39:0790 discovery()->WaitForCallToStartAndSimulateSuccess();
Martin Kreichgauer8987fd02018-07-20 22:42:0391 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Jun Choi4fc8b7812018-04-05 07:39:0792 device->ExpectCtap2CommandAndRespondWith(
93 CtapRequestCommand::kAuthenticatorGetAssertion,
94 test_data::kTestGetAssertionResponse);
95
96 discovery()->AddDevice(std::move(device));
97 get_assertion_callback().WaitForCallback();
98
99 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38100 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi4fc8b7812018-04-05 07:39:07101 EXPECT_TRUE(request_handler->is_complete());
102}
103
Jun Choi0e56e5dd2018-06-08 21:27:34104// Test a scenario where the connected authenticator is a U2F device.
105TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
Martin Kreichgauer8987fd02018-07-20 22:42:03106 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi4fc8b7812018-04-05 07:39:07107 discovery()->WaitForCallToStartAndSimulateSuccess();
108
Martin Kreichgauer8987fd02018-07-20 22:42:03109 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
Jun Choi0e56e5dd2018-06-08 21:27:34110 device->ExpectRequestAndRespondWith(
111 test_data::kU2fCheckOnlySignCommandApdu,
112 test_data::kApduEncodedNoErrorSignResponse);
113 device->ExpectRequestAndRespondWith(
114 test_data::kU2fSignCommandApdu,
115 test_data::kApduEncodedNoErrorSignResponse);
Jun Choi4fc8b7812018-04-05 07:39:07116
117 discovery()->AddDevice(std::move(device));
118 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Jun Choi0e56e5dd2018-06-08 21:27:34119 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38120 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi0e56e5dd2018-06-08 21:27:34121 EXPECT_TRUE(request_handler->is_complete());
122}
123
124// Test a scenario where the connected authenticator is a U2F device and
125// "WebAuthenticationCtap2" flag is not enabled.
126TEST_F(FidoGetAssertionHandlerTest, TestU2fSignWithoutCtapFlag) {
Jun Choid19453d2018-06-21 23:16:39127 InitFeatureListAndDisableCtapFlag();
Martin Kreichgauer8987fd02018-07-20 22:42:03128 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi0e56e5dd2018-06-08 21:27:34129 discovery()->WaitForCallToStartAndSimulateSuccess();
130
131 auto device = std::make_unique<MockFidoDevice>();
132 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
133 device->ExpectRequestAndRespondWith(
134 test_data::kU2fCheckOnlySignCommandApdu,
135 test_data::kApduEncodedNoErrorSignResponse);
136 device->ExpectRequestAndRespondWith(
137 test_data::kU2fSignCommandApdu,
138 test_data::kApduEncodedNoErrorSignResponse);
139
140 discovery()->AddDevice(std::move(device));
141 scoped_task_environment_.FastForwardUntilNoTasksRemain();
142 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
Jun Choi1beb4c22018-08-16 03:04:38143 EXPECT_TRUE(get_assertion_callback().value<0>());
Jun Choi0e56e5dd2018-06-08 21:27:34144 EXPECT_TRUE(request_handler->is_complete());
Jun Choi4fc8b7812018-04-05 07:39:07145}
146
Martin Kreichgauer2b753722018-07-17 01:06:01147TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {
148 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
149 test_data::kClientDataHash);
150 request.SetUserVerification(UserVerificationRequirement::kRequired);
151 auto request_handler =
152 CreateGetAssertionHandlerWithRequest(std::move(request));
153 discovery()->WaitForCallToStartAndSimulateSuccess();
154
Martin Kreichgauer8987fd02018-07-20 22:42:03155 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
Martin Kreichgauer2b753722018-07-17 01:06:01156 test_data::kTestGetInfoResponseWithoutUvSupport);
157
158 discovery()->AddDevice(std::move(device));
159
160 scoped_task_environment_.FastForwardUntilNoTasksRemain();
161 EXPECT_FALSE(get_assertion_callback().was_called());
162}
163
164TEST_F(FidoGetAssertionHandlerTest,
165 TestU2fSignRequestWithUserVerificationRequired) {
166 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
167 test_data::kClientDataHash);
168 request.SetAllowList(
169 {{CredentialType::kPublicKey,
170 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
171 request.SetUserVerification(UserVerificationRequirement::kRequired);
172 auto request_handler =
173 CreateGetAssertionHandlerWithRequest(std::move(request));
174 discovery()->WaitForCallToStartAndSimulateSuccess();
175
Martin Kreichgauer8987fd02018-07-20 22:42:03176 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
177 discovery()->AddDevice(std::move(device));
178
179 scoped_task_environment_.FastForwardUntilNoTasksRemain();
180 EXPECT_FALSE(get_assertion_callback().was_called());
181}
182
183TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) {
184 auto request_handler =
185 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
186 test_data::kRelyingPartyId, test_data::kClientDataHash));
187 discovery()->WaitForCallToStartAndSimulateSuccess();
188 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Martin Kreichgauer2b753722018-07-17 01:06:01189 device->ExpectCtap2CommandAndRespondWith(
Martin Kreichgauer8987fd02018-07-20 22:42:03190 CtapRequestCommand::kAuthenticatorGetAssertion,
191 test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
192
193 discovery()->AddDevice(std::move(device));
194
195 scoped_task_environment_.FastForwardUntilNoTasksRemain();
196 EXPECT_FALSE(get_assertion_callback().was_called());
197}
198
199// Tests a scenario where the authenticator responds with credential ID that
200// is not included in the allowed list.
201TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) {
202 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
203 test_data::kClientDataHash);
204 request.SetAllowList(
205 {{CredentialType::kPublicKey,
206 fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)}});
207 auto request_handler =
208 CreateGetAssertionHandlerWithRequest(std::move(request));
209 discovery()->WaitForCallToStartAndSimulateSuccess();
210 // Resident Keys must be disabled, otherwise allow list check is skipped.
211 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
212 test_data::kTestGetInfoResponseWithoutResidentKeySupport);
213 device->ExpectCtap2CommandAndRespondWith(
214 CtapRequestCommand::kAuthenticatorGetAssertion,
215 test_data::kTestGetAssertionResponse);
216
217 discovery()->AddDevice(std::move(device));
218
219 scoped_task_environment_.FastForwardUntilNoTasksRemain();
220 EXPECT_FALSE(get_assertion_callback().was_called());
221}
222
223// Tests a scenario where authenticator responds without user entity in its
224// response but client is expecting a resident key credential.
225TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) {
226 // Use a GetAssertion request with an empty allow list.
227 auto request_handler =
228 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
229 test_data::kRelyingPartyId, test_data::kClientDataHash));
230 discovery()->WaitForCallToStartAndSimulateSuccess();
231 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
232 device->ExpectCtap2CommandAndRespondWith(
233 CtapRequestCommand::kAuthenticatorGetAssertion,
234 test_data::kTestGetAssertionResponse);
Martin Kreichgauer2b753722018-07-17 01:06:01235
236 discovery()->AddDevice(std::move(device));
237
238 scoped_task_environment_.FastForwardUntilNoTasksRemain();
239 EXPECT_FALSE(get_assertion_callback().was_called());
240}
241
Jun Choie4aee4ff32018-08-13 19:35:07242// Tests a scenario where authenticator of incorrect transport type was used to
243// conduct CTAP GetAssertion call.
244TEST_F(FidoGetAssertionHandlerTest, IncorrectTransportType) {
245 // GetAssertion request that expects GetAssertion call for credential
246 // |CredentialType::kPublicKey| to be signed with Cable authenticator.
247 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
248 test_data::kClientDataHash);
249 request.SetAllowList(
250 {{CredentialType::kPublicKey,
251 fido_parsing_utils::Materialize(
252 test_data::kTestGetAssertionCredentialId),
253 {FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy}}});
254 auto request_handler =
255 CreateGetAssertionHandlerWithRequest(std::move(request));
256 discovery()->WaitForCallToStartAndSimulateSuccess();
257 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
258 // Since transport type of |device| is different from what the relying party
259 // defined in |request| above, this request should fail.
260 device->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
261 device->ExpectCtap2CommandAndRespondWith(
262 CtapRequestCommand::kAuthenticatorGetAssertion,
263 test_data::kTestGetAssertionResponse);
264
265 discovery()->AddDevice(std::move(device));
266
267 scoped_task_environment_.FastForwardUntilNoTasksRemain();
268 EXPECT_FALSE(get_assertion_callback().was_called());
269}
270
Jun Choi4fc8b7812018-04-05 07:39:07271} // namespace device