u2f_register_operation.cc 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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_register_operation.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/callback_helpers.h"
  8. #include "base/strings/string_number_conversions.h"
  9. #include "base/threading/sequenced_task_runner_handle.h"
  10. #include "components/apdu/apdu_response.h"
  11. #include "components/device_event_log/device_event_log.h"
  12. #include "device/fido/authenticator_make_credential_response.h"
  13. #include "device/fido/ctap_make_credential_request.h"
  14. #include "device/fido/device_response_converter.h"
  15. #include "device/fido/fido_constants.h"
  16. #include "device/fido/fido_device.h"
  17. #include "device/fido/fido_parsing_utils.h"
  18. #include "device/fido/u2f_command_constructor.h"
  19. namespace device {
  20. U2fRegisterOperation::U2fRegisterOperation(
  21. FidoDevice* device,
  22. const CtapMakeCredentialRequest& request,
  23. DeviceResponseCallback callback)
  24. : DeviceOperation(device, request, std::move(callback)) {}
  25. U2fRegisterOperation::~U2fRegisterOperation() = default;
  26. void U2fRegisterOperation::Start() {
  27. DCHECK(IsConvertibleToU2fRegisterCommand(request()));
  28. if (!request().exclude_list.empty()) {
  29. // First try signing with the excluded credentials to see whether this
  30. // device should be excluded.
  31. WinkAndTrySign();
  32. } else {
  33. WinkAndTryRegistration();
  34. }
  35. }
  36. void U2fRegisterOperation::Cancel() {
  37. canceled_ = true;
  38. }
  39. void U2fRegisterOperation::WinkAndTrySign() {
  40. device()->TryWink(base::BindOnce(&U2fRegisterOperation::TrySign,
  41. weak_factory_.GetWeakPtr()));
  42. }
  43. void U2fRegisterOperation::TrySign() {
  44. absl::optional<std::vector<uint8_t>> sign_command;
  45. if (probing_alternative_rp_id_) {
  46. CtapMakeCredentialRequest sign_request(request());
  47. sign_request.rp.id = *request().app_id_exclude;
  48. sign_command = ConvertToU2fSignCommandWithBogusChallenge(
  49. sign_request, excluded_key_handle());
  50. } else {
  51. sign_command = ConvertToU2fSignCommandWithBogusChallenge(
  52. request(), excluded_key_handle());
  53. }
  54. DispatchU2FCommand(
  55. std::move(sign_command),
  56. base::BindOnce(&U2fRegisterOperation::OnCheckForExcludedKeyHandle,
  57. weak_factory_.GetWeakPtr()));
  58. }
  59. void U2fRegisterOperation::OnCheckForExcludedKeyHandle(
  60. absl::optional<std::vector<uint8_t>> device_response) {
  61. if (canceled_) {
  62. return;
  63. }
  64. auto result = apdu::ApduResponse::Status::SW_WRONG_DATA;
  65. if (device_response) {
  66. const auto apdu_response =
  67. apdu::ApduResponse::CreateFromMessage(std::move(*device_response));
  68. if (apdu_response) {
  69. result = apdu_response->status();
  70. }
  71. }
  72. // Older U2F devices may respond with the length of the input as an error
  73. // response if the length is unexpected.
  74. if (result ==
  75. static_cast<apdu::ApduResponse::Status>(excluded_key_handle().size())) {
  76. result = apdu::ApduResponse::Status::SW_WRONG_LENGTH;
  77. }
  78. switch (result) {
  79. case apdu::ApduResponse::Status::SW_NO_ERROR:
  80. // Duplicate registration found. The device has already collected
  81. // user-presence.
  82. std::move(callback())
  83. .Run(CtapDeviceResponseCode::kCtap2ErrCredentialExcluded,
  84. absl::nullopt);
  85. break;
  86. case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
  87. // Duplicate registration found. Waiting for user touch.
  88. base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
  89. FROM_HERE,
  90. base::BindOnce(&U2fRegisterOperation::WinkAndTrySign,
  91. weak_factory_.GetWeakPtr()),
  92. kU2fRetryDelay);
  93. break;
  94. case apdu::ApduResponse::Status::SW_WRONG_DATA:
  95. case apdu::ApduResponse::Status::SW_WRONG_LENGTH:
  96. // Continue to iterate through the provided key handles in the exclude
  97. // list to check for already registered keys.
  98. current_key_handle_index_++;
  99. if (current_key_handle_index_ == request().exclude_list.size() &&
  100. !probing_alternative_rp_id_ && request().app_id_exclude) {
  101. // All elements of |request().exclude_list| have been tested, but
  102. // there's a second AppID so they need to be tested again.
  103. probing_alternative_rp_id_ = true;
  104. current_key_handle_index_ = 0;
  105. }
  106. if (current_key_handle_index_ < request().exclude_list.size()) {
  107. WinkAndTrySign();
  108. } else {
  109. // Reached the end of exclude list with no duplicate credential.
  110. // Proceed with registration.
  111. WinkAndTryRegistration();
  112. }
  113. break;
  114. default:
  115. // Some sort of failure occurred. Silently drop device request.
  116. FIDO_LOG(ERROR) << "Unexpected status " << static_cast<int>(result)
  117. << " from U2F device";
  118. std::move(callback())
  119. .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
  120. break;
  121. }
  122. }
  123. void U2fRegisterOperation::WinkAndTryRegistration() {
  124. device()->TryWink(base::BindOnce(&U2fRegisterOperation::TryRegistration,
  125. weak_factory_.GetWeakPtr()));
  126. }
  127. void U2fRegisterOperation::TryRegistration() {
  128. DispatchU2FCommand(
  129. ConvertToU2fRegisterCommand(request()),
  130. base::BindOnce(&U2fRegisterOperation::OnRegisterResponseReceived,
  131. weak_factory_.GetWeakPtr()));
  132. }
  133. void U2fRegisterOperation::OnRegisterResponseReceived(
  134. absl::optional<std::vector<uint8_t>> device_response) {
  135. if (canceled_) {
  136. return;
  137. }
  138. auto result = apdu::ApduResponse::Status::SW_WRONG_DATA;
  139. const auto apdu_response =
  140. device_response
  141. ? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
  142. : absl::nullopt;
  143. if (apdu_response) {
  144. result = apdu_response->status();
  145. }
  146. switch (result) {
  147. case apdu::ApduResponse::Status::SW_NO_ERROR: {
  148. FIDO_LOG(DEBUG)
  149. << "Received successful U2F register response from authenticator: "
  150. << base::HexEncode(apdu_response->data().data(),
  151. apdu_response->data().size());
  152. auto response =
  153. AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
  154. device()->DeviceTransport(),
  155. fido_parsing_utils::CreateSHA256Hash(request().rp.id),
  156. apdu_response->data());
  157. std::move(callback())
  158. .Run(CtapDeviceResponseCode::kSuccess, std::move(response));
  159. break;
  160. }
  161. case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
  162. // Waiting for user touch, retry after delay.
  163. base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
  164. FROM_HERE,
  165. base::BindOnce(&U2fRegisterOperation::WinkAndTryRegistration,
  166. weak_factory_.GetWeakPtr()),
  167. kU2fRetryDelay);
  168. break;
  169. default:
  170. // An error has occurred, quit trying this device.
  171. FIDO_LOG(ERROR) << "Unexpected status " << static_cast<int>(result)
  172. << " from U2F device";
  173. std::move(callback())
  174. .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
  175. break;
  176. }
  177. }
  178. const std::vector<uint8_t>& U2fRegisterOperation::excluded_key_handle() const {
  179. DCHECK_LT(current_key_handle_index_, request().exclude_list.size());
  180. return request().exclude_list[current_key_handle_index_].id;
  181. }
  182. } // namespace device