blob: fffaf21d219c9020312f15626ab58b38bc65167e [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 Choif7ab0df2018-04-05 21:48:1628using TestGetAssertionRequestCallback = test::StatusAndValueCallbackReceiver<
29 FidoReturnCode,
30 base::Optional<AuthenticatorGetAssertionResponse>>;
Jun Choi4fc8b7812018-04-05 07:39:0731
32} // namespace
33
34class FidoGetAssertionHandlerTest : public ::testing::Test {
35 public:
36 void ForgeNextHidDiscovery() {
37 discovery_ = scoped_fake_discovery_factory_.ForgeNextHidDiscovery();
38 }
39
Martin Kreichgauer8987fd02018-07-20 22:42:0340 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerU2f() {
Martin Kreichgauer2b753722018-07-17 01:06:0141 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
42 test_data::kClientDataHash);
43 request.SetAllowList(
Jan Wilken Doerrie726e197e2018-05-14 12:53:2544 {{CredentialType::kPublicKey,
Jun Choi0e56e5dd2018-06-08 21:27:3445 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
Martin Kreichgauer8987fd02018-07-20 22:42:0346 return CreateGetAssertionHandlerWithRequest(std::move(request));
47 }
Jun Choi4fc8b7812018-04-05 07:39:0748
Martin Kreichgauer8987fd02018-07-20 22:42:0349 std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandlerCtap() {
50 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
51 test_data::kClientDataHash);
52 request.SetAllowList({{CredentialType::kPublicKey,
53 fido_parsing_utils::Materialize(
54 test_data::kTestGetAssertionCredentialId)}});
Martin Kreichgauer2b753722018-07-17 01:06:0155 return CreateGetAssertionHandlerWithRequest(std::move(request));
56 }
57
58 std::unique_ptr<GetAssertionRequestHandler>
59 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
60 ForgeNextHidDiscovery();
61
Jun Choi4fc8b7812018-04-05 07:39:0762 return std::make_unique<GetAssertionRequestHandler>(
63 nullptr /* connector */,
Jun Choib60937e2018-04-12 17:02:3864 base::flat_set<FidoTransportProtocol>(
65 {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
Martin Kreichgauer2b753722018-07-17 01:06:0166 std::move(request), get_assertion_cb_.callback());
Jun Choi4fc8b7812018-04-05 07:39:0767 }
68
Jun Choid19453d2018-06-21 23:16:3969 void InitFeatureListAndDisableCtapFlag() {
70 scoped_feature_list_.InitAndDisableFeature(kNewCtap2Device);
Jun Choi0e56e5dd2018-06-08 21:27:3471 }
Jun Choi4fc8b7812018-04-05 07:39:0772
Jun Choi0e56e5dd2018-06-08 21:27:3473 test::FakeFidoDiscovery* discovery() const { return discovery_; }
Jun Choi4fc8b7812018-04-05 07:39:0774 TestGetAssertionRequestCallback& get_assertion_callback() {
75 return get_assertion_cb_;
76 }
77
78 protected:
79 base::test::ScopedTaskEnvironment scoped_task_environment_{
80 base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
Jun Choi0e56e5dd2018-06-08 21:27:3481 base::test::ScopedFeatureList scoped_feature_list_;
Jun Choi4fc8b7812018-04-05 07:39:0782 test::ScopedFakeFidoDiscoveryFactory scoped_fake_discovery_factory_;
83 test::FakeFidoDiscovery* discovery_;
84 TestGetAssertionRequestCallback get_assertion_cb_;
85};
86
Martin Kreichgauer8987fd02018-07-20 22:42:0387TEST_F(FidoGetAssertionHandlerTest, CtapRequestOnSingleDevice) {
88 auto request_handler = CreateGetAssertionHandlerCtap();
Jun Choi4fc8b7812018-04-05 07:39:0789 discovery()->WaitForCallToStartAndSimulateSuccess();
Martin Kreichgauer8987fd02018-07-20 22:42:0390 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Jun Choi4fc8b7812018-04-05 07:39:0791 device->ExpectCtap2CommandAndRespondWith(
92 CtapRequestCommand::kAuthenticatorGetAssertion,
93 test_data::kTestGetAssertionResponse);
94
95 discovery()->AddDevice(std::move(device));
96 get_assertion_callback().WaitForCallback();
97
98 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
99 EXPECT_TRUE(get_assertion_callback().value());
100 EXPECT_TRUE(request_handler->is_complete());
101}
102
Jun Choi0e56e5dd2018-06-08 21:27:34103// Test a scenario where the connected authenticator is a U2F device.
104TEST_F(FidoGetAssertionHandlerTest, TestU2fSign) {
Martin Kreichgauer8987fd02018-07-20 22:42:03105 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi4fc8b7812018-04-05 07:39:07106 discovery()->WaitForCallToStartAndSimulateSuccess();
107
Martin Kreichgauer8987fd02018-07-20 22:42:03108 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
Jun Choi0e56e5dd2018-06-08 21:27:34109 device->ExpectRequestAndRespondWith(
110 test_data::kU2fCheckOnlySignCommandApdu,
111 test_data::kApduEncodedNoErrorSignResponse);
112 device->ExpectRequestAndRespondWith(
113 test_data::kU2fSignCommandApdu,
114 test_data::kApduEncodedNoErrorSignResponse);
Jun Choi4fc8b7812018-04-05 07:39:07115
116 discovery()->AddDevice(std::move(device));
117 scoped_task_environment_.FastForwardUntilNoTasksRemain();
Jun Choi0e56e5dd2018-06-08 21:27:34118 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
119 EXPECT_TRUE(get_assertion_callback().value());
120 EXPECT_TRUE(request_handler->is_complete());
121}
122
123// Test a scenario where the connected authenticator is a U2F device and
124// "WebAuthenticationCtap2" flag is not enabled.
125TEST_F(FidoGetAssertionHandlerTest, TestU2fSignWithoutCtapFlag) {
Jun Choid19453d2018-06-21 23:16:39126 InitFeatureListAndDisableCtapFlag();
Martin Kreichgauer8987fd02018-07-20 22:42:03127 auto request_handler = CreateGetAssertionHandlerU2f();
Jun Choi0e56e5dd2018-06-08 21:27:34128 discovery()->WaitForCallToStartAndSimulateSuccess();
129
130 auto device = std::make_unique<MockFidoDevice>();
131 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
132 device->ExpectRequestAndRespondWith(
133 test_data::kU2fCheckOnlySignCommandApdu,
134 test_data::kApduEncodedNoErrorSignResponse);
135 device->ExpectRequestAndRespondWith(
136 test_data::kU2fSignCommandApdu,
137 test_data::kApduEncodedNoErrorSignResponse);
138
139 discovery()->AddDevice(std::move(device));
140 scoped_task_environment_.FastForwardUntilNoTasksRemain();
141 EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
142 EXPECT_TRUE(get_assertion_callback().value());
143 EXPECT_TRUE(request_handler->is_complete());
Jun Choi4fc8b7812018-04-05 07:39:07144}
145
Martin Kreichgauer2b753722018-07-17 01:06:01146TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {
147 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
148 test_data::kClientDataHash);
149 request.SetUserVerification(UserVerificationRequirement::kRequired);
150 auto request_handler =
151 CreateGetAssertionHandlerWithRequest(std::move(request));
152 discovery()->WaitForCallToStartAndSimulateSuccess();
153
Martin Kreichgauer8987fd02018-07-20 22:42:03154 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
Martin Kreichgauer2b753722018-07-17 01:06:01155 test_data::kTestGetInfoResponseWithoutUvSupport);
156
157 discovery()->AddDevice(std::move(device));
158
159 scoped_task_environment_.FastForwardUntilNoTasksRemain();
160 EXPECT_FALSE(get_assertion_callback().was_called());
161}
162
163TEST_F(FidoGetAssertionHandlerTest,
164 TestU2fSignRequestWithUserVerificationRequired) {
165 auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
166 test_data::kClientDataHash);
167 request.SetAllowList(
168 {{CredentialType::kPublicKey,
169 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
170 request.SetUserVerification(UserVerificationRequirement::kRequired);
171 auto request_handler =
172 CreateGetAssertionHandlerWithRequest(std::move(request));
173 discovery()->WaitForCallToStartAndSimulateSuccess();
174
Martin Kreichgauer8987fd02018-07-20 22:42:03175 auto device = MockFidoDevice::MakeU2fWithGetInfoExpectation();
176 discovery()->AddDevice(std::move(device));
177
178 scoped_task_environment_.FastForwardUntilNoTasksRemain();
179 EXPECT_FALSE(get_assertion_callback().was_called());
180}
181
182TEST_F(FidoGetAssertionHandlerTest, IncorrectRpIdHash) {
183 auto request_handler =
184 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
185 test_data::kRelyingPartyId, test_data::kClientDataHash));
186 discovery()->WaitForCallToStartAndSimulateSuccess();
187 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
Martin Kreichgauer2b753722018-07-17 01:06:01188 device->ExpectCtap2CommandAndRespondWith(
Martin Kreichgauer8987fd02018-07-20 22:42:03189 CtapRequestCommand::kAuthenticatorGetAssertion,
190 test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
191
192 discovery()->AddDevice(std::move(device));
193
194 scoped_task_environment_.FastForwardUntilNoTasksRemain();
195 EXPECT_FALSE(get_assertion_callback().was_called());
196}
197
198// Tests a scenario where the authenticator responds with credential ID that
199// is not included in the allowed list.
200TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) {
201 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
202 test_data::kClientDataHash);
203 request.SetAllowList(
204 {{CredentialType::kPublicKey,
205 fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)}});
206 auto request_handler =
207 CreateGetAssertionHandlerWithRequest(std::move(request));
208 discovery()->WaitForCallToStartAndSimulateSuccess();
209 // Resident Keys must be disabled, otherwise allow list check is skipped.
210 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
211 test_data::kTestGetInfoResponseWithoutResidentKeySupport);
212 device->ExpectCtap2CommandAndRespondWith(
213 CtapRequestCommand::kAuthenticatorGetAssertion,
214 test_data::kTestGetAssertionResponse);
215
216 discovery()->AddDevice(std::move(device));
217
218 scoped_task_environment_.FastForwardUntilNoTasksRemain();
219 EXPECT_FALSE(get_assertion_callback().was_called());
220}
221
222// Tests a scenario where authenticator responds without user entity in its
223// response but client is expecting a resident key credential.
224TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) {
225 // Use a GetAssertion request with an empty allow list.
226 auto request_handler =
227 CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest(
228 test_data::kRelyingPartyId, test_data::kClientDataHash));
229 discovery()->WaitForCallToStartAndSimulateSuccess();
230 auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
231 device->ExpectCtap2CommandAndRespondWith(
232 CtapRequestCommand::kAuthenticatorGetAssertion,
233 test_data::kTestGetAssertionResponse);
Martin Kreichgauer2b753722018-07-17 01:06:01234
235 discovery()->AddDevice(std::move(device));
236
237 scoped_task_environment_.FastForwardUntilNoTasksRemain();
238 EXPECT_FALSE(get_assertion_callback().was_called());
239}
240
Jun Choi4fc8b7812018-04-05 07:39:07241} // namespace device