blob: ddc9b98bf845bf2877052614a6407b0e6a5100f0 [file] [log] [blame]
// Copyright 2020 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/tpm_auth_block_utils.h"
#include <string>
#include <brillo/secure_blob.h>
#include "cryptohome/crypto_error.h"
#include "cryptohome/cryptohome_metrics.h"
#include "cryptohome/tpm.h"
#include "cryptohome/tpm_init.h"
#include "cryptohome/vault_keyset.pb.h"
namespace cryptohome {
TpmAuthBlockUtils::TpmAuthBlockUtils(Tpm* tpm, TpmInit* tpm_init)
: tpm_(tpm), tpm_init_(tpm_init) {}
// static
CryptoError TpmAuthBlockUtils::TpmErrorToCrypto(
Tpm::TpmRetryAction retry_action) {
switch (retry_action) {
case Tpm::kTpmRetryFatal:
// All errors mapped here will cause re-creating the cryptohome if
// they occur when decrypting the keyset.
return CryptoError::CE_TPM_FATAL;
case Tpm::kTpmRetryCommFailure:
case Tpm::kTpmRetryInvalidHandle:
case Tpm::kTpmRetryLoadFail:
case Tpm::kTpmRetryLater:
return CryptoError::CE_TPM_COMM_ERROR;
case Tpm::kTpmRetryDefendLock:
return CryptoError::CE_TPM_DEFEND_LOCK;
case Tpm::kTpmRetryReboot:
return CryptoError::CE_TPM_REBOOT;
default:
// TODO(chromium:709646): kTpmRetryFailNoRetry maps here now. Find
// a better corresponding CryptoError.
return CryptoError::CE_NONE;
}
}
// static
bool TpmAuthBlockUtils::TpmErrorIsRetriable(Tpm::TpmRetryAction retry_action) {
return retry_action == Tpm::kTpmRetryLoadFail ||
retry_action == Tpm::kTpmRetryInvalidHandle ||
retry_action == Tpm::kTpmRetryCommFailure;
}
bool TpmAuthBlockUtils::IsTPMPubkeyHash(const std::string& hash,
CryptoError* error) const {
brillo::SecureBlob pub_key_hash;
Tpm::TpmRetryAction retry_action =
tpm_->GetPublicKeyHash(tpm_init_->GetCryptohomeKey(), &pub_key_hash);
if (retry_action == Tpm::kTpmRetryLoadFail ||
retry_action == Tpm::kTpmRetryInvalidHandle) {
if (!tpm_init_->ReloadCryptohomeKey()) {
LOG(ERROR) << "Unable to reload key.";
retry_action = Tpm::kTpmRetryFailNoRetry;
} else {
retry_action =
tpm_->GetPublicKeyHash(tpm_init_->GetCryptohomeKey(), &pub_key_hash);
}
}
if (retry_action != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Unable to get the cryptohome public key from the TPM.";
ReportCryptohomeError(kCannotReadTpmPublicKey);
PopulateError(error, TpmErrorToCrypto(retry_action));
return false;
}
if ((hash.size() != pub_key_hash.size()) ||
(brillo::SecureMemcmp(hash.data(), pub_key_hash.data(),
pub_key_hash.size()))) {
PopulateError(error, CryptoError::CE_TPM_FATAL);
return false;
}
return true;
}
bool TpmAuthBlockUtils::CheckTPMReadiness(
const SerializedVaultKeyset& serialized, CryptoError* error) {
ReportEvkkEncryptionType(kTpmBackedEncryption);
if (!serialized.has_tpm_key()) {
LOG(ERROR) << "Decrypting with TPM, but no TPM key present.";
ReportCryptohomeError(kDecryptAttemptButTpmKeyMissing);
PopulateError(error, CryptoError::CE_TPM_FATAL);
return false;
}
// If the TPM is enabled but not owned, and the keyset is TPM wrapped, then
// it means the TPM has been cleared since the last login, and is not
// re-owned. In this case, the SRK is cleared and we cannot recover the
// keyset.
if (tpm_->IsEnabled() && !tpm_->IsOwned()) {
LOG(ERROR) << "Fatal error--the TPM is enabled but not owned, and this "
<< "keyset was wrapped by the TPM. It is impossible to "
<< "recover this keyset.";
ReportCryptohomeError(kDecryptAttemptButTpmNotOwned);
PopulateError(error, CryptoError::CE_TPM_FATAL);
return false;
}
if (!tpm_init_->HasCryptohomeKey()) {
tpm_init_->SetupTpm(/*load_key=*/true);
}
if (!tpm_init_->HasCryptohomeKey()) {
LOG(ERROR) << "Vault keyset is wrapped by the TPM, but the TPM is "
<< "unavailable.";
ReportCryptohomeError(kDecryptAttemptButTpmNotAvailable);
PopulateError(error, CryptoError::CE_TPM_COMM_ERROR);
return false;
}
// This is a validity check that the keys still match.
if (serialized.has_tpm_public_key_hash()) {
if (!IsTPMPubkeyHash(serialized.tpm_public_key_hash(), error)) {
LOG(ERROR) << "TPM public key hash mismatch.";
ReportCryptohomeError(kDecryptAttemptButTpmKeyMismatch);
return false;
}
}
return true;
}
} // namespace cryptohome