blob: dca5bd343e2717fd22d06ef64aaba605a64c56ae [file] [log] [blame]
// Copyright 2015 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 "tpm_manager/server/tpm2_status_impl.h"
#include <string>
#include <base/check.h>
#include <base/logging.h>
#include <trunks/error_codes.h>
#include <trunks/tpm_generated.h>
#include <trunks/trunks_factory_impl.h>
using trunks::TPM_RC;
using trunks::TPM_RC_SUCCESS;
namespace tpm_manager {
namespace {
// Keep it with sync to UMA enum list
// https://chromium.googlesource.com/chromium/src/+/HEAD/tools/metrics/histograms/enums.xml
// These values are persisted to logs, and should therefore never be renumbered
// nor reused.
enum class TpmAlerts {
kCamoBreach = 1,
kDmemParity = 2,
kDrfParity = 3,
kImemParity = 4,
kPgmFault = 5,
kCpuDIfBusError = 6,
kCpuDIfUpdateWatchdog = 7,
kCpuIIfBusError = 8,
kCpuIIfUpdateWatchdog = 9,
kCpuSIfBusError = 10,
kCpuSIfUpdateWatchdog = 11,
kDmaIfBusErr = 12,
kDmaIfUpdateWatchdog = 13,
kSpsIfBusErr = 14,
kSpsIfUpdateWatchdog = 15,
kUsbIfBusErr = 16,
kUsbIfUpdateWatchdog = 17,
kFuseDefaults = 18,
kDiffFail = 19,
kSoftwareAlert0 = 20,
kSoftwareAlert1 = 21,
kSoftwareAlert2 = 22,
kSoftwareAlert3 = 23,
kHearbitFail = 24,
kProcOpcodeHash = 25,
kSramParityScrub = 26,
kAesExecCtrMax = 27,
kAesHkey = 28,
kCertLookup = 29,
kFlashEntry = 30,
kPw = 31,
kShaExecCtrMax = 32,
kShaFault = 33,
kShaHkey = 34,
kPmuBatteryMon = 35,
kPmuWatchdog = 36,
kRtcDead = 37,
kTempMax = 38,
kTempMaxDiff = 39,
kTempMin = 40,
kRngOutOfSpec = 41,
kRngTimeout = 42,
kVoltageError = 43,
kXoJitteryTrim = 44,
kTPMAlertNumBuckets, // Must be the last entry.
};
constexpr size_t kTPMAlertNumBuckets =
static_cast<size_t>(TpmAlerts::kTPMAlertNumBuckets);
static_assert(kTPMAlertNumBuckets <= trunks::kAlertsMaxSize + 1,
"Number of UMA enums less than alerts set size");
// Maps alerts identifiers received from TMP firmware to UMA identifiers
constexpr TpmAlerts kH1AlertsMap[trunks::kH1AlertsSize] = {
TpmAlerts::kCamoBreach,
TpmAlerts::kDmemParity,
TpmAlerts::kDrfParity,
TpmAlerts::kImemParity,
TpmAlerts::kPgmFault,
TpmAlerts::kCpuDIfBusError,
TpmAlerts::kCpuDIfUpdateWatchdog,
TpmAlerts::kCpuIIfBusError,
TpmAlerts::kCpuIIfUpdateWatchdog,
TpmAlerts::kCpuSIfBusError,
TpmAlerts::kCpuSIfUpdateWatchdog,
TpmAlerts::kDmaIfBusErr,
TpmAlerts::kDmaIfUpdateWatchdog,
TpmAlerts::kSpsIfBusErr,
TpmAlerts::kSpsIfUpdateWatchdog,
TpmAlerts::kUsbIfBusErr,
TpmAlerts::kUsbIfUpdateWatchdog,
TpmAlerts::kFuseDefaults,
TpmAlerts::kDiffFail,
TpmAlerts::kSoftwareAlert0,
TpmAlerts::kSoftwareAlert1,
TpmAlerts::kSoftwareAlert2,
TpmAlerts::kSoftwareAlert3,
TpmAlerts::kHearbitFail,
TpmAlerts::kProcOpcodeHash,
TpmAlerts::kSramParityScrub,
TpmAlerts::kAesExecCtrMax,
TpmAlerts::kAesHkey,
TpmAlerts::kCertLookup,
TpmAlerts::kFlashEntry,
TpmAlerts::kPw,
TpmAlerts::kShaExecCtrMax,
TpmAlerts::kShaFault,
TpmAlerts::kShaHkey,
TpmAlerts::kPmuBatteryMon,
TpmAlerts::kPmuWatchdog,
TpmAlerts::kRtcDead,
TpmAlerts::kTempMax,
TpmAlerts::kTempMaxDiff,
TpmAlerts::kTempMin,
TpmAlerts::kRngOutOfSpec,
TpmAlerts::kRngTimeout,
TpmAlerts::kVoltageError,
TpmAlerts::kXoJitteryTrim,
};
tpm_manager::RoVerificationStatus MapRoStatus(
const trunks::TpmUtility::ApRoStatus& raw_status) {
switch (raw_status) {
case trunks::TpmUtility::ApRoStatus::kApRoNotRun:
return tpm_manager::RO_STATUS_NOT_TRIGGERED;
case trunks::TpmUtility::ApRoStatus::kApRoPass:
return tpm_manager::RO_STATUS_PASS;
case trunks::TpmUtility::ApRoStatus::kApRoFail:
return tpm_manager::RO_STATUS_FAIL;
case trunks::TpmUtility::ApRoStatus::kApRoUnsupportedUnknown:
return tpm_manager::RO_STATUS_UNSUPPORTED;
case trunks::TpmUtility::ApRoStatus::kApRoUnsupportedNotTriggered:
return tpm_manager::RO_STATUS_UNSUPPORTED_NOT_TRIGGERED;
case trunks::TpmUtility::ApRoStatus::kApRoUnsupportedTriggered:
return tpm_manager::RO_STATUS_UNSUPPORTED_TRIGGERED;
}
NOTREACHED();
}
} // namespace
Tpm2StatusImpl::Tpm2StatusImpl(const trunks::TrunksFactory& factory)
: trunks_factory_(factory),
trunks_tpm_state_(trunks_factory_.GetTpmState()),
trunks_tpm_utility_(trunks_factory_.GetTpmUtility()) {}
bool Tpm2StatusImpl::IsTpmEnabled() {
// For 2.0, TPM is always enabled.
return true;
}
bool Tpm2StatusImpl::GetTpmOwned(TpmStatus::TpmOwnershipStatus* status) {
CHECK(status);
if (kTpmOwned == ownership_status_) {
*status = kTpmOwned;
return true;
}
if (!Refresh()) {
return false;
}
if (trunks_tpm_state_->IsOwned() && TestTpmSrkAndSaltingSession()) {
ownership_status_ = kTpmOwned;
} else if (trunks_tpm_state_->IsOwnerPasswordSet()) {
ownership_status_ = kTpmPreOwned;
}
*status = ownership_status_;
return true;
}
bool Tpm2StatusImpl::GetDictionaryAttackInfo(uint32_t* counter,
uint32_t* threshold,
bool* lockout,
uint32_t* seconds_remaining) {
CHECK(counter);
CHECK(threshold);
CHECK(lockout);
CHECK(seconds_remaining);
if (!Refresh()) {
return false;
}
*counter = trunks_tpm_state_->GetLockoutCounter();
*threshold = trunks_tpm_state_->GetLockoutThreshold();
*lockout = trunks_tpm_state_->IsInLockout();
*seconds_remaining = trunks_tpm_state_->GetLockoutCounter() *
trunks_tpm_state_->GetLockoutInterval();
return true;
}
bool Tpm2StatusImpl::IsDictionaryAttackMitigationEnabled(bool* is_enabled) {
CHECK(is_enabled);
if (!Refresh()) {
return false;
}
*is_enabled = trunks_tpm_state_->GetLockoutInterval() != 0 ||
trunks_tpm_state_->GetLockoutRecovery() != 0;
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) {
CHECK(family);
CHECK(spec_level);
CHECK(manufacturer);
CHECK(tpm_model);
CHECK(firmware_version);
CHECK(vendor_specific);
if (!Refresh()) {
return false;
}
*family = trunks_tpm_state_->GetTpmFamily();
uint64_t level = trunks_tpm_state_->GetSpecificationLevel();
uint64_t revision = trunks_tpm_state_->GetSpecificationRevision();
*spec_level = (level << 32) | revision;
*manufacturer = trunks_tpm_state_->GetManufacturer();
*tpm_model = trunks_tpm_state_->GetTpmModel();
*firmware_version = trunks_tpm_state_->GetFirmwareVersion();
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) {
LOG(ERROR) << "Error initializing trunks tpm state: "
<< trunks::GetErrorString(result);
return false;
}
initialized_ = true;
return true;
}
void Tpm2StatusImpl::MarkRandomOwnerPasswordSet() {
LOG(ERROR) << __func__ << ": Not implemented";
}
bool Tpm2StatusImpl::SupportU2f() {
// We support U2F on Cr50.
if (trunks_tpm_utility_->IsCr50()) {
return true;
}
return false;
}
bool Tpm2StatusImpl::SupportPinweaver() {
uint8_t protocol_version;
if (trunks_tpm_utility_->PinWeaverIsSupported(0, &protocol_version) ==
TPM_RC_SUCCESS) {
return true;
}
return false;
}
GscVersion Tpm2StatusImpl::GetGscVersion() {
// Currently we don't have method to distinguish Ti50.
if (trunks_tpm_utility_->IsCr50()) {
return GscVersion::GSC_VERSION_CR50;
}
return GscVersion::GSC_VERSION_NOT_GSC;
}
bool Tpm2StatusImpl::TestTpmSrkAndSaltingSession() {
trunks::TPMT_PUBLIC public_area;
TPM_RC result = trunks_tpm_utility_->GetKeyPublicArea(trunks::kStorageRootKey,
&public_area);
if (result != TPM_RC_SUCCESS) {
LOG(WARNING) << "Failed to get the SRK public area: "
<< trunks::GetErrorString(result);
return false;
}
if (!(public_area.object_attributes & trunks::kSensitiveDataOrigin)) {
LOG(WARNING) << "SRK doesn't have kSensitiveDataOrigin attribute.";
return false;
}
if (!(public_area.object_attributes & trunks::kUserWithAuth)) {
LOG(WARNING) << "SRK doesn't have kUserWithAuth attribute.";
return false;
}
if (!(public_area.object_attributes & trunks::kNoDA)) {
LOG(WARNING) << "SRK doesn't have kNoDA attribute.";
return false;
}
if (!(public_area.object_attributes & trunks::kRestricted)) {
LOG(WARNING) << "SRK doesn't have kRestricted attribute.";
return false;
}
if (!(public_area.object_attributes & trunks::kDecrypt)) {
LOG(WARNING) << "SRK doesn't have kDecrypt attribute.";
return false;
}
// Check the salting session.
std::unique_ptr<trunks::HmacSession> session =
trunks_factory_.GetHmacSession();
result = session->StartUnboundSession(true, false);
if (result != TPM_RC_SUCCESS) {
LOG(WARNING) << "Failed to create unbound session: "
<< trunks::GetErrorString(result);
return false;
}
return true;
}
bool Tpm2StatusImpl::GetRoVerificationStatus(
tpm_manager::RoVerificationStatus* status) {
trunks::TpmUtility::ApRoStatus raw_status;
TPM_RC result = trunks_tpm_utility_->GetRoVerificationStatus(&raw_status);
if (result != TPM_RC_SUCCESS) {
return false;
}
*status = MapRoStatus(raw_status);
return true;
}
bool Tpm2StatusImpl::GetAlertsData(AlertsData* alerts) {
trunks::TpmAlertsData trunks_alerts;
TPM_RC result = trunks_tpm_utility_->GetAlertsData(&trunks_alerts);
if (result == trunks::TPM_RC_NO_SUCH_COMMAND) {
LOG(INFO) << "TPM GetAlertsData vendor command is not implemented";
return false;
} else if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error getting alerts data: "
<< trunks::GetErrorString(result);
memset(alerts, 0, sizeof(AlertsData));
return true;
} else if (trunks_alerts.chip_family != trunks::kFamilyH1) {
// Currently we support only H1 alerts
LOG(ERROR) << "Unknown alerts family: " << trunks_alerts.chip_family;
return false;
}
memset(alerts, 0, sizeof(AlertsData));
for (int i = 0; i < trunks_alerts.alerts_num; i++) {
size_t uma_idx = static_cast<size_t>(kH1AlertsMap[i]);
if (uma_idx <= 0 || uma_idx >= kTPMAlertNumBuckets) {
LOG(ERROR) << "Alert index " << i << " maps into invalid UMA enum index "
<< uma_idx;
} else {
alerts->counters[uma_idx] = trunks_alerts.counters[i];
}
}
return true;
}
} // namespace tpm_manager