| // 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<VendorTpm1::GscType> VendorTpm1::GetGscType() { | |
| return GscType::kNotGsc; | |
| } | |
| 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_.GetTpmHandle()); | |
| 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 |