| Index: tpm.cc
|
| diff --git a/tpm.cc b/tpm.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..83717b965d8687ceb3d7d254d36c583b66fac263
|
| --- /dev/null
|
| +++ b/tpm.cc
|
| @@ -0,0 +1,507 @@
|
| +// Copyright (c) 2009-2010 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.
|
| +
|
| +// Contains the implementation of class Tpm
|
| +
|
| +#include "tpm.h"
|
| +
|
| +#include <base/file_util.h>
|
| +#include <base/platform_thread.h>
|
| +#include <base/time.h>
|
| +#include <openssl/rsa.h>
|
| +#include <trousers/tss.h>
|
| +#include <trousers/trousers.h>
|
| +
|
| +namespace tpm_init {
|
| +
|
| +const char* kWellKnownSrkTmp = "1234567890";
|
| +const int kOwnerPasswordLength = 12;
|
| +const int kMaxTimeoutRetries = 5;
|
| +const char* kTpmCheckEnabledFile = "/sys/class/misc/tpm0/device/enabled";
|
| +const char* kTpmCheckOwnedFile = "/sys/class/misc/tpm0/device/owned";
|
| +const char* kTpmOwnedFile = "/var/lib/.tpm_owned";
|
| +const char* kOpenCryptokiPath = "/var/lib/opencryptoki";
|
| +const int kTpmConnectRetries = 10;
|
| +const int kTpmConnectIntervalMs = 100;
|
| +
|
| +Tpm::Tpm()
|
| + : context_handle_(0),
|
| + default_crypto_(new Crypto()),
|
| + crypto_(default_crypto_.get()),
|
| + owner_password_(),
|
| + password_sync_lock_(),
|
| + is_disabled_(true),
|
| + is_owned_(false) {
|
| +}
|
| +
|
| +Tpm::~Tpm() {
|
| + Disconnect();
|
| +}
|
| +
|
| +bool Tpm::Init() {
|
| + // Checking disabled and owned either via sysfs or via TSS calls will block if
|
| + // ownership is being taken by another thread or process. So for this to work
|
| + // well, Tpm::Init() needs to be called before InitializeTpm() is called. At
|
| + // that point, the public API for Tpm only checks these booleans, so other
|
| + // threads can check without being blocked. InitializeTpm() will reset the
|
| + // is_owned_ bit on success.
|
| + is_disabled_ = IsDisabledCheckViaSysfs();
|
| + is_owned_ = IsOwnedCheckViaSysfs();
|
| + return true;
|
| +}
|
| +
|
| +bool Tpm::Connect() {
|
| + if (context_handle_ == 0) {
|
| + TSS_HCONTEXT context_handle;
|
| + if (!OpenAndConnectTpm(&context_handle)) {
|
| + return false;
|
| + }
|
| +
|
| + context_handle_ = context_handle;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool Tpm::IsConnected() {
|
| + return (context_handle_ != 0);
|
| +}
|
| +
|
| +void Tpm::Disconnect() {
|
| + if (context_handle_) {
|
| + Tspi_Context_Close(context_handle_);
|
| + context_handle_ = 0;
|
| + }
|
| +}
|
| +
|
| +int Tpm::GetMaxRsaKeyCount() {
|
| + if (context_handle_ == 0) {
|
| + return -1;
|
| + }
|
| +
|
| + return GetMaxRsaKeyCountForContext(context_handle_);
|
| +}
|
| +
|
| +int Tpm::GetMaxRsaKeyCountForContext(TSS_HCONTEXT context_handle) {
|
| + int count = -1;
|
| + TSS_RESULT result;
|
| + TSS_HTPM tpm_handle;
|
| + if (!GetTpm(context_handle, &tpm_handle)) {
|
| + return count;
|
| + }
|
| +
|
| + UINT32 cap_length = 0;
|
| + BYTE* cap = NULL;
|
| + UINT32 subcap = TSS_TPMCAP_PROP_MAXKEYS;
|
| + if ((result = Tspi_TPM_GetCapability(tpm_handle, TSS_TPMCAP_PROPERTY,
|
| + sizeof(subcap),
|
| + reinterpret_cast<BYTE*>(&subcap),
|
| + &cap_length, &cap))) {
|
| + LOG(ERROR) << "Error calling Tspi_TPM_GetCapability: " << result;
|
| + return count;
|
| + }
|
| + if (cap_length == sizeof(int)) {
|
| + count = *(reinterpret_cast<int*>(cap));
|
| + }
|
| + Tspi_Context_FreeMemory(context_handle, cap);
|
| + return count;
|
| +}
|
| +
|
| +bool Tpm::OpenAndConnectTpm(TSS_HCONTEXT* context_handle) {
|
| + TSS_RESULT result;
|
| + TSS_HCONTEXT local_context_handle;
|
| + if ((result = Tspi_Context_Create(&local_context_handle))) {
|
| + LOG(ERROR) << "Error calling Tspi_Context_Create";
|
| + return false;
|
| + }
|
| +
|
| + for (int i = 0; i < kTpmConnectRetries; i++) {
|
| + if ((result = Tspi_Context_Connect(local_context_handle, NULL))) {
|
| + if (result == TSS_E_COMM_FAILURE) {
|
| + PlatformThread::Sleep(kTpmConnectIntervalMs);
|
| + } else {
|
| + LOG(ERROR) << "Error calling Tspi_Context_Connect: " << result;
|
| + Tspi_Context_Close(local_context_handle);
|
| + return false;
|
| + }
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (result) {
|
| + LOG(ERROR) << "Error calling Tspi_Context_Connect: " << result;
|
| + Tspi_Context_Close(local_context_handle);
|
| + return false;
|
| + }
|
| +
|
| + *context_handle = local_context_handle;
|
| + return true;
|
| +}
|
| +
|
| +bool Tpm::IsDisabledCheckViaSysfs() {
|
| + std::string contents;
|
| + if (!file_util::ReadFileToString(FilePath(kTpmCheckEnabledFile), &contents)) {
|
| + return false;
|
| + }
|
| + if (contents.size() < 1) {
|
| + return false;
|
| + }
|
| + return (contents[0] == '0');
|
| +}
|
| +
|
| +bool Tpm::IsOwnedCheckViaSysfs() {
|
| + std::string contents;
|
| + if (!file_util::ReadFileToString(FilePath(kTpmCheckOwnedFile), &contents)) {
|
| + return false;
|
| + }
|
| + if (contents.size() < 1) {
|
| + return false;
|
| + }
|
| + return (contents[0] != '0');
|
| +}
|
| +
|
| +bool Tpm::IsDisabledCheckViaContext(TSS_HCONTEXT context_handle) {
|
| + bool value = true;
|
| + TSS_RESULT result;
|
| + TSS_HTPM tpm_handle;
|
| + if (!GetTpm(context_handle, &tpm_handle)) {
|
| + return value;
|
| + }
|
| +
|
| + UINT32 cap_length = 0;
|
| + BYTE* cap = NULL;
|
| + if ((result = Tspi_TPM_GetCapability(tpm_handle, TSS_TPMCAP_FLAG,
|
| + 0, NULL, &cap_length, &cap))) {
|
| + LOG(ERROR) << "Error calling Tspi_TPM_GetCapability: " << result;
|
| + return value;
|
| + }
|
| + if (cap_length >= (2 * sizeof(int))) {
|
| + value = (((*(reinterpret_cast<int*>(cap))) & TPM_PF_DISABLE) != 0);
|
| + }
|
| + Tspi_Context_FreeMemory(context_handle, cap);
|
| + return value;
|
| +}
|
| +
|
| +bool Tpm::IsOwnedCheckViaContext(TSS_HCONTEXT context_handle) {
|
| + bool value = true;
|
| + TSS_RESULT result;
|
| + TSS_HTPM tpm_handle;
|
| + if (!GetTpm(context_handle, &tpm_handle)) {
|
| + LOG(ERROR) << "Error calling Tspi_Context_GetTpmObject: " << result;
|
| + return value;
|
| + }
|
| +
|
| + UINT32 cap_length = 0;
|
| + BYTE* cap = NULL;
|
| + if ((result = Tspi_TPM_GetCapability(tpm_handle, TSS_TPMCAP_FLAG,
|
| + 0, NULL, &cap_length, &cap))) {
|
| + LOG(ERROR) << "Error calling Tspi_TPM_GetCapability: " << result;
|
| + return value;
|
| + }
|
| + if (cap_length >= (2 * sizeof(int))) {
|
| + value = (((*(reinterpret_cast<int*>(cap))) & TPM_PF_OWNERSHIP) != 0);
|
| + }
|
| + Tspi_Context_FreeMemory(context_handle, cap);
|
| + return value;
|
| +}
|
| +
|
| +bool Tpm::CreateEndorsementKey(TSS_HCONTEXT context_handle) {
|
| + TSS_RESULT result;
|
| + TSS_HTPM tpm_handle;
|
| + if (!GetTpm(context_handle, &tpm_handle)) {
|
| + return false;
|
| + }
|
| +
|
| + TSS_HKEY local_key_handle;
|
| + TSS_FLAG init_flags = TSS_KEY_TYPE_LEGACY | TSS_KEY_SIZE_2048;
|
| + if ((result = Tspi_Context_CreateObject(context_handle,
|
| + TSS_OBJECT_TYPE_RSAKEY,
|
| + init_flags, &local_key_handle))) {
|
| + LOG(ERROR) << "Error calling Tspi_Context_CreateObject: " << result;
|
| + return false;
|
| + }
|
| +
|
| + if ((result = Tspi_TPM_CreateEndorsementKey(tpm_handle, local_key_handle,
|
| + NULL))) {
|
| + LOG(ERROR) << "Error calling Tspi_TPM_CreateEndorsementKey: " << result;
|
| + Tspi_Context_CloseObject(context_handle, local_key_handle);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool Tpm::IsEndorsementKeyAvailable(TSS_HCONTEXT context_handle) {
|
| + TSS_RESULT result;
|
| + TSS_HTPM tpm_handle;
|
| + if (!GetTpm(context_handle, &tpm_handle)) {
|
| + return false;
|
| + }
|
| +
|
| + TSS_HKEY local_key_handle;
|
| + if ((result = Tspi_TPM_GetPubEndorsementKey(tpm_handle, false, NULL,
|
| + &local_key_handle))) {
|
| + LOG(ERROR) << "Error calling Tspi_Context_CreateObject: " << result;
|
| + return false;
|
| + }
|
| +
|
| + Tspi_Context_CloseObject(context_handle, local_key_handle);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void Tpm::CreateOwnerPassword(SecureBlob* password) {
|
| + SecureBlob random(kOwnerPasswordLength);
|
| + crypto_->GetSecureRandom(static_cast<unsigned char*>(random.data()),
|
| + random.size());
|
| + SecureBlob tpm_password(kOwnerPasswordLength);
|
| + crypto_->AsciiEncodeToBuffer(random, static_cast<char*>(tpm_password.data()),
|
| + tpm_password.size());
|
| + password->swap(tpm_password);
|
| +}
|
| +
|
| +bool Tpm::TakeOwnership(TSS_HCONTEXT context_handle, int max_timeout_tries) {
|
| + SecureBlob owner_password;
|
| + CreateOwnerPassword(&owner_password);
|
| +
|
| + TSS_RESULT result;
|
| + TSS_HTPM tpm_handle;
|
| + if (!GetTpmWithAuth(context_handle, owner_password, &tpm_handle)) {
|
| + return false;
|
| + }
|
| +
|
| + TSS_HKEY srk_handle;
|
| + TSS_FLAG init_flags = TSS_KEY_TSP_SRK | TSS_KEY_AUTHORIZATION;
|
| + if ((result = Tspi_Context_CreateObject(context_handle,
|
| + TSS_OBJECT_TYPE_RSAKEY,
|
| + init_flags, &srk_handle))) {
|
| + LOG(ERROR) << "Error calling Tspi_Context_CreateObject: " << result;
|
| + return false;
|
| + }
|
| +
|
| + TSS_HPOLICY srk_usage_policy;
|
| + if ((result = Tspi_GetPolicyObject(srk_handle, TSS_POLICY_USAGE,
|
| + &srk_usage_policy))) {
|
| + LOG(ERROR) << "Error calling Tspi_GetPolicyObject: " << result;
|
| + Tspi_Context_CloseObject(context_handle, srk_handle);
|
| + return false;
|
| + }
|
| +
|
| + if ((result = Tspi_Policy_SetSecret(srk_usage_policy,
|
| + TSS_SECRET_MODE_PLAIN,
|
| + strlen(kWellKnownSrkTmp),
|
| + const_cast<BYTE *>(reinterpret_cast<const BYTE *>(kWellKnownSrkTmp))))) {
|
| + LOG(ERROR) << "Error calling Tspi_Policy_SetSecret: " << result;
|
| + Tspi_Context_CloseObject(context_handle, srk_handle);
|
| + return false;
|
| + }
|
| +
|
| + int retry_count = 0;
|
| + do {
|
| + result = Tspi_TPM_TakeOwnership(tpm_handle, srk_handle, 0);
|
| + retry_count++;
|
| + } while(((result == TDDL_E_TIMEOUT) ||
|
| + (result == (TSS_LAYER_TDDL | TDDL_E_TIMEOUT)) ||
|
| + (result == (TSS_LAYER_TDDL | TDDL_E_IOERROR))) &&
|
| + (retry_count < max_timeout_tries));
|
| +
|
| + if (result) {
|
| + LOG(ERROR) << "Error calling Tspi_TPM_TakeOwnership: " << result
|
| + << ", attempts: " << retry_count;
|
| + Tspi_Context_CloseObject(context_handle, srk_handle);
|
| + return false;
|
| + }
|
| +
|
| + Tspi_Context_CloseObject(context_handle, srk_handle);
|
| +
|
| + password_sync_lock_.Acquire();
|
| + owner_password_.swap(owner_password);
|
| + password_sync_lock_.Release();
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool Tpm::ZeroSrkPassword(TSS_HCONTEXT context_handle,
|
| + const SecureBlob& owner_password) {
|
| + TSS_RESULT result;
|
| + TSS_HTPM tpm_handle;
|
| + if (!GetTpmWithAuth(context_handle, owner_password, &tpm_handle)) {
|
| + return false;
|
| + }
|
| +
|
| + TSS_HKEY srk_handle;
|
| + TSS_UUID SRK_UUID = TSS_UUID_SRK;
|
| + if ((result = Tspi_Context_LoadKeyByUUID(context_handle, TSS_PS_TYPE_SYSTEM,
|
| + SRK_UUID, &srk_handle))) {
|
| + LOG(ERROR) << "Couldn't load SRK: " << result;
|
| + return false;
|
| + }
|
| +
|
| + TSS_HPOLICY policy_handle;
|
| + if ((result = Tspi_Context_CreateObject(context_handle,
|
| + TSS_OBJECT_TYPE_POLICY,
|
| + TSS_POLICY_USAGE,
|
| + &policy_handle))) {
|
| + LOG(ERROR) << "Error creating policy object: " << result;
|
| + Tspi_Context_CloseObject(context_handle, srk_handle);
|
| + return false;
|
| + }
|
| +
|
| + BYTE new_password[0];
|
| + if ((result = Tspi_Policy_SetSecret(policy_handle, TSS_SECRET_MODE_PLAIN,
|
| + 0, new_password))) {
|
| + LOG(ERROR) << "Error setting srk password: " << result;
|
| + Tspi_Context_CloseObject(context_handle, policy_handle);
|
| + Tspi_Context_CloseObject(context_handle, srk_handle);
|
| + return false;
|
| + }
|
| +
|
| + if ((result = Tspi_ChangeAuth(srk_handle,
|
| + tpm_handle,
|
| + policy_handle))) {
|
| + LOG(ERROR) << "Error creating policy object: " << result;
|
| + Tspi_Context_CloseObject(context_handle, policy_handle);
|
| + Tspi_Context_CloseObject(context_handle, srk_handle);
|
| + return false;
|
| + }
|
| +
|
| + Tspi_Context_CloseObject(context_handle, policy_handle);
|
| + Tspi_Context_CloseObject(context_handle, srk_handle);
|
| + return true;
|
| +}
|
| +
|
| +bool Tpm::UnrestrictSrk(TSS_HCONTEXT context_handle,
|
| + const SecureBlob& owner_password) {
|
| + TSS_RESULT result;
|
| + TSS_HTPM tpm_handle;
|
| + if (!GetTpmWithAuth(context_handle, owner_password, &tpm_handle)) {
|
| + return false;
|
| + }
|
| +
|
| + if ((result = Tspi_TPM_SetStatus(tpm_handle,
|
| + TSS_TPMSTATUS_DISABLEPUBSRKREAD,
|
| + false))) {
|
| + LOG(ERROR) << "Error calling Tspi_TPM_SetStatus: " << result;
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool Tpm::GetTpm(TSS_HCONTEXT context_handle, TSS_HTPM* tpm_handle) {
|
| + TSS_RESULT result;
|
| + TSS_HTPM local_tpm_handle;
|
| + if ((result = Tspi_Context_GetTpmObject(context_handle, &local_tpm_handle))) {
|
| + LOG(ERROR) << "Error calling Tspi_Context_GetTpmObject: " << result;
|
| + return false;
|
| + }
|
| +
|
| + *tpm_handle = local_tpm_handle;
|
| + return true;
|
| +}
|
| +
|
| +bool Tpm::GetTpmWithAuth(TSS_HCONTEXT context_handle,
|
| + const SecureBlob& owner_password,
|
| + TSS_HTPM* tpm_handle) {
|
| + TSS_RESULT result;
|
| + TSS_HTPM local_tpm_handle;
|
| + if (!GetTpm(context_handle, &local_tpm_handle)) {
|
| + LOG(ERROR) << "Error getting TPM handle";
|
| + return false;
|
| + }
|
| +
|
| + TSS_HPOLICY tpm_usage_policy;
|
| + if ((result = Tspi_GetPolicyObject(local_tpm_handle, TSS_POLICY_USAGE,
|
| + &tpm_usage_policy))) {
|
| + LOG(ERROR) << "Error calling Tspi_GetPolicyObject: " << result;
|
| + return false;
|
| + }
|
| +
|
| + if ((result = Tspi_Policy_SetSecret(tpm_usage_policy, TSS_SECRET_MODE_PLAIN,
|
| + owner_password.size(),
|
| + const_cast<BYTE *>(static_cast<const BYTE *>(
|
| + owner_password.const_data()))))) {
|
| + LOG(ERROR) << "Error calling Tspi_Policy_SetSecret: " << result;
|
| + return false;
|
| + }
|
| +
|
| + *tpm_handle = local_tpm_handle;
|
| + return true;
|
| +}
|
| +
|
| +bool Tpm::GetOwnerPassword(chromeos::Blob* owner_password) {
|
| + bool result = false;
|
| + if (password_sync_lock_.Try()) {
|
| + if (owner_password_.size() != 0) {
|
| + owner_password->assign(owner_password_.begin(), owner_password_.end());
|
| + result = true;
|
| + }
|
| + password_sync_lock_.Release();
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +bool Tpm::InitializeTpm() {
|
| + if (!IsConnected()) {
|
| + Connect();
|
| + }
|
| +
|
| + if (!IsConnected()) {
|
| + LOG(ERROR) << "Failed to connect to TPM";
|
| + return false;
|
| + }
|
| +
|
| + if (is_disabled_) {
|
| + LOG(ERROR) << "Error TPM is disabled";
|
| + return false;
|
| + }
|
| +
|
| + if (is_owned_) {
|
| + return false;
|
| + }
|
| +
|
| + file_util::Delete(FilePath(kOpenCryptokiPath), true);
|
| + file_util::Delete(FilePath(kTpmOwnedFile), false);
|
| +
|
| + if (!IsEndorsementKeyAvailable(context_handle_)) {
|
| + if (!CreateEndorsementKey(context_handle_)) {
|
| + LOG(ERROR) << "Failed to create endorsement key";
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + if (!IsEndorsementKeyAvailable(context_handle_)) {
|
| + LOG(ERROR) << "Endorsement key is not available";
|
| + return false;
|
| + }
|
| +
|
| + if (!TakeOwnership(context_handle_, kMaxTimeoutRetries)) {
|
| + LOG(ERROR) << "Take Ownership failed";
|
| + return false;
|
| + }
|
| +
|
| + SecureBlob owner_password;
|
| + password_sync_lock_.Acquire();
|
| + owner_password.assign(owner_password_.begin(), owner_password_.end());
|
| + password_sync_lock_.Release();
|
| +
|
| + if (!ZeroSrkPassword(context_handle_, owner_password)) {
|
| + LOG(ERROR) << "Couldn't zero SRK password";
|
| + return false;
|
| + }
|
| +
|
| + if (!UnrestrictSrk(context_handle_, owner_password)) {
|
| + LOG(ERROR) << "Couldn't unrestrict the SRK";
|
| + return false;
|
| + }
|
| +
|
| + is_owned_ = true;
|
| +
|
| + file_util::WriteFile(FilePath(kTpmOwnedFile), NULL, 0);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace tpm_init
|
|
|