blob: 96a402c32957aa733b39e8014782e295dbebcfa1 [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/auth_blocks/tpm_auth_block_utils.h"
#include <string>
#include <base/logging.h>
#include <brillo/secure_blob.h>
#include "cryptohome/crypto_error.h"
#include "cryptohome/cryptohome_key_loader.h"
#include "cryptohome/cryptohome_metrics.h"
#include "cryptohome/tpm.h"
#include "cryptohome/vault_keyset.pb.h"
using hwsec::StatusChain;
using hwsec::TPMErrorBase;
namespace cryptohome {
TpmAuthBlockUtils::TpmAuthBlockUtils(Tpm* tpm,
CryptohomeKeyLoader* cryptohome_key_loader)
: tpm_(tpm), cryptohome_key_loader_(cryptohome_key_loader) {}
CryptoError TpmAuthBlockUtils::TPMErrorToCrypto(
const StatusChain<hwsec::TPMErrorBase>& err) {
hwsec::TPMRetryAction action = err->ToTPMRetryAction();
switch (action) {
case hwsec::TPMRetryAction::kCommunication:
case hwsec::TPMRetryAction::kLater:
return CryptoError::CE_TPM_COMM_ERROR;
case hwsec::TPMRetryAction::kDefend:
return CryptoError::CE_TPM_DEFEND_LOCK;
case hwsec::TPMRetryAction::kReboot:
return CryptoError::CE_TPM_REBOOT;
default:
// TODO(chromium:709646): kNoRetry maps here now. Find
// a better corresponding CryptoError.
return CryptoError::CE_TPM_CRYPTO;
}
}
bool TpmAuthBlockUtils::TPMErrorIsRetriable(
const StatusChain<hwsec::TPMErrorBase>& err) {
hwsec::TPMRetryAction action = err->ToTPMRetryAction();
return action == hwsec::TPMRetryAction::kLater ||
action == hwsec::TPMRetryAction::kCommunication;
}
CryptoError TpmAuthBlockUtils::IsTPMPubkeyHash(
const brillo::SecureBlob& hash) const {
brillo::SecureBlob pub_key_hash;
if (StatusChain<TPMErrorBase> err = tpm_->GetPublicKeyHash(
cryptohome_key_loader_->GetCryptohomeKey(), &pub_key_hash)) {
if (TPMErrorIsRetriable(err)) {
if (!cryptohome_key_loader_->ReloadCryptohomeKey()) {
LOG(ERROR) << "Unable to reload key.";
ReportCryptohomeError(kCannotReadTpmPublicKey);
return CryptoError::CE_NO_PUBLIC_KEY_HASH;
} else {
err = tpm_->GetPublicKeyHash(cryptohome_key_loader_->GetCryptohomeKey(),
&pub_key_hash);
}
}
if (err) {
LOG(ERROR) << "Unable to get the cryptohome public key from the TPM: "
<< err;
ReportCryptohomeError(kCannotReadTpmPublicKey);
return TPMErrorToCrypto(err);
}
}
if ((hash.size() != pub_key_hash.size()) ||
(brillo::SecureMemcmp(hash.data(), pub_key_hash.data(),
pub_key_hash.size()))) {
return CryptoError::CE_TPM_FATAL;
}
return CryptoError::CE_NONE;
}
CryptoError TpmAuthBlockUtils::CheckTPMReadiness(
bool has_tpm_key,
bool has_tpm_public_key_hash,
const brillo::SecureBlob& tpm_public_key_hash) {
if (!has_tpm_key) {
LOG(ERROR) << "Decrypting with TPM, but no TPM key present.";
ReportCryptohomeError(kDecryptAttemptButTpmKeyMissing);
return CryptoError::CE_TPM_FATAL;
}
// 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);
return CryptoError::CE_TPM_FATAL;
}
if (!cryptohome_key_loader_->HasCryptohomeKey()) {
cryptohome_key_loader_->Init();
}
if (!cryptohome_key_loader_->HasCryptohomeKey()) {
LOG(ERROR) << "Vault keyset is wrapped by the TPM, but the TPM is "
<< "unavailable.";
ReportCryptohomeError(kDecryptAttemptButTpmNotAvailable);
return CryptoError::CE_TPM_COMM_ERROR;
}
// This is a validity check that the keys still match.
if (has_tpm_public_key_hash) {
CryptoError error = IsTPMPubkeyHash(tpm_public_key_hash);
if (error != CryptoError::CE_NONE) {
LOG(ERROR) << "TPM public key hash mismatch.";
ReportCryptohomeError(kDecryptAttemptButTpmKeyMismatch);
return error;
}
}
return CryptoError::CE_NONE;
}
} // namespace cryptohome