blob: 8eea55e35d727468f51b289befc2c6b4a46f64c6 [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/create_vault_keyset_rpc_impl.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/strings/string_number_conversions.h>
#include <libhwsec-foundation/crypto/aes.h>
#include <libhwsec-foundation/crypto/hmac.h>
#include <libhwsec-foundation/crypto/secure_blob_util.h>
#include <libhwsec-foundation/status/status_chain_or.h>
#include "cryptohome/auth_factor/manager.h"
#include "cryptohome/auth_factor/protobuf.h"
#include "cryptohome/error/cryptohome_error.h"
#include "cryptohome/username.h"
using cryptohome::error::CryptohomeCryptoError;
using cryptohome::error::CryptohomeError;
using cryptohome::error::ErrorActionSet;
using cryptohome::error::PossibleAction;
using hwsec_foundation::CreateRandomBlob;
using hwsec_foundation::CreateSecureRandomBlob;
using hwsec_foundation::HmacSha256Kdf;
using hwsec_foundation::kAesBlockSize;
using hwsec_foundation::status::MakeStatus;
using hwsec_foundation::status::OkStatus;
namespace cryptohome {
namespace {
constexpr uint32_t kTpm12Family = 0x312E3200;
} // namespace
CreateVaultKeysetRpcImpl::CreateVaultKeysetRpcImpl(
KeysetManagement* keyset_management,
const hwsec::CryptohomeFrontend* hwsec,
AuthBlockUtility* auth_block_utility,
AuthFactorManager* auth_factor_manager,
AuthFactorDriverManager* auth_factor_driver_manager)
: keyset_management_(keyset_management),
hwsec_(hwsec),
auth_block_utility_(auth_block_utility),
auth_factor_manager_(auth_factor_manager),
auth_factor_driver_manager_(auth_factor_driver_manager) {}
bool CreateVaultKeysetRpcImpl::ClearKeyDataFromInitialKeyset(
const ObfuscatedUsername& obfuscated_username, bool disable_key_data) {
// Remove KeyBlobs from the VaultKeyset and resave, as the
// keyset_management flags need a valid KeyBlobs to operate.
// Used only for the testing of legacy keysets which were created
// KeyBlobs was not a concept.
if (disable_key_data) {
// Load the freshly created VaultKeyset.
std::unique_ptr<VaultKeyset> created_vk =
keyset_management_->GetVaultKeyset(obfuscated_username,
initial_vault_keyset_->GetLabel());
if (created_vk) {
created_vk->ClearKeyData();
if (!created_vk->Save(created_vk->GetSourceFile())) {
LOG(ERROR) << "Failed to clear key blobs from the vault_keyset.";
return false;
}
}
}
return true;
}
void CreateVaultKeysetRpcImpl::CreateVaultKeyset(
const user_data_auth::CreateVaultKeysetRequest& request,
AuthSession& auth_session,
StatusCallback on_done) {
// Preconditions:
CHECK_EQ(request.auth_session_id(), auth_session.serialized_token());
// At this point AuthSession should be authenticated as it needs
// FileSystemKeys to wrap the new credentials.
if (!auth_session.authorized_intents().contains(AuthIntent::kDecrypt)) {
std::move(on_done).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(
kLocCreateVaultKeysetRpcImplUnauthedInCreateVaultKeyset),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION));
return;
}
// Create and initialize AuthFactorType.
std::optional<AuthFactorType> type = AuthFactorTypeFromProto(request.type());
if (!type) {
std::move(on_done).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocCreateVaultKeysetRpcImplNoAuthFactorType),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT));
return;
}
// Determine the AuthBlock type to use for auth block creation.
const AuthFactorDriver& factor_driver =
auth_factor_driver_manager_->GetDriver(*type);
CryptoStatusOr<AuthBlockType> auth_block_type =
auth_block_utility_->SelectAuthBlockTypeForCreation(
factor_driver.block_types());
if (!auth_block_type.ok()) {
std::move(on_done).Run(
MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocCreateVaultKeysetRpcImplyInvalidBlockType),
user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE)
.Wrap(std::move(auth_block_type).status()));
return;
}
// Create and initialize AuthInput.
AuthInput auth_input = {
.user_input = brillo::SecureBlob(request.passkey()),
.locked_to_single_user = auth_block_utility_->GetLockedToSingleUser(),
.username = auth_session.username(),
.obfuscated_username = auth_session.obfuscated_username()};
AuthFactorMetadata auth_factor_metadata;
// Generate the reset seed for AuthInput.
if (factor_driver.NeedsResetSecret()) {
// When using VaultKeyset, reset is implemented via a seed that's shared
// among all of the user's VKs. Hence copy it from the previously loaded VK.
if (!initial_vault_keyset_) {
std::move(on_done).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocCreateVaultKeysetRpcImplNoInitialVaultKeyset),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE));
}
auth_input.reset_seed = initial_vault_keyset_->GetResetSeed();
auth_input.reset_salt = CreateRandomBlob(kAesBlockSize);
auth_input.reset_secret = HmacSha256Kdf(auth_input.reset_salt.value(),
auth_input.reset_seed.value());
LOG(INFO) << "Reset seed, to generate the reset_secret for the test PIN "
"VaultKeyset, "
"is obtained from password VaultKeyset with label: "
<< initial_vault_keyset_->GetLabel();
}
KeyData key_data;
key_data.set_label(request.key_label());
switch (*type) {
case AuthFactorType::kPassword:
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
break;
case AuthFactorType::kPin:
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
key_data.mutable_policy()->set_low_entropy_credential(true);
break;
case AuthFactorType::kKiosk:
key_data.set_type(KeyData::KEY_TYPE_KIOSK);
break;
case AuthFactorType::kSmartCard: {
key_data.set_type(KeyData::KEY_TYPE_CHALLENGE_RESPONSE);
// Assign public_key_spki_der to AuthInput and KeyData structures.
std::string challenge_spki;
if (!base::HexStringToString(request.public_key_spki_der(),
&challenge_spki)) {
LOG(ERROR) << "Challenge SPKI Public Key DER is not hex encoded.";
std::move(on_done).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocCreateVaultKeysetRpcImplKeyNotHexEncoded),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT));
return;
}
hwsec::StatusOr<uint32_t> family = hwsec_->GetFamily();
if (!family.ok()) {
LOG(ERROR) << "Failed to get the TPM family: " << family.status();
std::move(on_done).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocCreateVaultKeysetRpcImplFailedTPMFamily),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT));
return;
}
// TPM 1.2 only supports SHA1, otherwise default to entire list of
// SHA algorithms.
std::vector<SerializedChallengeSignatureAlgorithm> algos;
if (family.value() == kTpm12Family) {
algos.push_back(
SerializedChallengeSignatureAlgorithm::kRsassaPkcs1V15Sha1);
} else {
for (auto algo :
{SerializedChallengeSignatureAlgorithm::kRsassaPkcs1V15Sha512,
SerializedChallengeSignatureAlgorithm::kRsassaPkcs1V15Sha384,
SerializedChallengeSignatureAlgorithm::kRsassaPkcs1V15Sha256,
SerializedChallengeSignatureAlgorithm::kRsassaPkcs1V15Sha1}) {
algos.push_back(algo);
}
}
// Initializes a ChallengeCredentialAuthInput.
// Append challenge algorithm and key_delegate_dbus_service_name for
// testing to ChallengeCredentialAuthInput struct.
if (!request.key_delegate_dbus_service_name().empty() &&
!challenge_spki.empty()) {
auth_input.challenge_credential_auth_input =
ChallengeCredentialAuthInput{
.challenge_signature_algorithms = algos,
.dbus_service_name = request.key_delegate_dbus_service_name(),
};
auto* challenge_key = key_data.add_challenge_response_key();
challenge_key->set_public_key_spki_der(challenge_spki);
auth_factor_metadata.metadata = SmartCardMetadata{
.public_key_spki_der = brillo::BlobFromString(challenge_spki)};
}
break;
}
default:
LOG(ERROR) << "Unimplemented AuthFactorType.";
std::move(on_done).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(
kLocCreateVaultKeysetRpcImplUnspecifiedAuthFactorType),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT));
return;
}
auto create_callback = base::BindOnce(
&CreateVaultKeysetRpcImpl::CreateAndPersistVaultKeyset,
weak_factory_.GetWeakPtr(), key_data, request.disable_key_data(),
std::ref(auth_session), std::move(on_done));
auth_block_utility_->CreateKeyBlobsWithAuthBlock(
auth_block_type.value(), auth_input, auth_factor_metadata,
std::move(create_callback));
}
void CreateVaultKeysetRpcImpl::CreateAndPersistVaultKeyset(
const KeyData& key_data,
const bool disable_key_data,
AuthSession& auth_session,
StatusCallback on_done,
CryptohomeStatus callback_error,
std::unique_ptr<KeyBlobs> key_blobs,
std::unique_ptr<AuthBlockState> auth_state) {
// callback_error, key_blobs and auth_state are returned by
// AuthBlock::CreateCallback.
if (!callback_error.ok() || key_blobs == nullptr || auth_state == nullptr) {
if (callback_error.ok()) {
callback_error = MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCreateVaultKeysetRpcImplNullParamInCallbackInAddKeyset),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
CryptoError::CE_OTHER_CRYPTO,
user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_NOT_IMPLEMENTED);
}
LOG(ERROR) << "KeyBlobs derivation failed before adding keyset.";
std::move(on_done).Run(
MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(
kLocCreateVaultKeysetRpcImplCreateFailedInAddKeyset),
user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED)
.Wrap(std::move(callback_error)));
return;
}
const ObfuscatedUsername& username = auth_session.obfuscated_username();
CryptohomeStatus status = AddVaultKeyset(
key_data.label(), key_data, username, auth_session.file_system_keyset(),
!auth_factor_manager_->GetAuthFactorMap(username).HasFactorWithStorage(
AuthFactorStorageType::kVaultKeyset),
VaultKeysetIntent{.backup = false}, std::move(key_blobs),
std::move(auth_state));
if (!status.ok()) {
std::move(on_done).Run(
MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(
kLocCreateVaultKeysetRpcImplAddVaultKeysetFailed),
user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED)
.Wrap(std::move(status)));
return;
}
if (!ClearKeyDataFromInitialKeyset(auth_session.obfuscated_username(),
disable_key_data)) {
std::move(on_done).Run(MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(
kLocCreateVaultKeysetRpcImplClearKeyDataFromInitialKeysetFailed),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED));
return;
}
// Discard the already-loaded auth factor map for the user, so that on the
// next access the factors will be reloaded with the new keyset.
auth_factor_manager_->DiscardAuthFactorMap(
auth_session.obfuscated_username());
std::move(on_done).Run(OkStatus<CryptohomeError>());
}
CryptohomeStatus CreateVaultKeysetRpcImpl::AddVaultKeyset(
const std::string& key_label,
const KeyData& key_data,
const ObfuscatedUsername& obfuscated_username,
const FileSystemKeyset& file_system_keyset,
bool is_initial_keyset,
VaultKeysetIntent vk_backup_intent,
std::unique_ptr<KeyBlobs> key_blobs,
std::unique_ptr<AuthBlockState> auth_state) {
CHECK(key_blobs);
CHECK(auth_state);
if (is_initial_keyset) {
// TODO(b/229825202): Migrate KeysetManagement and wrap the returned error.
CryptohomeStatusOr<std::unique_ptr<VaultKeyset>> vk_status =
keyset_management_->AddInitialKeyset(
vk_backup_intent, obfuscated_username, key_data,
/*challenge_credentials_keyset_info*/ std::nullopt,
file_system_keyset, std::move(*key_blobs.get()),
std::move(auth_state));
if (!vk_status.ok()) {
initial_vault_keyset_ = nullptr;
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(
kLocCreateVaultKeysetRpcImplAddInitialFailedInAddKeyset),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState,
PossibleAction::kReboot}),
user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED);
}
LOG(INFO) << "CreateVaultKeysetRpcImpl: added initial keyset "
<< key_data.label() << ".";
initial_vault_keyset_ = std::move(vk_status).value();
} else {
if (!initial_vault_keyset_) {
// This shouldn't normally happen, but is possible if, e.g., the backup VK
// is corrupted and the authentication completed via USS.
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(kLocCreateVaultKeysetRpcImplNoVkInAddKeyset),
ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}),
user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED);
}
CryptohomeStatus status = keyset_management_->AddKeyset(
vk_backup_intent, obfuscated_username, key_label, key_data,
*initial_vault_keyset_.get(), std::move(*key_blobs.get()),
std::move(auth_state), true /*clobber*/);
if (!status.ok()) {
return MakeStatus<CryptohomeError>(
CRYPTOHOME_ERR_LOC(
kLocCreateVaultKeysetRpcImplAddFailedInAddKeyset))
.Wrap(std::move(status));
}
LOG(INFO) << "CreateVaultKeysetRpcImpl: added additional keyset "
<< key_label << ".";
}
return OkStatus<CryptohomeError>();
}
} // namespace cryptohome