enrollment_handler_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. // Copyright 2019 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 <vector>
  5. #include "device/fido/fido_constants.h"
  6. #include "testing/gmock/include/gmock/gmock.h"
  7. #include "testing/gtest/include/gtest/gtest.h"
  8. #include "base/bind.h"
  9. #include "base/callback.h"
  10. #include "base/test/task_environment.h"
  11. #include "device/fido/bio/enrollment_handler.h"
  12. #include "device/fido/fido_transport_protocol.h"
  13. #include "device/fido/test_callback_receiver.h"
  14. #include "device/fido/virtual_fido_device_factory.h"
  15. namespace device {
  16. namespace {
  17. constexpr char kPIN[] = "1477";
  18. class BioEnrollmentHandlerTest : public ::testing::Test {
  19. void SetUp() override {
  20. virtual_device_factory_.SetSupportedProtocol(ProtocolVersion::kCtap2);
  21. virtual_device_factory_.mutable_state()->pin = kPIN;
  22. virtual_device_factory_.mutable_state()->pin_retries =
  23. device::kMaxPinRetries;
  24. }
  25. public:
  26. void OnEnroll(BioEnrollmentSampleStatus status, uint8_t remaining_samples) {
  27. if (status != BioEnrollmentSampleStatus::kGood) {
  28. sample_failures_++;
  29. return;
  30. }
  31. if (!sampling_) {
  32. sampling_ = true;
  33. remaining_samples_ = remaining_samples;
  34. return;
  35. }
  36. EXPECT_EQ(remaining_samples, --remaining_samples_);
  37. }
  38. protected:
  39. std::unique_ptr<BioEnrollmentHandler> MakeHandler() {
  40. return std::make_unique<BioEnrollmentHandler>(
  41. base::flat_set<FidoTransportProtocol>{
  42. FidoTransportProtocol::kUsbHumanInterfaceDevice},
  43. ready_callback_.callback(), error_callback_.callback(),
  44. base::BindRepeating(&BioEnrollmentHandlerTest::GetPIN,
  45. base::Unretained(this)),
  46. &virtual_device_factory_);
  47. }
  48. std::pair<CtapDeviceResponseCode, BioEnrollmentHandler::TemplateId>
  49. EnrollTemplate(BioEnrollmentHandler* handler) {
  50. test::StatusAndValueCallbackReceiver<CtapDeviceResponseCode,
  51. BioEnrollmentHandler::TemplateId>
  52. cb;
  53. handler->EnrollTemplate(MakeSampleCallback(), cb.callback());
  54. cb.WaitForCallback();
  55. return {cb.status(), cb.value()};
  56. }
  57. void GetPIN(uint32_t min_pin_length,
  58. int64_t attempts,
  59. base::OnceCallback<void(std::string)> provide_pin) {
  60. std::move(provide_pin).Run(kPIN);
  61. }
  62. BioEnrollmentHandler::SampleCallback MakeSampleCallback() {
  63. remaining_samples_ = 0;
  64. sample_failures_ = 0;
  65. sampling_ = false;
  66. return base::BindRepeating(&BioEnrollmentHandlerTest::OnEnroll,
  67. base::Unretained(this));
  68. }
  69. uint8_t remaining_samples_;
  70. size_t sample_failures_;
  71. bool sampling_;
  72. base::test::TaskEnvironment task_environment_;
  73. test::ValueCallbackReceiver<BioEnrollmentHandler::SensorInfo> ready_callback_;
  74. test::ValueCallbackReceiver<BioEnrollmentHandler::Error> error_callback_;
  75. test::VirtualFidoDeviceFactory virtual_device_factory_;
  76. };
  77. // Tests bio enrollment handler against device without PIN support.
  78. TEST_F(BioEnrollmentHandlerTest, NoPINSupport) {
  79. VirtualCtap2Device::Config config;
  80. config.pin_support = false;
  81. config.bio_enrollment_preview_support = true;
  82. virtual_device_factory_.SetCtap2Config(config);
  83. auto handler = MakeHandler();
  84. error_callback_.WaitForCallback();
  85. EXPECT_EQ(error_callback_.value(), BioEnrollmentHandler::Error::kNoPINSet);
  86. }
  87. // Tests bio enrollment handler against device with the forcePINChange flag on.
  88. TEST_F(BioEnrollmentHandlerTest, ForcePINChange) {
  89. VirtualCtap2Device::Config config;
  90. config.pin_support = true;
  91. config.bio_enrollment_preview_support = true;
  92. config.min_pin_length_support = true;
  93. config.pin_uv_auth_token_support = true;
  94. config.ctap2_versions = {Ctap2Version::kCtap2_1};
  95. virtual_device_factory_.mutable_state()->force_pin_change = true;
  96. virtual_device_factory_.SetCtap2Config(config);
  97. auto handler = MakeHandler();
  98. error_callback_.WaitForCallback();
  99. EXPECT_EQ(error_callback_.value(),
  100. BioEnrollmentHandler::Error::kForcePINChange);
  101. }
  102. // Tests enrollment handler PIN soft block.
  103. TEST_F(BioEnrollmentHandlerTest, SoftPINBlock) {
  104. VirtualCtap2Device::Config config;
  105. config.pin_support = true;
  106. config.bio_enrollment_preview_support = true;
  107. virtual_device_factory_.mutable_state()->pin = "1234";
  108. virtual_device_factory_.SetCtap2Config(config);
  109. auto handler = MakeHandler();
  110. error_callback_.WaitForCallback();
  111. EXPECT_EQ(error_callback_.value(),
  112. BioEnrollmentHandler::Error::kSoftPINBlock);
  113. }
  114. // Tests bio enrollment commands against an authenticator lacking support.
  115. TEST_F(BioEnrollmentHandlerTest, NoBioEnrollmentSupport) {
  116. VirtualCtap2Device::Config config;
  117. config.pin_support = true;
  118. virtual_device_factory_.SetCtap2Config(config);
  119. auto handler = MakeHandler();
  120. error_callback_.WaitForCallback();
  121. EXPECT_EQ(error_callback_.value(),
  122. BioEnrollmentHandler::Error::kAuthenticatorMissingBioEnrollment);
  123. }
  124. // Tests fingerprint enrollment lifecycle.
  125. TEST_F(BioEnrollmentHandlerTest, Enroll) {
  126. VirtualCtap2Device::Config config;
  127. config.pin_support = true;
  128. config.bio_enrollment_preview_support = true;
  129. virtual_device_factory_.SetCtap2Config(config);
  130. auto handler = MakeHandler();
  131. ready_callback_.WaitForCallback();
  132. auto [status, template_id] = EnrollTemplate(handler.get());
  133. EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess);
  134. EXPECT_FALSE(template_id.empty());
  135. }
  136. // Tests enrolling multiple fingerprints.
  137. TEST_F(BioEnrollmentHandlerTest, EnrollMultiple) {
  138. VirtualCtap2Device::Config config;
  139. config.pin_support = true;
  140. config.bio_enrollment_preview_support = true;
  141. virtual_device_factory_.SetCtap2Config(config);
  142. auto handler = MakeHandler();
  143. ready_callback_.WaitForCallback();
  144. // Multiple enrollments
  145. for (auto i = 0; i < 4; i++) {
  146. auto [status, template_id] = EnrollTemplate(handler.get());
  147. EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess);
  148. EXPECT_FALSE(template_id.empty());
  149. }
  150. // Enumerate to check enrollments.
  151. test::StatusAndValueCallbackReceiver<
  152. CtapDeviceResponseCode,
  153. absl::optional<std::map<std::vector<uint8_t>, std::string>>>
  154. cb;
  155. handler->EnumerateTemplates(cb.callback());
  156. cb.WaitForCallback();
  157. EXPECT_EQ(cb.status(), CtapDeviceResponseCode::kSuccess);
  158. EXPECT_EQ(cb.value(),
  159. (std::map<std::vector<uint8_t>, std::string>{{{1}, "Template1"},
  160. {{2}, "Template2"},
  161. {{3}, "Template3"},
  162. {{4}, "Template4"}}));
  163. }
  164. // Tests enrolling beyond maximum capacity.
  165. TEST_F(BioEnrollmentHandlerTest, EnrollMax) {
  166. VirtualCtap2Device::Config config;
  167. config.pin_support = true;
  168. config.bio_enrollment_preview_support = true;
  169. virtual_device_factory_.SetCtap2Config(config);
  170. auto handler = MakeHandler();
  171. ready_callback_.WaitForCallback();
  172. // Enroll until full.
  173. CtapDeviceResponseCode status;
  174. BioEnrollmentHandler::TemplateId template_id;
  175. for (;;) {
  176. std::tie(status, template_id) = EnrollTemplate(handler.get());
  177. if (status != CtapDeviceResponseCode::kSuccess)
  178. break;
  179. }
  180. EXPECT_EQ(status, CtapDeviceResponseCode::kCtap2ErrFpDatabaseFull);
  181. EXPECT_TRUE(template_id.empty());
  182. }
  183. // Tests enumerating with no enrollments.
  184. TEST_F(BioEnrollmentHandlerTest, EnumerateNone) {
  185. VirtualCtap2Device::Config config;
  186. config.pin_support = true;
  187. config.bio_enrollment_preview_support = true;
  188. virtual_device_factory_.SetCtap2Config(config);
  189. auto handler = MakeHandler();
  190. ready_callback_.WaitForCallback();
  191. test::StatusAndValueCallbackReceiver<
  192. CtapDeviceResponseCode,
  193. absl::optional<std::map<std::vector<uint8_t>, std::string>>>
  194. cb;
  195. handler->EnumerateTemplates(cb.callback());
  196. cb.WaitForCallback();
  197. EXPECT_EQ(cb.status(), CtapDeviceResponseCode::kCtap2ErrInvalidOption);
  198. EXPECT_EQ(cb.value(), absl::nullopt);
  199. }
  200. // Tests enumerating with one enrollment.
  201. TEST_F(BioEnrollmentHandlerTest, EnumerateOne) {
  202. VirtualCtap2Device::Config config;
  203. config.pin_support = true;
  204. config.bio_enrollment_preview_support = true;
  205. virtual_device_factory_.SetCtap2Config(config);
  206. auto handler = MakeHandler();
  207. ready_callback_.WaitForCallback();
  208. // Enroll - skip response validation
  209. auto [status, template_id] = EnrollTemplate(handler.get());
  210. EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess);
  211. EXPECT_FALSE(template_id.empty());
  212. // Enumerate
  213. test::StatusAndValueCallbackReceiver<
  214. CtapDeviceResponseCode,
  215. absl::optional<std::map<std::vector<uint8_t>, std::string>>>
  216. cb1;
  217. handler->EnumerateTemplates(cb1.callback());
  218. cb1.WaitForCallback();
  219. EXPECT_EQ(cb1.status(), CtapDeviceResponseCode::kSuccess);
  220. EXPECT_EQ(cb1.value(),
  221. (std::map<std::vector<uint8_t>, std::string>{{{1}, "Template1"}}));
  222. }
  223. // Tests renaming an enrollment (success and failure).
  224. TEST_F(BioEnrollmentHandlerTest, Rename) {
  225. VirtualCtap2Device::Config config;
  226. config.pin_support = true;
  227. config.bio_enrollment_preview_support = true;
  228. virtual_device_factory_.SetCtap2Config(config);
  229. auto handler = MakeHandler();
  230. ready_callback_.WaitForCallback();
  231. // Rename non-existent enrollment.
  232. test::ValueCallbackReceiver<CtapDeviceResponseCode> cb0;
  233. handler->RenameTemplate({1}, "OtherFingerprint1", cb0.callback());
  234. cb0.WaitForCallback();
  235. EXPECT_EQ(cb0.value(), CtapDeviceResponseCode::kCtap2ErrInvalidOption);
  236. // Enroll - skip response validation.
  237. auto [status, template_id] = EnrollTemplate(handler.get());
  238. EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess);
  239. EXPECT_FALSE(template_id.empty());
  240. // Rename non-existent enrollment.
  241. test::ValueCallbackReceiver<CtapDeviceResponseCode> cb2;
  242. handler->RenameTemplate(template_id, "OtherFingerprint1", cb2.callback());
  243. cb2.WaitForCallback();
  244. EXPECT_EQ(cb2.value(), CtapDeviceResponseCode::kSuccess);
  245. // Enumerate to validate renaming.
  246. test::StatusAndValueCallbackReceiver<
  247. CtapDeviceResponseCode,
  248. absl::optional<std::map<std::vector<uint8_t>, std::string>>>
  249. cb3;
  250. handler->EnumerateTemplates(cb3.callback());
  251. cb3.WaitForCallback();
  252. EXPECT_EQ(cb3.status(), CtapDeviceResponseCode::kSuccess);
  253. EXPECT_EQ(cb3.value(), (std::map<std::vector<uint8_t>, std::string>{
  254. {template_id, "OtherFingerprint1"}}));
  255. }
  256. // Tests deleting an enrollment (success and failure).
  257. TEST_F(BioEnrollmentHandlerTest, Delete) {
  258. VirtualCtap2Device::Config config;
  259. config.pin_support = true;
  260. config.bio_enrollment_preview_support = true;
  261. virtual_device_factory_.SetCtap2Config(config);
  262. auto handler = MakeHandler();
  263. ready_callback_.WaitForCallback();
  264. // Delete non-existent enrollment.
  265. test::ValueCallbackReceiver<CtapDeviceResponseCode> cb0;
  266. handler->DeleteTemplate({1}, cb0.callback());
  267. cb0.WaitForCallback();
  268. EXPECT_EQ(cb0.value(), CtapDeviceResponseCode::kCtap2ErrInvalidOption);
  269. // Enroll - skip response validation.
  270. auto [status, template_id] = EnrollTemplate(handler.get());
  271. EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess);
  272. EXPECT_FALSE(template_id.empty());
  273. // Delete existing enrollment.
  274. test::ValueCallbackReceiver<CtapDeviceResponseCode> cb2;
  275. handler->DeleteTemplate({1}, cb2.callback());
  276. cb2.WaitForCallback();
  277. EXPECT_EQ(cb2.value(), CtapDeviceResponseCode::kSuccess);
  278. // Attempt to delete again to prove enrollment is gone.
  279. test::ValueCallbackReceiver<CtapDeviceResponseCode> cb3;
  280. handler->DeleteTemplate({1}, cb3.callback());
  281. cb3.WaitForCallback();
  282. EXPECT_EQ(cb3.value(), CtapDeviceResponseCode::kCtap2ErrInvalidOption);
  283. }
  284. // Test that enrollment succeeds even if one of the samples yields an error
  285. // status. The error status should be propagated into the SampleCallback.
  286. TEST_F(BioEnrollmentHandlerTest, SampleError) {
  287. VirtualCtap2Device::Config config;
  288. config.pin_support = true;
  289. config.bio_enrollment_preview_support = true;
  290. virtual_device_factory_.SetCtap2Config(config);
  291. virtual_device_factory_.mutable_state()->bio_enrollment_next_sample_error =
  292. true;
  293. auto handler = MakeHandler();
  294. ready_callback_.WaitForCallback();
  295. auto [status, template_id] = EnrollTemplate(handler.get());
  296. EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess);
  297. EXPECT_EQ(sample_failures_, 1u);
  298. }
  299. // Test that enrollment succeeds even if one of the samples yields a timeout
  300. // status. The timeout status should not propagate into the SampleCallback.
  301. TEST_F(BioEnrollmentHandlerTest, SampleNoUserActivity) {
  302. VirtualCtap2Device::Config config;
  303. config.pin_support = true;
  304. config.bio_enrollment_preview_support = true;
  305. virtual_device_factory_.SetCtap2Config(config);
  306. virtual_device_factory_.mutable_state()->bio_enrollment_next_sample_timeout =
  307. true;
  308. auto handler = MakeHandler();
  309. ready_callback_.WaitForCallback();
  310. auto [status, template_id] = EnrollTemplate(handler.get());
  311. EXPECT_EQ(status, CtapDeviceResponseCode::kSuccess);
  312. EXPECT_EQ(sample_failures_, 0u);
  313. }
  314. } // namespace
  315. } // namespace device