123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "device/fido/credential_management_handler.h"
- #include <utility>
- #include "base/bind.h"
- #include "base/check_op.h"
- #include "base/notreached.h"
- #include "device/fido/fido_authenticator.h"
- #include "device/fido/fido_constants.h"
- #include "device/fido/pin.h"
- #include "device/fido/public_key_credential_descriptor.h"
- namespace device {
- CredentialManagementHandler::CredentialManagementHandler(
- FidoDiscoveryFactory* fido_discovery_factory,
- const base::flat_set<FidoTransportProtocol>& supported_transports,
- ReadyCallback ready_callback,
- GetPINCallback get_pin_callback,
- FinishedCallback finished_callback)
- : FidoRequestHandlerBase(fido_discovery_factory, supported_transports),
- ready_callback_(std::move(ready_callback)),
- get_pin_callback_(std::move(get_pin_callback)),
- finished_callback_(std::move(finished_callback)) {
- Start();
- }
- CredentialManagementHandler::~CredentialManagementHandler() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- }
- void CredentialManagementHandler::DispatchRequest(
- FidoAuthenticator* authenticator) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (state_ != State::kWaitingForTouch) {
- return;
- }
- authenticator->GetTouch(base::BindOnce(&CredentialManagementHandler::OnTouch,
- weak_factory_.GetWeakPtr(),
- authenticator));
- }
- void CredentialManagementHandler::OnTouch(FidoAuthenticator* authenticator) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (state_ != State::kWaitingForTouch) {
- return;
- }
- state_ = State::kGettingRetries;
- CancelActiveAuthenticators(authenticator->GetId());
- if (authenticator->SupportedProtocol() != ProtocolVersion::kCtap2 ||
- !authenticator->Options() ||
- !(authenticator->Options()->supports_credential_management ||
- authenticator->Options()->supports_credential_management_preview)) {
- state_ = State::kFinished;
- std::move(finished_callback_)
- .Run(CredentialManagementStatus::
- kAuthenticatorMissingCredentialManagement);
- return;
- }
- if (authenticator->Options()->client_pin_availability !=
- AuthenticatorSupportedOptions::ClientPinAvailability::
- kSupportedAndPinSet) {
- // The authenticator doesn't have a PIN/UV set up or doesn't support PINs.
- // We should implement in-flow PIN setting, but for now just tell the user
- // to set a PIN themselves.
- state_ = State::kFinished;
- std::move(finished_callback_).Run(CredentialManagementStatus::kNoPINSet);
- return;
- }
- if (authenticator->ForcePINChange()) {
- state_ = State::kFinished;
- std::move(finished_callback_)
- .Run(CredentialManagementStatus::kForcePINChange);
- return;
- }
- authenticator_ = authenticator;
- authenticator_->GetPinRetries(
- base::BindOnce(&CredentialManagementHandler::OnRetriesResponse,
- weak_factory_.GetWeakPtr()));
- }
- void CredentialManagementHandler::OnRetriesResponse(
- CtapDeviceResponseCode status,
- absl::optional<pin::RetriesResponse> response) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(state_, State::kGettingRetries);
- if (status != CtapDeviceResponseCode::kSuccess) {
- state_ = State::kFinished;
- std::move(finished_callback_)
- .Run(CredentialManagementStatus::kAuthenticatorResponseInvalid);
- return;
- }
- if (response->retries == 0) {
- state_ = State::kFinished;
- std::move(finished_callback_)
- .Run(CredentialManagementStatus::kHardPINBlock);
- return;
- }
- state_ = State::kWaitingForPIN;
- get_pin_callback_.Run(
- {.min_pin_length = authenticator_->CurrentMinPINLength(),
- .pin_retries = response->retries,
- .supports_update_user_information =
- authenticator_->SupportsUpdateUserInformation()},
- base::BindOnce(&CredentialManagementHandler::OnHavePIN,
- weak_factory_.GetWeakPtr()));
- }
- void CredentialManagementHandler::OnHavePIN(std::string pin) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(State::kWaitingForPIN, state_);
- if (authenticator_ == nullptr) {
- // Authenticator was detached. The request will already have been canceled
- // but this callback may have been waiting in a queue.
- return;
- }
- state_ = State::kGettingPINToken;
- authenticator_->GetPINToken(
- std::move(pin), {pin::Permissions::kCredentialManagement},
- /*rp_id=*/absl::nullopt,
- base::BindOnce(&CredentialManagementHandler::OnHavePINToken,
- weak_factory_.GetWeakPtr()));
- }
- void CredentialManagementHandler::OnHavePINToken(
- CtapDeviceResponseCode status,
- absl::optional<pin::TokenResponse> response) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(state_, State::kGettingPINToken);
- if (status == CtapDeviceResponseCode::kCtap2ErrPinInvalid) {
- state_ = State::kGettingRetries;
- authenticator_->GetPinRetries(
- base::BindOnce(&CredentialManagementHandler::OnRetriesResponse,
- weak_factory_.GetWeakPtr()));
- return;
- }
- if (status != CtapDeviceResponseCode::kSuccess) {
- CredentialManagementStatus error;
- switch (status) {
- case CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked:
- error = CredentialManagementStatus::kSoftPINBlock;
- break;
- case CtapDeviceResponseCode::kCtap2ErrPinBlocked:
- error = CredentialManagementStatus::kHardPINBlock;
- break;
- default:
- error = CredentialManagementStatus::kAuthenticatorResponseInvalid;
- break;
- }
- state_ = State::kFinished;
- std::move(finished_callback_).Run(error);
- return;
- }
- state_ = State::kReady;
- pin_token_ = response;
- std::move(ready_callback_).Run();
- }
- void CredentialManagementHandler::GetCredentials(
- GetCredentialsCallback callback) {
- DCHECK(state_ == State::kReady && !get_credentials_callback_);
- if (!authenticator_) {
- // AuthenticatorRemoved() may have been called, but the observer would have
- // seen a FidoAuthenticatorRemoved() call.
- NOTREACHED();
- return;
- }
- get_credentials_callback_ = std::move(callback);
- state_ = State::kGettingCredentials;
- authenticator_->GetCredentialsMetadata(
- *pin_token_,
- base::BindOnce(&CredentialManagementHandler::OnCredentialsMetadata,
- weak_factory_.GetWeakPtr()));
- }
- static void OnDeleteCredential(
- CredentialManagementHandler::DeleteCredentialCallback callback,
- CtapDeviceResponseCode status,
- absl::optional<DeleteCredentialResponse> response) {
- std::move(callback).Run(status);
- }
- void CredentialManagementHandler::DeleteCredential(
- const PublicKeyCredentialDescriptor& credential_id,
- DeleteCredentialCallback callback) {
- DCHECK(state_ == State::kReady && !get_credentials_callback_);
- if (!authenticator_) {
- // AuthenticatorRemoved() may have been called, but the observer would have
- // seen a FidoAuthenticatorRemoved() call.
- NOTREACHED();
- return;
- }
- DCHECK(pin_token_);
- authenticator_->DeleteCredential(
- *pin_token_, credential_id,
- base::BindOnce(&OnDeleteCredential, std::move(callback)));
- }
- void CredentialManagementHandler::OnDeleteCredentials(
- std::vector<device::PublicKeyCredentialDescriptor> remaining_credential_ids,
- CredentialManagementHandler::DeleteCredentialCallback callback,
- CtapDeviceResponseCode status,
- absl::optional<DeleteCredentialResponse> response) {
- if (status != CtapDeviceResponseCode::kSuccess ||
- remaining_credential_ids.empty()) {
- std::move(callback).Run(status);
- return;
- }
- if (!authenticator_) {
- // |authenticator_| could have been removed during a bulk deletion. The
- // observer would have already gotten an AuthenticatorRemoved() call, so no
- // need to resolve |callback|.
- return;
- }
- device::PublicKeyCredentialDescriptor credential_id =
- std::move(remaining_credential_ids.back());
- remaining_credential_ids.pop_back();
- authenticator_->DeleteCredential(
- *pin_token_, credential_id,
- base::BindOnce(&CredentialManagementHandler::OnDeleteCredentials,
- weak_factory_.GetWeakPtr(),
- std::move(remaining_credential_ids), std::move(callback)));
- }
- void CredentialManagementHandler::DeleteCredentials(
- std::vector<device::PublicKeyCredentialDescriptor> credential_ids,
- DeleteCredentialCallback callback) {
- DCHECK(state_ == State::kReady && !get_credentials_callback_);
- if (!authenticator_) {
- // AuthenticatorRemoved() may have been called, but the observer would have
- // seen a FidoAuthenticatorRemoved() call.
- NOTREACHED();
- return;
- }
- DCHECK(pin_token_);
- if (credential_ids.empty()) {
- std::move(callback).Run(CtapDeviceResponseCode::kSuccess);
- return;
- }
- device::PublicKeyCredentialDescriptor credential_id =
- std::move(credential_ids.back());
- credential_ids.pop_back();
- authenticator_->DeleteCredential(
- *pin_token_, credential_id,
- base::BindOnce(&CredentialManagementHandler::OnDeleteCredentials,
- weak_factory_.GetWeakPtr(), std::move(credential_ids),
- std::move(callback)));
- }
- static void OnUpdateUserInformation(
- CredentialManagementHandler::UpdateUserInformationCallback callback,
- CtapDeviceResponseCode status,
- absl::optional<UpdateUserInformationResponse> response) {
- std::move(callback).Run(status);
- }
- void CredentialManagementHandler::UpdateUserInformation(
- const PublicKeyCredentialDescriptor& credential_id,
- const PublicKeyCredentialUserEntity& updated_user,
- UpdateUserInformationCallback callback) {
- DCHECK(state_ == State::kReady && !get_credentials_callback_);
- if (!authenticator_) {
- // AuthenticatorRemoved() may have been called, but the observer would have
- // seen a FidoAuthenticatorRemoved() call.
- NOTREACHED();
- return;
- }
- DCHECK(pin_token_);
- authenticator_->UpdateUserInformation(
- *pin_token_, credential_id, updated_user,
- base::BindOnce(&OnUpdateUserInformation, std::move(callback)));
- }
- void CredentialManagementHandler::OnCredentialsMetadata(
- CtapDeviceResponseCode status,
- absl::optional<CredentialsMetadataResponse> response) {
- if (status != CtapDeviceResponseCode::kSuccess) {
- state_ = State::kFinished;
- std::move(get_credentials_callback_)
- .Run(status, absl::nullopt, absl::nullopt);
- return;
- }
- authenticator_->EnumerateCredentials(
- *pin_token_,
- base::BindOnce(&CredentialManagementHandler::OnEnumerateCredentials,
- weak_factory_.GetWeakPtr(), std::move(*response)));
- }
- void CredentialManagementHandler::OnEnumerateCredentials(
- CredentialsMetadataResponse metadata_response,
- CtapDeviceResponseCode status,
- absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
- responses) {
- if (status != CtapDeviceResponseCode::kSuccess) {
- state_ = State::kFinished;
- std::move(get_credentials_callback_)
- .Run(status, absl::nullopt, absl::nullopt);
- return;
- }
- // Sort credentials by (RP ID, userId) ascending.
- for (auto& response : *responses) {
- std::sort(response.credentials.begin(), response.credentials.end(),
- [](const EnumerateCredentialsResponse& a,
- const EnumerateCredentialsResponse& b) {
- return a.user.id < b.user.id;
- });
- }
- std::sort(responses->begin(), responses->end(),
- [](const AggregatedEnumerateCredentialsResponse& a,
- const AggregatedEnumerateCredentialsResponse& b) {
- return a.rp.id < b.rp.id;
- });
- state_ = State::kReady;
- std::move(get_credentials_callback_)
- .Run(status, std::move(responses),
- metadata_response.num_estimated_remaining_credentials);
- }
- void CredentialManagementHandler::AuthenticatorRemoved(
- FidoDiscoveryBase* discovery,
- FidoAuthenticator* authenticator) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- FidoRequestHandlerBase::AuthenticatorRemoved(discovery, authenticator);
- if (authenticator != authenticator_ || state_ == State::kFinished) {
- return;
- }
- authenticator_ = nullptr;
- state_ = State::kFinished;
- std::move(finished_callback_).Run(CredentialManagementStatus::kSuccess);
- }
- } // namespace device
|