u2f_sign_operation_unittest.cc 16 KB

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