blob: aaa69d3cb30c59421a6e6cb008d6aacf84eeb638 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "libhwsec/backend/tpm2/vendor.h"
#include <array>
#include <cinttypes>
#include <cstdint>
#include <string>
#include <utility>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/stringprintf.h>
#include <base/sys_byteorder.h>
#include <libhwsec-foundation/crypto/sha.h>
#include <libhwsec-foundation/status/status_chain_macros.h>
#include <tpm_manager/proto_bindings/tpm_manager.pb.h>
#include <tpm_manager-client/tpm_manager/dbus-proxies.h>
#include <trunks/tpm_generated.h>
#include <trunks/tpm_utility.h>
#include "libhwsec/error/tpm2_error.h"
#include "libhwsec/error/tpm_manager_error.h"
using brillo::BlobFromString;
using brillo::BlobToString;
using hwsec_foundation::Sha256;
using hwsec_foundation::status::MakeStatus;
namespace hwsec {
namespace {
Status GetResponseStatus(const std::string& response) {
std::string buffer(response);
trunks::TPM_ST tag;
RETURN_IF_ERROR(
MakeStatus<TPM2Error>(trunks::Parse_TPM_ST(&buffer, &tag, nullptr)))
.WithStatus<TPMError>("Failed to parse response tag");
trunks::UINT32 response_size;
RETURN_IF_ERROR(MakeStatus<TPM2Error>(
trunks::Parse_UINT32(&buffer, &response_size, nullptr)))
.WithStatus<TPMError>("Failed to parse response size");
if (response_size != response.size()) {
return MakeStatus<TPMError>("Mismatch response size",
TPMRetryAction::kNoRetry);
}
trunks::TPM_RC rc;
RETURN_IF_ERROR(
MakeStatus<TPM2Error>(trunks::Parse_TPM_RC(&buffer, &rc, nullptr)))
.WithStatus<TPMError>("Failed to parse TPM_RC");
Status status = MakeStatus<TPM2Error>(rc);
if (status.ok()) {
return OkStatus();
}
if (status.err_status()->ToTPMRetryAction() !=
TPMRetryAction::kCommunication) {
// Communication error is the only valid retry action for vendor commands.
LOG(WARNING) << "TPM vendor command error: " << status;
return OkStatus();
}
return status;
}
} // namespace
Status VendorTpm2::EnsureVersionInfo() {
if (version_info_.has_value()) {
return OkStatus();
}
tpm_manager::GetVersionInfoRequest request;
tpm_manager::GetVersionInfoReply reply;
if (brillo::ErrorPtr err; !tpm_manager_.GetVersionInfo(
request, &reply, &err, Proxy::kDefaultDBusTimeoutMs)) {
return MakeStatus<TPMError>(TPMRetryAction::kCommunication)
.Wrap(std::move(err));
}
RETURN_IF_ERROR(MakeStatus<TPMManagerError>(reply.status()));
version_info_ = std::move(reply);
return OkStatus();
}
StatusOr<uint32_t> VendorTpm2::GetFamily() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->family();
}
StatusOr<uint64_t> VendorTpm2::GetSpecLevel() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->spec_level();
}
StatusOr<uint32_t> VendorTpm2::GetManufacturer() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->manufacturer();
}
StatusOr<uint32_t> VendorTpm2::GetTpmModel() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->tpm_model();
}
StatusOr<uint64_t> VendorTpm2::GetFirmwareVersion() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->firmware_version();
}
StatusOr<brillo::Blob> VendorTpm2::GetVendorSpecific() {
RETURN_IF_ERROR(EnsureVersionInfo());
return brillo::BlobFromString(version_info_->vendor_specific());
}
StatusOr<int32_t> VendorTpm2::GetFingerprint() {
RETURN_IF_ERROR(EnsureVersionInfo());
// The exact encoding doesn't matter as long as its unambiguous, stable and
// contains all information present in the version fields.
std::string encoded_parameters = base::StringPrintf(
"%08" PRIx32 "%016" PRIx64 "%08" PRIx32 "%08" PRIx32 "%016" PRIx64
"%016zx",
version_info_->family(), version_info_->spec_level(),
version_info_->manufacturer(), version_info_->tpm_model(),
version_info_->firmware_version(),
version_info_->vendor_specific().size());
encoded_parameters.append(version_info_->vendor_specific());
brillo::Blob hash = Sha256(brillo::BlobFromString(encoded_parameters));
// Return the first 31 bits from |hash|.
uint32_t result = static_cast<uint32_t>(hash[0]) |
static_cast<uint32_t>(hash[1]) << 8 |
static_cast<uint32_t>(hash[2]) << 16 |
static_cast<uint32_t>(hash[3]) << 24;
return result & 0x7fffffff;
}
StatusOr<VendorTpm2::GscType> VendorTpm2::GetGscType() {
RETURN_IF_ERROR(EnsureVersionInfo());
switch (version_info_->gsc_version()) {
case tpm_manager::GscVersion::GSC_VERSION_CR50:
return GscType::kCr50;
case tpm_manager::GscVersion::GSC_VERSION_TI50:
return GscType::kTi50;
default:
return GscType::kNotGsc;
}
}
StatusOr<bool> VendorTpm2::IsSrkRocaVulnerable() {
return false;
}
StatusOr<brillo::Blob> VendorTpm2::GetLegacyRsuDeviceId() {
std::string device_id;
RETURN_IF_ERROR(MakeStatus<TPM2Error>(
context_.GetTpmUtility().GetRsuDeviceId(&device_id)))
.WithStatus<TPMError>("Failed to get the RSU device ID");
return BlobFromString(device_id);
}
StatusOr<brillo::Blob> VendorTpm2::GetRsuDeviceId() {
// Try to read the virtual NV RSU device ID first.
StatusOr<brillo::Blob> device_id = ro_data_.Read(RoSpace::kRsuDeviceId);
if (device_id.ok()) {
if (!device_id->empty()) {
return device_id;
} else {
LOG(WARNING) << "Empty virtual NV device ID.";
}
} else {
LOG(WARNING) << "Failed to get virtual NV device ID: "
<< device_id.err_status();
}
// Some older version of cr50 don't support virtual NV RSU device ID,
// fallback to the legacy method.
return GetLegacyRsuDeviceId();
}
StatusOr<IFXFieldUpgradeInfo> VendorTpm2::GetIFXFieldUpgradeInfo() {
return MakeStatus<TPMError>("Unsupported command", TPMRetryAction::kNoRetry);
}
Status VendorTpm2::DeclareTpmFirmwareStable() {
if (fw_declared_stable_) {
return OkStatus();
}
RETURN_IF_ERROR(MakeStatus<TPM2Error>(
context_.GetTpmUtility().DeclareTpmFirmwareStable()))
.WithStatus<TPMError>("Failed to declare TPM firmware stable");
fw_declared_stable_ = true;
return OkStatus();
}
StatusOr<VendorTpm2::RwVersion> VendorTpm2::GetRwVersion() {
RETURN_IF_ERROR(EnsureVersionInfo());
// The rw_version format is x.y.z
auto ver = base::SplitString(version_info_->rw_version(), ".",
base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
if (ver.size() != 3) {
return MakeStatus<TPMError>("Incorrect response size",
TPMRetryAction::kNoRetry);
}
VendorTpm2::RwVersion rw_version;
base::StringToUint(ver[0], &rw_version.epoch);
base::StringToUint(ver[1], &rw_version.major);
base::StringToUint(ver[2], &rw_version.minor);
return rw_version;
}
StatusOr<brillo::Blob> VendorTpm2::SendRawCommand(const brillo::Blob& command) {
std::string response = context_.GetCommandTransceiver().SendCommandAndWait(
BlobToString(command));
RETURN_IF_ERROR(GetResponseStatus(response));
return BlobFromString(response);
}
} // namespace hwsec