blob: 0a838c8302af0a552b7d4d796cf02a0c5c7aa087 [file] [log] [blame] [edit]
// 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