tpm_manager: Expose TPM version information.
This adds version information fields to the result of the GetTpmStatus
operation and adds the corresponding logic to acquire the relevant
information via trunks / trousers and fill in the reply.
BUG=chromium:728134
TEST=unit tests
Change-Id: I0006e69b2d92837574960d2f306e1a04376e29ea
Reviewed-on: https://chromium-review.googlesource.com/517797
Commit-Ready: Mattias Nissler <mnissler@chromium.org>
Tested-by: Mattias Nissler <mnissler@chromium.org>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
diff --git a/tpm_manager/common/tpm_manager.proto b/tpm_manager/common/tpm_manager.proto
index 61292d9..ae8552b 100644
--- a/tpm_manager/common/tpm_manager.proto
+++ b/tpm_manager/common/tpm_manager.proto
@@ -230,6 +230,24 @@
optional bool dictionary_attack_lockout_in_effect = 7;
// The number of seconds remaining in the lockout.
optional uint32 dictionary_attack_lockout_seconds_remaining = 8;
+ // TPM version information.
+ message TpmVersionInfo {
+ // TPM family. We use the TPM 2.0 style encoding, e.g.:
+ // * TPM 1.2: "1.2" -> 0x312e3200
+ // * TPM 2.0: "2.0" -> 0x322e3000
+ optional uint32 family = 1;
+ // TPM spec level.
+ optional uint64 spec_level = 2;
+ // Manufacturer code.
+ optional uint32 manufacturer = 3;
+ // TPM model number.
+ optional uint32 tpm_model = 4;
+ // Firmware version.
+ optional uint64 firmware_version = 5;
+ // Vendor specific information.
+ optional bytes vendor_specific = 6;
+ }
+ optional TpmVersionInfo version_info = 9;
}
message TakeOwnershipRequest {
diff --git a/tpm_manager/server/mock_tpm_status.cc b/tpm_manager/server/mock_tpm_status.cc
index 54d8dd2..1df7594 100644
--- a/tpm_manager/server/mock_tpm_status.cc
+++ b/tpm_manager/server/mock_tpm_status.cc
@@ -16,9 +16,9 @@
#include "tpm_manager/server/mock_tpm_status.h"
-using testing::_;
using testing::Invoke;
using testing::Return;
+using testing::_;
namespace tpm_manager {
@@ -33,11 +33,28 @@
return true;
}
+bool GetDefaultVersionInfo(uint32_t* family,
+ uint64_t* spec_level,
+ uint32_t* manufacturer,
+ uint32_t* tpm_model,
+ uint64_t* firmware_version,
+ std::vector<uint8_t>* vendor_specific) {
+ *family = 0x312e3200;
+ *spec_level = (0ULL << 32) | 138;
+ *manufacturer = 0x90091;
+ *tpm_model = 0x1234;
+ *firmware_version = 0xdeadc0de;
+ *vendor_specific = { 0xda, 0x7a };
+ return true;
+}
+
MockTpmStatus::MockTpmStatus() {
ON_CALL(*this, IsTpmEnabled()).WillByDefault(Return(true));
ON_CALL(*this, IsTpmOwned()).WillByDefault(Return(true));
ON_CALL(*this, GetDictionaryAttackInfo(_, _, _, _))
.WillByDefault(Invoke(GetDefaultDictionaryAttackInfo));
+ ON_CALL(*this, GetVersionInfo(_, _, _, _, _, _))
+ .WillByDefault(Invoke(GetDefaultVersionInfo));
}
MockTpmStatus::~MockTpmStatus() {}
diff --git a/tpm_manager/server/mock_tpm_status.h b/tpm_manager/server/mock_tpm_status.h
index 35f00dc..7269b49 100644
--- a/tpm_manager/server/mock_tpm_status.h
+++ b/tpm_manager/server/mock_tpm_status.h
@@ -35,6 +35,13 @@
int* threshold,
bool* lockout,
int* seconds_remaining));
+ MOCK_METHOD6(GetVersionInfo,
+ bool(uint32_t* family,
+ uint64_t* spec_level,
+ uint32_t* manufacturer,
+ uint32_t* tpm_model,
+ uint64_t* firmware_version,
+ std::vector<uint8_t>* vendor_specific));
};
} // namespace tpm_manager
diff --git a/tpm_manager/server/tpm2_status_impl.cc b/tpm_manager/server/tpm2_status_impl.cc
index 9cd0a00..d172ba6 100644
--- a/tpm_manager/server/tpm2_status_impl.cc
+++ b/tpm_manager/server/tpm2_status_impl.cc
@@ -68,6 +68,42 @@
return true;
}
+bool Tpm2StatusImpl::GetVersionInfo(uint32_t* family,
+ uint64_t* spec_level,
+ uint32_t* manufacturer,
+ uint32_t* tpm_model,
+ uint64_t* firmware_version,
+ std::vector<uint8_t>* vendor_specific) {
+ if (!Refresh()) {
+ return false;
+ }
+
+ if (family) {
+ *family = trunks_tpm_state_->GetTpmFamily();
+ }
+ if (spec_level) {
+ uint64_t level = trunks_tpm_state_->GetSpecificationLevel();
+ uint64_t revision = trunks_tpm_state_->GetSpecificationRevision();
+ *spec_level = (level << 32) | revision;
+ }
+ if (manufacturer) {
+ *manufacturer = trunks_tpm_state_->GetManufacturer();
+ }
+ if (tpm_model) {
+ *tpm_model = trunks_tpm_state_->GetTpmModel();
+ }
+ if (firmware_version) {
+ *firmware_version = trunks_tpm_state_->GetFirmwareVersion();
+ }
+ if (vendor_specific) {
+ std::string vendor_id_string = trunks_tpm_state_->GetVendorIDString();
+ const uint8_t* data =
+ reinterpret_cast<const uint8_t*>(vendor_id_string.data());
+ vendor_specific->assign(data, data + vendor_id_string.size());
+ }
+ return true;
+}
+
bool Tpm2StatusImpl::Refresh() {
TPM_RC result = trunks_tpm_state_->Initialize();
if (result != TPM_RC_SUCCESS) {
diff --git a/tpm_manager/server/tpm2_status_impl.h b/tpm_manager/server/tpm2_status_impl.h
index 6fe4218..0cb1373 100644
--- a/tpm_manager/server/tpm2_status_impl.h
+++ b/tpm_manager/server/tpm2_status_impl.h
@@ -33,13 +33,19 @@
explicit Tpm2StatusImpl(const trunks::TrunksFactory& factory);
~Tpm2StatusImpl() override = default;
- // TpmState methods.
+ // TpmStatus methods.
bool IsTpmEnabled() override;
bool IsTpmOwned() override;
bool GetDictionaryAttackInfo(int* counter,
int* threshold,
bool* lockout,
int* seconds_remaining) override;
+ bool GetVersionInfo(uint32_t* family,
+ uint64_t* spec_level,
+ uint32_t* manufacturer,
+ uint32_t* tpm_model,
+ uint64_t* firmware_version,
+ std::vector<uint8_t>* vendor_specific) override;
private:
// Refreshes the Tpm state information. Can be called as many times as needed
diff --git a/tpm_manager/server/tpm_manager_service.cc b/tpm_manager/server/tpm_manager_service.cc
index ecbebea..39db1cc 100644
--- a/tpm_manager/server/tpm_manager_service.cc
+++ b/tpm_manager/server/tpm_manager_service.cc
@@ -146,6 +146,26 @@
reply->set_dictionary_attack_lockout_seconds_remaining(
lockout_time_remaining);
}
+ uint32_t family;
+ uint64_t spec_level;
+ uint32_t manufacturer;
+ uint32_t tpm_model;
+ uint64_t firmware_version;
+ std::vector<uint8_t> vendor_specific;
+ if (tpm_status_->GetVersionInfo(&family, &spec_level, &manufacturer,
+ &tpm_model, &firmware_version,
+ &vendor_specific)) {
+ GetTpmStatusReply::TpmVersionInfo* version_info =
+ reply->mutable_version_info();
+ version_info->set_family(family);
+ version_info->set_spec_level(spec_level);
+ version_info->set_manufacturer(manufacturer);
+ version_info->set_tpm_model(tpm_model);
+ version_info->set_firmware_version(firmware_version);
+ version_info->set_vendor_specific(
+ reinterpret_cast<char*>(vendor_specific.data()),
+ vendor_specific.size());
+ }
reply->set_status(STATUS_SUCCESS);
}
diff --git a/tpm_manager/server/tpm_manager_service_test.cc b/tpm_manager/server/tpm_manager_service_test.cc
index 50664c3..52575fc 100644
--- a/tpm_manager/server/tpm_manager_service_test.cc
+++ b/tpm_manager/server/tpm_manager_service_test.cc
@@ -25,13 +25,13 @@
#include "tpm_manager/server/mock_tpm_status.h"
#include "tpm_manager/server/tpm_manager_service.h"
-using testing::_;
using testing::AtLeast;
using testing::Invoke;
using testing::NiceMock;
using testing::Return;
using testing::SaveArg;
using testing::SetArgPointee;
+using testing::_;
namespace {
@@ -142,6 +142,19 @@
*seconds_remaining = 7;
return true;
}));
+ EXPECT_CALL(mock_tpm_status_, GetVersionInfo(_, _, _, _, _, _))
+ .WillRepeatedly(Invoke([](uint32_t* family, uint64_t* spec_level,
+ uint32_t* manufacturer, uint32_t* tpm_model,
+ uint64_t* firmware_version,
+ std::vector<uint8_t>* vendor_specific) {
+ *family = 8;
+ *spec_level = 9;
+ *manufacturer = 10;
+ *tpm_model = 11;
+ *firmware_version = 12;
+ *vendor_specific = { 0xda, 0x7a };
+ return true;
+ }));
LocalData local_data;
local_data.set_owner_password(kOwnerPassword);
EXPECT_CALL(mock_local_data_store_, Read(_))
@@ -156,6 +169,12 @@
EXPECT_EQ(6, reply.dictionary_attack_threshold());
EXPECT_TRUE(reply.dictionary_attack_lockout_in_effect());
EXPECT_EQ(7, reply.dictionary_attack_lockout_seconds_remaining());
+ EXPECT_EQ(8, reply.version_info().family());
+ EXPECT_EQ(9, reply.version_info().spec_level());
+ EXPECT_EQ(10, reply.version_info().manufacturer());
+ EXPECT_EQ(11, reply.version_info().tpm_model());
+ EXPECT_EQ(12, reply.version_info().firmware_version());
+ EXPECT_EQ("\xda\x7a", reply.version_info().vendor_specific());
Quit();
};
GetTpmStatusRequest request;
diff --git a/tpm_manager/server/tpm_status.h b/tpm_manager/server/tpm_status.h
index a6be635..51bf25b 100644
--- a/tpm_manager/server/tpm_status.h
+++ b/tpm_manager/server/tpm_status.h
@@ -17,6 +17,10 @@
#ifndef TPM_MANAGER_SERVER_TPM_STATUS_H_
#define TPM_MANAGER_SERVER_TPM_STATUS_H_
+#include <stdint.h>
+
+#include <vector>
+
namespace tpm_manager {
// TpmStatus is an interface class that reports status information for some kind
@@ -35,6 +39,13 @@
int* threshold,
bool* lockout,
int* seconds_remaining) = 0;
+ // Get TPM hardware and software version information.
+ virtual bool GetVersionInfo(uint32_t* family,
+ uint64_t* spec_level,
+ uint32_t* manufacturer,
+ uint32_t* tpm_model,
+ uint64_t* firmware_version,
+ std::vector<uint8_t>* vendor_specific) = 0;
};
} // namespace tpm_manager
diff --git a/tpm_manager/server/tpm_status_impl.cc b/tpm_manager/server/tpm_status_impl.cc
index 5a9da2d..a021286 100644
--- a/tpm_manager/server/tpm_status_impl.cc
+++ b/tpm_manager/server/tpm_status_impl.cc
@@ -26,6 +26,8 @@
// Minimum size of TPM_DA_INFO struct.
const size_t kMinimumDaInfoSize = 21;
+// Minimum size of TPM_CAP_VERSION_INFO struct.
+const size_t kMinimumVersionInfoSize = 17;
bool TpmStatusImpl::IsTpmEnabled() {
if (!is_enable_initialized_) {
@@ -73,6 +75,57 @@
return true;
}
+bool TpmStatusImpl::GetVersionInfo(uint32_t* family,
+ uint64_t* spec_level,
+ uint32_t* manufacturer,
+ uint32_t* tpm_model,
+ uint64_t* firmware_version,
+ std::vector<uint8_t>* vendor_specific) {
+ std::string capability_data;
+ if (!GetCapability(TSS_TPMCAP_VERSION_VAL, 0, &capability_data, nullptr) ||
+ capability_data.size() < kMinimumVersionInfoSize ||
+ static_cast<uint16_t>(capability_data[1]) != TPM_TAG_CAP_VERSION_INFO) {
+ LOG(ERROR) << "Error getting TPM version capability data.";
+ return false;
+ }
+
+ TPM_CAP_VERSION_INFO tpm_version;
+ uint64_t offset = 0;
+ std::vector<BYTE> bytes(capability_data.begin(), capability_data.end());
+ Trspi_UnloadBlob_CAP_VERSION_INFO(&offset, bytes.data(), &tpm_version);
+ if (family) {
+ *family = 0x312e3200;
+ }
+ if (spec_level) {
+ *spec_level = (static_cast<uint64_t>(tpm_version.specLevel) << 32) |
+ tpm_version.errataRev;
+ }
+ if (manufacturer) {
+ *manufacturer = (tpm_version.tpmVendorID[0] << 24) |
+ (tpm_version.tpmVendorID[1] << 16) |
+ (tpm_version.tpmVendorID[2] << 8) |
+ (tpm_version.tpmVendorID[3] << 0);
+ }
+ if (tpm_model) {
+ // There's no generic model field in the spec. Model information might be
+ // present in the vendor-specific data returned by CAP_VERSION_INFO, so if
+ // we ever require to know the model, we'll need to check with hardware
+ // vendors for the best way to determine it.
+ *tpm_model = ~0;
+ }
+ if (firmware_version) {
+ *firmware_version =
+ (tpm_version.version.revMajor << 8) | tpm_version.version.revMinor;
+ }
+ if (vendor_specific) {
+ const uint8_t* data =
+ reinterpret_cast<const uint8_t*>(tpm_version.vendorSpecific);
+ vendor_specific->assign(data, data + tpm_version.vendorSpecificSize);
+ }
+ free(tpm_version.vendorSpecific);
+ return true;
+}
+
void TpmStatusImpl::RefreshOwnedEnabledInfo() {
TSS_RESULT result;
std::string capability_data;
diff --git a/tpm_manager/server/tpm_status_impl.h b/tpm_manager/server/tpm_status_impl.h
index c86157d..be6e075 100644
--- a/tpm_manager/server/tpm_status_impl.h
+++ b/tpm_manager/server/tpm_status_impl.h
@@ -42,6 +42,12 @@
int* threshold,
bool* lockout,
int* seconds_remaining) override;
+ bool GetVersionInfo(uint32_t* family,
+ uint64_t* spec_level,
+ uint32_t* manufacturer,
+ uint32_t* tpm_model,
+ uint64_t* firmware_version,
+ std::vector<uint8_t>* vendor_specific) override;
private:
// This method refreshes the |is_owned_| and |is_enabled_| status of the