| // |
| // Copyright (C) 2015 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 "tpm_manager/server/tpm_nvram_impl.h" |
| |
| #include <arpa/inet.h> |
| |
| #include <string> |
| |
| #include <base/logging.h> |
| #include <base/stl_util.h> |
| #include <trousers/scoped_tss_type.h> |
| |
| #include "tpm_manager/common/tpm_manager.pb.h" |
| #include "tpm_manager/server/local_data_store.h" |
| #include "tpm_manager/server/tpm_util.h" |
| |
| namespace tpm_manager { |
| |
| using trousers::ScopedTssMemory; |
| using trousers::ScopedTssNvStore; |
| using trousers::ScopedTssPcrs; |
| |
| namespace { |
| |
| // PCR0 at locality 1 is used to differentiate between developed and normal |
| // mode. Restricting nvram to the PCR0 value in locality 1 prevents nvram from |
| // persisting across mode switch. |
| const unsigned int kTpmBootPCR = 0; |
| const unsigned int kTpmPCRLocality = 1; |
| |
| void MapAttributesFromTpm(TPM_NV_PER_ATTRIBUTES tpm_flags, |
| std::vector<NvramSpaceAttribute>* attributes) { |
| if (tpm_flags & TPM_NV_PER_WRITEDEFINE) |
| attributes->push_back(NVRAM_PERSISTENT_WRITE_LOCK); |
| if (tpm_flags & TPM_NV_PER_WRITE_STCLEAR) |
| attributes->push_back(NVRAM_BOOT_WRITE_LOCK); |
| if (tpm_flags & TPM_NV_PER_READ_STCLEAR) |
| attributes->push_back(NVRAM_BOOT_READ_LOCK); |
| if (tpm_flags & TPM_NV_PER_AUTHWRITE) |
| attributes->push_back(NVRAM_WRITE_AUTHORIZATION); |
| if (tpm_flags & TPM_NV_PER_AUTHREAD) |
| attributes->push_back(NVRAM_READ_AUTHORIZATION); |
| if (tpm_flags & TPM_NV_PER_GLOBALLOCK) |
| attributes->push_back(NVRAM_GLOBAL_LOCK); |
| if (tpm_flags & TPM_NV_PER_PPWRITE) |
| attributes->push_back(NVRAM_PLATFORM_WRITE); |
| if (tpm_flags & TPM_NV_PER_OWNERWRITE) |
| attributes->push_back(NVRAM_OWNER_WRITE); |
| if (tpm_flags & TPM_NV_PER_OWNERREAD) |
| attributes->push_back(NVRAM_OWNER_READ); |
| } |
| |
| TPM_NV_PER_ATTRIBUTES MapAttributesToTpm( |
| const std::vector<NvramSpaceAttribute>& attributes) { |
| TPM_NV_PER_ATTRIBUTES tpm_flags = 0; |
| for (auto attribute : attributes) { |
| switch (attribute) { |
| case NVRAM_PERSISTENT_WRITE_LOCK: |
| tpm_flags |= TPM_NV_PER_WRITEDEFINE; |
| break; |
| case NVRAM_BOOT_WRITE_LOCK: |
| tpm_flags |= TPM_NV_PER_WRITE_STCLEAR; |
| break; |
| case NVRAM_BOOT_READ_LOCK: |
| tpm_flags |= TPM_NV_PER_READ_STCLEAR; |
| break; |
| case NVRAM_WRITE_AUTHORIZATION: |
| tpm_flags |= TPM_NV_PER_AUTHWRITE; |
| break; |
| case NVRAM_READ_AUTHORIZATION: |
| tpm_flags |= TPM_NV_PER_AUTHREAD; |
| break; |
| case NVRAM_GLOBAL_LOCK: |
| tpm_flags |= TPM_NV_PER_GLOBALLOCK; |
| break; |
| case NVRAM_PLATFORM_WRITE: |
| tpm_flags |= TPM_NV_PER_PPWRITE; |
| break; |
| case NVRAM_OWNER_WRITE: |
| tpm_flags |= TPM_NV_PER_OWNERWRITE; |
| break; |
| case NVRAM_OWNER_READ: |
| tpm_flags |= TPM_NV_PER_OWNERREAD; |
| break; |
| default: |
| break; |
| } |
| } |
| return tpm_flags; |
| } |
| |
| NvramResult MapTpmError(TSS_RESULT tpm_error) { |
| switch (TPM_ERROR(tpm_error)) { |
| case TPM_SUCCESS: |
| return NVRAM_RESULT_SUCCESS; |
| case TPM_E_BAD_PARAMETER: |
| case TPM_E_PER_NOWRITE: |
| case TPM_E_AUTH_CONFLICT: |
| return NVRAM_RESULT_INVALID_PARAMETER; |
| case TPM_E_AREA_LOCKED: |
| case TPM_E_READ_ONLY: |
| case TPM_E_WRITE_LOCKED: |
| case TPM_E_DISABLED_CMD: |
| return NVRAM_RESULT_OPERATION_DISABLED; |
| case TPM_E_AUTHFAIL: |
| case TPM_E_NO_NV_PERMISSION: |
| case TPM_E_WRONGPCRVAL: |
| return NVRAM_RESULT_ACCESS_DENIED; |
| case TPM_E_NOSPACE: |
| case TPM_E_RESOURCES: |
| case TPM_E_SIZE: |
| return NVRAM_RESULT_INSUFFICIENT_SPACE; |
| case TPM_E_BADINDEX: |
| case TPM_E_BAD_HANDLE: |
| return NVRAM_RESULT_SPACE_DOES_NOT_EXIST; |
| } |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| |
| } // namespace |
| |
| TpmNvramImpl::TpmNvramImpl(LocalDataStore* local_data_store) |
| : local_data_store_(local_data_store) {} |
| |
| NvramResult TpmNvramImpl::DefineSpace( |
| uint32_t index, |
| size_t size, |
| const std::vector<NvramSpaceAttribute>& attributes, |
| const std::string& authorization_value, |
| NvramSpacePolicy policy) { |
| std::string owner_password; |
| if (!GetOwnerPassword(&owner_password)) { |
| return NVRAM_RESULT_OPERATION_DISABLED; |
| } |
| TpmConnection owner_connection(owner_password); |
| ScopedTssNvStore nv_handle(owner_connection.GetContext()); |
| if (!InitializeNvramHandle(index, &nv_handle, &owner_connection)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| TSS_RESULT result; |
| result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_DATASIZE, 0, size); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not set size on NVRAM object: " << size; |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| // Set permissions attributes. |
| result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_PERMISSIONS, 0, |
| MapAttributesToTpm(attributes)); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not set permissions on NVRAM object"; |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| // Set authorization. |
| if (!authorization_value.empty() && |
| !SetUsagePolicy(authorization_value, &nv_handle, &owner_connection)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| // Bind to PCR0. |
| TSS_HPCRS pcr_handle = 0; |
| ScopedTssPcrs scoped_pcr_handle(owner_connection.GetContext()); |
| if (policy == NVRAM_POLICY_PCR0) { |
| if (!SetCompositePcr0(&scoped_pcr_handle, &owner_connection)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| pcr_handle = scoped_pcr_handle; |
| } |
| result = Tspi_NV_DefineSpace(nv_handle, pcr_handle, /*Read*/ |
| pcr_handle /*Write*/); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not define NVRAM space: " << index; |
| return MapTpmError(result); |
| } |
| return NVRAM_RESULT_SUCCESS; |
| } |
| |
| NvramResult TpmNvramImpl::DestroySpace(uint32_t index) { |
| std::string owner_password; |
| if (!GetOwnerPassword(&owner_password)) { |
| return NVRAM_RESULT_OPERATION_DISABLED; |
| } |
| TpmConnection owner_connection(owner_password); |
| ScopedTssNvStore nv_handle(owner_connection.GetContext()); |
| if (!InitializeNvramHandle(index, &nv_handle, &owner_connection)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| TSS_RESULT result = Tspi_NV_ReleaseSpace(nv_handle); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not release NVRAM space: " << index; |
| return MapTpmError(result); |
| } |
| return NVRAM_RESULT_SUCCESS; |
| } |
| |
| NvramResult TpmNvramImpl::WriteSpace(uint32_t index, |
| const std::string& data, |
| const std::string& authorization_value) { |
| std::vector<NvramSpaceAttribute> attributes; |
| NvramResult result = |
| GetSpaceInfo(index, nullptr, nullptr, nullptr, &attributes, nullptr); |
| if (result != NVRAM_RESULT_SUCCESS) { |
| return result; |
| } |
| ScopedTssNvStore nv_handle(tpm_connection_.GetContext()); |
| if (!InitializeNvramHandle(index, &nv_handle, &tpm_connection_)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| for (const auto& attribute : attributes) { |
| if (attribute == NVRAM_OWNER_WRITE) { |
| if (!SetOwnerPolicy(&nv_handle)) { |
| return NVRAM_RESULT_OPERATION_DISABLED; |
| } |
| break; |
| } |
| if (attribute == NVRAM_WRITE_AUTHORIZATION) { |
| if (!SetUsagePolicy(authorization_value, &nv_handle, &tpm_connection_)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| break; |
| } |
| } |
| TSS_RESULT tpm_result = Tspi_NV_WriteValue( |
| nv_handle, 0 /* offset */, data.size(), |
| reinterpret_cast<BYTE*>(const_cast<char*>(data.data()))); |
| if (TPM_ERROR(tpm_result)) { |
| TPM_LOG(ERROR, tpm_result) << "Could not write to NVRAM space: " << index; |
| return MapTpmError(tpm_result); |
| } |
| return NVRAM_RESULT_SUCCESS; |
| } |
| |
| NvramResult TpmNvramImpl::ReadSpace(uint32_t index, |
| std::string* data, |
| const std::string& authorization_value) { |
| CHECK(data); |
| size_t nvram_size; |
| std::vector<NvramSpaceAttribute> attributes; |
| NvramResult result = |
| GetSpaceInfo(index, &nvram_size, nullptr, nullptr, &attributes, nullptr); |
| if (result != NVRAM_RESULT_SUCCESS) { |
| return result; |
| } |
| ScopedTssNvStore nv_handle(tpm_connection_.GetContext()); |
| if (!InitializeNvramHandle(index, &nv_handle, &tpm_connection_)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| for (const auto& attribute : attributes) { |
| if (attribute == NVRAM_OWNER_READ) { |
| if (!SetOwnerPolicy(&nv_handle)) { |
| return NVRAM_RESULT_OPERATION_DISABLED; |
| } |
| break; |
| } |
| if (attribute == NVRAM_READ_AUTHORIZATION) { |
| if (!SetUsagePolicy(authorization_value, &nv_handle, &tpm_connection_)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| break; |
| } |
| } |
| data->resize(nvram_size); |
| // The Tpm1.2 Specification defines the maximum read size of 128 bytes. |
| // Therefore we have to loop through the data returned. |
| const size_t kMaxDataSize = 128; |
| uint32_t offset = 0; |
| while (offset < nvram_size) { |
| uint32_t chunk_size = std::min(nvram_size - offset, kMaxDataSize); |
| ScopedTssMemory space_data(tpm_connection_.GetContext()); |
| TSS_RESULT tpm_result = |
| Tspi_NV_ReadValue(nv_handle, offset, &chunk_size, space_data.ptr()); |
| if (TPM_ERROR(tpm_result)) { |
| TPM_LOG(ERROR, tpm_result) << "Could not read from NVRAM space: " |
| << index; |
| data->clear(); |
| return MapTpmError(tpm_result); |
| } |
| if (!space_data.value()) { |
| LOG(ERROR) << "No data read from NVRAM space: " << index; |
| data->clear(); |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| CHECK_LE((offset + chunk_size), data->size()); |
| data->replace(offset, chunk_size, |
| reinterpret_cast<char*>(space_data.value()), chunk_size); |
| offset += chunk_size; |
| } |
| return NVRAM_RESULT_SUCCESS; |
| } |
| |
| NvramResult TpmNvramImpl::LockSpace(uint32_t index, |
| bool lock_read, |
| bool lock_write, |
| const std::string& authorization_value) { |
| std::vector<NvramSpaceAttribute> attributes; |
| NvramResult result = |
| GetSpaceInfo(index, nullptr, nullptr, nullptr, &attributes, nullptr); |
| if (result != NVRAM_RESULT_SUCCESS) { |
| return result; |
| } |
| if (lock_read) { |
| ScopedTssNvStore nv_handle(tpm_connection_.GetContext()); |
| if (!InitializeNvramHandle(index, &nv_handle, &tpm_connection_)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| for (const auto& attribute : attributes) { |
| if (attribute == NVRAM_OWNER_READ) { |
| if (!SetOwnerPolicy(&nv_handle)) { |
| return NVRAM_RESULT_OPERATION_DISABLED; |
| } |
| break; |
| } |
| if (attribute == NVRAM_READ_AUTHORIZATION) { |
| if (!SetUsagePolicy(authorization_value, &nv_handle, |
| &tpm_connection_)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| break; |
| } |
| } |
| uint32_t size = 0; |
| ScopedTssMemory space_data(tpm_connection_.GetContext()); |
| TSS_RESULT tpm_result = |
| Tspi_NV_ReadValue(nv_handle, 0, &size, space_data.ptr()); |
| if (TPM_ERROR(tpm_result)) { |
| TPM_LOG(ERROR, tpm_result) << "Could not lock read for NVRAM space: " |
| << index; |
| return MapTpmError(tpm_result); |
| } |
| } |
| if (lock_write) { |
| ScopedTssNvStore nv_handle(tpm_connection_.GetContext()); |
| if (!InitializeNvramHandle(index, &nv_handle, &tpm_connection_)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| for (const auto& attribute : attributes) { |
| if (attribute == NVRAM_OWNER_WRITE) { |
| if (!SetOwnerPolicy(&nv_handle)) { |
| return NVRAM_RESULT_OPERATION_DISABLED; |
| } |
| break; |
| } |
| if (attribute == NVRAM_WRITE_AUTHORIZATION) { |
| if (!SetUsagePolicy(authorization_value, &nv_handle, |
| &tpm_connection_)) { |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| break; |
| } |
| } |
| BYTE not_used; |
| TSS_RESULT tpm_result = Tspi_NV_WriteValue(nv_handle, 0, 0, ¬_used); |
| if (TPM_ERROR(tpm_result)) { |
| TPM_LOG(ERROR, tpm_result) << "Could not lock write for NVRAM space: " |
| << index; |
| return MapTpmError(tpm_result); |
| } |
| } |
| return NVRAM_RESULT_SUCCESS; |
| } |
| |
| NvramResult TpmNvramImpl::ListSpaces(std::vector<uint32_t>* index_list) { |
| uint32_t nv_list_data_length = 0; |
| ScopedTssMemory nv_list_data(tpm_connection_.GetContext()); |
| TSS_RESULT result = |
| Tspi_TPM_GetCapability(tpm_connection_.GetTpm(), TSS_TPMCAP_NV_LIST, 0, |
| nullptr, &nv_list_data_length, nv_list_data.ptr()); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetCapability"; |
| return MapTpmError(result); |
| } |
| // Walk the list and check if the index exists. |
| uint32_t* nv_list = reinterpret_cast<uint32_t*>(nv_list_data.value()); |
| uint32_t nv_list_length = nv_list_data_length / sizeof(uint32_t); |
| for (uint32_t i = 0; i < nv_list_length; ++i) { |
| // TPM data is network byte order. |
| index_list->push_back(ntohl(nv_list[i])); |
| } |
| return NVRAM_RESULT_SUCCESS; |
| } |
| |
| NvramResult TpmNvramImpl::GetSpaceInfo( |
| uint32_t index, |
| size_t* size, |
| bool* is_read_locked, |
| bool* is_write_locked, |
| std::vector<NvramSpaceAttribute>* attributes, |
| NvramSpacePolicy* policy) { |
| UINT32 nv_index_data_length = 0; |
| ScopedTssMemory nv_index_data(tpm_connection_.GetContext()); |
| TSS_RESULT result = |
| Tspi_TPM_GetCapability(tpm_connection_.GetTpm(), TSS_TPMCAP_NV_INDEX, |
| sizeof(index), reinterpret_cast<BYTE*>(&index), |
| &nv_index_data_length, nv_index_data.ptr()); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetCapability"; |
| return MapTpmError(result); |
| } |
| UINT64 offset = 0; |
| Trspi_UnloadBlob_NV_DATA_PUBLIC(&offset, nv_index_data.value(), nullptr); |
| if (nv_index_data_length < offset) { |
| LOG(ERROR) << "Not enough data from Tspi_TPM_GetCapability."; |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| TPM_NV_DATA_PUBLIC info; |
| offset = 0; |
| result = |
| Trspi_UnloadBlob_NV_DATA_PUBLIC(&offset, nv_index_data.value(), &info); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Error calling Trspi_UnloadBlob_NV_DATA_PUBLIC"; |
| return NVRAM_RESULT_DEVICE_ERROR; |
| } |
| if (size) { |
| *size = info.dataSize; |
| } |
| if (is_read_locked) { |
| *is_read_locked = info.bReadSTClear; |
| } |
| if (is_write_locked) { |
| *is_write_locked = info.bWriteSTClear || info.bWriteDefine; |
| } |
| if (attributes) { |
| MapAttributesFromTpm(info.permission.attributes, attributes); |
| } |
| if (policy) { |
| if (info.pcrInfoWrite.pcrSelection.sizeOfSelect > 0 && |
| (info.pcrInfoWrite.pcrSelection.pcrSelect[0] & 1) != 0) { |
| *policy = NVRAM_POLICY_PCR0; |
| } else { |
| *policy = NVRAM_POLICY_NONE; |
| } |
| } |
| return NVRAM_RESULT_SUCCESS; |
| } |
| |
| bool TpmNvramImpl::InitializeNvramHandle(uint32_t index, |
| ScopedTssNvStore* nv_handle, |
| TpmConnection* connection) { |
| TSS_RESULT result = Tspi_Context_CreateObject( |
| connection->GetContext(), TSS_OBJECT_TYPE_NV, 0, nv_handle->ptr()); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not acquire an NVRAM object handle"; |
| return false; |
| } |
| result = Tspi_SetAttribUint32(nv_handle->value(), TSS_TSPATTRIB_NV_INDEX, 0, |
| index); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not set index on NVRAM object: " << index; |
| return false; |
| } |
| return true; |
| } |
| |
| bool TpmNvramImpl::SetOwnerPolicy(ScopedTssNvStore* nv_handle) { |
| std::string owner_password; |
| if (!GetOwnerPassword(&owner_password)) { |
| return false; |
| } |
| return SetUsagePolicy(owner_password, nv_handle, &tpm_connection_); |
| } |
| |
| bool TpmNvramImpl::SetUsagePolicy(const std::string& authorization_value, |
| trousers::ScopedTssNvStore* nv_handle, |
| TpmConnection* connection) { |
| trousers::ScopedTssPolicy policy_handle(connection->GetContext()); |
| TSS_RESULT result; |
| result = Tspi_Context_CreateObject(connection->GetContext(), |
| TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, |
| policy_handle.ptr()); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject"; |
| return false; |
| } |
| result = Tspi_Policy_SetSecret( |
| policy_handle, TSS_SECRET_MODE_PLAIN, authorization_value.size(), |
| reinterpret_cast<BYTE*>(const_cast<char*>(authorization_value.data()))); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret"; |
| return false; |
| } |
| result = |
| Tspi_Policy_AssignToObject(policy_handle.value(), nv_handle->value()); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not set NVRAM object policy."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool TpmNvramImpl::SetCompositePcr0(ScopedTssPcrs* pcr_handle, |
| TpmConnection* connection) { |
| TSS_RESULT result = Tspi_Context_CreateObject( |
| connection->GetContext(), TSS_OBJECT_TYPE_PCRS, |
| TSS_PCRS_STRUCT_INFO_SHORT, pcr_handle->ptr()); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not acquire PCR object handle"; |
| return false; |
| } |
| uint32_t pcr_len; |
| ScopedTssMemory pcr_value(connection->GetContext()); |
| result = Tspi_TPM_PcrRead(connection->GetTpm(), kTpmBootPCR, &pcr_len, |
| pcr_value.ptr()); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not read PCR0 value"; |
| return false; |
| } |
| result = Tspi_PcrComposite_SetPcrValue(pcr_handle->value(), kTpmBootPCR, |
| pcr_len, pcr_value.value()); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not set value for PCR0 in PCR handle"; |
| return false; |
| } |
| result = |
| Tspi_PcrComposite_SetPcrLocality(pcr_handle->value(), kTpmPCRLocality); |
| if (TPM_ERROR(result)) { |
| TPM_LOG(ERROR, result) << "Could not set locality for PCR0 in PCR handle"; |
| return false; |
| } |
| return true; |
| } |
| |
| bool TpmNvramImpl::GetOwnerPassword(std::string* owner_password) { |
| LocalData local_data; |
| if (!local_data_store_->Read(&local_data)) { |
| LOG(ERROR) << "Error reading local data for owner password."; |
| return false; |
| } |
| if (local_data.owner_password().empty()) { |
| LOG(ERROR) << "No owner password present in tpm local_data."; |
| return false; |
| } |
| owner_password->assign(local_data.owner_password()); |
| return true; |
| } |
| |
| } // namespace tpm_manager |