credential_management_handler.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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/credential_management_handler.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/check_op.h"
  8. #include "base/notreached.h"
  9. #include "device/fido/fido_authenticator.h"
  10. #include "device/fido/fido_constants.h"
  11. #include "device/fido/pin.h"
  12. #include "device/fido/public_key_credential_descriptor.h"
  13. namespace device {
  14. CredentialManagementHandler::CredentialManagementHandler(
  15. FidoDiscoveryFactory* fido_discovery_factory,
  16. const base::flat_set<FidoTransportProtocol>& supported_transports,
  17. ReadyCallback ready_callback,
  18. GetPINCallback get_pin_callback,
  19. FinishedCallback finished_callback)
  20. : FidoRequestHandlerBase(fido_discovery_factory, supported_transports),
  21. ready_callback_(std::move(ready_callback)),
  22. get_pin_callback_(std::move(get_pin_callback)),
  23. finished_callback_(std::move(finished_callback)) {
  24. Start();
  25. }
  26. CredentialManagementHandler::~CredentialManagementHandler() {
  27. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  28. }
  29. void CredentialManagementHandler::DispatchRequest(
  30. FidoAuthenticator* authenticator) {
  31. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  32. if (state_ != State::kWaitingForTouch) {
  33. return;
  34. }
  35. authenticator->GetTouch(base::BindOnce(&CredentialManagementHandler::OnTouch,
  36. weak_factory_.GetWeakPtr(),
  37. authenticator));
  38. }
  39. void CredentialManagementHandler::OnTouch(FidoAuthenticator* authenticator) {
  40. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  41. if (state_ != State::kWaitingForTouch) {
  42. return;
  43. }
  44. state_ = State::kGettingRetries;
  45. CancelActiveAuthenticators(authenticator->GetId());
  46. if (authenticator->SupportedProtocol() != ProtocolVersion::kCtap2 ||
  47. !authenticator->Options() ||
  48. !(authenticator->Options()->supports_credential_management ||
  49. authenticator->Options()->supports_credential_management_preview)) {
  50. state_ = State::kFinished;
  51. std::move(finished_callback_)
  52. .Run(CredentialManagementStatus::
  53. kAuthenticatorMissingCredentialManagement);
  54. return;
  55. }
  56. if (authenticator->Options()->client_pin_availability !=
  57. AuthenticatorSupportedOptions::ClientPinAvailability::
  58. kSupportedAndPinSet) {
  59. // The authenticator doesn't have a PIN/UV set up or doesn't support PINs.
  60. // We should implement in-flow PIN setting, but for now just tell the user
  61. // to set a PIN themselves.
  62. state_ = State::kFinished;
  63. std::move(finished_callback_).Run(CredentialManagementStatus::kNoPINSet);
  64. return;
  65. }
  66. if (authenticator->ForcePINChange()) {
  67. state_ = State::kFinished;
  68. std::move(finished_callback_)
  69. .Run(CredentialManagementStatus::kForcePINChange);
  70. return;
  71. }
  72. authenticator_ = authenticator;
  73. authenticator_->GetPinRetries(
  74. base::BindOnce(&CredentialManagementHandler::OnRetriesResponse,
  75. weak_factory_.GetWeakPtr()));
  76. }
  77. void CredentialManagementHandler::OnRetriesResponse(
  78. CtapDeviceResponseCode status,
  79. absl::optional<pin::RetriesResponse> response) {
  80. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  81. DCHECK_EQ(state_, State::kGettingRetries);
  82. if (status != CtapDeviceResponseCode::kSuccess) {
  83. state_ = State::kFinished;
  84. std::move(finished_callback_)
  85. .Run(CredentialManagementStatus::kAuthenticatorResponseInvalid);
  86. return;
  87. }
  88. if (response->retries == 0) {
  89. state_ = State::kFinished;
  90. std::move(finished_callback_)
  91. .Run(CredentialManagementStatus::kHardPINBlock);
  92. return;
  93. }
  94. state_ = State::kWaitingForPIN;
  95. get_pin_callback_.Run(
  96. {.min_pin_length = authenticator_->CurrentMinPINLength(),
  97. .pin_retries = response->retries,
  98. .supports_update_user_information =
  99. authenticator_->SupportsUpdateUserInformation()},
  100. base::BindOnce(&CredentialManagementHandler::OnHavePIN,
  101. weak_factory_.GetWeakPtr()));
  102. }
  103. void CredentialManagementHandler::OnHavePIN(std::string pin) {
  104. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  105. DCHECK_EQ(State::kWaitingForPIN, state_);
  106. if (authenticator_ == nullptr) {
  107. // Authenticator was detached. The request will already have been canceled
  108. // but this callback may have been waiting in a queue.
  109. return;
  110. }
  111. state_ = State::kGettingPINToken;
  112. authenticator_->GetPINToken(
  113. std::move(pin), {pin::Permissions::kCredentialManagement},
  114. /*rp_id=*/absl::nullopt,
  115. base::BindOnce(&CredentialManagementHandler::OnHavePINToken,
  116. weak_factory_.GetWeakPtr()));
  117. }
  118. void CredentialManagementHandler::OnHavePINToken(
  119. CtapDeviceResponseCode status,
  120. absl::optional<pin::TokenResponse> response) {
  121. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  122. DCHECK_EQ(state_, State::kGettingPINToken);
  123. if (status == CtapDeviceResponseCode::kCtap2ErrPinInvalid) {
  124. state_ = State::kGettingRetries;
  125. authenticator_->GetPinRetries(
  126. base::BindOnce(&CredentialManagementHandler::OnRetriesResponse,
  127. weak_factory_.GetWeakPtr()));
  128. return;
  129. }
  130. if (status != CtapDeviceResponseCode::kSuccess) {
  131. CredentialManagementStatus error;
  132. switch (status) {
  133. case CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked:
  134. error = CredentialManagementStatus::kSoftPINBlock;
  135. break;
  136. case CtapDeviceResponseCode::kCtap2ErrPinBlocked:
  137. error = CredentialManagementStatus::kHardPINBlock;
  138. break;
  139. default:
  140. error = CredentialManagementStatus::kAuthenticatorResponseInvalid;
  141. break;
  142. }
  143. state_ = State::kFinished;
  144. std::move(finished_callback_).Run(error);
  145. return;
  146. }
  147. state_ = State::kReady;
  148. pin_token_ = response;
  149. std::move(ready_callback_).Run();
  150. }
  151. void CredentialManagementHandler::GetCredentials(
  152. GetCredentialsCallback callback) {
  153. DCHECK(state_ == State::kReady && !get_credentials_callback_);
  154. if (!authenticator_) {
  155. // AuthenticatorRemoved() may have been called, but the observer would have
  156. // seen a FidoAuthenticatorRemoved() call.
  157. NOTREACHED();
  158. return;
  159. }
  160. get_credentials_callback_ = std::move(callback);
  161. state_ = State::kGettingCredentials;
  162. authenticator_->GetCredentialsMetadata(
  163. *pin_token_,
  164. base::BindOnce(&CredentialManagementHandler::OnCredentialsMetadata,
  165. weak_factory_.GetWeakPtr()));
  166. }
  167. static void OnDeleteCredential(
  168. CredentialManagementHandler::DeleteCredentialCallback callback,
  169. CtapDeviceResponseCode status,
  170. absl::optional<DeleteCredentialResponse> response) {
  171. std::move(callback).Run(status);
  172. }
  173. void CredentialManagementHandler::DeleteCredential(
  174. const PublicKeyCredentialDescriptor& credential_id,
  175. DeleteCredentialCallback callback) {
  176. DCHECK(state_ == State::kReady && !get_credentials_callback_);
  177. if (!authenticator_) {
  178. // AuthenticatorRemoved() may have been called, but the observer would have
  179. // seen a FidoAuthenticatorRemoved() call.
  180. NOTREACHED();
  181. return;
  182. }
  183. DCHECK(pin_token_);
  184. authenticator_->DeleteCredential(
  185. *pin_token_, credential_id,
  186. base::BindOnce(&OnDeleteCredential, std::move(callback)));
  187. }
  188. void CredentialManagementHandler::OnDeleteCredentials(
  189. std::vector<device::PublicKeyCredentialDescriptor> remaining_credential_ids,
  190. CredentialManagementHandler::DeleteCredentialCallback callback,
  191. CtapDeviceResponseCode status,
  192. absl::optional<DeleteCredentialResponse> response) {
  193. if (status != CtapDeviceResponseCode::kSuccess ||
  194. remaining_credential_ids.empty()) {
  195. std::move(callback).Run(status);
  196. return;
  197. }
  198. if (!authenticator_) {
  199. // |authenticator_| could have been removed during a bulk deletion. The
  200. // observer would have already gotten an AuthenticatorRemoved() call, so no
  201. // need to resolve |callback|.
  202. return;
  203. }
  204. device::PublicKeyCredentialDescriptor credential_id =
  205. std::move(remaining_credential_ids.back());
  206. remaining_credential_ids.pop_back();
  207. authenticator_->DeleteCredential(
  208. *pin_token_, credential_id,
  209. base::BindOnce(&CredentialManagementHandler::OnDeleteCredentials,
  210. weak_factory_.GetWeakPtr(),
  211. std::move(remaining_credential_ids), std::move(callback)));
  212. }
  213. void CredentialManagementHandler::DeleteCredentials(
  214. std::vector<device::PublicKeyCredentialDescriptor> credential_ids,
  215. DeleteCredentialCallback callback) {
  216. DCHECK(state_ == State::kReady && !get_credentials_callback_);
  217. if (!authenticator_) {
  218. // AuthenticatorRemoved() may have been called, but the observer would have
  219. // seen a FidoAuthenticatorRemoved() call.
  220. NOTREACHED();
  221. return;
  222. }
  223. DCHECK(pin_token_);
  224. if (credential_ids.empty()) {
  225. std::move(callback).Run(CtapDeviceResponseCode::kSuccess);
  226. return;
  227. }
  228. device::PublicKeyCredentialDescriptor credential_id =
  229. std::move(credential_ids.back());
  230. credential_ids.pop_back();
  231. authenticator_->DeleteCredential(
  232. *pin_token_, credential_id,
  233. base::BindOnce(&CredentialManagementHandler::OnDeleteCredentials,
  234. weak_factory_.GetWeakPtr(), std::move(credential_ids),
  235. std::move(callback)));
  236. }
  237. static void OnUpdateUserInformation(
  238. CredentialManagementHandler::UpdateUserInformationCallback callback,
  239. CtapDeviceResponseCode status,
  240. absl::optional<UpdateUserInformationResponse> response) {
  241. std::move(callback).Run(status);
  242. }
  243. void CredentialManagementHandler::UpdateUserInformation(
  244. const PublicKeyCredentialDescriptor& credential_id,
  245. const PublicKeyCredentialUserEntity& updated_user,
  246. UpdateUserInformationCallback callback) {
  247. DCHECK(state_ == State::kReady && !get_credentials_callback_);
  248. if (!authenticator_) {
  249. // AuthenticatorRemoved() may have been called, but the observer would have
  250. // seen a FidoAuthenticatorRemoved() call.
  251. NOTREACHED();
  252. return;
  253. }
  254. DCHECK(pin_token_);
  255. authenticator_->UpdateUserInformation(
  256. *pin_token_, credential_id, updated_user,
  257. base::BindOnce(&OnUpdateUserInformation, std::move(callback)));
  258. }
  259. void CredentialManagementHandler::OnCredentialsMetadata(
  260. CtapDeviceResponseCode status,
  261. absl::optional<CredentialsMetadataResponse> response) {
  262. if (status != CtapDeviceResponseCode::kSuccess) {
  263. state_ = State::kFinished;
  264. std::move(get_credentials_callback_)
  265. .Run(status, absl::nullopt, absl::nullopt);
  266. return;
  267. }
  268. authenticator_->EnumerateCredentials(
  269. *pin_token_,
  270. base::BindOnce(&CredentialManagementHandler::OnEnumerateCredentials,
  271. weak_factory_.GetWeakPtr(), std::move(*response)));
  272. }
  273. void CredentialManagementHandler::OnEnumerateCredentials(
  274. CredentialsMetadataResponse metadata_response,
  275. CtapDeviceResponseCode status,
  276. absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
  277. responses) {
  278. if (status != CtapDeviceResponseCode::kSuccess) {
  279. state_ = State::kFinished;
  280. std::move(get_credentials_callback_)
  281. .Run(status, absl::nullopt, absl::nullopt);
  282. return;
  283. }
  284. // Sort credentials by (RP ID, userId) ascending.
  285. for (auto& response : *responses) {
  286. std::sort(response.credentials.begin(), response.credentials.end(),
  287. [](const EnumerateCredentialsResponse& a,
  288. const EnumerateCredentialsResponse& b) {
  289. return a.user.id < b.user.id;
  290. });
  291. }
  292. std::sort(responses->begin(), responses->end(),
  293. [](const AggregatedEnumerateCredentialsResponse& a,
  294. const AggregatedEnumerateCredentialsResponse& b) {
  295. return a.rp.id < b.rp.id;
  296. });
  297. state_ = State::kReady;
  298. std::move(get_credentials_callback_)
  299. .Run(status, std::move(responses),
  300. metadata_response.num_estimated_remaining_credentials);
  301. }
  302. void CredentialManagementHandler::AuthenticatorRemoved(
  303. FidoDiscoveryBase* discovery,
  304. FidoAuthenticator* authenticator) {
  305. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  306. FidoRequestHandlerBase::AuthenticatorRemoved(discovery, authenticator);
  307. if (authenticator != authenticator_ || state_ == State::kFinished) {
  308. return;
  309. }
  310. authenticator_ = nullptr;
  311. state_ = State::kFinished;
  312. std::move(finished_callback_).Run(CredentialManagementStatus::kSuccess);
  313. }
  314. } // namespace device