u2f_sign_operation.cc 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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 <utility>
  6. #include "base/bind.h"
  7. #include "base/strings/string_number_conversions.h"
  8. #include "base/threading/sequenced_task_runner_handle.h"
  9. #include "components/apdu/apdu_response.h"
  10. #include "components/device_event_log/device_event_log.h"
  11. #include "device/fido/authenticator_get_assertion_response.h"
  12. #include "device/fido/ctap_get_assertion_request.h"
  13. #include "device/fido/device_response_converter.h"
  14. #include "device/fido/fido_device.h"
  15. #include "device/fido/fido_parsing_utils.h"
  16. #include "device/fido/u2f_command_constructor.h"
  17. namespace device {
  18. U2fSignOperation::U2fSignOperation(FidoDevice* device,
  19. const CtapGetAssertionRequest& request,
  20. DeviceResponseCallback callback)
  21. : DeviceOperation(device, request, std::move(callback)) {}
  22. U2fSignOperation::~U2fSignOperation() = default;
  23. void U2fSignOperation::Start() {
  24. if (!request().allow_list.empty()) {
  25. if (request().alternative_application_parameter.has_value()) {
  26. // Try the alternative value first. This is because the U2F Zero
  27. // authenticator (at least) crashes if we try the wrong AppID first.
  28. app_param_type_ = ApplicationParameterType::kAlternative;
  29. }
  30. WinkAndTrySign();
  31. } else {
  32. // In order to make U2F authenticators blink on sign request with an empty
  33. // allow list, we send fake enrollment to the device and error out when the
  34. // user has provided presence.
  35. WinkAndTryFakeEnrollment();
  36. }
  37. }
  38. void U2fSignOperation::Cancel() {
  39. canceled_ = true;
  40. }
  41. void U2fSignOperation::WinkAndTrySign() {
  42. device()->TryWink(
  43. base::BindOnce(&U2fSignOperation::TrySign, weak_factory_.GetWeakPtr()));
  44. }
  45. void U2fSignOperation::TrySign() {
  46. DispatchU2FCommand(
  47. ConvertToU2fSignCommand(request(), app_param_type_, key_handle()),
  48. base::BindOnce(&U2fSignOperation::OnSignResponseReceived,
  49. weak_factory_.GetWeakPtr()));
  50. }
  51. void U2fSignOperation::OnSignResponseReceived(
  52. absl::optional<std::vector<uint8_t>> device_response) {
  53. if (canceled_) {
  54. return;
  55. }
  56. auto result = apdu::ApduResponse::Status::SW_WRONG_DATA;
  57. const auto apdu_response =
  58. device_response
  59. ? apdu::ApduResponse::CreateFromMessage(std::move(*device_response))
  60. : absl::nullopt;
  61. if (apdu_response) {
  62. result = apdu_response->status();
  63. }
  64. // Older U2F devices may respond with the length of the input as an error
  65. // response if the length is unexpected.
  66. if (result == static_cast<apdu::ApduResponse::Status>(key_handle().size())) {
  67. result = apdu::ApduResponse::Status::SW_WRONG_LENGTH;
  68. }
  69. switch (result) {
  70. case apdu::ApduResponse::Status::SW_NO_ERROR: {
  71. auto application_parameter =
  72. app_param_type_ == ApplicationParameterType::kPrimary
  73. ? fido_parsing_utils::CreateSHA256Hash(request().rp_id)
  74. : request().alternative_application_parameter.value_or(
  75. std::array<uint8_t, kRpIdHashLength>());
  76. auto sign_response =
  77. AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
  78. std::move(application_parameter), apdu_response->data(),
  79. key_handle());
  80. if (!sign_response) {
  81. std::move(callback())
  82. .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
  83. return;
  84. }
  85. FIDO_LOG(DEBUG)
  86. << "Received successful U2F sign response from authenticator: "
  87. << base::HexEncode(apdu_response->data().data(),
  88. apdu_response->data().size());
  89. std::move(callback())
  90. .Run(CtapDeviceResponseCode::kSuccess, std::move(sign_response));
  91. break;
  92. }
  93. case apdu::ApduResponse::Status::SW_WRONG_DATA:
  94. case apdu::ApduResponse::Status::SW_WRONG_LENGTH:
  95. if (app_param_type_ == ApplicationParameterType::kAlternative) {
  96. // |application_parameter_| failed, but there is also
  97. // the primary value to try.
  98. app_param_type_ = ApplicationParameterType::kPrimary;
  99. WinkAndTrySign();
  100. } else if (++current_key_handle_index_ < request().allow_list.size()) {
  101. // Key is not for this device. Try signing with the next key.
  102. if (request().alternative_application_parameter.has_value()) {
  103. app_param_type_ = ApplicationParameterType::kAlternative;
  104. }
  105. WinkAndTrySign();
  106. } else {
  107. // No provided key was accepted by this device. Send registration
  108. // (i.e. fake enroll) request to device.
  109. TryFakeEnrollment();
  110. }
  111. break;
  112. case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
  113. // Waiting for user touch. Retry after 200 milliseconds delay.
  114. base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
  115. FROM_HERE,
  116. base::BindOnce(&U2fSignOperation::WinkAndTrySign,
  117. weak_factory_.GetWeakPtr()),
  118. kU2fRetryDelay);
  119. break;
  120. default:
  121. // Some sort of failure occurred. Abandon this device and move on.
  122. std::move(callback())
  123. .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
  124. return;
  125. }
  126. }
  127. void U2fSignOperation::WinkAndTryFakeEnrollment() {
  128. device()->TryWink(base::BindOnce(&U2fSignOperation::TryFakeEnrollment,
  129. weak_factory_.GetWeakPtr()));
  130. }
  131. void U2fSignOperation::TryFakeEnrollment() {
  132. DispatchU2FCommand(
  133. ConstructBogusU2fRegistrationCommand(),
  134. base::BindOnce(&U2fSignOperation::OnEnrollmentResponseReceived,
  135. weak_factory_.GetWeakPtr()));
  136. }
  137. void U2fSignOperation::OnEnrollmentResponseReceived(
  138. absl::optional<std::vector<uint8_t>> device_response) {
  139. if (canceled_) {
  140. return;
  141. }
  142. auto result = apdu::ApduResponse::Status::SW_WRONG_DATA;
  143. if (device_response) {
  144. const auto apdu_response =
  145. apdu::ApduResponse::CreateFromMessage(std::move(*device_response));
  146. if (apdu_response) {
  147. result = apdu_response->status();
  148. }
  149. }
  150. switch (result) {
  151. case apdu::ApduResponse::Status::SW_NO_ERROR:
  152. std::move(callback())
  153. .Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, absl::nullopt);
  154. break;
  155. case apdu::ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
  156. // Waiting for user touch. Retry after 200 milliseconds delay.
  157. base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
  158. FROM_HERE,
  159. base::BindOnce(&U2fSignOperation::TryFakeEnrollment,
  160. weak_factory_.GetWeakPtr()),
  161. kU2fRetryDelay);
  162. break;
  163. default:
  164. // Some sort of failure occurred. Abandon this device and move on.
  165. std::move(callback())
  166. .Run(CtapDeviceResponseCode::kCtap2ErrOther, absl::nullopt);
  167. return;
  168. }
  169. }
  170. const std::vector<uint8_t>& U2fSignOperation::key_handle() const {
  171. DCHECK_LT(current_key_handle_index_, request().allow_list.size());
  172. return request().allow_list.at(current_key_handle_index_).id;
  173. }
  174. } // namespace device