fido_device_authenticator_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. // Copyright 2020 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/fido_device_authenticator.h"
  5. #include <memory>
  6. #include "base/memory/raw_ptr.h"
  7. #include "base/memory/scoped_refptr.h"
  8. #include "base/test/bind.h"
  9. #include "base/test/task_environment.h"
  10. #include "device/fido/fido_constants.h"
  11. #include "device/fido/fido_parsing_utils.h"
  12. #include "device/fido/fido_types.h"
  13. #include "device/fido/large_blob.h"
  14. #include "device/fido/pin.h"
  15. #include "device/fido/test_callback_receiver.h"
  16. #include "device/fido/virtual_ctap2_device.h"
  17. #include "device/fido/virtual_fido_device.h"
  18. #include "testing/gmock/include/gmock/gmock.h"
  19. #include "testing/gtest/include/gtest/gtest.h"
  20. #include "third_party/abseil-cpp/absl/types/optional.h"
  21. namespace device {
  22. namespace {
  23. using WriteCallback =
  24. device::test::ValueCallbackReceiver<CtapDeviceResponseCode>;
  25. using ReadCallback = device::test::StatusAndValueCallbackReceiver<
  26. CtapDeviceResponseCode,
  27. absl::optional<std::vector<std::pair<LargeBlobKey, LargeBlob>>>>;
  28. using PinCallback = device::test::StatusAndValueCallbackReceiver<
  29. CtapDeviceResponseCode,
  30. absl::optional<pin::TokenResponse>>;
  31. using TouchCallback = device::test::TestCallbackReceiver<>;
  32. constexpr LargeBlobKey kDummyKey1 = {{0x01}};
  33. constexpr LargeBlobKey kDummyKey2 = {{0x02}};
  34. // The actual values for the "original size" that these blobs are supposed to
  35. // inflate to are not important here.
  36. const LargeBlob kSmallBlob1({'r', 'o', 's', 'a'}, 42);
  37. const LargeBlob kSmallBlob2({'l', 'u', 'm', 'a'}, 9000);
  38. const LargeBlob kSmallBlob3({'s', 't', 'a', 'r'}, 99);
  39. constexpr size_t kLargeBlobStorageSize = 4096;
  40. constexpr char kPin[] = "1234";
  41. class FidoDeviceAuthenticatorTest : public testing::Test {
  42. protected:
  43. void SetUp() override {
  44. VirtualCtap2Device::Config config;
  45. config.pin_support = true;
  46. config.large_blob_support = true;
  47. config.resident_key_support = true;
  48. config.available_large_blob_storage = kLargeBlobStorageSize;
  49. config.pin_uv_auth_token_support = true;
  50. config.ctap2_versions = {Ctap2Version::kCtap2_1};
  51. SetUpAuthenticator(std::move(config));
  52. }
  53. protected:
  54. void SetUpAuthenticator(VirtualCtap2Device::Config config) {
  55. authenticator_state_ = base::MakeRefCounted<VirtualFidoDevice::State>();
  56. auto virtual_device =
  57. std::make_unique<VirtualCtap2Device>(authenticator_state_, config);
  58. virtual_device_ = virtual_device.get();
  59. authenticator_ =
  60. std::make_unique<FidoDeviceAuthenticator>(std::move(virtual_device));
  61. device::test::TestCallbackReceiver<> callback;
  62. authenticator_->InitializeAuthenticator(callback.callback());
  63. callback.WaitForCallback();
  64. }
  65. scoped_refptr<VirtualFidoDevice::State> authenticator_state_;
  66. std::unique_ptr<FidoDeviceAuthenticator> authenticator_;
  67. raw_ptr<VirtualCtap2Device> virtual_device_;
  68. private:
  69. base::test::SingleThreadTaskEnvironment task_environment_;
  70. };
  71. TEST_F(FidoDeviceAuthenticatorTest, TestReadEmptyLargeBlob) {
  72. ReadCallback callback;
  73. authenticator_->ReadLargeBlob({kDummyKey1}, absl::nullopt,
  74. callback.callback());
  75. callback.WaitForCallback();
  76. EXPECT_EQ(callback.status(), CtapDeviceResponseCode::kSuccess);
  77. EXPECT_EQ(callback.value()->size(), 0u);
  78. }
  79. TEST_F(FidoDeviceAuthenticatorTest, TestReadInvalidLargeBlob) {
  80. authenticator_state_->large_blob[0] += 1;
  81. ReadCallback callback;
  82. authenticator_->ReadLargeBlob({kDummyKey1}, absl::nullopt,
  83. callback.callback());
  84. callback.WaitForCallback();
  85. EXPECT_EQ(callback.status(),
  86. CtapDeviceResponseCode::kCtap2ErrIntegrityFailure);
  87. EXPECT_FALSE(callback.value());
  88. }
  89. // Test reading and writing a blob that fits in a single fragment.
  90. TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlob) {
  91. WriteCallback write_callback;
  92. authenticator_->WriteLargeBlob(kSmallBlob1, {kDummyKey1}, absl::nullopt,
  93. write_callback.callback());
  94. write_callback.WaitForCallback();
  95. ASSERT_EQ(write_callback.value(), CtapDeviceResponseCode::kSuccess);
  96. ReadCallback read_callback;
  97. authenticator_->ReadLargeBlob({kDummyKey1}, absl::nullopt,
  98. read_callback.callback());
  99. read_callback.WaitForCallback();
  100. ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
  101. auto large_blob_array = read_callback.value();
  102. ASSERT_TRUE(large_blob_array);
  103. ASSERT_EQ(large_blob_array->size(), 1u);
  104. EXPECT_EQ(large_blob_array->at(0).first, kDummyKey1);
  105. EXPECT_EQ(large_blob_array->at(0).second, kSmallBlob1);
  106. }
  107. // Test reading and writing a blob that must fit in multiple fragments.
  108. TEST_F(FidoDeviceAuthenticatorTest, TestWriteLargeBlob) {
  109. std::vector<uint8_t> large_blob_contents;
  110. large_blob_contents.reserve(2048);
  111. for (size_t i = 0; i < large_blob_contents.capacity(); ++i) {
  112. large_blob_contents.emplace_back(i % 0xFF);
  113. }
  114. LargeBlob large_blob(std::move(large_blob_contents), 9999);
  115. WriteCallback write_callback;
  116. authenticator_->WriteLargeBlob(large_blob, {kDummyKey1}, absl::nullopt,
  117. write_callback.callback());
  118. write_callback.WaitForCallback();
  119. ASSERT_EQ(write_callback.value(), CtapDeviceResponseCode::kSuccess);
  120. ReadCallback read_callback;
  121. authenticator_->ReadLargeBlob({kDummyKey1}, absl::nullopt,
  122. read_callback.callback());
  123. read_callback.WaitForCallback();
  124. ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
  125. auto large_blob_array = read_callback.value();
  126. ASSERT_TRUE(large_blob_array);
  127. ASSERT_EQ(large_blob_array->size(), 1u);
  128. EXPECT_EQ(large_blob_array->at(0).first, kDummyKey1);
  129. EXPECT_EQ(large_blob_array->at(0).second, large_blob);
  130. }
  131. // Test reading and writing a blob using a PinUvAuthToken.
  132. TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlobWithToken) {
  133. virtual_device_->SetPin(kPin);
  134. PinCallback pin_callback;
  135. authenticator_->GetPINToken(kPin, {pin::Permissions::kLargeBlobWrite},
  136. /*rp_id=*/absl::nullopt, pin_callback.callback());
  137. pin_callback.WaitForCallback();
  138. ASSERT_EQ(pin_callback.status(), CtapDeviceResponseCode::kSuccess);
  139. pin::TokenResponse pin_token = *pin_callback.value();
  140. WriteCallback write_callback;
  141. authenticator_->WriteLargeBlob(kSmallBlob1, {kDummyKey1}, pin_token,
  142. write_callback.callback());
  143. write_callback.WaitForCallback();
  144. ASSERT_EQ(write_callback.value(), CtapDeviceResponseCode::kSuccess);
  145. ReadCallback read_callback;
  146. authenticator_->ReadLargeBlob({kDummyKey1}, pin_token,
  147. read_callback.callback());
  148. read_callback.WaitForCallback();
  149. ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
  150. auto large_blob_array = read_callback.value();
  151. ASSERT_TRUE(large_blob_array);
  152. ASSERT_EQ(large_blob_array->size(), 1u);
  153. EXPECT_EQ(large_blob_array->at(0).first, kDummyKey1);
  154. EXPECT_EQ(large_blob_array->at(0).second, kSmallBlob1);
  155. }
  156. // Test updating a large blob in an array with multiple entries corresponding to
  157. // other keys.
  158. TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
  159. WriteCallback write_callback1;
  160. authenticator_->WriteLargeBlob(kSmallBlob1, {kDummyKey1}, absl::nullopt,
  161. write_callback1.callback());
  162. write_callback1.WaitForCallback();
  163. ASSERT_EQ(write_callback1.value(), CtapDeviceResponseCode::kSuccess);
  164. WriteCallback write_callback2;
  165. authenticator_->WriteLargeBlob(kSmallBlob2, {kDummyKey2}, absl::nullopt,
  166. write_callback2.callback());
  167. write_callback2.WaitForCallback();
  168. ASSERT_EQ(write_callback2.value(), CtapDeviceResponseCode::kSuccess);
  169. // Update the first entry.
  170. WriteCallback write_callback3;
  171. authenticator_->WriteLargeBlob(kSmallBlob3, {kDummyKey1}, absl::nullopt,
  172. write_callback3.callback());
  173. write_callback3.WaitForCallback();
  174. ASSERT_EQ(write_callback3.value(), CtapDeviceResponseCode::kSuccess);
  175. ReadCallback read_callback;
  176. authenticator_->ReadLargeBlob({kDummyKey1, kDummyKey2}, absl::nullopt,
  177. read_callback.callback());
  178. read_callback.WaitForCallback();
  179. ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
  180. auto large_blob_array = read_callback.value();
  181. ASSERT_TRUE(large_blob_array);
  182. EXPECT_THAT(*large_blob_array, testing::UnorderedElementsAre(
  183. std::make_pair(kDummyKey1, kSmallBlob3),
  184. std::make_pair(kDummyKey2, kSmallBlob2)));
  185. }
  186. // Test attempting to write a large blob with a serialized size larger than the
  187. // maximum. Chrome should not attempt writing the blob in this case.
  188. TEST_F(FidoDeviceAuthenticatorTest, TestWriteLargeBlobTooLarge) {
  189. // First write a valid blob to make sure it isn't overwritten.
  190. WriteCallback write_callback1;
  191. authenticator_->WriteLargeBlob(kSmallBlob1, {kDummyKey1}, absl::nullopt,
  192. write_callback1.callback());
  193. write_callback1.WaitForCallback();
  194. ASSERT_EQ(write_callback1.value(), CtapDeviceResponseCode::kSuccess);
  195. // Then, attempt writing a blob that is too large.
  196. std::vector<uint8_t> large_blob_contents;
  197. large_blob_contents.reserve(kLargeBlobStorageSize + 1);
  198. for (size_t i = 0; i < large_blob_contents.capacity(); ++i) {
  199. large_blob_contents.emplace_back(i % 0xFF);
  200. }
  201. LargeBlob large_blob(std::move(large_blob_contents), 9999);
  202. WriteCallback write_callback2;
  203. authenticator_->WriteLargeBlob(large_blob, {kDummyKey1}, absl::nullopt,
  204. write_callback2.callback());
  205. write_callback2.WaitForCallback();
  206. ASSERT_EQ(write_callback2.value(),
  207. CtapDeviceResponseCode::kCtap2ErrRequestTooLarge);
  208. // Make sure the first blob was not overwritten.
  209. ReadCallback read_callback;
  210. authenticator_->ReadLargeBlob({kDummyKey1}, absl::nullopt,
  211. read_callback.callback());
  212. read_callback.WaitForCallback();
  213. ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
  214. auto large_blob_array = read_callback.value();
  215. ASSERT_TRUE(large_blob_array);
  216. ASSERT_EQ(large_blob_array->size(), 1u);
  217. EXPECT_EQ(kDummyKey1, large_blob_array->at(0).first);
  218. EXPECT_EQ(kSmallBlob1, large_blob_array->at(0).second);
  219. }
  220. // Tests getting a touch.
  221. TEST_F(FidoDeviceAuthenticatorTest, TestGetTouch) {
  222. for (Ctap2Version version :
  223. {Ctap2Version::kCtap2_0, Ctap2Version::kCtap2_1}) {
  224. SCOPED_TRACE(std::string("CTAP ") +
  225. (version == Ctap2Version::kCtap2_0 ? "2.0" : "2.1"));
  226. VirtualCtap2Device::Config config;
  227. config.ctap2_versions = {version};
  228. SetUpAuthenticator(std::move(config));
  229. TouchCallback callback;
  230. bool touch_pressed = false;
  231. authenticator_state_->simulate_press_callback =
  232. base::BindLambdaForTesting([&](VirtualFidoDevice* device) {
  233. touch_pressed = true;
  234. return true;
  235. });
  236. authenticator_->GetTouch(callback.callback());
  237. callback.WaitForCallback();
  238. EXPECT_TRUE(touch_pressed);
  239. }
  240. }
  241. } // namespace
  242. } // namespace device