//
// 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_utility_impl.h"

#include <base/logging.h>
#include <base/memory/scoped_ptr.h>
#include <base/sha1.h>
#include <base/stl_util.h>
#include <crypto/openssl_util.h>
#include <crypto/secure_hash.h>
#include <crypto/sha2.h>
#include <openssl/aes.h>
#include <openssl/rand.h>

#include "trunks/authorization_delegate.h"
#include "trunks/blob_parser.h"
#include "trunks/error_codes.h"
#include "trunks/hmac_authorization_delegate.h"
#include "trunks/hmac_session.h"
#include "trunks/policy_session.h"
#include "trunks/scoped_key_handle.h"
#include "trunks/tpm_constants.h"
#include "trunks/tpm_state.h"
#include "trunks/trunks_factory.h"

namespace {

const char kPlatformPassword[] = "cros-platform";
const char kWellKnownPassword[] = "cros-password";
const size_t kMaxPasswordLength = 32;
// The below maximum is defined in TPM 2.0 Library Spec Part 2 Section 13.1
const uint32_t kMaxNVSpaceIndex = (1<<24) - 1;

// 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;
}

std::string HashString(const std::string& plaintext,
                       trunks::TPM_ALG_ID hash_alg) {
  switch (hash_alg) {
    case trunks::TPM_ALG_SHA1:
      return base::SHA1HashString(plaintext);
    case trunks::TPM_ALG_SHA256:
      return crypto::SHA256HashString(plaintext);
  }
  NOTREACHED();
  return std::string();
}

}  // namespace

