blob: fb52557702f1d057b6117557a73bdd3c59bb7d63 [file] [log] [blame]
// Copyright 2022 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 "vtpm/backends/real_tpm_handle_manager.h"
#include <memory>
#include <string>
#include <vector>
#include <base/check.h>
#include <base/logging.h>
#include <trunks/tpm_generated.h>
#include <trunks/trunks_factory.h>
#include "vtpm/backends/scoped_host_key_handle.h"
namespace vtpm {
namespace {
bool IsTransient(trunks::TPM_HANDLE handle) {
return (handle & trunks::HR_RANGE_MASK) == (trunks::HR_TRANSIENT);
}
bool IsPersistent(trunks::TPM_HANDLE handle) {
return (handle & trunks::HR_RANGE_MASK) == (trunks::HR_PERSISTENT);
}
} // namespace
RealTpmHandleManager::RealTpmHandleManager(
trunks::TrunksFactory* trunks_factory,
std::map<trunks::TPM_HANDLE, Blob*> table)
: trunks_factory_(trunks_factory), handle_mapping_table_(table) {
CHECK(trunks_factory_);
for (const auto& entry : handle_mapping_table_) {
DCHECK(IsPersistent(entry.first))
<< "Handle with Unsupported handle type: " << entry.first;
}
}
bool RealTpmHandleManager::IsHandleTypeSuppoerted(trunks::TPM_HANDLE handle) {
return IsTransient(handle) || IsPersistent(handle);
}
trunks::TPM_RC RealTpmHandleManager::GetHandleList(
trunks::TPM_HANDLE starting_handle,
std::vector<trunks::TPM_HANDLE>* found_handles) {
if (!IsHandleTypeSuppoerted(starting_handle)) {
return trunks::TPM_RC_HANDLE;
}
if (IsPersistent(starting_handle)) {
for (auto iter = handle_mapping_table_.lower_bound(starting_handle);
iter != handle_mapping_table_.end(); ++iter) {
Blob* blob = iter->second;
std::string blob_not_used;
const trunks::TPM_RC rc = blob->Get(blob_not_used);
if (rc) {
found_handles->clear();
return rc;
}
// Note that the handle type is not validated because we support only 1
// type for now, and invalid entries are guarded in the constructor. But
// it wont stand when we have multiple supported types that are maintained
// in `handle_mapping_table_`.
found_handles->push_back(iter->first);
}
} else if (IsTransient(starting_handle)) {
for (auto iter = child_parent_table_.lower_bound(starting_handle);
iter != child_parent_table_.end(); ++iter) {
found_handles->push_back(iter->first);
}
} else {
NOTREACHED();
}
return trunks::TPM_RC_SUCCESS;
}
trunks::TPM_RC RealTpmHandleManager::TranslateHandle(
trunks::TPM_HANDLE handle, ScopedHostKeyHandle* host_handle) {
if (!IsHandleTypeSuppoerted(handle)) {
return trunks::TPM_RC_HANDLE;
}
if (IsTransient(handle)) {
// If the is transient, it must be in `child_parent_table_` because it must
// be derived from the virtual root keys.
if (child_parent_table_.count(handle) > 0) {
// Copy the value as it is, for we don't do virtualization of a transient
// handle.
*host_handle = ScopedHostKeyHandle(this, handle);
return trunks::TPM_RC_SUCCESS;
}
return trunks::TPM_RC_HANDLE;
}
DCHECK(IsPersistent(handle));
auto iter = handle_mapping_table_.find(handle);
if (iter == handle_mapping_table_.end()) {
return trunks::TPM_RC_HANDLE;
}
// Load the corresponding transient host key.
std::string host_key_blob;
trunks::TPM_RC rc = iter->second->Get(host_key_blob);
if (rc) {
return rc;
}
// Load the key to host TPM.
// Always use the correct auth. If the guest feeds wrong auth, the follow-up
// operation will fail anyway.
std::unique_ptr<trunks::AuthorizationDelegate> empty_password_authorization =
trunks_factory_->GetPasswordAuthorization(std::string());
trunks::TPM_HANDLE raw_host_handle;
rc = trunks_factory_->GetTpmUtility()->LoadKey(
host_key_blob, empty_password_authorization.get(), &raw_host_handle);
if (rc) {
return rc;
}
// Construct the ScopedHostKeyHandle.
*host_handle = ScopedHostKeyHandle(this, raw_host_handle, raw_host_handle);
return trunks::TPM_RC_SUCCESS;
}
trunks::TPM_RC RealTpmHandleManager::FlushHostHandle(
trunks::TPM_HANDLE handle) {
// Should not flush the handle if other loaded handles are derived from it.
if (child_count_table_.count(handle) > 0) {
return trunks::TPM_RC_SUCCESS;
}
return trunks_factory_->GetTpm()->FlushContextSync(
handle, /*authorization_delegate=*/nullptr);
}
void RealTpmHandleManager::OnLoad(trunks::TPM_HANDLE parent,
trunks::TPM_HANDLE child) {
child_parent_table_[child].push_back(parent);
++child_count_table_[parent];
}
void RealTpmHandleManager::OnUnload(trunks::TPM_HANDLE handle) {
// The performance is suboptimal due to repeated access to the same entry in
// the hash table below, but it seems alright in favor of readability.
if (child_parent_table_.count(handle) == 0) {
LOG(WARNING) << __func__ << ": handle does not exist.";
return;
}
for (trunks::TPM_HANDLE parent : child_parent_table_[handle]) {
--child_count_table_[parent];
if (child_count_table_[parent] == 0) {
child_count_table_.erase(parent);
FlushHostHandle(parent);
}
}
child_parent_table_.erase(handle);
}
} // namespace vtpm