1 // 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 "device/fido/u2f_sign_operation.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "base/test/task_environment.h"
11 #include "crypto/ec_private_key.h"
12 #include "device/fido/authenticator_get_assertion_response.h"
13 #include "device/fido/ctap_get_assertion_request.h"
14 #include "device/fido/fido_constants.h"
15 #include "device/fido/fido_parsing_utils.h"
16 #include "device/fido/fido_test_data.h"
17 #include "device/fido/mock_fido_device.h"
18 #include "device/fido/test_callback_receiver.h"
19 #include "device/fido/virtual_u2f_device.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 namespace device {
24
25 using ::testing::_;
26 using ::testing::InSequence;
27
28 namespace {
29
30 using TestSignCallback = ::device::test::StatusAndValueCallbackReceiver<
31 CtapDeviceResponseCode,
32 base::Optional<AuthenticatorGetAssertionResponse>>;
33
34 } // namespace
35
36 class U2fSignOperationTest : public ::testing::Test {
37 public:
CreateSignRequest(std::vector<std::vector<uint8_t>> key_handles)38 CtapGetAssertionRequest CreateSignRequest(
39 std::vector<std::vector<uint8_t>> key_handles) {
40 CtapGetAssertionRequest request(test_data::kRelyingPartyId,
41 test_data::kClientDataJson);
42
43 for (auto& key_handle : key_handles) {
44 request.allow_list.emplace_back(CredentialType::kPublicKey,
45 std::move(key_handle));
46 }
47 return request;
48 }
49
sign_callback_receiver()50 TestSignCallback& sign_callback_receiver() { return sign_callback_receiver_; }
51
52 protected:
53 base::test::TaskEnvironment task_environment_;
54 TestSignCallback sign_callback_receiver_;
55 };
56
TEST_F(U2fSignOperationTest,SignSuccess)57 TEST_F(U2fSignOperationTest, SignSuccess) {
58 auto request = CreateSignRequest(
59 {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
60
61 auto device = std::make_unique<MockFidoDevice>();
62 EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device"));
63 device->ExpectWinkedAtLeastOnce();
64 InSequence s;
65 device->ExpectRequestAndRespondWith(
66 test_data::kU2fSignCommandApdu,
67 test_data::kApduEncodedNoErrorSignResponse);
68
69 auto u2f_sign = std::make_unique<U2fSignOperation>(
70 device.get(), std::move(request), sign_callback_receiver().callback());
71 u2f_sign->Start();
72
73 sign_callback_receiver().WaitForCallback();
74 EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
75 sign_callback_receiver().status());
76 EXPECT_THAT(sign_callback_receiver().value()->signature(),
77 ::testing::ElementsAreArray(test_data::kU2fSignature));
78 EXPECT_THAT(sign_callback_receiver().value()->raw_credential_id(),
79 ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
80 }
81
TEST_F(U2fSignOperationTest,SignSuccessWithFakeDevice)82 TEST_F(U2fSignOperationTest, SignSuccessWithFakeDevice) {
83 auto private_key = crypto::ECPrivateKey::Create();
84 std::string public_key;
85 private_key->ExportRawPublicKey(&public_key);
86 auto hash = fido_parsing_utils::CreateSHA256Hash(public_key);
87 std::vector<uint8_t> key_handle(hash.begin(), hash.end());
88 auto request = CreateSignRequest({key_handle});
89
90 auto device = std::make_unique<VirtualU2fDevice>();
91 device->mutable_state()->registrations.emplace(
92 key_handle, VirtualFidoDevice::RegistrationData(
93 std::move(private_key), test_data::kApplicationParameter,
94 42 /* counter */));
95
96 auto u2f_sign = std::make_unique<U2fSignOperation>(
97 device.get(), std::move(request), sign_callback_receiver().callback());
98 u2f_sign->Start();
99
100 sign_callback_receiver().WaitForCallback();
101 EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
102 sign_callback_receiver().status());
103
104 // Just a sanity check, we don't verify the actual signature.
105 ASSERT_GE(32u + 1u + 4u + 8u, // Minimal ECDSA signature is 8 bytes
106 sign_callback_receiver()
107 .value()
108 ->auth_data()
109 .SerializeToByteArray()
110 .size());
111 EXPECT_EQ(0x01,
112 sign_callback_receiver()
113 .value()
114 ->auth_data()
115 .SerializeToByteArray()[32]); // UP flag
116 // Counter is incremented for every sign request so this counter should have
117 // been incremented once.
118 EXPECT_EQ(43, sign_callback_receiver()
119 .value()
120 ->auth_data()
121 .SerializeToByteArray()[36]); // counter
122 }
123
TEST_F(U2fSignOperationTest,DelayedSuccess)124 TEST_F(U2fSignOperationTest, DelayedSuccess) {
125 auto request = CreateSignRequest(
126 {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
127
128 // Simulates a device that times out waiting for user touch once before
129 // responding successfully.
130 auto device = std::make_unique<MockFidoDevice>();
131 EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
132 device->ExpectWinkedAtLeastOnce();
133
134 InSequence s;
135 device->ExpectRequestAndRespondWith(
136 test_data::kU2fSignCommandApdu,
137 test_data::kU2fConditionNotSatisfiedApduResponse);
138 device->ExpectRequestAndRespondWith(
139 test_data::kU2fSignCommandApdu,
140 test_data::kApduEncodedNoErrorSignResponse);
141
142 auto u2f_sign = std::make_unique<U2fSignOperation>(
143 device.get(), std::move(request), sign_callback_receiver().callback());
144 u2f_sign->Start();
145
146 sign_callback_receiver().WaitForCallback();
147 EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
148 sign_callback_receiver().status());
149 EXPECT_THAT(sign_callback_receiver().value()->signature(),
150 ::testing::ElementsAreArray(test_data::kU2fSignature));
151 EXPECT_THAT(sign_callback_receiver().value()->raw_credential_id(),
152 ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
153 }
154
TEST_F(U2fSignOperationTest,MultipleHandles)155 TEST_F(U2fSignOperationTest, MultipleHandles) {
156 // Two wrong keys followed by a correct key ensuring the wrong keys will be
157 // tested first.
158 auto request = CreateSignRequest(
159 {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
160 fido_parsing_utils::Materialize(test_data::kKeyHandleBeta),
161 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
162
163 auto device = std::make_unique<MockFidoDevice>();
164 EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
165 device->ExpectWinkedAtLeastOnce();
166 InSequence s;
167 // Wrong key would respond with SW_WRONG_DATA.
168 device->ExpectRequestAndRespondWith(
169 test_data::kU2fSignCommandApduWithKeyAlpha,
170 test_data::kU2fWrongDataApduResponse);
171 device->ExpectRequestAndRespondWith(test_data::kU2fSignCommandApduWithKeyBeta,
172 test_data::kU2fWrongDataApduResponse);
173 device->ExpectRequestAndRespondWith(
174 test_data::kU2fSignCommandApdu,
175 test_data::kApduEncodedNoErrorSignResponse);
176
177 auto u2f_sign = std::make_unique<U2fSignOperation>(
178 device.get(), std::move(request), sign_callback_receiver().callback());
179 u2f_sign->Start();
180
181 sign_callback_receiver().WaitForCallback();
182 EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
183 sign_callback_receiver().status());
184 EXPECT_THAT(sign_callback_receiver().value()->signature(),
185 ::testing::ElementsAreArray(test_data::kU2fSignature));
186 EXPECT_THAT(sign_callback_receiver().value()->raw_credential_id(),
187 ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
188 }
189
TEST_F(U2fSignOperationTest,MultipleHandlesLengthError)190 TEST_F(U2fSignOperationTest, MultipleHandlesLengthError) {
191 // One wrong key that responds with key handle length followed by a correct
192 // key.
193 auto request = CreateSignRequest(
194 {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
195 fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
196
197 auto device = std::make_unique<MockFidoDevice>();
198 EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
199 device->ExpectWinkedAtLeastOnce();
200 InSequence s;
201
202 // Wrong key would respond with the key handle length.
203 device->ExpectRequestAndRespondWith(
204 test_data::kU2fSignCommandApduWithKeyAlpha,
205 test_data::kU2fKeyHandleSizeApduResponse);
206 device->ExpectRequestAndRespondWith(
207 test_data::kU2fSignCommandApdu,
208 test_data::kApduEncodedNoErrorSignResponse);
209
210 auto u2f_sign = std::make_unique<U2fSignOperation>(
211 device.get(), std::move(request), sign_callback_receiver().callback());
212 u2f_sign->Start();
213
214 sign_callback_receiver().WaitForCallback();
215 EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
216 sign_callback_receiver().status());
217 EXPECT_THAT(sign_callback_receiver().value()->signature(),
218 ::testing::ElementsAreArray(test_data::kU2fSignature));
219 EXPECT_THAT(sign_callback_receiver().value()->raw_credential_id(),
220 ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
221 }
222
223 // Test that Fake U2F registration is invoked when no credentials in the allowed
224 // list are recognized by the device.
TEST_F(U2fSignOperationTest,FakeEnroll)225 TEST_F(U2fSignOperationTest, FakeEnroll) {
226 auto request = CreateSignRequest(
227 {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
228 fido_parsing_utils::Materialize(test_data::kKeyHandleBeta)});
229
230 auto device = std::make_unique<MockFidoDevice>();
231 device->ExpectWinkedAtLeastOnce();
232 InSequence s;
233 device->ExpectRequestAndRespondWith(
234 test_data::kU2fSignCommandApduWithKeyAlpha,
235 test_data::kU2fWrongDataApduResponse);
236 device->ExpectRequestAndRespondWith(test_data::kU2fSignCommandApduWithKeyBeta,
237 test_data::kU2fWrongDataApduResponse);
238 device->ExpectRequestAndRespondWith(
239 test_data::kU2fFakeRegisterCommand,
240 test_data::kApduEncodedNoErrorRegisterResponse);
241
242 auto u2f_sign = std::make_unique<U2fSignOperation>(
243 device.get(), std::move(request), sign_callback_receiver().callback());
244 u2f_sign->Start();
245 sign_callback_receiver().WaitForCallback();
246
247 EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
248 sign_callback_receiver().status());
249 EXPECT_FALSE(sign_callback_receiver().value());
250 }
251
252 // Tests that U2F fake enrollment should be re-tried repeatedly if no
253 // credentials are valid for the authenticator and user presence is not
254 // obtained.
TEST_F(U2fSignOperationTest,DelayedFakeEnrollment)255 TEST_F(U2fSignOperationTest, DelayedFakeEnrollment) {
256 auto request = CreateSignRequest(
257 {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
258
259 // Simulates a device that times out waiting for user presence during fake
260 // enrollment.
261 auto device = std::make_unique<MockFidoDevice>();
262 EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device0"));
263 device->ExpectWinkedAtLeastOnce();
264 InSequence s;
265 device->ExpectRequestAndRespondWith(test_data::kU2fSignCommandApdu,
266 test_data::kU2fWrongDataApduResponse);
267 device->ExpectRequestAndRespondWith(
268 test_data::kU2fFakeRegisterCommand,
269 test_data::kU2fConditionNotSatisfiedApduResponse);
270 device->ExpectRequestAndRespondWith(
271 test_data::kU2fFakeRegisterCommand,
272 test_data::kApduEncodedNoErrorRegisterResponse);
273
274 auto u2f_sign = std::make_unique<U2fSignOperation>(
275 device.get(), std::move(request), sign_callback_receiver().callback());
276 u2f_sign->Start();
277 sign_callback_receiver().WaitForCallback();
278
279 EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
280 sign_callback_receiver().status());
281 EXPECT_FALSE(sign_callback_receiver().value());
282 }
283
284 // Tests that request is dropped gracefully if device returns error on all
285 // requests (including fake enrollment).
TEST_F(U2fSignOperationTest,FakeEnrollErroringOut)286 TEST_F(U2fSignOperationTest, FakeEnrollErroringOut) {
287 auto request = CreateSignRequest(
288 {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
289
290 // Simulates a device that errors out on all requests (including the sign
291 // request and fake registration attempt). The device should then be abandoned
292 // to prevent the test from crashing or timing out.
293 auto device = std::make_unique<MockFidoDevice>();
294 EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device0"));
295 device->ExpectWinkedAtLeastOnce();
296 InSequence s;
297 device->ExpectRequestAndRespondWith(test_data::kU2fSignCommandApdu,
298 test_data::kU2fWrongDataApduResponse);
299 device->ExpectRequestAndRespondWith(test_data::kU2fFakeRegisterCommand,
300 test_data::kU2fWrongDataApduResponse);
301
302 auto u2f_sign = std::make_unique<U2fSignOperation>(
303 device.get(), std::move(request), sign_callback_receiver().callback());
304 u2f_sign->Start();
305 sign_callback_receiver().WaitForCallback();
306
307 EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
308 sign_callback_receiver().status());
309 EXPECT_FALSE(sign_callback_receiver().value());
310 }
311
312 // Tests the scenario where device returns success response, but the response is
313 // unparse-able.
TEST_F(U2fSignOperationTest,SignWithCorruptedResponse)314 TEST_F(U2fSignOperationTest, SignWithCorruptedResponse) {
315 auto request = CreateSignRequest(
316 {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
317
318 auto device = std::make_unique<MockFidoDevice>();
319 EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
320 device->ExpectWinkedAtLeastOnce();
321 InSequence s;
322 device->ExpectRequestAndRespondWith(test_data::kU2fSignCommandApdu,
323 test_data::kTestCorruptedU2fSignResponse);
324
325 auto u2f_sign = std::make_unique<U2fSignOperation>(
326 device.get(), std::move(request), sign_callback_receiver().callback());
327 u2f_sign->Start();
328 sign_callback_receiver().WaitForCallback();
329
330 EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
331 sign_callback_receiver().status());
332 EXPECT_FALSE(sign_callback_receiver().value());
333 }
334
TEST_F(U2fSignOperationTest,AlternativeApplicationParameter)335 TEST_F(U2fSignOperationTest, AlternativeApplicationParameter) {
336 auto request = CreateSignRequest(
337 {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
338 request.app_id = test_data::kAppId;
339 request.alternative_application_parameter =
340 fido_parsing_utils::Materialize(base::span<const uint8_t, 32>(
341 test_data::kAlternativeApplicationParameter));
342
343 auto device = std::make_unique<MockFidoDevice>();
344 EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
345 device->ExpectWinkedAtLeastOnce();
346 InSequence s;
347 // The first request will use the alternative app_param.
348 device->ExpectRequestAndRespondWith(
349 test_data::kU2fSignCommandApduWithAlternativeApplicationParameter,
350 test_data::kApduEncodedNoErrorSignResponse);
351
352 auto u2f_sign = std::make_unique<U2fSignOperation>(
353 device.get(), std::move(request), sign_callback_receiver().callback());
354 u2f_sign->Start();
355 sign_callback_receiver().WaitForCallback();
356
357 EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
358 sign_callback_receiver().status());
359 const auto& response_value = sign_callback_receiver().value();
360 EXPECT_THAT(response_value->signature(),
361 ::testing::ElementsAreArray(test_data::kU2fSignature));
362 EXPECT_THAT(response_value->raw_credential_id(),
363 ::testing::ElementsAreArray(test_data::kU2fSignKeyHandle));
364 EXPECT_THAT(response_value->GetRpIdHash(),
365 ::testing::ElementsAreArray(base::span<const uint8_t, 32>(
366 test_data::kAlternativeApplicationParameter)));
367 }
368
369 // This is a regression test in response to https://crbug.com/833398.
TEST_F(U2fSignOperationTest,AlternativeApplicationParameterRejection)370 TEST_F(U2fSignOperationTest, AlternativeApplicationParameterRejection) {
371 auto request = CreateSignRequest(
372 {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
373 request.app_id = test_data::kAppId;
374 request.alternative_application_parameter =
375 fido_parsing_utils::Materialize(base::span<const uint8_t, 32>(
376 test_data::kAlternativeApplicationParameter));
377
378 auto device = std::make_unique<MockFidoDevice>();
379 EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
380 device->ExpectWinkedAtLeastOnce();
381 InSequence s;
382 // The first request will use the alternative app_param, which will be
383 // rejected.
384 device->ExpectRequestAndRespondWith(
385 test_data::kU2fSignCommandApduWithAlternativeApplicationParameter,
386 test_data::kU2fWrongDataApduResponse);
387 // After the rejection, request with primary application parameter should
388 // be tried, which will also be rejected.
389 device->ExpectRequestAndRespondWith(test_data::kU2fSignCommandApdu,
390 test_data::kU2fWrongDataApduResponse);
391 // The second rejection will trigger a bogus register command. This will be
392 // rejected as well, triggering the device to be abandoned.
393 device->ExpectRequestAndRespondWith(test_data::kU2fFakeRegisterCommand,
394 test_data::kU2fWrongDataApduResponse);
395
396 auto u2f_sign = std::make_unique<U2fSignOperation>(
397 device.get(), std::move(request), sign_callback_receiver().callback());
398 u2f_sign->Start();
399 sign_callback_receiver().WaitForCallback();
400
401 EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
402 sign_callback_receiver().status());
403 EXPECT_FALSE(sign_callback_receiver().value());
404 }
405
406 } // namespace device
407