namespace trunks {

TpmUtilityImpl::TpmUtilityImpl(const TrunksFactory& factory)
    : factory_(factory) {
  crypto::EnsureOpenSSLInit();
}

TpmUtilityImpl::~TpmUtilityImpl() {
}

TPM_RC TpmUtilityImpl::Startup() {
  TPM_RC result = TPM_RC_SUCCESS;
  Tpm* tpm = factory_.GetTpm();
  result = tpm->StartupSync(TPM_SU_CLEAR, nullptr);
  // 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. */, nullptr);
  if (result) {
    LOG(ERROR) << __func__ << ": " << GetErrorString(result);
    return result;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::Clear() {
  TPM_RC result = TPM_RC_SUCCESS;
  scoped_ptr<AuthorizationDelegate> password_delegate(
      factory_.GetPasswordAuthorization(""));
  result = factory_.GetTpm()->ClearSync(TPM_RH_PLATFORM,
                                        NameFromHandle(TPM_RH_PLATFORM),
                                        password_delegate.get());
  // If there was an error in the initialization, platform auth is in a bad
  // state.
  if (result == TPM_RC_AUTH_MISSING) {
    scoped_ptr<AuthorizationDelegate> authorization(
        factory_.GetPasswordAuthorization(kPlatformPassword));
    result = factory_.GetTpm()->ClearSync(TPM_RH_PLATFORM,
                                          NameFromHandle(TPM_RH_PLATFORM),
                                          authorization.get());
  }
  if (GetFormatOneError(result) == TPM_RC_BAD_AUTH) {
    LOG(INFO) << "Clear failed because of BAD_AUTH. This probably means "
              << "that the TPM was already initialized.";
    return result;
  }
  if (result) {
    LOG(ERROR) << "Failed to clear the TPM: " << GetErrorString(result);
  }
  return result;
}

void TpmUtilityImpl::Shutdown() {
  TPM_RC return_code = factory_.GetTpm()->ShutdownSync(TPM_SU_CLEAR, nullptr);
  if (return_code && return_code != TPM_RC_INITIALIZE) {
    // This should not happen, but if it does, there is nothing we can do.
    LOG(ERROR) << "Error shutting down: " << GetErrorString(return_code);
  }
}

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()) {
    scoped_ptr<AuthorizationDelegate> empty_password(
        factory_.GetPasswordAuthorization(""));
    result = SetHierarchyAuthorization(TPM_RH_PLATFORM,
                                       kPlatformPassword,
                                       empty_password.get());
    if (GetFormatOneError(result) == TPM_RC_BAD_AUTH) {
      // Most likely the platform password has already been set.
      result = TPM_RC_SUCCESS;
    }
    if (result != TPM_RC_SUCCESS) {
      LOG(ERROR) << __func__ << ": " << GetErrorString(result);
      return result;
    }
    result = AllocatePCR(kPlatformPassword);
    if (result != TPM_RC_SUCCESS) {
      LOG(ERROR) << __func__ << ": " << GetErrorString(result);
      return result;
    }
    scoped_ptr<AuthorizationDelegate> authorization(
        factory_.GetPasswordAuthorization(kPlatformPassword));
    result = DisablePlatformHierarchy(authorization.get());
    if (result != TPM_RC_SUCCESS) {
      LOG(ERROR) << __func__ << ": " << GetErrorString(result);
      return result;
    }
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::AllocatePCR(const std::string& platform_password) {
  TPM_RC result;
  TPMI_YES_NO more_data = YES;
  TPMS_CAPABILITY_DATA capability_data;
  result = factory_.GetTpm()->GetCapabilitySync(
      TPM_CAP_PCRS, 0 /*property (not used)*/, 1 /*property_count*/, &more_data,
      &capability_data, nullptr /*authorization_delegate*/);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error querying PCRs: " << GetErrorString(result);
    return result;
  }
  TPML_PCR_SELECTION& existing_pcrs = capability_data.data.assigned_pcr;
  bool sha256_needed = true;
  std::vector<TPMI_ALG_HASH> pcr_banks_to_remove;
  for (uint32_t i = 0; i < existing_pcrs.count; ++i) {
    if (existing_pcrs.pcr_selections[i].hash == TPM_ALG_SHA256) {
      sha256_needed = false;
    } else {
      pcr_banks_to_remove.push_back(existing_pcrs.pcr_selections[i].hash);
    }
  }
  if (!sha256_needed && pcr_banks_to_remove.empty()) {
    return TPM_RC_SUCCESS;
  }
  TPML_PCR_SELECTION pcr_allocation;
  memset(&pcr_allocation, 0, sizeof(pcr_allocation));
  if (sha256_needed) {
    pcr_allocation.pcr_selections[pcr_allocation.count].hash = TPM_ALG_SHA256;
    pcr_allocation.pcr_selections[pcr_allocation.count].sizeof_select =
        PCR_SELECT_MIN;
    for (int i = 0; i < PCR_SELECT_MIN; ++i) {
      pcr_allocation.pcr_selections[pcr_allocation.count].pcr_select[i] = 0xff;
    }
    ++pcr_allocation.count;
  }
  for (auto pcr_type : pcr_banks_to_remove) {
    pcr_allocation.pcr_selections[pcr_allocation.count].hash = pcr_type;
    pcr_allocation.pcr_selections[pcr_allocation.count].sizeof_select =
        PCR_SELECT_MAX;
    ++pcr_allocation.count;
  }
  scoped_ptr<AuthorizationDelegate> platform_delegate(
      factory_.GetPasswordAuthorization(platform_password));
  TPMI_YES_NO allocation_success;
  uint32_t max_pcr;
  uint32_t size_needed;
  uint32_t size_available;
  result = factory_.GetTpm()->PCR_AllocateSync(TPM_RH_PLATFORM,
                                               NameFromHandle(TPM_RH_PLATFORM),
                                               pcr_allocation,
                                               &allocation_success,
                                               &max_pcr,
                                               &size_needed,
                                               &size_available,
                                               platform_delegate.get());
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error allocating PCRs: " << GetErrorString(result);
    return result;
  }
  if (allocation_success != YES) {
    LOG(ERROR) << "PCR allocation unsuccessful.";
    return TPM_RC_FAILURE;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::TakeOwnership(const std::string& owner_password,
                                     const std::string& endorsement_password,
                                     const std::string& lockout_password) {
  // First we set the storage hierarchy authorization to the well know default
  // password.
  TPM_RC result = TPM_RC_SUCCESS;
  result = SetKnownOwnerPassword(kWellKnownPassword);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error injecting known password: " << GetErrorString(result);
    return result;
  }

  result = CreateStorageRootKeys(kWellKnownPassword);
  if (result) {
    LOG(ERROR) << "Error creating SRKs: " << GetErrorString(result);
    return result;
  }
  result = CreateSaltingKey(kWellKnownPassword);
  if (result) {
    LOG(ERROR) << "Error creating salting key: "
               << GetErrorString(result);
    return result;
  }

  scoped_ptr<HmacSession> session = factory_.GetHmacSession();
  result = session->StartUnboundSession(true);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error initializing AuthorizationSession: "
               << GetErrorString(result);
    return result;
  }
  scoped_ptr<TpmState> tpm_state(factory_.GetTpmState());
  result = tpm_state->Initialize();
  session->SetEntityAuthorizationValue("");
  session->SetFutureAuthorizationValue(endorsement_password);
  if (!tpm_state->IsEndorsementPasswordSet()) {
    result = SetHierarchyAuthorization(TPM_RH_ENDORSEMENT,
                                       endorsement_password,
                                       session->GetDelegate());
    if (result) {
      LOG(ERROR) << __func__ << ": " << GetErrorString(result);
      return result;
    }
  }
  session->SetFutureAuthorizationValue(lockout_password);
  if (!tpm_state->IsLockoutPasswordSet()) {
    result = SetHierarchyAuthorization(TPM_RH_LOCKOUT,
                                       lockout_password,
                                       session->GetDelegate());
    if (result) {
      LOG(ERROR) << __func__ << ": " << GetErrorString(result);
      return result;
    }
  }
  // We take ownership of owner hierarchy last.
  session->SetEntityAuthorizationValue(kWellKnownPassword);
  session->SetFutureAuthorizationValue(owner_password);
  result = SetHierarchyAuthorization(TPM_RH_OWNER,
                                     owner_password,
                                     session->GetDelegate());
  if ((GetFormatOneError(result) == TPM_RC_BAD_AUTH) &&
      tpm_state->IsOwnerPasswordSet()) {
    LOG(WARNING) << "Error changing owner password. This probably because "
                 << "ownership is already taken.";
    return TPM_RC_SUCCESS;
  } else if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error changing owner authorization: "
               << GetErrorString(result);
    return result;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::StirRandom(const std::string& entropy_data,
                                  AuthorizationDelegate* delegate) {
  std::string digest = crypto::SHA256HashString(entropy_data);
  TPM2B_SENSITIVE_DATA random_bytes = Make_TPM2B_SENSITIVE_DATA(digest);
  return factory_.GetTpm()->StirRandomSync(random_bytes, delegate);
}

TPM_RC TpmUtilityImpl::GenerateRandom(size_t num_bytes,
                                      AuthorizationDelegate* delegate,
                                      std::string* random_data) {
  CHECK(random_data);
  size_t bytes_left = num_bytes;
  random_data->clear();
  TPM_RC rc;
  TPM2B_DIGEST digest;
  while (bytes_left > 0) {
    rc = factory_.GetTpm()->GetRandomSync(bytes_left,
                                          &digest,
                                          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,
                                 AuthorizationDelegate* 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,
                                           delegate);
}

TPM_RC TpmUtilityImpl::ReadPCR(int pcr_index, std::string* pcr_value) {
  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_MIN;
  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,
                                              nullptr);
  if (rc) {
    LOG(INFO) << "Error trying to read a pcr: " << GetErrorString(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, 1U);
  pcr_value->assign(StringFrom_TPM2B_DIGEST(pcr_values.digests[0]));
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::AsymmetricEncrypt(TPM_HANDLE key_handle,
                                         TPM_ALG_ID scheme,
                                         TPM_ALG_ID hash_alg,
                                         const std::string& plaintext,
                                         AuthorizationDelegate* delegate,
                                         std::string* ciphertext) {
  TPMT_RSA_DECRYPT in_scheme;
  if (hash_alg == TPM_ALG_NULL) {
    hash_alg = TPM_ALG_SHA256;
  }
  if (scheme == TPM_ALG_RSAES) {
    in_scheme.scheme = TPM_ALG_RSAES;
  } else if (scheme == TPM_ALG_OAEP || scheme == TPM_ALG_NULL) {
    in_scheme.scheme = TPM_ALG_OAEP;
    in_scheme.details.oaep.hash_alg = hash_alg;
  } else {
    LOG(ERROR) << "Invalid Signing scheme used.";
    return SAPI_RC_BAD_PARAMETER;
  }

  TPMT_PUBLIC public_area;
  TPM_RC result = GetKeyPublicArea(key_handle, &public_area);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error finding public area for: " << key_handle;
    return result;
  } else if (public_area.type != TPM_ALG_RSA) {
    LOG(ERROR) << "Key handle given is not an RSA key";
    return SAPI_RC_BAD_PARAMETER;
  } else if ((public_area.object_attributes & kDecrypt) == 0) {
    LOG(ERROR) << "Key handle given is not a decryption key";
    return SAPI_RC_BAD_PARAMETER;
  }
  if ((public_area.object_attributes & kRestricted) != 0) {
    LOG(ERROR) << "Cannot use RSAES for encryption with a restricted key";
    return SAPI_RC_BAD_PARAMETER;
  }
  std::string key_name;
  result = ComputeKeyName(public_area, &key_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error computing key name for: " << key_handle;
    return result;
  }

  TPM2B_DATA label;
  label.size = 0;
  TPM2B_PUBLIC_KEY_RSA in_message = Make_TPM2B_PUBLIC_KEY_RSA(plaintext);
  TPM2B_PUBLIC_KEY_RSA out_message;
  result = factory_.GetTpm()->RSA_EncryptSync(key_handle,
                                              key_name,
                                              in_message,
                                              in_scheme,
                                              label,
                                              &out_message,
                                              delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error performing RSA encrypt: "
               << GetErrorString(result);
    return result;
  }
  ciphertext->assign(StringFrom_TPM2B_PUBLIC_KEY_RSA(out_message));
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::AsymmetricDecrypt(TPM_HANDLE key_handle,
                                         TPM_ALG_ID scheme,
                                         TPM_ALG_ID hash_alg,
                                         const std::string& ciphertext,
                                         AuthorizationDelegate* delegate,
                                         std::string* plaintext) {
  TPMT_RSA_DECRYPT in_scheme;
  if (hash_alg == TPM_ALG_NULL) {
    hash_alg = TPM_ALG_SHA256;
  }
  if (scheme == TPM_ALG_RSAES) {
    in_scheme.scheme = TPM_ALG_RSAES;
  } else if (scheme == TPM_ALG_OAEP || scheme == TPM_ALG_NULL) {
    in_scheme.scheme = TPM_ALG_OAEP;
    in_scheme.details.oaep.hash_alg = hash_alg;
  } else {
    LOG(ERROR) << "Invalid Signing scheme used.";
    return SAPI_RC_BAD_PARAMETER;
  }
  TPM_RC result;
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  TPMT_PUBLIC public_area;
  result = GetKeyPublicArea(key_handle, &public_area);
  if (result) {
    LOG(ERROR) << "Error finding public area for: " << key_handle;
    return result;
  } else if (public_area.type != TPM_ALG_RSA) {
    LOG(ERROR) << "Key handle given is not an RSA key";
    return SAPI_RC_BAD_PARAMETER;
  } else if ((public_area.object_attributes & kDecrypt) == 0) {
    LOG(ERROR) << "Key handle given is not a decryption key";
    return SAPI_RC_BAD_PARAMETER;
  }
  if ((public_area.object_attributes & kRestricted) != 0) {
    LOG(ERROR) << "Cannot use RSAES for encryption with a restricted key";
    return SAPI_RC_BAD_PARAMETER;
  }
  std::string key_name;
  result = ComputeKeyName(public_area, &key_name);
  if (result) {
    LOG(ERROR) << "Error computing key name for: " << key_handle;
    return result;
  }

  TPM2B_DATA label;
  label.size = 0;
  TPM2B_PUBLIC_KEY_RSA in_message = Make_TPM2B_PUBLIC_KEY_RSA(ciphertext);
  TPM2B_PUBLIC_KEY_RSA out_message;
  result = factory_.GetTpm()->RSA_DecryptSync(key_handle,
                                              key_name,
                                              in_message,
                                              in_scheme,
                                              label,
                                              &out_message,
                                              delegate);
  if (result) {
    LOG(ERROR) << "Error performing RSA decrypt: "
               << GetErrorString(result);
    return result;
  }
  plaintext->assign(StringFrom_TPM2B_PUBLIC_KEY_RSA(out_message));
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::Sign(TPM_HANDLE key_handle,
                            TPM_ALG_ID scheme,
                            TPM_ALG_ID hash_alg,
                            const std::string& plaintext,
                            AuthorizationDelegate* delegate,
                            std::string* signature) {
  TPMT_SIG_SCHEME in_scheme;
  if (hash_alg == TPM_ALG_NULL) {
    hash_alg = TPM_ALG_SHA256;
  }
  if (scheme == TPM_ALG_RSAPSS) {
    in_scheme.scheme = TPM_ALG_RSAPSS;
    in_scheme.details.rsapss.hash_alg = hash_alg;
  } else if (scheme == TPM_ALG_RSASSA || scheme == TPM_ALG_NULL) {
    in_scheme.scheme = TPM_ALG_RSASSA;
    in_scheme.details.rsassa.hash_alg = hash_alg;
  } else {
    LOG(ERROR) << "Invalid Signing scheme used.";
    return SAPI_RC_BAD_PARAMETER;
  }
  TPM_RC result;
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  TPMT_PUBLIC public_area;
  result = GetKeyPublicArea(key_handle, &public_area);
  if (result) {
    LOG(ERROR) << "Error finding public area for: " << key_handle;
    return result;
  } else if (public_area.type != TPM_ALG_RSA) {
    LOG(ERROR) << "Key handle given is not an RSA key";
    return SAPI_RC_BAD_PARAMETER;
  } else if ((public_area.object_attributes & kSign) == 0) {
    LOG(ERROR) << "Key handle given is not a signging key";
    return SAPI_RC_BAD_PARAMETER;
  } else if ((public_area.object_attributes & kRestricted) != 0) {
    LOG(ERROR) << "Key handle references a restricted key";
    return SAPI_RC_BAD_PARAMETER;
  }

  std::string key_name;
  result = ComputeKeyName(public_area, &key_name);
  if (result) {
    LOG(ERROR) << "Error computing key name for: " << key_handle;
    return result;
  }
  std::string digest = HashString(plaintext, hash_alg);
  TPM2B_DIGEST tpm_digest = Make_TPM2B_DIGEST(digest);
  TPMT_SIGNATURE signature_out;
  TPMT_TK_HASHCHECK validation;
  validation.tag = TPM_ST_HASHCHECK;
  validation.hierarchy = TPM_RH_NULL;
  validation.digest.size = 0;
  result = factory_.GetTpm()->SignSync(key_handle,
                                       key_name,
                                       tpm_digest,
                                       in_scheme,
                                       validation,
                                       &signature_out,
                                       delegate);
  if (result) {
    LOG(ERROR) << "Error signing digest: " << GetErrorString(result);
    return result;
  }
  if (scheme == TPM_ALG_RSAPSS) {
    signature->resize(signature_out.signature.rsapss.sig.size);
    signature->assign(StringFrom_TPM2B_PUBLIC_KEY_RSA(
        signature_out.signature.rsapss.sig));
  } else {
    signature->resize(signature_out.signature.rsassa.sig.size);
    signature->assign(StringFrom_TPM2B_PUBLIC_KEY_RSA(
        signature_out.signature.rsassa.sig));
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::Verify(TPM_HANDLE key_handle,
                              TPM_ALG_ID scheme,
                              TPM_ALG_ID hash_alg,
                              const std::string& plaintext,
                              const std::string& signature,
                              AuthorizationDelegate* delegate) {
  TPMT_PUBLIC public_area;
  TPM_RC return_code = GetKeyPublicArea(key_handle, &public_area);
  if (return_code) {
    LOG(ERROR) << "Error finding public area for: " << key_handle;
    return return_code;
  } else if (public_area.type != TPM_ALG_RSA) {
    LOG(ERROR) << "Key handle given is not an RSA key";
    return SAPI_RC_BAD_PARAMETER;
  } else if ((public_area.object_attributes & kSign) == 0) {
    LOG(ERROR) << "Key handle given is not a signing key";
    return SAPI_RC_BAD_PARAMETER;
  } else if ((public_area.object_attributes & kRestricted) != 0) {
    LOG(ERROR) << "Cannot use RSAPSS for signing with a restricted key";
    return SAPI_RC_BAD_PARAMETER;
  }
  if (hash_alg == TPM_ALG_NULL) {
    hash_alg = TPM_ALG_SHA256;
  }

  TPMT_SIGNATURE signature_in;
  if (scheme == TPM_ALG_RSAPSS) {
    signature_in.sig_alg = TPM_ALG_RSAPSS;
    signature_in.signature.rsapss.hash = hash_alg;
    signature_in.signature.rsapss.sig = Make_TPM2B_PUBLIC_KEY_RSA(signature);
  } else if (scheme == TPM_ALG_NULL || scheme == TPM_ALG_RSASSA) {
    signature_in.sig_alg = TPM_ALG_RSASSA;
    signature_in.signature.rsassa.hash = hash_alg;
    signature_in.signature.rsassa.sig = Make_TPM2B_PUBLIC_KEY_RSA(signature);
  } else {
    LOG(ERROR) << "Invalid scheme used to verify signature.";
    return SAPI_RC_BAD_PARAMETER;
  }
  std::string key_name;
  TPMT_TK_VERIFIED verified;
  std::string digest = HashString(plaintext, hash_alg);
  TPM2B_DIGEST tpm_digest = Make_TPM2B_DIGEST(digest);
  return_code = factory_.GetTpm()->VerifySignatureSync(key_handle,
                                                       key_name,
                                                       tpm_digest,
                                                       signature_in,
                                                       &verified,
                                                       delegate);
  if (return_code == TPM_RC_SIGNATURE) {
    LOG(WARNING) << "Incorrect signature for given digest.";
    return TPM_RC_SIGNATURE;
  } else if (return_code && return_code != TPM_RC_SIGNATURE) {
    LOG(ERROR) << "Error verifying signature: " << GetErrorString(return_code);
    return return_code;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::CertifyCreation(TPM_HANDLE key_handle,
                                       const std::string& creation_blob) {
  TPM2B_CREATION_DATA creation_data;
  TPM2B_DIGEST creation_hash;
  TPMT_TK_CREATION creation_ticket;
  if (!factory_.GetBlobParser()->ParseCreationBlob(creation_blob,
                                                   &creation_data,
                                                   &creation_hash,
                                                   &creation_ticket)) {
    LOG(ERROR) << "Error parsing CreationBlob.";
    return SAPI_RC_BAD_PARAMETER;
  }
  TPM2B_DATA qualifying_data;
  qualifying_data.size = 0;
  TPMT_SIG_SCHEME in_scheme;
  in_scheme.scheme = TPM_ALG_NULL;
  TPM2B_ATTEST certify_info;
  TPMT_SIGNATURE signature;
  scoped_ptr<AuthorizationDelegate> delegate =
      factory_.GetPasswordAuthorization("");
  TPM_RC result = factory_.GetTpm()->CertifyCreationSync(TPM_RH_NULL,
                                                         "",
                                                         key_handle,
                                                         "",
                                                         qualifying_data,
                                                         creation_hash,
                                                         in_scheme,
                                                         creation_ticket,
                                                         &certify_info,
                                                         &signature,
                                                         delegate.get());
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error certifying key creation: " << GetErrorString(result);
    return result;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::ChangeKeyAuthorizationData(
    TPM_HANDLE key_handle,
    const std::string& new_password,
    AuthorizationDelegate* delegate,
    std::string* key_blob) {
  TPM_RC result;
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  std::string key_name;
  std::string parent_name;
  result = GetKeyName(key_handle, &key_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error getting Key name for key_handle: "
               << GetErrorString(result);
    return result;
  }
  result = GetKeyName(kRSAStorageRootKey, &parent_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error getting Key name for RSA-SRK: "
               << GetErrorString(result);
    return result;
  }
  TPM2B_AUTH new_auth = Make_TPM2B_DIGEST(new_password);
  TPM2B_PRIVATE new_private_data;
  new_private_data.size = 0;
  result = factory_.GetTpm()->ObjectChangeAuthSync(key_handle,
                                                   key_name,
                                                   kRSAStorageRootKey,
                                                   parent_name,
                                                   new_auth,
                                                   &new_private_data,
                                                   delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error changing object authorization data: "
               << GetErrorString(result);
    return result;
  }
  if (key_blob) {
    TPMT_PUBLIC public_data;
    result = GetKeyPublicArea(key_handle, &public_data);
    if (result != TPM_RC_SUCCESS) {
      return result;
    }
    if (!factory_.GetBlobParser()->SerializeKeyBlob(
        Make_TPM2B_PUBLIC(public_data), new_private_data, key_blob)) {
      return SAPI_RC_BAD_TCTI_STRUCTURE;
    }
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::ImportRSAKey(AsymmetricKeyUsage key_type,
                                    const std::string& modulus,
                                    uint32_t public_exponent,
                                    const std::string& prime_factor,
                                    const std::string& password,
                                    AuthorizationDelegate* delegate,
                                    std::string* key_blob) {
  TPM_RC result;
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  std::string parent_name;
  result = GetKeyName(kRSAStorageRootKey, &parent_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error getting Key name for RSA-SRK: "
               << GetErrorString(result);
    return result;
  }
  TPMT_PUBLIC public_area = CreateDefaultPublicArea(TPM_ALG_RSA);
  public_area.object_attributes = kUserWithAuth | kNoDA;
  switch (key_type) {
    case AsymmetricKeyUsage::kDecryptKey:
      public_area.object_attributes |= kDecrypt;
      break;
    case AsymmetricKeyUsage::kSignKey:
      public_area.object_attributes |= kSign;
      break;
    case AsymmetricKeyUsage::kDecryptAndSignKey:
      public_area.object_attributes |= (kSign | kDecrypt);
      break;
  }
  public_area.parameters.rsa_detail.key_bits = modulus.size() * 8;
  public_area.parameters.rsa_detail.exponent = public_exponent;
  public_area.unique.rsa = Make_TPM2B_PUBLIC_KEY_RSA(modulus);
  TPM2B_DATA encryption_key;
  encryption_key.size = kAesKeySize;
  CHECK_EQ(RAND_bytes(encryption_key.buffer, encryption_key.size), 1) <<
      "Error generating a cryptographically random Aes Key.";
  TPM2B_PUBLIC public_data = Make_TPM2B_PUBLIC(public_area);
  TPM2B_ENCRYPTED_SECRET in_sym_seed = Make_TPM2B_ENCRYPTED_SECRET("");
  TPMT_SYM_DEF_OBJECT symmetric_alg;
  symmetric_alg.algorithm = TPM_ALG_AES;
  symmetric_alg.key_bits.aes = kAesKeySize * 8;
  symmetric_alg.mode.aes = TPM_ALG_CFB;
  TPMT_SENSITIVE in_sensitive;
  in_sensitive.sensitive_type = TPM_ALG_RSA;
  in_sensitive.auth_value = Make_TPM2B_DIGEST(password);
  in_sensitive.seed_value = Make_TPM2B_DIGEST("");
  in_sensitive.sensitive.rsa = Make_TPM2B_PRIVATE_KEY_RSA(prime_factor);
  TPM2B_PRIVATE private_data;
  result = EncryptPrivateData(in_sensitive, public_area,
                              &private_data, &encryption_key);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error creating encrypted private struct: "
               << GetErrorString(result);
    return result;
  }
  TPM2B_PRIVATE tpm_private_data;
  tpm_private_data.size = 0;
  result = factory_.GetTpm()->ImportSync(kRSAStorageRootKey,
                                         parent_name,
                                         encryption_key,
                                         public_data,
                                         private_data,
                                         in_sym_seed,
                                         symmetric_alg,
                                         &tpm_private_data,
                                         delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error importing key: " << GetErrorString(result);
    return result;
  }
  if (key_blob) {
    if (!factory_.GetBlobParser()->SerializeKeyBlob(
        public_data, tpm_private_data, key_blob)) {
      return SAPI_RC_BAD_TCTI_STRUCTURE;
    }
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::CreateRSAKeyPair(AsymmetricKeyUsage key_type,
                                        int modulus_bits,
                                        uint32_t public_exponent,
                                        const std::string& password,
                                        const std::string& policy_digest,
                                        bool use_only_policy_authorization,
                                        int creation_pcr_index,
                                        AuthorizationDelegate* delegate,
                                        std::string* key_blob,
                                        std::string* creation_blob) {
  CHECK(key_blob);
  TPM_RC result;
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  std::string parent_name;
  result = GetKeyName(kRSAStorageRootKey, &parent_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error getting Key name for RSA-SRK: "
               << GetErrorString(result);
    return result;
  }
  TPMT_PUBLIC public_area = CreateDefaultPublicArea(TPM_ALG_RSA);
  public_area.auth_policy = Make_TPM2B_DIGEST(policy_digest);
  public_area.object_attributes |=
      (kSensitiveDataOrigin | kUserWithAuth | kNoDA);
  switch (key_type) {
    case AsymmetricKeyUsage::kDecryptKey:
      public_area.object_attributes |= kDecrypt;
      break;
    case AsymmetricKeyUsage::kSignKey:
      public_area.object_attributes |= kSign;
      break;
    case AsymmetricKeyUsage::kDecryptAndSignKey:
      public_area.object_attributes |= (kSign | kDecrypt);
      break;
  }
  if (use_only_policy_authorization && !policy_digest.empty()) {
    public_area.object_attributes |= kAdminWithPolicy;
    public_area.object_attributes &= (~kUserWithAuth);
  }
  public_area.parameters.rsa_detail.key_bits = modulus_bits;
  public_area.parameters.rsa_detail.exponent = public_exponent;
  TPML_PCR_SELECTION creation_pcrs = {};
  if (creation_pcr_index == kNoCreationPCR) {
    creation_pcrs.count = 0;
  } else if (creation_pcr_index < 0 ||
             creation_pcr_index > (PCR_SELECT_MIN * 8)) {
    LOG(ERROR) << "Creation PCR index is not within the allocated bank.";
    return SAPI_RC_BAD_PARAMETER;
  } else {
    creation_pcrs.count = 1;
    creation_pcrs.pcr_selections[0].hash = TPM_ALG_SHA256;
    creation_pcrs.pcr_selections[0].sizeof_select = PCR_SELECT_MIN;
    creation_pcrs.pcr_selections[0].pcr_select[creation_pcr_index / 8] =
        1 << (creation_pcr_index % 8);
  }
  TPMS_SENSITIVE_CREATE sensitive;
  sensitive.user_auth = Make_TPM2B_DIGEST(password);
  sensitive.data = Make_TPM2B_SENSITIVE_DATA("");
  TPM2B_SENSITIVE_CREATE sensitive_create = Make_TPM2B_SENSITIVE_CREATE(
      sensitive);
  TPM2B_DATA outside_info = Make_TPM2B_DATA("");
  TPM2B_PUBLIC out_public;
  out_public.size = 0;
  TPM2B_PRIVATE out_private;
  out_private.size = 0;
  TPM2B_CREATION_DATA creation_data;
  TPM2B_DIGEST creation_hash;
  TPMT_TK_CREATION creation_ticket;
  result = factory_.GetTpm()->CreateSync(kRSAStorageRootKey,
                                         parent_name,
                                         sensitive_create,
                                         Make_TPM2B_PUBLIC(public_area),
                                         outside_info,
                                         creation_pcrs,
                                         &out_private,
                                         &out_public,
                                         &creation_data,
                                         &creation_hash,
                                         &creation_ticket,
                                         delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error creating RSA key: " << GetErrorString(result);
    return result;
  }
  if (!factory_.GetBlobParser()->SerializeKeyBlob(
      out_public, out_private, key_blob)) {
    return SAPI_RC_BAD_TCTI_STRUCTURE;
  }
  if (creation_blob) {
    if (!factory_.GetBlobParser()->SerializeCreationBlob(
        creation_data, creation_hash, creation_ticket, creation_blob)) {
      return SAPI_RC_BAD_TCTI_STRUCTURE;
    }
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::LoadKey(const std::string& key_blob,
                               AuthorizationDelegate* delegate,
                               TPM_HANDLE* key_handle) {
  CHECK(key_handle);
  TPM_RC result;
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  std::string parent_name;
  result = GetKeyName(kRSAStorageRootKey, &parent_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error getting parent key name: " << GetErrorString(result);
    return result;
  }
  TPM2B_PUBLIC in_public;
  TPM2B_PRIVATE in_private;
  if (!factory_.GetBlobParser()->ParseKeyBlob(
      key_blob, &in_public, &in_private)) {
    return SAPI_RC_BAD_TCTI_STRUCTURE;
  }
  TPM2B_NAME key_name;
  key_name.size = 0;
  result = factory_.GetTpm()->LoadSync(kRSAStorageRootKey,
                                       parent_name,
                                       in_private,
                                       in_public,
                                       key_handle,
                                       &key_name,
                                       delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error loading key: " << GetErrorString(result);
    return result;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::GetKeyName(TPM_HANDLE handle, std::string* name) {
  CHECK(name);
  TPM_RC result;
  TPMT_PUBLIC public_data;
  result = GetKeyPublicArea(handle, &public_data);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error fetching public info: " << GetErrorString(result);
    return result;
  }
  result = ComputeKeyName(public_data, name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error computing key name: " << GetErrorString(result);
    return TPM_RC_SUCCESS;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::GetKeyPublicArea(TPM_HANDLE handle,
                                        TPMT_PUBLIC* public_data) {
  CHECK(public_data);
  TPM2B_NAME out_name;
  TPM2B_PUBLIC public_area;
  TPM2B_NAME qualified_name;
  std::string handle_name;  // Unused
  TPM_RC return_code = factory_.GetTpm()->ReadPublicSync(handle,
                                                         handle_name,
                                                         &public_area,
                                                         &out_name,
                                                         &qualified_name,
                                                         nullptr);
  if (return_code) {
    LOG(ERROR) << "Error getting public area for object: " << handle;
    return return_code;
  }
  *public_data = public_area.public_area;
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::SealData(const std::string& data_to_seal,
                                const std::string& policy_digest,
                                AuthorizationDelegate* delegate,
                                std::string* sealed_data) {
  CHECK(sealed_data);
  TPM_RC result;
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  std::string parent_name;
  result = GetKeyName(kRSAStorageRootKey, &parent_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error getting Key name for RSA-SRK: "
               << GetErrorString(result);
    return result;
  }
  // We seal data to the TPM by creating a KEYEDHASH object with sign and
  // decrypt attributes disabled.
  TPMT_PUBLIC public_area = CreateDefaultPublicArea(TPM_ALG_KEYEDHASH);
  public_area.auth_policy = Make_TPM2B_DIGEST(policy_digest);
  public_area.object_attributes = kAdminWithPolicy | kNoDA;
  public_area.unique.keyed_hash.size = 0;
  TPML_PCR_SELECTION creation_pcrs = {};
  TPMS_SENSITIVE_CREATE sensitive;
  sensitive.user_auth = Make_TPM2B_DIGEST("");
  sensitive.data = Make_TPM2B_SENSITIVE_DATA(data_to_seal);
  TPM2B_SENSITIVE_CREATE sensitive_create = Make_TPM2B_SENSITIVE_CREATE(
      sensitive);
  TPM2B_DATA outside_info = Make_TPM2B_DATA("");
  TPM2B_PUBLIC out_public;
  out_public.size = 0;
  TPM2B_PRIVATE out_private;
  out_private.size = 0;
  TPM2B_CREATION_DATA creation_data;
  TPM2B_DIGEST creation_hash;
  TPMT_TK_CREATION creation_ticket;
  result = factory_.GetTpm()->CreateSync(kRSAStorageRootKey,
                                         parent_name,
                                         sensitive_create,
                                         Make_TPM2B_PUBLIC(public_area),
                                         outside_info,
                                         creation_pcrs,
                                         &out_private,
                                         &out_public,
                                         &creation_data,
                                         &creation_hash,
                                         &creation_ticket,
                                         delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error creating sealed object: " << GetErrorString(result);
    return result;
  }
  if (!factory_.GetBlobParser()->SerializeKeyBlob(
      out_public, out_private, sealed_data)) {
    return SAPI_RC_BAD_TCTI_STRUCTURE;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::UnsealData(const std::string& sealed_data,
                                  AuthorizationDelegate* delegate,
                                  std::string* unsealed_data) {
  CHECK(unsealed_data);
  TPM_RC result;
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  TPM_HANDLE object_handle;
  scoped_ptr<AuthorizationDelegate> password_delegate =
      factory_.GetPasswordAuthorization("");
  result = LoadKey(sealed_data, password_delegate.get(), &object_handle);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error loading sealed object: " << GetErrorString(result);
    return result;
  }
  ScopedKeyHandle sealed_object(factory_, object_handle);
  std::string object_name;
  result = GetKeyName(sealed_object.get(), &object_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error getting object name: " << GetErrorString(result);
    return result;
  }
  TPM2B_SENSITIVE_DATA out_data;
  result = factory_.GetTpm()->UnsealSync(sealed_object.get(), object_name,
                                         &out_data, delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error unsealing object: " << GetErrorString(result);
    return result;
  }
  *unsealed_data = StringFrom_TPM2B_SENSITIVE_DATA(out_data);
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::StartSession(HmacSession* session) {
  TPM_RC result = session->StartUnboundSession(true  /* enable_encryption */);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error starting unbound session: " << GetErrorString(result);
    return result;
  }
  session->SetEntityAuthorizationValue("");
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::GetPolicyDigestForPcrValue(int pcr_index,
                                                  const std::string& pcr_value,
                                                  std::string* policy_digest) {
  CHECK(policy_digest);
  scoped_ptr<PolicySession> session = factory_.GetTrialSession();
  TPM_RC result = session->StartUnboundSession(false);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error starting unbound trial session: "
               << GetErrorString(result);
    return result;
  }
  std::string mutable_pcr_value;
  if (pcr_value.empty()) {
    result = ReadPCR(pcr_index, &mutable_pcr_value);
    if (result != TPM_RC_SUCCESS) {
      LOG(ERROR) << "Error reading pcr_value: " << GetErrorString(result);
      return result;
    }
  } else {
    mutable_pcr_value = pcr_value;
  }
  result = session->PolicyPCR(pcr_index, mutable_pcr_value);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error restricting policy to PCR value: "
               << GetErrorString(result);
    return result;
  }
  result = session->GetDigest(policy_digest);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error getting policy digest: " << GetErrorString(result);
    return result;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::DefineNVSpace(uint32_t index,
                                     size_t num_bytes,
                                     AuthorizationDelegate* delegate) {
  TPM_RC result;
  if (num_bytes > MAX_NV_INDEX_SIZE) {
    result = SAPI_RC_BAD_SIZE;
    LOG(ERROR) << "Cannot define non-volatile space of given size: "
               << GetErrorString(result);
    return result;
  }
  if (index > kMaxNVSpaceIndex) {
    result = SAPI_RC_BAD_PARAMETER;
    LOG(ERROR) << "Cannot define non-volatile space with the given index: "
               << GetErrorString(result);
    return result;
  }
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  uint32_t nv_index = NV_INDEX_FIRST + index;
  TPMS_NV_PUBLIC public_data;
  public_data.nv_index = nv_index;
  public_data.name_alg = TPM_ALG_SHA256;
  // We define the following attributes for NVSpaces created:
  // TPMA_NV_NO_DA: Dictionary attack does not trigger on authorization errors.
  // TPMA_NV_OWNERWRITE: Owner authorization must be provided on write actions.
  // TPMA_NV_WRITEDEFINE: NVSpace is write lockable, and lock persists across
  //                      reboot.
  // TPMA_NV_AUTHREAD: The index authValue (default: "") can be used to
  //                   authorize read actions.
  public_data.attributes = TPMA_NV_NO_DA |
                           TPMA_NV_OWNERWRITE |
                           TPMA_NV_WRITEDEFINE |
                           TPMA_NV_AUTHREAD;
  public_data.auth_policy = Make_TPM2B_DIGEST("");
  public_data.data_size = num_bytes;
  TPM2B_AUTH authorization = Make_TPM2B_DIGEST("");
  TPM2B_NV_PUBLIC public_area = Make_TPM2B_NV_PUBLIC(public_data);
  result = factory_.GetTpm()->NV_DefineSpaceSync(
      TPM_RH_OWNER,
      NameFromHandle(TPM_RH_OWNER),
      authorization,
      public_area,
      delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error defining non-volatile space: "
               << GetErrorString(result);
    return result;
  }
  nvram_public_area_map_[index] = public_data;
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::DestroyNVSpace(uint32_t index,
                                      AuthorizationDelegate* delegate) {
  TPM_RC result;
  if (index > kMaxNVSpaceIndex) {
    result = SAPI_RC_BAD_PARAMETER;
    LOG(ERROR) << "Cannot undefine non-volatile space with the given index: "
               << GetErrorString(result);
    return result;
  }
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  std::string nv_name;
  result = GetNVSpaceName(index, &nv_name);
  if (result != TPM_RC_SUCCESS) {
    return result;
  }
  uint32_t nv_index = NV_INDEX_FIRST + index;
  result = factory_.GetTpm()->NV_UndefineSpaceSync(
      TPM_RH_OWNER,
      NameFromHandle(TPM_RH_OWNER),
      nv_index,
      nv_name,
      delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error undefining non-volatile space: "
               << GetErrorString(result);
    return result;
  }
  nvram_public_area_map_.erase(index);
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::LockNVSpace(uint32_t index,
                                   AuthorizationDelegate* delegate) {
  TPM_RC result;
  if (index > kMaxNVSpaceIndex) {
    result = SAPI_RC_BAD_PARAMETER;
    LOG(ERROR) << "Cannot lock non-volatile space with the given index: "
               << GetErrorString(result);
    return result;
  }
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  std::string nv_name;
  result = GetNVSpaceName(index, &nv_name);
  if (result != TPM_RC_SUCCESS) {
    return result;
  }
  uint32_t nv_index = NV_INDEX_FIRST + index;
  result = factory_.GetTpm()->NV_WriteLockSync(TPM_RH_OWNER,
                                               NameFromHandle(TPM_RH_OWNER),
                                               nv_index,
                                               nv_name,
                                               delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error locking non-volatile spaces: "
               << GetErrorString(result);
    return result;
  }
  auto it = nvram_public_area_map_.find(index);
  if (it != nvram_public_area_map_.end()) {
    it->second.attributes |= TPMA_NV_WRITELOCKED;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::WriteNVSpace(uint32_t index,
                                    uint32_t offset,
                                    const std::string& nvram_data,
                                    AuthorizationDelegate* delegate) {
  TPM_RC result;
  if (nvram_data.size() > MAX_NV_BUFFER_SIZE) {
    result = SAPI_RC_BAD_SIZE;
    LOG(ERROR) << "Insufficient buffer for non-volatile write: "
               << GetErrorString(result);
    return result;
  }
  if (index > kMaxNVSpaceIndex) {
    result = SAPI_RC_BAD_PARAMETER;
    LOG(ERROR) << "Cannot write to non-volatile space with the given index: "
               << GetErrorString(result);
    return result;
  }
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  std::string nv_name;
  result = GetNVSpaceName(index, &nv_name);
  if (result != TPM_RC_SUCCESS) {
    return result;
  }
  uint32_t nv_index = NV_INDEX_FIRST + index;
  result = factory_.GetTpm()->NV_WriteSync(TPM_RH_OWNER,
                                           NameFromHandle(TPM_RH_OWNER),
                                           nv_index,
                                           nv_name,
                                           Make_TPM2B_MAX_NV_BUFFER(nvram_data),
                                           offset,
                                           delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error writing to non-volatile space: "
               << GetErrorString(result);
    return result;
  }
  auto it = nvram_public_area_map_.find(index);
  if (it != nvram_public_area_map_.end()) {
    it->second.attributes |= TPMA_NV_WRITTEN;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::ReadNVSpace(uint32_t index,
                                   uint32_t offset,
                                   size_t num_bytes,
                                   std::string* nvram_data,
                                   AuthorizationDelegate* delegate) {
  TPM_RC result;
  if (num_bytes > MAX_NV_BUFFER_SIZE) {
    result = SAPI_RC_BAD_SIZE;
    LOG(ERROR) << "Insufficient buffer for non-volatile read: "
               << GetErrorString(result);
    return result;
  }
  if (index > kMaxNVSpaceIndex) {
    result = SAPI_RC_BAD_PARAMETER;
    LOG(ERROR) << "Cannot read from non-volatile space with the given index: "
               << GetErrorString(result);
    return result;
  }
  if (delegate == nullptr) {
    result = SAPI_RC_INVALID_SESSIONS;
    LOG(ERROR) << "This method needs a valid authorization delegate: "
               << GetErrorString(result);
    return result;
  }
  std::string nv_name;
  result = GetNVSpaceName(index, &nv_name);
  if (result != TPM_RC_SUCCESS) {
    return result;
  }
  uint32_t nv_index = NV_INDEX_FIRST + index;
  TPM2B_MAX_NV_BUFFER data_buffer;
  data_buffer.size = 0;
  result = factory_.GetTpm()->NV_ReadSync(nv_index,
                                          nv_name,
                                          nv_index,
                                          nv_name,
                                          num_bytes,
                                          offset,
                                          &data_buffer,
                                          delegate);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error reading from non-volatile space: "
               << GetErrorString(result);
    return result;
  }
  nvram_data->assign(StringFrom_TPM2B_MAX_NV_BUFFER(data_buffer));
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::GetNVSpaceName(uint32_t index, std::string* name) {
  TPM_RC result;
  if (index > kMaxNVSpaceIndex) {
    result = SAPI_RC_BAD_PARAMETER;
    LOG(ERROR) << "Cannot read from non-volatile space with the given index: "
               << GetErrorString(result);
    return result;
  }
  TPMS_NV_PUBLIC nv_public_data;
  result = GetNVSpacePublicArea(index, &nv_public_data);
  if (result != TPM_RC_SUCCESS) {
    return result;
  }
  result = ComputeNVSpaceName(nv_public_data, name);
  if (result != TPM_RC_SUCCESS) {
    return result;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::GetNVSpacePublicArea(uint32_t index,
                                            TPMS_NV_PUBLIC* public_data) {
  TPM_RC result;
  if (index > kMaxNVSpaceIndex) {
    result = SAPI_RC_BAD_PARAMETER;
    LOG(ERROR) << "Cannot read from non-volatile space with the given index: "
               << GetErrorString(result);
    return result;
  }
  auto it = nvram_public_area_map_.find(index);
  if (it != nvram_public_area_map_.end()) {
    *public_data = it->second;
    return TPM_RC_SUCCESS;
  }
  TPM2B_NAME nvram_name;
  TPM2B_NV_PUBLIC public_area;
  public_area.nv_public.nv_index = 0;
  uint32_t nv_index = NV_INDEX_FIRST + index;
  result = factory_.GetTpm()->NV_ReadPublicSync(nv_index,
                                                "",
                                                &public_area,
                                                &nvram_name,
                                                nullptr);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error reading non-volatile space public information: "
               << GetErrorString(result);
    return result;
  }
  *public_data = public_area.nv_public;
  nvram_public_area_map_[index] = public_area.nv_public;
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::SetKnownOwnerPassword(
    const std::string& known_owner_password) {
  scoped_ptr<TpmState> tpm_state(factory_.GetTpmState());
  TPM_RC result = tpm_state->Initialize();
  if (result) {
    LOG(ERROR) << __func__ << ": " << GetErrorString(result);
    return result;
  }
  scoped_ptr<AuthorizationDelegate> delegate =
      factory_.GetPasswordAuthorization("");
  if (tpm_state->IsOwnerPasswordSet()) {
    LOG(INFO) << "Owner password is already set. "
              << "This is normal if ownership is already taken.";
    return TPM_RC_SUCCESS;
  }
  result = SetHierarchyAuthorization(TPM_RH_OWNER,
                                     known_owner_password,
                                     delegate.get());
  if (result) {
    LOG(ERROR) << "Error setting storage hierarchy authorization "
               << "to its default value: " << GetErrorString(result);
    return result;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::CreateStorageRootKeys(
    const std::string& owner_password) {
  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;
  }
  Tpm* tpm = factory_.GetTpm();
  TPML_PCR_SELECTION creation_pcrs;
  creation_pcrs.count = 0;
  TPMS_SENSITIVE_CREATE sensitive;
  sensitive.user_auth = Make_TPM2B_DIGEST("");
  sensitive.data = Make_TPM2B_SENSITIVE_DATA("");
  TPM_HANDLE object_handle;
  TPM2B_CREATION_DATA creation_data;
  TPM2B_DIGEST creation_digest;
  TPMT_TK_CREATION creation_ticket;
  TPM2B_NAME object_name;
  object_name.size = 0;
  scoped_ptr<AuthorizationDelegate> delegate =
      factory_.GetPasswordAuthorization(owner_password);
  if (tpm_state->IsRSASupported()) {
    bool exists = false;
    result = DoesPersistentKeyExist(kRSAStorageRootKey, &exists);
    if (result) {
      return result;
    }
    if (!exists) {
      TPMT_PUBLIC public_area = CreateDefaultPublicArea(TPM_ALG_RSA);
      public_area.object_attributes |=
          (kSensitiveDataOrigin | kUserWithAuth | kNoDA |
           kRestricted | kDecrypt);
      public_area.parameters.rsa_detail.symmetric.algorithm = TPM_ALG_AES;
      public_area.parameters.rsa_detail.symmetric.key_bits.aes = 128;
      public_area.parameters.rsa_detail.symmetric.mode.aes = TPM_ALG_CFB;
      TPM2B_PUBLIC rsa_public_area = Make_TPM2B_PUBLIC(public_area);
      result = tpm->CreatePrimarySync(TPM_RH_OWNER,
                                      NameFromHandle(TPM_RH_OWNER),
                                      Make_TPM2B_SENSITIVE_CREATE(sensitive),
                                      rsa_public_area,
                                      Make_TPM2B_DATA(""),
                                      creation_pcrs,
                                      &object_handle,
                                      &rsa_public_area,
                                      &creation_data,
                                      &creation_digest,
                                      &creation_ticket,
                                      &object_name,
                                      delegate.get());
      if (result) {
        LOG(ERROR) << __func__ << ": " << GetErrorString(result);
        return result;
      }
      ScopedKeyHandle rsa_key(factory_, object_handle);
      // This will make the key persistent.
      result = tpm->EvictControlSync(TPM_RH_OWNER,
                                     NameFromHandle(TPM_RH_OWNER),
                                     object_handle,
                                     StringFrom_TPM2B_NAME(object_name),
                                     kRSAStorageRootKey,
                                     delegate.get());
      if (result != TPM_RC_SUCCESS) {
        LOG(ERROR) << __func__ << ": " << GetErrorString(result);
        return result;
      }
      LOG(INFO) << "Created RSA SRK.";
    } else {
      LOG(INFO) << "Skip RSA SRK because it already exists.";
    }
  } else {
    LOG(INFO) << "Skip RSA SRK because RSA is not supported.";
  }

  // Do it again for ECC.
  if (tpm_state->IsRSASupported()) {
    bool exists = false;
    result = DoesPersistentKeyExist(kECCStorageRootKey, &exists);
    if (result) {
      return result;
    }
    if (!exists) {
      TPMT_PUBLIC public_area = CreateDefaultPublicArea(TPM_ALG_ECC);
      public_area.object_attributes |=
          (kSensitiveDataOrigin | kUserWithAuth | kNoDA |
           kRestricted | kDecrypt);
      public_area.parameters.ecc_detail.symmetric.algorithm = TPM_ALG_AES;
      public_area.parameters.ecc_detail.symmetric.key_bits.aes = 128;
      public_area.parameters.ecc_detail.symmetric.mode.aes = TPM_ALG_CFB;
      TPM2B_PUBLIC ecc_public_area = Make_TPM2B_PUBLIC(public_area);
      result = tpm->CreatePrimarySync(TPM_RH_OWNER,
                                      NameFromHandle(TPM_RH_OWNER),
                                      Make_TPM2B_SENSITIVE_CREATE(sensitive),
                                      ecc_public_area,
                                      Make_TPM2B_DATA(""),
                                      creation_pcrs,
                                      &object_handle,
                                      &ecc_public_area,
                                      &creation_data,
                                      &creation_digest,
                                      &creation_ticket,
                                      &object_name,
                                      delegate.get());
      if (result) {
        LOG(ERROR) << __func__ << ": " << GetErrorString(result);
        return result;
      }
      ScopedKeyHandle ecc_key(factory_, object_handle);
      // This will make the key persistent.
      result = tpm->EvictControlSync(TPM_RH_OWNER,
                                     NameFromHandle(TPM_RH_OWNER),
                                     object_handle,
                                     StringFrom_TPM2B_NAME(object_name),
                                     kECCStorageRootKey,
                                     delegate.get());
      if (result != TPM_RC_SUCCESS) {
        LOG(ERROR) << __func__ << ": " << GetErrorString(result);
        return result;
      }
      LOG(INFO) << "Created ECC SRK.";
    } else {
      LOG(INFO) << "Skip ECC SRK because it already exists.";
    }
  } else {
    LOG(INFO) << "Skip ECC SRK because ECC is not supported.";
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::CreateSaltingKey(const std::string& owner_password) {
  bool exists = false;
  TPM_RC result = DoesPersistentKeyExist(kSaltingKey, &exists);
  if (result != TPM_RC_SUCCESS) {
    return result;
  }
  if (exists) {
    LOG(INFO) << "Salting key already exists.";
    return TPM_RC_SUCCESS;
  }
  std::string parent_name;
  result = GetKeyName(kRSAStorageRootKey, &parent_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error getting Key name for RSA-SRK: "
               << GetErrorString(result);
    return result;
  }
  TPMT_PUBLIC public_area = CreateDefaultPublicArea(TPM_ALG_RSA);
  public_area.name_alg = TPM_ALG_SHA256;
  public_area.object_attributes |=
      kSensitiveDataOrigin | kUserWithAuth | kNoDA | kDecrypt;
  TPML_PCR_SELECTION creation_pcrs;
  creation_pcrs.count = 0;
  TPMS_SENSITIVE_CREATE sensitive;
  sensitive.user_auth = Make_TPM2B_DIGEST("");
  sensitive.data = Make_TPM2B_SENSITIVE_DATA("");
  TPM2B_SENSITIVE_CREATE sensitive_create = Make_TPM2B_SENSITIVE_CREATE(
      sensitive);
  TPM2B_DATA outside_info = Make_TPM2B_DATA("");

  TPM2B_PRIVATE out_private;
  out_private.size = 0;
  TPM2B_PUBLIC out_public;
  out_public.size = 0;
  TPM2B_CREATION_DATA creation_data;
  TPM2B_DIGEST creation_hash;
  TPMT_TK_CREATION creation_ticket;
  // TODO(usanghi): MITM vulnerability with SaltingKey creation.
  // Currently we cannot verify the key returned by the TPM.
  // crbug.com/442331
  scoped_ptr<AuthorizationDelegate> delegate =
      factory_.GetPasswordAuthorization("");
  result = factory_.GetTpm()->CreateSync(kRSAStorageRootKey,
                                         parent_name,
                                         sensitive_create,
                                         Make_TPM2B_PUBLIC(public_area),
                                         outside_info,
                                         creation_pcrs,
                                         &out_private,
                                         &out_public,
                                         &creation_data,
                                         &creation_hash,
                                         &creation_ticket,
                                         delegate.get());
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error creating salting key: " << GetErrorString(result);
    return result;
  }
  TPM2B_NAME key_name;
  key_name.size = 0;
  TPM_HANDLE key_handle;
  result = factory_.GetTpm()->LoadSync(kRSAStorageRootKey,
                                       parent_name,
                                       out_private,
                                       out_public,
                                       &key_handle,
                                       &key_name,
                                       delegate.get());
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error loading salting key: " << GetErrorString(result);
    return result;
  }
  ScopedKeyHandle key(factory_, key_handle);
  scoped_ptr<AuthorizationDelegate> owner_delegate =
      factory_.GetPasswordAuthorization(owner_password);
  result = factory_.GetTpm()->EvictControlSync(TPM_RH_OWNER,
                                               NameFromHandle(TPM_RH_OWNER),
                                               key_handle,
                                               StringFrom_TPM2B_NAME(key_name),
                                               kSaltingKey,
                                               owner_delegate.get());
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << __func__ << ": " << GetErrorString(result);
    return result;
  }
  return TPM_RC_SUCCESS;
}

TPMT_PUBLIC TpmUtilityImpl::CreateDefaultPublicArea(TPM_ALG_ID key_alg) {
  TPMT_PUBLIC public_area;
  public_area.name_alg = TPM_ALG_SHA256;
  public_area.auth_policy = Make_TPM2B_DIGEST("");
  public_area.object_attributes = kFixedTPM | kFixedParent;
  if (key_alg == TPM_ALG_RSA) {
    public_area.type = TPM_ALG_RSA;
    public_area.parameters.rsa_detail.scheme.scheme = TPM_ALG_NULL;
    public_area.parameters.rsa_detail.symmetric.algorithm = TPM_ALG_NULL;
    public_area.parameters.rsa_detail.key_bits = 2048;
    public_area.parameters.rsa_detail.exponent = 0;
    public_area.unique.rsa = Make_TPM2B_PUBLIC_KEY_RSA("");
  } else if (key_alg == TPM_ALG_ECC) {
    public_area.type = TPM_ALG_ECC;
    public_area.parameters.ecc_detail.curve_id = TPM_ECC_NIST_P256;
    public_area.parameters.ecc_detail.kdf.scheme = TPM_ALG_NULL;
    public_area.unique.ecc.x = Make_TPM2B_ECC_PARAMETER("");
    public_area.unique.ecc.y = Make_TPM2B_ECC_PARAMETER("");
  } else if (key_alg == TPM_ALG_KEYEDHASH) {
    public_area.type = TPM_ALG_KEYEDHASH;
    public_area.parameters.keyed_hash_detail.scheme.scheme = TPM_ALG_NULL;
  } else {
    LOG(WARNING) << "Unrecognized key_type. Not filling parameters.";
  }
  return public_area;
}

TPM_RC TpmUtilityImpl::SetHierarchyAuthorization(
    TPMI_RH_HIERARCHY_AUTH hierarchy,
    const std::string& password,
    AuthorizationDelegate* authorization) {
  if (password.size() > kMaxPasswordLength) {
    LOG(ERROR) << "Hierarchy passwords can be at most " << kMaxPasswordLength
               << " bytes. Current password length is: " << password.size();
    return SAPI_RC_BAD_SIZE;
  }
  return factory_.GetTpm()->HierarchyChangeAuthSync(
      hierarchy,
      NameFromHandle(hierarchy),
      Make_TPM2B_DIGEST(password),
      authorization);
}

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);
}

TPM_RC TpmUtilityImpl::ComputeKeyName(const TPMT_PUBLIC& public_area,
                                      std::string* object_name) {
  CHECK(object_name);
  if (public_area.type == TPM_ALG_ERROR) {
    // We do not compute a name for empty public area.
    object_name->clear();
    return TPM_RC_SUCCESS;
  }
  std::string serialized_public_area;
  TPM_RC result = Serialize_TPMT_PUBLIC(public_area, &serialized_public_area);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error serializing public area: " << GetErrorString(result);
    return result;
  }
  std::string serialized_name_alg;
  result = Serialize_TPM_ALG_ID(TPM_ALG_SHA256, &serialized_name_alg);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error serializing public area: " << GetErrorString(result);
    return result;
  }
  object_name->assign(serialized_name_alg +
                      crypto::SHA256HashString(serialized_public_area));
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::ComputeNVSpaceName(const TPMS_NV_PUBLIC& nv_public_area,
                                          std::string* nv_name) {
  CHECK(nv_name);
  if ((nv_public_area.nv_index & NV_INDEX_FIRST) == 0) {
    // If the index is not an nvram index, we do not compute a name.
    nv_name->clear();
    return TPM_RC_SUCCESS;
  }
  std::string serialized_public_area;
  TPM_RC result = Serialize_TPMS_NV_PUBLIC(nv_public_area,
                                           &serialized_public_area);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error serializing public area: " << GetErrorString(result);
    return result;
  }
  std::string serialized_name_alg;
  result = Serialize_TPM_ALG_ID(TPM_ALG_SHA256, &serialized_name_alg);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error serializing public area: " << GetErrorString(result);
    return result;
  }
  nv_name->assign(serialized_name_alg +
                  crypto::SHA256HashString(serialized_public_area));
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::EncryptPrivateData(const TPMT_SENSITIVE& sensitive_area,
                                          const TPMT_PUBLIC& public_area,
                                          TPM2B_PRIVATE* encrypted_private_data,
                                          TPM2B_DATA* encryption_key) {
  TPM2B_SENSITIVE sensitive_data = Make_TPM2B_SENSITIVE(sensitive_area);
  std::string serialized_sensitive_data;
  TPM_RC result = Serialize_TPM2B_SENSITIVE(sensitive_data,
                                            &serialized_sensitive_data);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error serializing sensitive data: "
               << GetErrorString(result);
    return result;
  }
  std::string object_name;
  result = ComputeKeyName(public_area, &object_name);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error computing object name: " << GetErrorString(result);
    return result;
  }
  TPM2B_DIGEST inner_integrity = Make_TPM2B_DIGEST(crypto::SHA256HashString(
      serialized_sensitive_data + object_name));
  std::string serialized_inner_integrity;
  result = Serialize_TPM2B_DIGEST(inner_integrity, &serialized_inner_integrity);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error serializing inner integrity: "
               << GetErrorString(result);
    return result;
  }
  std::string unencrypted_private_data = serialized_inner_integrity +
                                         serialized_sensitive_data;
  AES_KEY key;
  AES_set_encrypt_key(encryption_key->buffer, kAesKeySize * 8, &key);
  std::string private_data_string(unencrypted_private_data.size(), 0);
  int iv_in = 0;
  unsigned char iv[MAX_AES_BLOCK_SIZE_BYTES] = {0};
  AES_cfb128_encrypt(
    reinterpret_cast<const unsigned char*>(unencrypted_private_data.data()),
    reinterpret_cast<unsigned char*>(string_as_array(&private_data_string)),
    unencrypted_private_data.size(), &key, iv, &iv_in, AES_ENCRYPT);
  *encrypted_private_data = Make_TPM2B_PRIVATE(private_data_string);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << "Error making private area: "
               << GetErrorString(result);
    return result;
  }
  return TPM_RC_SUCCESS;
}

TPM_RC TpmUtilityImpl::DoesPersistentKeyExist(TPMI_DH_PERSISTENT key_handle,
                                              bool* exists) {
  TPM_RC result;
  TPMI_YES_NO more_data = YES;
  TPMS_CAPABILITY_DATA capability_data;
  result = factory_.GetTpm()->GetCapabilitySync(
      TPM_CAP_HANDLES, key_handle, 1 /*property_count*/, &more_data,
      &capability_data, nullptr /*authorization_delegate*/);
  if (result != TPM_RC_SUCCESS) {
    LOG(ERROR) << __func__
               << ": Error querying handles: " << GetErrorString(result);
    return result;
  }
  TPML_HANDLE& handles = capability_data.data.handles;
  *exists = (handles.count == 1 && handles.handle[0] == key_handle);
  return TPM_RC_SUCCESS;
}

}  // namespace trunks