blob: b8c6ad043d3590c3eb5098824f1b600014b10860 [file] [log] [blame]
// Copyright 2023 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_factor/types/fingerprint.h"
#include <cstdint>
#include <limits>
#include <map>
#include <utility>
#include <vector>
#include <base/time/time.h>
#include <libhwsec/frontend/cryptohome/frontend.h>
#include <libhwsec-foundation/crypto/secure_blob_util.h>
#include <libhwsec-foundation/status/status_chain.h>
#include <libhwsec-foundation/status/status_chain_macros.h>
#include "cryptohome/auth_blocks/biometrics_auth_block_service.h"
#include "cryptohome/auth_blocks/fingerprint_auth_block.h"
#include "cryptohome/auth_blocks/prepare_token.h"
#include "cryptohome/auth_factor/label_arity.h"
#include "cryptohome/error/action.h"
#include "cryptohome/error/cryptohome_error.h"
#include "cryptohome/error/cryptohome_tpm_error.h"
#include "cryptohome/error/locations.h"
#include "cryptohome/flatbuffer_schemas/auth_factor.h"
#include "cryptohome/username.h"
namespace cryptohome {
namespace {
using ::cryptohome::error::CryptohomeCryptoError;
using ::cryptohome::error::CryptohomeError;
using ::cryptohome::error::CryptohomeTPMError;
using ::cryptohome::error::ErrorActionSet;
using ::cryptohome::error::PossibleAction;
using ::hwsec::PinWeaverManagerFrontend;
using ::hwsec::PinWeaverManagerFrontend::AuthChannel::kFingerprintAuthChannel;
using ::hwsec_foundation::status::MakeStatus;
using ::hwsec_foundation::status::OkStatus;
constexpr uint32_t kInfiniteDelay = std::numeric_limits<uint32_t>::max();
constexpr size_t kResetSecretSize = 32;
constexpr struct {
uint32_t attempts;
uint32_t delay;
} kDefaultDelaySchedule[] = {
{5, kInfiniteDelay},
};
constexpr base::TimeDelta kExpirationLockout = base::Days(1);
std::vector<hwsec::OperationPolicySetting> GetValidPoliciesOfUser(
const ObfuscatedUsername& obfuscated_username) {
return std::vector<hwsec::OperationPolicySetting>{
hwsec::OperationPolicySetting{
.device_config_settings =
hwsec::DeviceConfigSettings{
.current_user =
hwsec::DeviceConfigSettings::CurrentUserSetting{
.username = std::nullopt,
},
},
},
hwsec::OperationPolicySetting{
.device_config_settings =
hwsec::DeviceConfigSettings{
.current_user =
hwsec::DeviceConfigSettings::CurrentUserSetting{
.username = *obfuscated_username,
},
},
},
};
}
} // namespace
bool FingerprintAuthFactorDriver::IsSupportedByHardware() const {
return FingerprintAuthBlock::IsSupported(*crypto_, bio_service_).ok();
}
AuthFactorDriver::PrepareRequirement
FingerprintAuthFactorDriver::GetPrepareRequirement(
AuthFactorPreparePurpose purpose) const {
switch (purpose) {
case AuthFactorPreparePurpose::kPrepareAddAuthFactor:
return PrepareRequirement::kOnce;
case AuthFactorPreparePurpose::kPrepareAuthenticateAuthFactor:
return PrepareRequirement::kEach;
}
}
void FingerprintAuthFactorDriver::PrepareForAdd(
const PrepareInput& prepare_input,
PreparedAuthFactorToken::Consumer callback) {
if (!bio_service_) {
std::move(callback).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFpPrepareForAddNoService),
ErrorActionSet(
{PossibleAction::kDevCheckUnexpectedState, PossibleAction::kAuth}),
user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_INVALID_ARGUMENT));
return;
}
// reset_secret here represents the existing/created rate limiter leaf's reset
// secret. The same value will be used as the reset secret for the actual
// fingerprint credential leaf. It usually never needs to be reset as
// its authentication shouldn't never fail, but we still need to be able to
// reset it when it's locked.
if (!prepare_input.rate_limiter_label.has_value() ||
!prepare_input.reset_secret.has_value()) {
std::move(callback).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFpNoResetSecretInPrepareAdd),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT));
return;
}
bio_service_->GetNonce(base::BindOnce(
&FingerprintAuthFactorDriver::PrepareForAddOnGetNonce,
weak_factory_.GetWeakPtr(), std::move(callback), prepare_input));
}
void FingerprintAuthFactorDriver::PrepareForAddOnGetNonce(
PreparedAuthFactorToken::Consumer callback,
const PrepareInput& prepare_input,
std::optional<brillo::Blob> nonce) {
CHECK(bio_service_);
CHECK(prepare_input.rate_limiter_label.has_value());
CHECK(prepare_input.reset_secret.has_value());
if (!nonce.has_value()) {
std::move(callback).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFpPrepareAddGetNonceFailed),
ErrorActionSet({PossibleAction::kReboot}),
user_data_auth::CRYPTOHOME_ERROR_FINGERPRINT_ERROR_INTERNAL));
return;
}
hwsec::StatusOr<PinWeaverManagerFrontend::StartBiometricsAuthReply> reply =
crypto_->GetPinWeaverManager()->StartBiometricsAuth(
kFingerprintAuthChannel, *prepare_input.rate_limiter_label,
std::move(*nonce));
if (!reply.ok()) {
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFpPrepareAddStartBioAuthFailed))
.Wrap(
MakeStatus<CryptohomeTPMError>(std::move(reply).err_status())));
return;
}
hwsec::Status reset_status = crypto_->GetPinWeaverManager()->ResetCredential(
*prepare_input.rate_limiter_label, *prepare_input.reset_secret,
PinWeaverManagerFrontend::ResetType::kWrongAttempts);
if (!reset_status.ok()) {
// TODO(b/275027852): Report metrics because we silently fail here.
LOG(WARNING) << "Failed to reset rate-limiter during PrepareForAdd. This "
"doesn't block the creation but shouldn't normally happen.";
}
BiometricsAuthBlockService::OperationInput input{
.nonce = std::move(reply->server_nonce),
.encrypted_label_seed = std::move(reply->encrypted_he_secret),
.iv = std::move(reply->iv),
};
bio_service_->StartEnrollSession(type(), std::move(input),
std::move(callback));
}
void FingerprintAuthFactorDriver::PrepareForAuthenticate(
const PrepareInput& prepare_input,
PreparedAuthFactorToken::Consumer callback) {
if (!bio_service_) {
std::move(callback).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFpPrepareForAuthNoService),
ErrorActionSet(
{PossibleAction::kDevCheckUnexpectedState, PossibleAction::kAuth}),
user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_INVALID_ARGUMENT));
return;
}
if (!prepare_input.rate_limiter_label.has_value()) {
std::move(callback).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFpNoResetSecretInPrepareAuth),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT));
return;
}
bio_service_->GetNonce(base::BindOnce(
&FingerprintAuthFactorDriver::PrepareForAuthOnGetNonce,
weak_factory_.GetWeakPtr(), std::move(callback), prepare_input));
}
void FingerprintAuthFactorDriver::PrepareForAuthOnGetNonce(
PreparedAuthFactorToken::Consumer callback,
const PrepareInput& prepare_input,
std::optional<brillo::Blob> nonce) {
CHECK(bio_service_);
CHECK(prepare_input.rate_limiter_label.has_value());
if (!nonce.has_value()) {
std::move(callback).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFpPrepareAuthGetNonceFailed),
ErrorActionSet({PossibleAction::kReboot}),
user_data_auth::CRYPTOHOME_ERROR_FINGERPRINT_ERROR_INTERNAL));
return;
}
hwsec::StatusOr<PinWeaverManagerFrontend::StartBiometricsAuthReply> reply =
crypto_->GetPinWeaverManager()->StartBiometricsAuth(
kFingerprintAuthChannel, *prepare_input.rate_limiter_label,
std::move(*nonce));
if (!reply.ok()) {
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFpPrepareAuthStartBioAuthFailed))
.Wrap(
MakeStatus<CryptohomeTPMError>(std::move(reply).err_status())));
return;
}
BiometricsAuthBlockService::OperationInput input{
.nonce = std::move(reply->server_nonce),
.encrypted_label_seed = std::move(reply->encrypted_he_secret),
.iv = std::move(reply->iv),
};
bio_service_->StartAuthenticateSession(type(), prepare_input.username,
std::move(input), std::move(callback));
}
bool FingerprintAuthFactorDriver::NeedsResetSecret() const {
return false;
}
bool FingerprintAuthFactorDriver::NeedsRateLimiter() const {
return true;
}
CryptohomeStatus FingerprintAuthFactorDriver::TryCreateRateLimiter(
const ObfuscatedUsername& username, DecryptedUss& decrypted_uss) {
std::optional<uint64_t> rate_limiter_label =
decrypted_uss.encrypted().fingerprint_rate_limiter_id();
if (rate_limiter_label.has_value()) {
return OkStatus<CryptohomeError>();
}
auto reset_secret =
hwsec_foundation::CreateSecureRandomBlob(kResetSecretSize);
std::vector<hwsec::OperationPolicySetting> policies =
GetValidPoliciesOfUser(username);
std::map<uint32_t, uint32_t> delay_sched;
for (const auto& entry : kDefaultDelaySchedule) {
delay_sched[entry.attempts] = entry.delay;
}
hwsec::StatusOr<uint64_t> result =
crypto_->GetPinWeaverManager()->InsertRateLimiter(
kFingerprintAuthChannel, policies, reset_secret, delay_sched,
kExpirationLockout.InSeconds());
if (!result.ok()) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocAuthFactorFpTryCreateRateLimiterInsertFailed))
.Wrap(MakeStatus<error::CryptohomeTPMError>(
std::move(result).err_status()));
}
uint64_t& label = result.value();
// Attempt to populate the USS with the values.
{
auto transaction = decrypted_uss.StartTransaction();
RETURN_IF_ERROR(transaction.InitializeFingerprintRateLimiterId(label));
RETURN_IF_ERROR(transaction.InsertRateLimiterResetSecret(
type(), std::move(reset_secret)));
RETURN_IF_ERROR(std::move(transaction).Commit());
}
return OkStatus<CryptohomeError>();
}
bool FingerprintAuthFactorDriver::IsDelaySupported() const {
return true;
}
CryptohomeStatusOr<base::TimeDelta> FingerprintAuthFactorDriver::GetFactorDelay(
const ObfuscatedUsername& username, const AuthFactor& factor) const {
// Do all the error checks to make sure the input is useful.
if (factor.type() != type()) {
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(
kLocAuthFactorFingerprintGetFactorDelayWrongFactorType),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
ASSIGN_OR_RETURN(
const EncryptedUss* uss, uss_manager_->LoadEncrypted(username),
_.WithStatus<CryptohomeError>(CRYPTOHOME_ERR_LOC(
kLocAuthFactorFingerprintGetFactorDelayLoadMetadataFailed)));
if (!uss->fingerprint_rate_limiter_id()) {
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFingerprintGetFactorDelayNoLabel),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
// Try and extract the delay from pinweaver manager..
auto delay_in_seconds = crypto_->GetPinWeaverManager()->GetDelayInSeconds(
*uss->fingerprint_rate_limiter_id());
if (!delay_in_seconds.ok()) {
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(
kLocAuthFactorFingerprintGetFactorDelayReadFailed))
.Wrap(MakeStatus<error::CryptohomeTPMError>(
std::move(delay_in_seconds).err_status()));
}
// Return the extracted time, handling the max value case.
if (*delay_in_seconds == std::numeric_limits<uint32_t>::max()) {
return base::TimeDelta::Max();
} else {
return base::Seconds(*delay_in_seconds);
}
}
bool FingerprintAuthFactorDriver::IsExpirationSupported() const {
return true;
}
CryptohomeStatusOr<base::TimeDelta>
FingerprintAuthFactorDriver::GetTimeUntilExpiration(
const ObfuscatedUsername& username, const AuthFactor& factor) const {
// Do all the error checks to make sure the input is useful.
if (factor.type() != type()) {
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFingerprintIsExpiredWrongFactorType),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
ASSIGN_OR_RETURN(const EncryptedUss* uss,
uss_manager_->LoadEncrypted(username),
_.WithStatus<CryptohomeError>(CRYPTOHOME_ERR_LOC(
kLocAuthFactorFingerprintIsExpiredLoadMetadataFailed)));
if (!uss->fingerprint_rate_limiter_id()) {
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFingerprintIsExpiredNoLabel),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
// Try and extract the expiration from pinweaver manager.
hwsec::StatusOr<std::optional<uint32_t>> time_until_expiration_in_seconds =
crypto_->GetPinWeaverManager()->GetExpirationInSeconds(
*uss->fingerprint_rate_limiter_id());
if (!time_until_expiration_in_seconds.ok()) {
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFingerprintIsExpiredReadFailed))
.Wrap(MakeStatus<error::CryptohomeTPMError>(
std::move(time_until_expiration_in_seconds).err_status()));
}
// Currently fingerprint auth factors should always have finite expiraition.
if (!time_until_expiration_in_seconds->has_value()) {
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocAuthFactorFingerprintIsExpiredNoExpiration),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE);
}
return base::Seconds(time_until_expiration_in_seconds->value());
}
AuthFactorLabelArity FingerprintAuthFactorDriver::GetAuthFactorLabelArity()
const {
return AuthFactorLabelArity::kMultiple;
}
std::optional<user_data_auth::AuthFactor>
FingerprintAuthFactorDriver::TypedConvertToProto(
const CommonMetadata& common,
const FingerprintMetadata& typed_metadata) const {
user_data_auth::AuthFactor proto;
proto.set_type(user_data_auth::AUTH_FACTOR_TYPE_FINGERPRINT);
user_data_auth::FingerprintMetadata& fingerprint_metadata =
*proto.mutable_fingerprint_metadata();
if (typed_metadata.was_migrated.has_value()) {
fingerprint_metadata.set_was_migrated(*typed_metadata.was_migrated);
}
return proto;
}
} // namespace cryptohome