//
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "trunks/tpm_state_impl.h"
#include
#include
#include "trunks/error_codes.h"
#include "trunks/tpm_generated.h"
#include "trunks/trunks_factory.h"
namespace {
// From definition of TPMA_PERMANENT.
const trunks::TPMA_PERMANENT kOwnerAuthSetMask = 1U;
const trunks::TPMA_PERMANENT kEndorsementAuthSetMask = 1U << 1;
const trunks::TPMA_PERMANENT kLockoutAuthSetMask = 1U << 2;
const trunks::TPMA_PERMANENT kInLockoutMask = 1U << 9;
// From definition of TPMA_STARTUP_CLEAR.
const trunks::TPMA_STARTUP_CLEAR kPlatformHierarchyMask = 1U;
const trunks::TPMA_STARTUP_CLEAR kStorageHierarchyMask = 1U << 1;
const trunks::TPMA_STARTUP_CLEAR kEndorsementHierarchyMask = 1U << 2;
const trunks::TPMA_STARTUP_CLEAR kOrderlyShutdownMask = 1U << 31;
} // namespace
namespace trunks {
TpmStateImpl::TpmStateImpl(const TrunksFactory& factory) : factory_(factory) {}
TPM_RC TpmStateImpl::Initialize() {
TPM_RC result = CacheTpmProperties();
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Failed to query TPM properties: " << GetErrorString(result);
return result;
}
if (tpm_properties_.count(TPM_PT_PERMANENT) == 0 ||
tpm_properties_.count(TPM_PT_STARTUP_CLEAR) == 0) {
LOG(ERROR) << "Required properties missing!";
return TRUNKS_RC_INVALID_TPM_CONFIGURATION;
}
result = CacheAlgorithmProperties();
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Failed to query TPM algorithms: " << GetErrorString(result);
return result;
}
initialized_ = true;
return TPM_RC_SUCCESS;
}
bool TpmStateImpl::IsOwnerPasswordSet() {
CHECK(initialized_);
return ((tpm_properties_[TPM_PT_PERMANENT] & kOwnerAuthSetMask) ==
kOwnerAuthSetMask);
}
bool TpmStateImpl::IsEndorsementPasswordSet() {
CHECK(initialized_);
return ((tpm_properties_[TPM_PT_PERMANENT] & kEndorsementAuthSetMask) ==
kEndorsementAuthSetMask);
}
bool TpmStateImpl::IsLockoutPasswordSet() {
CHECK(initialized_);
return ((tpm_properties_[TPM_PT_PERMANENT] & kLockoutAuthSetMask) ==
kLockoutAuthSetMask);
}
bool TpmStateImpl::IsOwned() {
return (IsOwnerPasswordSet() && IsEndorsementPasswordSet() &&
IsLockoutPasswordSet());
}
bool TpmStateImpl::IsInLockout() {
CHECK(initialized_);
return ((tpm_properties_[TPM_PT_PERMANENT] & kInLockoutMask) ==
kInLockoutMask);
}
bool TpmStateImpl::IsPlatformHierarchyEnabled() {
CHECK(initialized_);
return ((tpm_properties_[TPM_PT_STARTUP_CLEAR] & kPlatformHierarchyMask) ==
kPlatformHierarchyMask);
}
bool TpmStateImpl::IsStorageHierarchyEnabled() {
CHECK(initialized_);
return ((tpm_properties_[TPM_PT_STARTUP_CLEAR] & kStorageHierarchyMask) ==
kStorageHierarchyMask);
}
bool TpmStateImpl::IsEndorsementHierarchyEnabled() {
CHECK(initialized_);
return ((tpm_properties_[TPM_PT_STARTUP_CLEAR] & kEndorsementHierarchyMask) ==
kEndorsementHierarchyMask);
}
bool TpmStateImpl::IsEnabled() {
return (!IsPlatformHierarchyEnabled() && IsStorageHierarchyEnabled() &&
IsEndorsementHierarchyEnabled());
}
bool TpmStateImpl::WasShutdownOrderly() {
CHECK(initialized_);
return ((tpm_properties_[TPM_PT_STARTUP_CLEAR] & kOrderlyShutdownMask) ==
kOrderlyShutdownMask);
}
bool TpmStateImpl::IsRSASupported() {
CHECK(initialized_);
return (algorithm_properties_.count(TPM_ALG_RSA) > 0);
}
bool TpmStateImpl::IsECCSupported() {
CHECK(initialized_);
return (algorithm_properties_.count(TPM_ALG_ECC) > 0);
}
uint32_t TpmStateImpl::GetLockoutCounter() {
CHECK(initialized_);
return tpm_properties_[TPM_PT_LOCKOUT_COUNTER];
}
uint32_t TpmStateImpl::GetLockoutThreshold() {
CHECK(initialized_);
return tpm_properties_[TPM_PT_MAX_AUTH_FAIL];
}
uint32_t TpmStateImpl::GetLockoutInterval() {
CHECK(initialized_);
return tpm_properties_[TPM_PT_LOCKOUT_INTERVAL];
}
uint32_t TpmStateImpl::GetLockoutRecovery() {
CHECK(initialized_);
return tpm_properties_[TPM_PT_LOCKOUT_RECOVERY];
}
uint32_t TpmStateImpl::GetMaxNVSize() {
CHECK(initialized_);
uint32_t max_nv_size;
if (!GetTpmProperty(TPM_PT_NV_INDEX_MAX, &max_nv_size)) {
max_nv_size = 2048;
}
uint32_t max_nv_buffer;
if (GetTpmProperty(TPM_PT_NV_BUFFER_MAX, &max_nv_buffer) &&
max_nv_buffer < max_nv_size) {
max_nv_size = max_nv_buffer;
}
return max_nv_size;
}
bool TpmStateImpl::GetTpmProperty(TPM_PT property, uint32_t* value) {
CHECK(initialized_);
if (tpm_properties_.count(property) == 0) {
return false;
}
if (value) {
*value = tpm_properties_[property];
}
return true;
}
bool TpmStateImpl::GetAlgorithmProperties(TPM_ALG_ID algorithm,
TPMA_ALGORITHM* properties) {
CHECK(initialized_);
if (algorithm_properties_.count(algorithm) == 0) {
return false;
}
if (properties) {
*properties = algorithm_properties_[algorithm];
}
return true;
}
TPM_RC TpmStateImpl::GetCapability(const CapabilityCallback& callback,
TPM_CAP capability,
uint32_t property,
uint32_t max_properties_per_call) {
TPMI_YES_NO more_data = YES;
while (more_data) {
TPMS_CAPABILITY_DATA capability_data;
TPM_RC result = factory_.GetTpm()->GetCapabilitySync(
capability, property, max_properties_per_call, &more_data,
&capability_data, nullptr);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << ": " << GetErrorString(result);
return result;
}
if (capability_data.capability != capability) {
LOG(ERROR) << __func__ << ": Unexpected capability data.";
return SAPI_RC_MALFORMED_RESPONSE;
}
uint32_t next_property = callback.Run(capability_data.data);
if (more_data) {
if (next_property == 0) {
LOG(ERROR) << __func__ << ": No properties in response.";
return SAPI_RC_MALFORMED_RESPONSE;
}
if (next_property <= property) {
LOG(ERROR) << __func__ << ": Lower properties in response.";
return SAPI_RC_MALFORMED_RESPONSE;
}
property = next_property;
}
}
return TPM_RC_SUCCESS;
}
TPM_RC TpmStateImpl::CacheTpmProperties() {
CapabilityCallback callback = base::Bind(
[](TpmStateImpl* impl, const TPMU_CAPABILITIES& capability_data) {
uint32_t next_property = 0;
for (uint32_t i = 0;
i < capability_data.tpm_properties.count && i < MAX_TPM_PROPERTIES;
++i) {
const TPMS_TAGGED_PROPERTY& property =
capability_data.tpm_properties.tpm_property[i];
VLOG(1) << "TPM Property 0x" << std::hex << property.property
<< " = 0x" << property.value;
impl->tpm_properties_[property.property] = property.value;
next_property = property.property + 1;
}
return next_property;
}, base::Unretained(this));
if (tpm_properties_.empty()) {
TPM_RC result = GetCapability(callback, TPM_CAP_TPM_PROPERTIES, PT_FIXED,
MAX_TPM_PROPERTIES);
if (result != TPM_RC_SUCCESS) {
return result;
}
}
return GetCapability(callback, TPM_CAP_TPM_PROPERTIES, PT_VAR,
MAX_TPM_PROPERTIES);
}
TPM_RC TpmStateImpl::CacheAlgorithmProperties() {
CapabilityCallback callback = base::Bind(
[](TpmStateImpl* impl, const TPMU_CAPABILITIES& capability_data) {
uint32_t next_property = 0;
for (uint32_t i = 0;
i < capability_data.algorithms.count && i < MAX_CAP_ALGS; ++i) {
const TPMS_ALG_PROPERTY& property =
capability_data.algorithms.alg_properties[i];
VLOG(1) << "Algorithm Properties 0x" << std::hex << property.alg
<< " = 0x" << property.alg_properties;
impl->algorithm_properties_[property.alg] = property.alg_properties;
next_property = property.alg + 1;
}
return next_property;
}, base::Unretained(this));
if (algorithm_properties_.empty()) {
return GetCapability(callback, TPM_CAP_ALGS, TPM_ALG_FIRST, MAX_CAP_ALGS);
}
return TPM_RC_SUCCESS;
}
} // namespace trunks