| // Copyright 2014 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 "trunks/tpm_utility_impl.h" |
| |
| #include <base/logging.h> |
| #include <base/memory/scoped_ptr.h> |
| #include <base/stl_util.h> |
| #include <crypto/secure_hash.h> |
| #include <crypto/sha2.h> |
| |
| #include "trunks/authorization_delegate.h" |
| #include "trunks/error_codes.h" |
| #include "trunks/null_authorization_delegate.h" |
| #include "trunks/tpm_state.h" |
| #include "trunks/trunks_factory.h" |
| |
| namespace { |
| |
| const char kPlatformPassword[] = "cros-platform"; |
| |
| // Returns a serialized representation of the unmodified handle. This is useful |
| // for predefined handle values, like TPM_RH_OWNER. For details on what types of |
| // handles use this name formula see Table 3 in the TPM 2.0 Library Spec Part 1 |
| // (Section 16 - Names). |
| std::string NameFromHandle(trunks::TPM_HANDLE handle) { |
| std::string name; |
| trunks::Serialize_TPM_HANDLE(handle, &name); |
| return name; |
| } |
| |
| } // namespace |
| |
| namespace trunks { |
| |
| TpmUtilityImpl::TpmUtilityImpl(const TrunksFactory& factory) |
| : factory_(factory) { |
| } |
| |
| TpmUtilityImpl::~TpmUtilityImpl() { |
| } |
| |
| TPM_RC TpmUtilityImpl::Startup() { |
| TPM_RC result = TPM_RC_SUCCESS; |
| Tpm* tpm = factory_.GetTpm(); |
| NullAuthorizationDelegate authorization; |
| result = tpm->StartupSync(TPM_SU_CLEAR, &authorization); |
| // Ignore TPM_RC_INITIALIZE, that means it was already started. |
| if (result && result != TPM_RC_INITIALIZE) { |
| LOG(ERROR) << __func__ << ": " << GetErrorString(result); |
| return result; |
| } |
| result = tpm->SelfTestSync(YES /* Full test. */, &authorization); |
| if (result) { |
| LOG(ERROR) << __func__ << ": " << GetErrorString(result); |
| return result; |
| } |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC TpmUtilityImpl::InitializeTpm() { |
| TPM_RC result = TPM_RC_SUCCESS; |
| scoped_ptr<TpmState> tpm_state(factory_.GetTpmState()); |
| result = tpm_state->Initialize(); |
| if (result) { |
| LOG(ERROR) << __func__ << ": " << GetErrorString(result); |
| return result; |
| } |
| // Warn about various unexpected conditions. |
| if (!tpm_state->WasShutdownOrderly()) { |
| LOG(WARNING) << "WARNING: The last TPM shutdown was not orderly."; |
| } |
| if (tpm_state->IsInLockout()) { |
| LOG(WARNING) << "WARNING: The TPM is currently in lockout."; |
| } |
| |
| // We expect the firmware has already locked down the platform hierarchy. If |
| // it hasn't, do it now. |
| if (tpm_state->IsPlatformHierarchyEnabled()) { |
| result = SetPlatformAuthorization(kPlatformPassword); |
| if (result) { |
| LOG(ERROR) << __func__ << ": " << GetErrorString(result); |
| return result; |
| } |
| scoped_ptr<AuthorizationDelegate> authorization( |
| factory_.GetPasswordAuthorization(kPlatformPassword)); |
| result = DisablePlatformHierarchy(authorization.get()); |
| if (result) { |
| LOG(ERROR) << __func__ << ": " << GetErrorString(result); |
| return result; |
| } |
| } |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC TpmUtilityImpl::StirRandom(const std::string& entropy_data) { |
| NullAuthorizationDelegate null_delegate; |
| std::string digest = crypto::SHA256HashString(entropy_data); |
| TPM2B_SENSITIVE_DATA random_bytes = Make_TPM2B_SENSITIVE_DATA(digest); |
| return factory_.GetTpm()->StirRandomSync(random_bytes, &null_delegate); |
| } |
| |
| TPM_RC TpmUtilityImpl::GenerateRandom(int num_bytes, |
| std::string* random_data) { |
| NullAuthorizationDelegate null_delegate; |
| int bytes_left = num_bytes; |
| random_data->clear(); |
| TPM_RC rc; |
| TPM2B_DIGEST digest; |
| while (bytes_left > 0) { |
| rc = factory_.GetTpm()->GetRandomSync(bytes_left, |
| &digest, |
| &null_delegate); |
| if (rc) { |
| LOG(ERROR) << "Error getting random data from tpm."; |
| return rc; |
| } |
| random_data->append(StringFrom_TPM2B_DIGEST(digest)); |
| bytes_left -= digest.size; |
| } |
| CHECK_EQ(random_data->size(), num_bytes); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC TpmUtilityImpl::ExtendPCR(int pcr_index, |
| const std::string& extend_data) { |
| NullAuthorizationDelegate null_delegate; |
| if (pcr_index < 0 || pcr_index >= IMPLEMENTATION_PCR) { |
| LOG(ERROR) << "Using a PCR index that isnt implemented."; |
| return TPM_RC_FAILURE; |
| } |
| TPM_HANDLE pcr_handle = HR_PCR + pcr_index; |
| std::string pcr_name = NameFromHandle(pcr_handle); |
| TPML_DIGEST_VALUES digests; |
| digests.count = 1; |
| digests.digests[0].hash_alg = TPM_ALG_SHA256; |
| crypto::SHA256HashString(extend_data, |
| digests.digests[0].digest.sha256, |
| crypto::kSHA256Length); |
| return factory_.GetTpm()->PCR_ExtendSync(pcr_handle, |
| pcr_name, |
| digests, |
| &null_delegate); |
| } |
| |
| TPM_RC TpmUtilityImpl::ReadPCR(int pcr_index, std::string* pcr_value) { |
| NullAuthorizationDelegate null_delegate; |
| TPML_PCR_SELECTION pcr_select_in; |
| uint32_t pcr_update_counter; |
| TPML_PCR_SELECTION pcr_select_out; |
| TPML_DIGEST pcr_values; |
| // This process of selecting pcrs is highlighted in TPM 2.0 Library Spec |
| // Part 2 (Section 10.5 - PCR structures). |
| uint8_t pcr_select_index = pcr_index / 8; |
| uint8_t pcr_select_byte = 1 << (pcr_index % 8); |
| pcr_select_in.count = 1; |
| pcr_select_in.pcr_selections[0].hash = TPM_ALG_SHA256; |
| pcr_select_in.pcr_selections[0].sizeof_select = pcr_select_index + 1; |
| pcr_select_in.pcr_selections[0].pcr_select[pcr_select_index] = |
| pcr_select_byte; |
| |
| TPM_RC rc = factory_.GetTpm()->PCR_ReadSync(pcr_select_in, |
| &pcr_update_counter, |
| &pcr_select_out, |
| &pcr_values, |
| &null_delegate); |
| if (rc) { |
| LOG(INFO) << "Error trying to read a pcr: " << rc; |
| return rc; |
| } |
| if (pcr_select_out.count != 1 || |
| pcr_select_out.pcr_selections[0].sizeof_select != |
| (pcr_select_index + 1) || |
| pcr_select_out.pcr_selections[0].pcr_select[pcr_select_index] != |
| pcr_select_byte) { |
| LOG(ERROR) << "TPM did not return the requested PCR"; |
| return TPM_RC_FAILURE; |
| } |
| CHECK_GE(pcr_values.count, 1); |
| pcr_value->assign(StringFrom_TPM2B_DIGEST(pcr_values.digests[0])); |
| return TPM_RC_SUCCESS; |
| } |
| |
| TPM_RC TpmUtilityImpl::SetPlatformAuthorization(const std::string& password) { |
| scoped_ptr<AuthorizationDelegate> authorization( |
| factory_.GetPasswordAuthorization("")); |
| CHECK_LE(password.size(), 32); |
| TPM_RC result = factory_.GetTpm()->HierarchyChangeAuthSync( |
| TPM_RH_PLATFORM, |
| NameFromHandle(TPM_RH_PLATFORM), |
| Make_TPM2B_DIGEST(password), |
| authorization.get()); |
| if (GetFormatOneError(result) == TPM_RC_BAD_AUTH) { |
| // Most likely the platform hierarchy password has already been set. |
| return TPM_RC_SUCCESS; |
| } |
| return result; |
| } |
| |
| TPM_RC TpmUtilityImpl::DisablePlatformHierarchy( |
| AuthorizationDelegate* authorization) { |
| return factory_.GetTpm()->HierarchyControlSync( |
| TPM_RH_PLATFORM, // The authorizing entity. |
| NameFromHandle(TPM_RH_PLATFORM), |
| TPM_RH_PLATFORM, // The target hierarchy. |
| 0, // Disable. |
| authorization); |
| } |
| |
| } // namespace trunks |