| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cryptohome/auth_blocks/fp_service.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include <absl/cleanup/cleanup.h> |
| #include <base/functional/bind.h> |
| #include <cryptohome/proto_bindings/UserDataAuth.pb.h> |
| |
| #include "cryptohome/error/cryptohome_error.h" |
| #include "cryptohome/error/locations.h" |
| #include "cryptohome/fingerprint_manager.h" |
| #include "cryptohome/signalling.h" |
| #include "cryptohome/util/async_init.h" |
| |
| namespace cryptohome { |
| namespace { |
| |
| using cryptohome::error::CryptohomeError; |
| using cryptohome::error::ErrorActionSet; |
| using cryptohome::error::PossibleAction; |
| using cryptohome::error::PrimaryAction; |
| using hwsec_foundation::status::MakeStatus; |
| using hwsec_foundation::status::OkStatus; |
| |
| } // namespace |
| |
| FingerprintAuthBlockService::Token::Token() |
| : PreparedAuthFactorToken(AuthFactorType::kLegacyFingerprint, {}), |
| terminate_(*this) {} |
| |
| void FingerprintAuthBlockService::Token::AttachToService( |
| FingerprintAuthBlockService* service) { |
| service_ = service; |
| } |
| |
| CryptohomeStatus FingerprintAuthBlockService::Token::TerminateAuthFactor() { |
| if (service_) { |
| service_->Terminate(); |
| } |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| void FingerprintAuthBlockService::CheckSessionStartResult( |
| std::unique_ptr<Token> token, |
| PreparedAuthFactorToken::Consumer on_done, |
| bool success) { |
| // If Start fails, the token will be destroyed and considered no longer |
| // active. On success the token is still active and this will be cancelled. |
| absl::Cleanup clear_active_token = [this]() { active_token_ = nullptr; }; |
| |
| if (!success) { |
| CryptohomeStatus cryptohome_status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocFpServiceStartSessionFailure), |
| ErrorActionSet({PossibleAction::kRetry}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_FINGERPRINT_ERROR_INTERNAL); |
| std::move(on_done).Run(std::move(cryptohome_status)); |
| return; |
| } |
| |
| if (!fp_manager_) { |
| CryptohomeStatus status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocFpServiceCheckSessionStartCouldNotGetFpManager), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_FINGERPRINT_ERROR_INTERNAL); |
| std::move(on_done).Run(std::move(status)); |
| return; |
| } |
| |
| std::move(clear_active_token).Cancel(); |
| fp_manager_->SetSignalCallback(base::BindRepeating( |
| &FingerprintAuthBlockService::Capture, weak_factory_.GetWeakPtr())); |
| token->AttachToService(this); |
| std::move(on_done).Run(std::move(token)); |
| } |
| |
| FingerprintAuthBlockService::FingerprintAuthBlockService( |
| AsyncInitPtr<FingerprintManager> fp_manager, |
| AsyncInitPtr<SignallingInterface> signalling) |
| : fp_manager_(fp_manager), signalling_(signalling) {} |
| |
| std::unique_ptr<FingerprintAuthBlockService> |
| FingerprintAuthBlockService::MakeNullService() { |
| // Construct an instance of the service with a getter callbacks that always |
| // return null and a signal sender that does nothing. |
| return std::make_unique<FingerprintAuthBlockService>( |
| AsyncInitPtr<FingerprintManager>(nullptr), |
| AsyncInitPtr<SignallingInterface>(nullptr)); |
| } |
| |
| CryptohomeStatus FingerprintAuthBlockService::Verify() { |
| if (!fp_manager_) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocFpServiceVerifyCouldNotGetFpManager), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_FINGERPRINT_ERROR_INTERNAL); |
| } |
| |
| // If there is no active token then the service has not been started and the |
| // verification should fail. |
| if (!active_token_) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocFpServiceCheckResultNoAuthSession), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_FINGERPRINT_ERROR_INTERNAL); |
| } |
| |
| // Use the latest scan result to decide the response status. |
| switch (scan_result_) { |
| case FingerprintScanStatus::SUCCESS: |
| return OkStatus<CryptohomeError>(); |
| case FingerprintScanStatus::FAILED_RETRY_ALLOWED: |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocFpServiceCheckResultFailedYesRetry), |
| ErrorActionSet(PrimaryAction::kIncorrectAuth), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_FINGERPRINT_RETRY_REQUIRED); |
| case FingerprintScanStatus::FAILED_RETRY_NOT_ALLOWED: |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocFpServiceCheckResultFailedNoRetry), |
| ErrorActionSet(PrimaryAction::kFactorLockedOut), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_FINGERPRINT_DENIED); |
| } |
| } |
| |
| void FingerprintAuthBlockService::Start( |
| ObfuscatedUsername obfuscated_username, |
| PreparedAuthFactorToken::Consumer on_done) { |
| if (!fp_manager_) { |
| CryptohomeStatus status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocFpServiceStartScanCouldNotGetFpManager), |
| ErrorActionSet({PossibleAction::kRetry}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_ATTESTATION_NOT_READY); |
| std::move(on_done).Run(std::move(status)); |
| return; |
| } |
| |
| if (active_token_) { |
| CryptohomeStatus status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocFpServiceStartConcurrentSession), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_FINGERPRINT_DENIED); |
| std::move(on_done).Run(std::move(status)); |
| return; |
| } |
| |
| // Set up a callback with the manager to check the start session result. |
| auto token = std::make_unique<Token>(); |
| active_token_ = token.get(); |
| fp_manager_->StartAuthSessionAsyncForUser( |
| obfuscated_username, |
| base::BindOnce(&FingerprintAuthBlockService::CheckSessionStartResult, |
| weak_factory_.GetWeakPtr(), std::move(token), |
| std::move(on_done))); |
| } |
| |
| void FingerprintAuthBlockService::Terminate() { |
| active_token_ = nullptr; |
| scan_result_ = FingerprintScanStatus::FAILED_RETRY_NOT_ALLOWED; |
| EndAuthSession(); |
| } |
| |
| void FingerprintAuthBlockService::Capture(FingerprintScanStatus status) { |
| // If the session has been terminated, there will be no active token. In this |
| // case, no-op when the callback is triggered. |
| if (!active_token_) { |
| return; |
| } |
| scan_result_ = status; |
| user_data_auth::AuthScanResult outgoing_signal; |
| switch (status) { |
| case FingerprintScanStatus::SUCCESS: |
| outgoing_signal.set_fingerprint_result( |
| user_data_auth::FINGERPRINT_SCAN_RESULT_SUCCESS); |
| break; |
| case FingerprintScanStatus::FAILED_RETRY_ALLOWED: |
| outgoing_signal.set_fingerprint_result( |
| user_data_auth::FINGERPRINT_SCAN_RESULT_RETRY); |
| break; |
| case FingerprintScanStatus::FAILED_RETRY_NOT_ALLOWED: |
| outgoing_signal.set_fingerprint_result( |
| user_data_auth::FINGERPRINT_SCAN_RESULT_LOCKOUT); |
| break; |
| } |
| if (signalling_) { |
| signalling_->SendAuthScanResult(outgoing_signal); |
| } |
| } |
| |
| void FingerprintAuthBlockService::EndAuthSession() { |
| if (fp_manager_) { |
| fp_manager_->EndAuthSession(); |
| } |
| } |
| |
| FingerprintVerifier::FingerprintVerifier(FingerprintAuthBlockService* service) |
| : AsyncCredentialVerifier(AuthFactorType::kLegacyFingerprint, "", {}), |
| service_(service) {} |
| |
| void FingerprintVerifier::VerifyAsync(const AuthInput& unused, |
| StatusCallback callback) const { |
| return std::move(callback).Run(service_->Verify()); |
| } |
| |
| } // namespace cryptohome |