blob: 1bade992eba74740b98a961ff21812238f8b791d [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 "cryptohome/auth_blocks/async_challenge_credential_auth_block.h"
#include <memory>
#include <utility>
#include <variant>
#include <base/check.h>
#include <base/logging.h>
#include <base/notreached.h>
#include "cryptohome/auth_blocks/auth_block_state.h"
#include "cryptohome/auth_blocks/libscrypt_compat_auth_block.h"
#include "cryptohome/challenge_credentials/challenge_credentials_helper_impl.h"
#include "cryptohome/cryptohome_metrics.h"
#include "cryptohome/error/location_utils.h"
#include "cryptohome/key_objects.h"
using cryptohome::error::CryptohomeCryptoError;
using cryptohome::error::ErrorAction;
using cryptohome::error::ErrorActionSet;
using hwsec_foundation::status::MakeStatus;
using hwsec_foundation::status::OkStatus;
using hwsec_foundation::status::StatusChain;
namespace cryptohome {
AsyncChallengeCredentialAuthBlock::AsyncChallengeCredentialAuthBlock(
Tpm* tpm,
ChallengeCredentialsHelper* challenge_credentials_helper,
std::unique_ptr<KeyChallengeService> key_challenge_service,
const std::string& account_id)
: AuthBlock(kSignatureChallengeProtected),
tpm_(tpm),
challenge_credentials_helper_(challenge_credentials_helper),
key_challenge_service_(std::move(key_challenge_service)),
account_id_(account_id) {
CHECK(tpm_);
CHECK(challenge_credentials_helper_);
CHECK(key_challenge_service_);
}
void AsyncChallengeCredentialAuthBlock::Create(const AuthInput& auth_input,
CreateCallback callback) {
if (!key_challenge_service_) {
LOG(ERROR) << __func__ << ": No valid key challenge service.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(kLocAsyncChalCredAuthBlockNoKeyServiceInCreate),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO),
nullptr, nullptr);
return;
}
if (!auth_input.obfuscated_username.has_value()) {
LOG(ERROR) << __func__ << ": No valid obfuscated username.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(kLocAsyncChalCredAuthBlockNoInputUserInCreate),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO),
nullptr, nullptr);
return;
}
if (!auth_input.challenge_credential_auth_input.has_value()) {
LOG(ERROR) << __func__ << ": No valid challenge credential auth input.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(kLocAsyncChalCredAuthBlockNoInputAuthInCreate),
ErrorActionSet(
{ErrorAction::kDevCheckUnexpectedState, ErrorAction::kAuth}),
CryptoError::CE_OTHER_CRYPTO),
nullptr, nullptr);
return;
}
if (auth_input.challenge_credential_auth_input.value()
.challenge_signature_algorithms.empty()) {
LOG(ERROR) << __func__ << ": No valid challenge signature algorithms.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(kLocAsyncChalCredAuthBlockNoInputAlgInCreate),
ErrorActionSet(
{ErrorAction::kDevCheckUnexpectedState, ErrorAction::kAuth}),
CryptoError::CE_OTHER_CRYPTO),
nullptr, nullptr);
return;
}
structure::ChallengePublicKeyInfo public_key_info{
.public_key_spki_der = auth_input.challenge_credential_auth_input.value()
.public_key_spki_der,
.signature_algorithm = auth_input.challenge_credential_auth_input.value()
.challenge_signature_algorithms,
};
const std::string& obfuscated_username =
auth_input.obfuscated_username.value();
challenge_credentials_helper_->GenerateNew(
std::move(account_id_), std::move(public_key_info), obfuscated_username,
std::move(key_challenge_service_),
base::BindOnce(&AsyncChallengeCredentialAuthBlock::CreateContinue,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void AsyncChallengeCredentialAuthBlock::CreateContinue(
CreateCallback callback,
std::unique_ptr<structure::SignatureChallengeInfo> signature_challenge_info,
std::unique_ptr<brillo::SecureBlob> passkey) {
if (!passkey) {
// TODO(b/229569484): Make ChallengeCredentialHelper pass the error in.
LOG(ERROR) << __func__ << ": Failed to obtain challenge-response passkey.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocAsyncChalCredAuthBlockServiceGenerateFailedInCreate),
ErrorActionSet({ErrorAction::kRetry, ErrorAction::kAuth,
ErrorAction::kReboot}),
CryptoError::CE_OTHER_CRYPTO),
nullptr, nullptr);
return;
}
// We only need passkey for the AuthInput.
AuthInput auth_input = {.user_input = std::move(*passkey)};
auto key_blobs = std::make_unique<KeyBlobs>();
auto scrypt_auth_state = std::make_unique<AuthBlockState>();
LibScryptCompatAuthBlock scrypt_auth_block;
CryptoStatus error = scrypt_auth_block.Create(
auth_input, scrypt_auth_state.get(), key_blobs.get());
if (!error.ok()) {
LOG(ERROR) << __func__
<< "scrypt creation failed for challenge credential.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocAsyncChalCredAuthBlockCannotCreateScryptInCreate))
.Wrap(std::move(error)),
nullptr, nullptr);
return;
}
if (auto* scrypt_state = std::get_if<LibScryptCompatAuthBlockState>(
&scrypt_auth_state->state)) {
ChallengeCredentialAuthBlockState cc_state = {
.scrypt_state = std::move(*scrypt_state),
.keyset_challenge_info = std::move(*signature_challenge_info),
};
auto auth_block_state = std::make_unique<AuthBlockState>(
AuthBlockState{.state = std::move(cc_state)});
std::move(callback).Run(OkStatus<CryptohomeCryptoError>(),
std::move(key_blobs), std::move(auth_block_state));
} else {
// This should never happen, but handling it anyway on the safe side.
NOTREACHED() << "scrypt derivation failed for challenge credential.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocAsyncChalCredAuthBlockScryptDerivationFailedInCreate),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO),
nullptr, nullptr);
}
}
void AsyncChallengeCredentialAuthBlock::Derive(const AuthInput& auth_input,
const AuthBlockState& state,
DeriveCallback callback) {
if (!key_challenge_service_) {
LOG(ERROR) << __func__ << ": No valid key challenge service.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(kLocAsyncChalCredAuthBlockNoKeyServiceInDerive),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO),
nullptr);
return;
}
const ChallengeCredentialAuthBlockState* cc_state =
std::get_if<ChallengeCredentialAuthBlockState>(&state.state);
if (cc_state == nullptr) {
LOG(ERROR) << __func__
<< "Invalid state for challenge credential AuthBlock.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocAsyncChalCredAuthBlockInvalidBlockStateInDerive),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_FATAL),
nullptr);
return;
}
if (!cc_state->keyset_challenge_info.has_value()) {
LOG(ERROR)
<< __func__
<< "No signature challenge info in challenge credential AuthBlock.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocAsyncChalCredAuthBlockNoChallengeInfoInDerive),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO),
nullptr);
return;
}
const structure::SignatureChallengeInfo& keyset_challenge_info =
cc_state->keyset_challenge_info.value();
if (!keyset_challenge_info.salt_signature_algorithm.has_value()) {
LOG(ERROR)
<< __func__
<< "No signature algorithm info in challenge credential AuthBlock.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocAsyncChalCredAuthBlockNoAlgorithmInfoInDerive),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO),
nullptr);
return;
}
structure::ChallengePublicKeyInfo public_key_info{
.public_key_spki_der = keyset_challenge_info.public_key_spki_der,
.signature_algorithm =
{keyset_challenge_info.salt_signature_algorithm.value()},
};
AuthBlockState scrypt_state = {.state = cc_state->scrypt_state};
challenge_credentials_helper_->Decrypt(
std::move(account_id_), std::move(public_key_info),
cc_state->keyset_challenge_info.value(),
auth_input.locked_to_single_user.value_or(false),
std::move(key_challenge_service_),
base::BindOnce(&AsyncChallengeCredentialAuthBlock::DeriveContinue,
weak_factory_.GetWeakPtr(), std::move(callback),
std::move(scrypt_state)));
return;
}
void AsyncChallengeCredentialAuthBlock::DeriveContinue(
DeriveCallback callback,
const AuthBlockState& scrypt_state,
std::unique_ptr<brillo::SecureBlob> passkey) {
if (!passkey) {
LOG(ERROR) << __func__ << ": Failed to obtain challenge-response passkey.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocAsyncChalCredAuthBlockServiceDeriveFailedInDerive),
ErrorActionSet({ErrorAction::kRetry, ErrorAction::kAuth,
ErrorAction::kReboot, ErrorAction::kIncorrectAuth}),
CryptoError::CE_OTHER_CRYPTO),
nullptr);
return;
}
// We only need passkey for the LibScryptCompatAuthBlock AuthInput.
AuthInput auth_input = {.user_input = std::move(*passkey)};
LibScryptCompatAuthBlock scrypt_auth_block;
auto key_blobs = std::make_unique<KeyBlobs>();
CryptoStatus error =
scrypt_auth_block.Derive(auth_input, scrypt_state, key_blobs.get());
if (!error.ok()) {
LOG(ERROR) << __func__
<< "scrypt derivation failed for challenge credential.";
std::move(callback).Run(
MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocAsyncChalCredAuthBlockScryptDeriveFailedInDerive))
.Wrap(std::move(error)),
nullptr);
return;
}
std::move(callback).Run(OkStatus<CryptohomeCryptoError>(),
std::move(key_blobs));
return;
}
} // namespace cryptohome