enrollment.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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 "device/fido/bio/enrollment.h"
  5. #include "components/cbor/diagnostic_writer.h"
  6. #include "components/cbor/writer.h"
  7. #include "components/device_event_log/device_event_log.h"
  8. namespace device {
  9. static void SetPinAuth(BioEnrollmentRequest* request,
  10. const pin::TokenResponse& token) {
  11. request->modality = BioEnrollmentModality::kFingerprint;
  12. std::vector<uint8_t> pin_auth;
  13. if (request->params)
  14. pin_auth = *cbor::Writer::Write(cbor::Value(request->params.value()));
  15. if (request->subcommand)
  16. pin_auth.insert(pin_auth.begin(), static_cast<int>(*request->subcommand));
  17. pin_auth.insert(pin_auth.begin(), static_cast<int>(*request->modality));
  18. std::tie(request->pin_protocol, request->pin_auth) =
  19. token.PinAuth(std::move(pin_auth));
  20. }
  21. // static
  22. BioEnrollmentRequest BioEnrollmentRequest::ForGetModality(Version version) {
  23. BioEnrollmentRequest request(version);
  24. request.get_modality = true;
  25. return request;
  26. }
  27. // static
  28. BioEnrollmentRequest BioEnrollmentRequest::ForGetSensorInfo(Version version) {
  29. BioEnrollmentRequest request(version);
  30. request.modality = BioEnrollmentModality::kFingerprint;
  31. request.subcommand = BioEnrollmentSubCommand::kGetFingerprintSensorInfo;
  32. return request;
  33. }
  34. // static
  35. BioEnrollmentRequest BioEnrollmentRequest::ForEnrollBegin(
  36. Version version,
  37. const pin::TokenResponse& token) {
  38. BioEnrollmentRequest request(version);
  39. request.subcommand = BioEnrollmentSubCommand::kEnrollBegin;
  40. SetPinAuth(&request, token);
  41. return request;
  42. }
  43. // static
  44. BioEnrollmentRequest BioEnrollmentRequest::ForEnrollNextSample(
  45. Version version,
  46. const pin::TokenResponse& token,
  47. std::vector<uint8_t> template_id) {
  48. BioEnrollmentRequest request(version);
  49. request.subcommand = BioEnrollmentSubCommand::kEnrollCaptureNextSample;
  50. request.params = cbor::Value::MapValue();
  51. request.params->emplace(
  52. static_cast<int>(BioEnrollmentSubCommandParam::kTemplateId),
  53. cbor::Value(template_id));
  54. SetPinAuth(&request, token);
  55. return request;
  56. }
  57. // static
  58. BioEnrollmentRequest BioEnrollmentRequest::ForCancel(Version version) {
  59. BioEnrollmentRequest request(version);
  60. request.modality = BioEnrollmentModality::kFingerprint;
  61. request.subcommand = BioEnrollmentSubCommand::kCancelCurrentEnrollment;
  62. return request;
  63. }
  64. // static
  65. BioEnrollmentRequest BioEnrollmentRequest::ForEnumerate(
  66. Version version,
  67. const pin::TokenResponse& token) {
  68. BioEnrollmentRequest request(version);
  69. request.subcommand = BioEnrollmentSubCommand::kEnumerateEnrollments;
  70. SetPinAuth(&request, token);
  71. return request;
  72. }
  73. // static
  74. BioEnrollmentRequest BioEnrollmentRequest::ForRename(
  75. Version version,
  76. const pin::TokenResponse& token,
  77. std::vector<uint8_t> id,
  78. std::string name) {
  79. BioEnrollmentRequest request(version);
  80. request.subcommand = BioEnrollmentSubCommand::kSetFriendlyName;
  81. request.params = cbor::Value::MapValue();
  82. request.params->emplace(
  83. static_cast<int>(BioEnrollmentSubCommandParam::kTemplateId),
  84. std::move(id));
  85. request.params->emplace(
  86. static_cast<int>(BioEnrollmentSubCommandParam::kTemplateFriendlyName),
  87. std::move(name));
  88. SetPinAuth(&request, token);
  89. return request;
  90. }
  91. // static
  92. BioEnrollmentRequest BioEnrollmentRequest::ForDelete(
  93. Version version,
  94. const pin::TokenResponse& token,
  95. std::vector<uint8_t> id) {
  96. BioEnrollmentRequest request(version);
  97. request.subcommand = BioEnrollmentSubCommand::kRemoveEnrollment;
  98. request.params = cbor::Value::MapValue();
  99. request.params->emplace(
  100. static_cast<int>(BioEnrollmentSubCommandParam::kTemplateId),
  101. std::move(id));
  102. SetPinAuth(&request, token);
  103. return request;
  104. }
  105. BioEnrollmentRequest::BioEnrollmentRequest(BioEnrollmentRequest&&) = default;
  106. BioEnrollmentRequest& BioEnrollmentRequest::operator=(BioEnrollmentRequest&&) =
  107. default;
  108. BioEnrollmentRequest::BioEnrollmentRequest(Version v) : version(v) {}
  109. BioEnrollmentRequest::~BioEnrollmentRequest() = default;
  110. // static
  111. absl::optional<BioEnrollmentResponse> BioEnrollmentResponse::Parse(
  112. const absl::optional<cbor::Value>& cbor_response) {
  113. BioEnrollmentResponse response;
  114. if (!cbor_response || !cbor_response->is_map()) {
  115. return response;
  116. }
  117. const auto& response_map = cbor_response->GetMap();
  118. // modality
  119. auto it = response_map.find(
  120. cbor::Value(static_cast<int>(BioEnrollmentResponseKey::kModality)));
  121. if (it != response_map.end()) {
  122. if (!it->second.is_unsigned()) {
  123. return absl::nullopt;
  124. }
  125. response.modality =
  126. ToBioEnrollmentEnum<BioEnrollmentModality>(it->second.GetUnsigned());
  127. if (!response.modality) {
  128. return absl::nullopt;
  129. }
  130. }
  131. // fingerprint kind
  132. it = response_map.find(cbor::Value(
  133. static_cast<int>(BioEnrollmentResponseKey::kFingerprintKind)));
  134. if (it != response_map.end()) {
  135. if (!it->second.is_unsigned()) {
  136. return absl::nullopt;
  137. }
  138. response.fingerprint_kind =
  139. ToBioEnrollmentEnum<BioEnrollmentFingerprintKind>(
  140. it->second.GetUnsigned());
  141. if (!response.fingerprint_kind) {
  142. return absl::nullopt;
  143. }
  144. }
  145. // max captures required for enroll
  146. it = response_map.find(cbor::Value(static_cast<int>(
  147. BioEnrollmentResponseKey::kMaxCaptureSamplesRequiredForEnroll)));
  148. if (it != response_map.end()) {
  149. if (!it->second.is_unsigned() ||
  150. it->second.GetUnsigned() > std::numeric_limits<uint8_t>::max()) {
  151. return absl::nullopt;
  152. }
  153. response.max_samples_for_enroll = it->second.GetUnsigned();
  154. }
  155. // template id
  156. it = response_map.find(
  157. cbor::Value(static_cast<int>(BioEnrollmentResponseKey::kTemplateId)));
  158. if (it != response_map.end()) {
  159. if (!it->second.is_bytestring()) {
  160. return absl::nullopt;
  161. }
  162. response.template_id = it->second.GetBytestring();
  163. }
  164. // last enroll sample status
  165. it = response_map.find(cbor::Value(
  166. static_cast<int>(BioEnrollmentResponseKey::kLastEnrollSampleStatus)));
  167. if (it != response_map.end()) {
  168. if (!it->second.is_unsigned()) {
  169. return absl::nullopt;
  170. }
  171. response.last_status = ToBioEnrollmentEnum<BioEnrollmentSampleStatus>(
  172. it->second.GetUnsigned());
  173. if (!response.last_status) {
  174. return absl::nullopt;
  175. }
  176. }
  177. // remaining samples
  178. it = response_map.find(cbor::Value(
  179. static_cast<int>(BioEnrollmentResponseKey::kRemainingSamples)));
  180. if (it != response_map.end()) {
  181. if (!it->second.is_unsigned() ||
  182. it->second.GetUnsigned() > std::numeric_limits<uint8_t>::max()) {
  183. return absl::nullopt;
  184. }
  185. response.remaining_samples = it->second.GetUnsigned();
  186. }
  187. // enumerated template infos
  188. it = response_map.find(
  189. cbor::Value(static_cast<int>(BioEnrollmentResponseKey::kTemplateInfos)));
  190. if (it != response_map.end()) {
  191. if (!it->second.is_array()) {
  192. return absl::nullopt;
  193. }
  194. std::map<std::vector<uint8_t>, std::string> template_infos;
  195. for (const auto& bio_template : it->second.GetArray()) {
  196. if (!bio_template.is_map()) {
  197. return absl::nullopt;
  198. }
  199. const cbor::Value::MapValue& template_map = bio_template.GetMap();
  200. // id (required)
  201. auto template_it = template_map.find(cbor::Value(
  202. static_cast<int>(BioEnrollmentTemplateInfoParam::kTemplateId)));
  203. if (template_it == template_map.end() ||
  204. !template_it->second.is_bytestring()) {
  205. return absl::nullopt;
  206. }
  207. std::vector<uint8_t> id = template_it->second.GetBytestring();
  208. if (template_infos.find(id) != template_infos.end()) {
  209. // Found an existing ID, invalid response.
  210. return absl::nullopt;
  211. }
  212. // name (optional)
  213. std::string name;
  214. template_it = template_map.find(cbor::Value(static_cast<int>(
  215. BioEnrollmentTemplateInfoParam::kTemplateFriendlyName)));
  216. if (template_it != template_map.end()) {
  217. if (!template_it->second.is_string()) {
  218. return absl::nullopt;
  219. }
  220. name = template_it->second.GetString();
  221. }
  222. template_infos[std::move(id)] = std::move(name);
  223. }
  224. response.template_infos = std::move(template_infos);
  225. }
  226. it = response_map.find(cbor::Value(
  227. static_cast<int>(BioEnrollmentResponseKey::kMaxTemplateFriendlyName)));
  228. if (it != response_map.end()) {
  229. if (!it->second.is_unsigned() ||
  230. it->second.GetUnsigned() > std::numeric_limits<uint32_t>::max()) {
  231. return absl::nullopt;
  232. }
  233. response.max_template_friendly_name = it->second.GetUnsigned();
  234. }
  235. return std::move(response);
  236. }
  237. BioEnrollmentResponse::BioEnrollmentResponse() = default;
  238. BioEnrollmentResponse::BioEnrollmentResponse(BioEnrollmentResponse&&) = default;
  239. BioEnrollmentResponse::~BioEnrollmentResponse() = default;
  240. bool BioEnrollmentResponse::operator==(const BioEnrollmentResponse& r) const {
  241. return modality == r.modality && fingerprint_kind == r.fingerprint_kind &&
  242. max_samples_for_enroll == r.max_samples_for_enroll &&
  243. template_id == r.template_id && last_status == r.last_status &&
  244. remaining_samples == r.remaining_samples &&
  245. template_infos == r.template_infos;
  246. }
  247. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  248. AsCTAPRequestValuePair(const BioEnrollmentRequest& request) {
  249. cbor::Value::MapValue map;
  250. using Key = BioEnrollmentRequestKey;
  251. if (request.modality) {
  252. map.emplace(static_cast<int>(Key::kModality),
  253. static_cast<int>(*request.modality));
  254. }
  255. if (request.subcommand) {
  256. map.emplace(static_cast<int>(Key::kSubCommand),
  257. static_cast<int>(*request.subcommand));
  258. }
  259. if (request.params) {
  260. map.emplace(static_cast<int>(Key::kSubCommandParams), *request.params);
  261. }
  262. if (request.pin_protocol) {
  263. map.emplace(static_cast<int>(Key::kPinProtocol),
  264. static_cast<uint8_t>(*request.pin_protocol));
  265. }
  266. if (request.pin_auth) {
  267. map.emplace(static_cast<int>(Key::kPinAuth), *request.pin_auth);
  268. }
  269. if (request.get_modality) {
  270. map.emplace(static_cast<int>(Key::kGetModality), *request.get_modality);
  271. }
  272. return {request.version == BioEnrollmentRequest::Version::kDefault
  273. ? CtapRequestCommand::kAuthenticatorBioEnrollment
  274. : CtapRequestCommand::kAuthenticatorBioEnrollmentPreview,
  275. cbor::Value(std::move(map))};
  276. }
  277. } // namespace device