blob: 7708aadc4cb544afdfc5d8c6d9438a0f96ead475 [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/cryptohome_recovery_auth_block.h"
#include <memory>
#include <utility>
#include <variant>
#include <base/check.h>
#include <base/logging.h>
#include <brillo/secure_blob.h>
#include <libhwsec-foundation/crypto/aes.h>
#include <libhwsec-foundation/crypto/hkdf.h>
#include <libhwsec-foundation/crypto/scrypt.h>
#include <libhwsec-foundation/crypto/secure_blob_util.h>
#include "cryptohome/auth_blocks/auth_block_state.h"
#include "cryptohome/auth_blocks/revocation.h"
#include "cryptohome/crypto_error.h"
#include "cryptohome/cryptohome_metrics.h"
#include "cryptohome/cryptorecovery/recovery_crypto_fake_tpm_backend_impl.h"
#include "cryptohome/cryptorecovery/recovery_crypto_hsm_cbor_serialization.h"
#include "cryptohome/cryptorecovery/recovery_crypto_impl.h"
#include "cryptohome/error/location_utils.h"
#include "cryptohome/tpm.h"
using cryptohome::cryptorecovery::HsmPayload;
using cryptohome::cryptorecovery::HsmResponsePlainText;
using cryptohome::cryptorecovery::OnboardingMetadata;
using cryptohome::cryptorecovery::RecoveryCryptoImpl;
using cryptohome::error::CryptohomeCryptoError;
using cryptohome::error::ErrorAction;
using cryptohome::error::ErrorActionSet;
using hwsec_foundation::CreateSecureRandomBlob;
using hwsec_foundation::DeriveSecretsScrypt;
using hwsec_foundation::kAesBlockSize;
using hwsec_foundation::kDefaultAesKeySize;
using hwsec_foundation::status::MakeStatus;
using hwsec_foundation::status::OkStatus;
using hwsec_foundation::status::StatusChain;
namespace cryptohome {
namespace {
cryptorecovery::RecoveryCryptoTpmBackend* GetRecoveryCryptoTpmBackend(
Tpm* tpm) {
cryptorecovery::RecoveryCryptoTpmBackend* tpm_backend =
tpm->GetRecoveryCryptoBackend();
if (!tpm_backend) {
LOG(ERROR) << "RecoveryCryptoTpmBackend is null";
return nullptr;
}
return tpm_backend;
}
} // namespace
CryptohomeRecoveryAuthBlock::CryptohomeRecoveryAuthBlock(Tpm* tpm)
: CryptohomeRecoveryAuthBlock(tpm, nullptr) {}
CryptohomeRecoveryAuthBlock::CryptohomeRecoveryAuthBlock(
Tpm* tpm, LECredentialManager* le_manager)
: SyncAuthBlock(/*derivation_type=*/kCryptohomeRecovery),
tpm_(tpm),
le_manager_(le_manager) {
DCHECK(tpm_);
}
CryptoStatus CryptohomeRecoveryAuthBlock::Create(
const AuthInput& auth_input,
AuthBlockState* auth_block_state,
KeyBlobs* key_blobs) {
DCHECK(key_blobs);
DCHECK(auth_input.cryptohome_recovery_auth_input.has_value());
auto cryptohome_recovery_auth_input =
auth_input.cryptohome_recovery_auth_input.value();
DCHECK(cryptohome_recovery_auth_input.mediator_pub_key.has_value());
brillo::SecureBlob salt =
CreateSecureRandomBlob(CRYPTOHOME_DEFAULT_KEY_SALT_SIZE);
const brillo::SecureBlob& mediator_pub_key =
cryptohome_recovery_auth_input.mediator_pub_key.value();
std::unique_ptr<RecoveryCryptoImpl> recovery =
RecoveryCryptoImpl::Create(GetRecoveryCryptoTpmBackend(tpm_));
if (!recovery) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockCantCreateRecoveryInCreate),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState,
ErrorAction::kReboot, ErrorAction::kAuth}),
CryptoError::CE_OTHER_CRYPTO);
}
// Generates HSM payload that would be persisted on a chromebook.
HsmPayload hsm_payload;
brillo::SecureBlob rsa_pub_key;
brillo::SecureBlob destination_share;
brillo::SecureBlob recovery_key;
brillo::SecureBlob channel_pub_key;
brillo::SecureBlob channel_priv_key;
// TODO(b/184924482): set values in onboarding_metadata.
OnboardingMetadata onboarding_metadata;
if (!recovery->GenerateHsmPayload(mediator_pub_key, onboarding_metadata,
&hsm_payload, &rsa_pub_key,
&destination_share, &recovery_key,
&channel_pub_key, &channel_priv_key)) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockGenerateHSMPayloadFailedInCreate),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState,
ErrorAction::kReboot, ErrorAction::kAuth}),
CryptoError::CE_OTHER_CRYPTO);
}
// Generate wrapped keys from the recovery key.
// TODO(b/184924482): change wrapped keys to USS key after USS is implemented.
brillo::SecureBlob aes_skey(kDefaultAesKeySize);
brillo::SecureBlob vkk_iv(kAesBlockSize);
if (!DeriveSecretsScrypt(recovery_key, salt, {&aes_skey, &vkk_iv})) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockScryptDeriveFailedInCreate),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState,
ErrorAction::kReboot, ErrorAction::kAuth}),
CryptoError::CE_OTHER_FATAL);
}
key_blobs->vkk_key = aes_skey;
key_blobs->vkk_iv = vkk_iv;
key_blobs->chaps_iv = vkk_iv;
// Save generated data in auth_block_state.
CryptohomeRecoveryAuthBlockState auth_state;
brillo::SecureBlob hsm_payload_cbor;
if (!SerializeHsmPayloadToCbor(hsm_payload, &hsm_payload_cbor)) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockCborConvFailedInCreate),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState,
ErrorAction::kReboot, ErrorAction::kAuth}),
CryptoError::CE_OTHER_FATAL);
}
auth_state.hsm_payload = hsm_payload_cbor;
// TODO(b/184924482): wrap the destination share with TPM.
auth_state.plaintext_destination_share = destination_share;
// TODO(b/196192089): store encrypted keys.
auth_state.channel_priv_key = channel_priv_key;
auth_state.channel_pub_key = channel_pub_key;
auth_state.salt = std::move(salt);
*auth_block_state = AuthBlockState{.state = std::move(auth_state)};
if (revocation::IsRevocationSupported(tpm_)) {
DCHECK(le_manager_);
RevocationState revocation_state;
CryptoError err =
revocation::Create(le_manager_, &revocation_state, key_blobs);
if (err != CryptoError::CE_NONE) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockRevocationCreateFailedInCreate),
ErrorActionSet(
{ErrorAction::kDevCheckUnexpectedState, ErrorAction::kReboot}),
err);
}
auth_block_state->revocation_state = revocation_state;
}
return OkStatus<CryptohomeCryptoError>();
}
CryptoStatus CryptohomeRecoveryAuthBlock::Derive(const AuthInput& auth_input,
const AuthBlockState& state,
KeyBlobs* key_blobs) {
DCHECK(key_blobs);
const CryptohomeRecoveryAuthBlockState* auth_state;
if (!(auth_state =
std::get_if<CryptohomeRecoveryAuthBlockState>(&state.state))) {
DLOG(FATAL) << "Invalid AuthBlockState";
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockInvalidBlockStateInDerive),
ErrorActionSet(
{ErrorAction::kDevCheckUnexpectedState, ErrorAction::kAuth}),
CryptoError::CE_OTHER_CRYPTO);
}
DCHECK(auth_input.cryptohome_recovery_auth_input.has_value());
auto cryptohome_recovery_auth_input =
auth_input.cryptohome_recovery_auth_input.value();
DCHECK(cryptohome_recovery_auth_input.epoch_response.has_value());
cryptorecovery::CryptoRecoveryEpochResponse epoch_response =
cryptohome_recovery_auth_input.epoch_response.value();
DCHECK(cryptohome_recovery_auth_input.ephemeral_pub_key.has_value());
const brillo::SecureBlob& ephemeral_pub_key =
cryptohome_recovery_auth_input.ephemeral_pub_key.value();
DCHECK(cryptohome_recovery_auth_input.recovery_response.has_value());
cryptorecovery::CryptoRecoveryRpcResponse response_proto =
cryptohome_recovery_auth_input.recovery_response.value();
brillo::SecureBlob plaintext_destination_share =
auth_state->plaintext_destination_share.value();
brillo::SecureBlob channel_priv_key = auth_state->channel_priv_key.value();
brillo::SecureBlob salt = auth_state->salt.value();
std::unique_ptr<RecoveryCryptoImpl> recovery =
RecoveryCryptoImpl::Create(GetRecoveryCryptoTpmBackend(tpm_));
if (!recovery) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockCantCreateRecoveryInDerive),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState,
ErrorAction::kReboot, ErrorAction::kAuth}),
CryptoError::CE_OTHER_CRYPTO);
}
HsmResponsePlainText response_plain_text;
if (!recovery->DecryptResponsePayload(channel_priv_key, epoch_response,
response_proto, &response_plain_text)) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockDecryptFailedInDerive),
ErrorActionSet({ErrorAction::kIncorrectAuth, ErrorAction::kReboot,
ErrorAction::kAuth}),
CryptoError::CE_OTHER_CRYPTO);
}
brillo::SecureBlob recovery_key;
if (!recovery->RecoverDestination(
response_plain_text.dealer_pub_key,
response_plain_text.key_auth_value, plaintext_destination_share,
ephemeral_pub_key, response_plain_text.mediated_point,
&recovery_key)) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockRecoveryFailedInDerive),
ErrorActionSet({ErrorAction::kIncorrectAuth, ErrorAction::kReboot,
ErrorAction::kAuth}),
CryptoError::CE_OTHER_CRYPTO);
}
// Generate wrapped keys from the recovery key.
// TODO(b/184924482): change wrapped keys to USS key after USS is implemented.
brillo::SecureBlob aes_skey(kDefaultAesKeySize);
brillo::SecureBlob vkk_iv(kAesBlockSize);
if (!DeriveSecretsScrypt(recovery_key, salt, {&aes_skey, &vkk_iv})) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockScryptDeriveFailedInDerive),
ErrorActionSet({ErrorAction::kDevCheckUnexpectedState,
ErrorAction::kReboot, ErrorAction::kAuth}),
CryptoError::CE_OTHER_FATAL);
}
key_blobs->vkk_key = aes_skey;
key_blobs->vkk_iv = vkk_iv;
key_blobs->chaps_iv = vkk_iv;
if (state.revocation_state.has_value()) {
DCHECK(revocation::IsRevocationSupported(tpm_));
DCHECK(le_manager_);
CryptoError crypto_err = revocation::Derive(
le_manager_, state.revocation_state.value(), key_blobs);
if (crypto_err != CryptoError::CE_NONE) {
return MakeStatus<CryptohomeCryptoError>(
CRYPTOHOME_ERR_LOC(
kLocCryptohomeRecoveryAuthBlockRevocationDeriveFailedInDerive),
ErrorActionSet(
{ErrorAction::kDevCheckUnexpectedState, ErrorAction::kReboot}),
crypto_err);
}
}
return OkStatus<CryptohomeCryptoError>();
}
} // namespace cryptohome