blob: ee7ea63f830eb3a1826db76becd04ee87676f8e9 [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/tpm1/vendor.h"
#include <cinttypes>
#include <cstdint>
#include <string>
#include <utility>
#include <base/strings/stringprintf.h>
#include <crypto/scoped_openssl_types.h>
#include <libhwsec-foundation/crypto/rsa.h>
#include <libhwsec-foundation/crypto/sha.h>
#include <libhwsec-foundation/status/status_chain_macros.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <tpm_manager-client/tpm_manager/dbus-proxies.h>
#include <tpm_manager/proto_bindings/tpm_manager.pb.h>
#include "libhwsec/backend/tpm1/static_utils.h"
#include "libhwsec/error/tpm1_error.h"
#include "libhwsec/error/tpm_manager_error.h"
using brillo::BlobFromString;
using brillo::BlobToString;
using hwsec_foundation::Sha256;
using hwsec_foundation::TestRocaVulnerable;
using hwsec_foundation::status::MakeStatus;
namespace hwsec {
namespace {
constexpr uint8_t kIfxFieldUpgradeRequest[] = {0x11, 0x00, 0x00};
constexpr uint32_t kFieldUpgradeInfo2Size = 106;
Status ParseIFXFirmwarePackage(
overalls::Overalls& overalls,
uint64_t* offset,
uint8_t* buffer,
uint64_t capacity,
IFXFieldUpgradeInfo::FirmwarePackage& firmware_package) {
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls.Orspi_UnloadBlob_UINT32_s(
offset, &firmware_package.package_id, buffer, capacity)));
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls.Orspi_UnloadBlob_UINT32_s(
offset, &firmware_package.version, buffer, capacity)));
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls.Orspi_UnloadBlob_UINT32_s(
offset, &firmware_package.stale_version, buffer, capacity)));
return OkStatus();
}
} // namespace
Status VendorTpm1::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> VendorTpm1::GetFamily() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->family();
}
StatusOr<uint64_t> VendorTpm1::GetSpecLevel() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->spec_level();
}
StatusOr<uint32_t> VendorTpm1::GetManufacturer() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->manufacturer();
}
StatusOr<uint32_t> VendorTpm1::GetTpmModel() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->tpm_model();
}
StatusOr<uint64_t> VendorTpm1::GetFirmwareVersion() {
RETURN_IF_ERROR(EnsureVersionInfo());
return version_info_->firmware_version();
}
StatusOr<brillo::Blob> VendorTpm1::GetVendorSpecific() {
RETURN_IF_ERROR(EnsureVersionInfo());
return brillo::BlobFromString(version_info_->vendor_specific());
}
StatusOr<int32_t> VendorTpm1::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<bool> VendorTpm1::IsSrkRocaVulnerable() {
ASSIGN_OR_RETURN(ScopedKey srk,
key_management_.GetPersistentKey(
KeyManagement::PersistentKeyType::kStorageRootKey));
ASSIGN_OR_RETURN(const KeyTpm1& srk_data,
key_management_.GetKeyData(srk.GetKey()));
ASSIGN_OR_RETURN(
const crypto::ScopedRSA& public_srk,
ParseRsaFromTpmPubkeyBlob(overalls_, srk_data.cache.pubkey_blob),
_.WithStatus<TPMError>("Failed to parse RSA public key"));
const BIGNUM* n = nullptr;
RSA_get0_key(public_srk.get(), &n, nullptr, nullptr);
return TestRocaVulnerable(n);
}
StatusOr<brillo::Blob> VendorTpm1::GetRsuDeviceId() {
return MakeStatus<TPMError>("Unsupported command", TPMRetryAction::kNoRetry);
}
StatusOr<IFXFieldUpgradeInfo> VendorTpm1::GetIFXFieldUpgradeInfo() {
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
ASSIGN_OR_RETURN(TSS_HTPM tpm_handle, tss_helper_.GetUserTpmHandle());
uint32_t length;
ScopedTssMemory response(overalls_, context);
brillo::Blob request(
kIfxFieldUpgradeRequest,
kIfxFieldUpgradeRequest + sizeof(kIfxFieldUpgradeRequest));
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_TPM_FieldUpgrade(
tpm_handle, request.size(), request.data(), &length, response.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_TPM_FieldUpgrade");
// Parse the response.
uint64_t offset = 0;
uint16_t size = 0;
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Orspi_UnloadBlob_UINT16_s(
&offset, &size, response.value(), length)));
if (size != length - sizeof(uint16_t)) {
return MakeStatus<TPMError>("FieldUpgrade response size mismatch",
TPMRetryAction::kNoRetry);
}
if (size != kFieldUpgradeInfo2Size) {
return MakeStatus<TPMError>("Unknown FieldUpgrade response size",
TPMRetryAction::kNoRetry);
}
IFXFieldUpgradeInfo info = {};
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Orspi_UnloadBlob_UINT16_s(
&offset, nullptr, response.value(), length)));
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Orspi_UnloadBlob_UINT16_s(
&offset, &info.max_data_size, response.value(), length)));
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Orspi_UnloadBlob_UINT16_s(
&offset, nullptr, response.value(), length)));
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Orspi_UnloadBlob_UINT32_s(
&offset, nullptr, response.value(), length)));
offset += 34;
RETURN_IF_ERROR(ParseIFXFirmwarePackage(overalls_, &offset, response.value(),
length, info.bootloader));
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Orspi_UnloadBlob_UINT16_s(
&offset, nullptr, response.value(), length)));
RETURN_IF_ERROR(ParseIFXFirmwarePackage(overalls_, &offset, response.value(),
length, info.firmware[0]));
RETURN_IF_ERROR(ParseIFXFirmwarePackage(overalls_, &offset, response.value(),
length, info.firmware[1]));
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Orspi_UnloadBlob_UINT16_s(
&offset, &info.status, response.value(), length)));
RETURN_IF_ERROR(ParseIFXFirmwarePackage(overalls_, &offset, response.value(),
length, info.process_fw));
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Orspi_UnloadBlob_UINT16_s(
&offset, nullptr, response.value(), length)));
offset += 6;
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Orspi_UnloadBlob_UINT16_s(
&offset, &info.field_upgrade_counter, response.value(), length)));
return info;
}
Status VendorTpm1::DeclareTpmFirmwareStable() {
// No-op on TPM1.2
return OkStatus();
}
StatusOr<VendorTpm1::RwVersion> VendorTpm1::GetRwVersion() {
return MakeStatus<TPMError>("Unimplemented", TPMRetryAction::kNoRetry);
}
StatusOr<brillo::Blob> VendorTpm1::SendRawCommand(const brillo::Blob& command) {
return MakeStatus<TPMError>("Unimplemented", TPMRetryAction::kNoRetry);
}
} // namespace hwsec