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