blob: 845432104988623761b971f23a85797df58fa12c [file] [log] [blame]
// Copyright (c) 2012 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 "cryptohome/tpm_impl.h"
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <arpa/inet.h>
#include <base/memory/free_deleter.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <base/threading/platform_thread.h>
#include <base/time/time.h>
#include <base/values.h>
#include <crypto/scoped_openssl_types.h>
#include <libhwsec/overalls/overalls_api.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <trousers/scoped_tss_type.h>
#include <trousers/tss.h>
#include <trousers/trousers.h> // NOLINT(build/include_alpha) - needs tss.h
#include "cryptohome/cryptohome_metrics.h"
#include "cryptohome/cryptolib.h"
#include "cryptohome/tpm1_static_utils.h"
#include "cryptohome/tpm_metrics.h"
#define TPM_LOG(severity, result) \
LOG(severity) << ::cryptohome::FormatTrousersErrorCode(result) << ": "
using base::PlatformThread;
using brillo::Blob;
using brillo::BlobFromString;
using brillo::CombineBlobs;
using brillo::SecureBlob;
using hwsec::overalls::GetOveralls;
using trousers::ScopedTssContext;
using trousers::ScopedTssKey;
using trousers::ScopedTssMemory;
using trousers::ScopedTssNvStore;
using trousers::ScopedTssObject;
using trousers::ScopedTssPcrs;
using trousers::ScopedTssPolicy;
namespace cryptohome {
namespace {
typedef std::unique_ptr<BYTE, base::FreeDeleter> ScopedByteArray;
// The DER encoding of SHA-256 DigestInfo as defined in PKCS #1.
const unsigned char kSha256DigestInfo[] = {
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
};
// This is the well known UUID present in TPM1.2 implemenations. It is used
// to load the cryptohome key into a TPM1.2 in a legacy path.
const TSS_UUID kCryptohomeWellKnownUuid = {0x0203040b, 0, 0, 0, 0,
{0, 9, 8, 1, 0, 3}};
// The size of PCR value.
constexpr uint32_t kPcrValueSize = 20;
// The list of possible PCR0 values.
const uint8_t kValidPcr0Values[][kPcrValueSize] {
// dev_mode_enabled: false, recovery_enabled: false, firmware_type: unknown
{0xFB, 0x33, 0x40, 0x39, 0xCF, 0x9C, 0x06, 0xB9, 0xF8, 0x02, 0x37, 0xC6, 0xF9,
0x22, 0x2F, 0x7B, 0xF4, 0x8C, 0x2D, 0xB5},
// dev_mode_enabled: false, recovery_enabled: false, firmware_type: verified
{0x06, 0x4A, 0xEC, 0x9B, 0xBD, 0x94, 0xDE, 0xA1, 0x23, 0x1A, 0xE7, 0x57, 0x67,
0x64, 0x7F, 0x09, 0x8C, 0x39, 0x8E, 0x79},
// dev_mode_enabled: false, recovery_enabled: false, firmware_type: developer
{0xEE, 0x56, 0xC3, 0x2D, 0x5F, 0x7B, 0xFA, 0x56, 0xBA, 0x92, 0x6F, 0x66, 0x27,
0x0E, 0xC2, 0xE9, 0xD0, 0x1C, 0x64, 0xB3},
// dev_mode_enabled: false, recovery_enabled: true, firmware_type: unknown
{0x1E, 0x74, 0x50, 0x33, 0xAD, 0x91, 0x58, 0x53, 0xB4, 0x4C, 0x94, 0x39, 0x11,
0x6F, 0x31, 0x1D, 0xD8, 0x50, 0x11, 0xC8},
// dev_mode_enabled: false, recovery_enabled: true, firmware_type: verified
{0x17, 0x18, 0xB0, 0x87, 0xA1, 0x6A, 0x4E, 0x80, 0xE7, 0x50, 0x74, 0xC8, 0x11,
0x09, 0xDE, 0x23, 0x1A, 0x04, 0xC0, 0x0B},
// dev_mode_enabled: false, recovery_enabled: true, firmware_type: developer
{0x08, 0x20, 0xC4, 0xB6, 0x7B, 0x33, 0xB2, 0x81, 0x25, 0x6B, 0x03, 0x53, 0xDC,
0xC9, 0xFA, 0x43, 0x66, 0xE4, 0x27, 0xAF},
// dev_mode_enabled: true, recovery_enabled: false, firmware_type: unknown
{0x06, 0xEF, 0x08, 0xEF, 0x0B, 0xB0, 0x0F, 0xEC, 0x98, 0xE0, 0x56, 0x73, 0xBC,
0x03, 0xAF, 0x1C, 0x51, 0x5C, 0xE4, 0x89},
// dev_mode_enabled: true, recovery_enabled: false, firmware_type: verified
{0x86, 0x5A, 0xED, 0xD3, 0x37, 0x51, 0x8E, 0x56, 0xF6, 0x48, 0x44, 0x0B, 0x81,
0xB4, 0xCB, 0xD9, 0x35, 0x9F, 0xDF, 0xF3},
// dev_mode_enabled: true, recovery_enabled: false, firmware_type: developer
{0x29, 0x56, 0x57, 0x4C, 0x9D, 0x6A, 0x95, 0xD3, 0x15, 0x8A, 0x81, 0x45, 0x71,
0x6A, 0x25, 0x7C, 0x3F, 0x6D, 0x62, 0xC1},
// dev_mode_enabled: true, recovery_enabled: true, firmware_type: unknown
{0x09, 0xA2, 0xF8, 0x5D, 0xF3, 0x0C, 0x43, 0x81, 0x83, 0x06, 0x24, 0xA1, 0xFF,
0x2B, 0xDA, 0xAF, 0xDD, 0x5A, 0x15, 0x79},
// dev_mode_enabled: true, recovery_enabled: true, firmware_type: verified
{0x56, 0x91, 0x6C, 0x71, 0x5C, 0xD6, 0x04, 0x64, 0x3B, 0x05, 0xC2, 0xF0, 0xB7,
0xBB, 0x08, 0xD1, 0x7D, 0xC2, 0x18, 0xFA},
// dev_mode_enabled: true, recovery_enabled: true, firmware_type: developer
{0x36, 0x56, 0x6D, 0xE7, 0x9C, 0x23, 0x94, 0x2F, 0x1B, 0x79, 0xCF, 0xD7, 0xDE,
0x56, 0xE6, 0x9B, 0xE4, 0xFE, 0xCD, 0x72}
};
// Returns whether given input is a valid value for PCR0 state.
bool IsValidPcr0Value(const Blob& pcr_value) {
if (pcr_value.size() != kPcrValueSize) {
return false;
}
for (const uint8_t* value : kValidPcr0Values) {
if (!memcmp(value, pcr_value.data(), pcr_value.size()))
return true;
}
return false;
}
Tpm::TpmRetryAction ResultToRetryActionWithMessage(TSS_RESULT result,
const std::string& message) {
Tpm::TpmRetryAction status = Tpm::kTpmRetryFatal;
ReportTpmResult(GetTpmResultSample(result));
switch (ERROR_CODE(result)) {
case ERROR_CODE(TSS_SUCCESS):
status = Tpm::kTpmRetryNone;
break;
case ERROR_CODE(TSS_E_COMM_FAILURE):
LOG(ERROR) << "Communications failure with the TPM.";
ReportCryptohomeError(kTssCommunicationFailure);
status = Tpm::kTpmRetryCommFailure;
break;
case ERROR_CODE(TSS_E_INVALID_HANDLE):
LOG(ERROR) << "Invalid handle to the TPM.";
ReportCryptohomeError(kTssInvalidHandle);
status = Tpm::kTpmRetryInvalidHandle;
break;
case ERROR_CODE(TCS_E_KM_LOADFAILED):
LOG(ERROR) << "Key load failed; problem with parent key authorization.";
ReportCryptohomeError(kTcsKeyLoadFailed);
status = Tpm::kTpmRetryLoadFail;
break;
case ERROR_CODE(TPM_E_DEFEND_LOCK_RUNNING):
LOG(ERROR) << "The TPM is defending itself against possible dictionary "
<< "attacks.";
ReportCryptohomeError(kTpmDefendLockRunning);
status = Tpm::kTpmRetryDefendLock;
break;
// This error code occurs when the TPM is in an error state.
case ERROR_CODE(TPM_E_FAIL):
status = Tpm::kTpmRetryReboot;
ReportCryptohomeError(kTpmFail);
LOG(ERROR) << "The TPM returned TPM_E_FAIL. A reboot is required.";
break;
default:
status = Tpm::kTpmRetryFailNoRetry;
TPM_LOG(ERROR, result) << (message.size() ? message
: "Retrying will not help.");
break;
}
return status;
}
Tpm::TpmRetryAction ResultToRetryAction(TSS_RESULT result) {
return ResultToRetryActionWithMessage(result, "");
}
// Creates a DER encoded RSA public key given a serialized TPM_PUBKEY.
//
// Parameters
// public_key - A serialized TPM_PUBKEY as returned by Tspi_Key_GetPubKey.
// public_key_der - The same public key in DER encoded form.
bool ConvertPublicKeyToDER(const SecureBlob& public_key,
SecureBlob* public_key_der) {
crypto::ScopedRSA rsa =
ParseRsaFromTpmPubkeyBlob(Blob(public_key.begin(), public_key.end()));
if (!rsa) {
return false;
}
int der_length = i2d_RSAPublicKey(rsa.get(), NULL);
if (der_length < 0) {
LOG(ERROR) << "Failed to DER-encode public key.";
return false;
}
public_key_der->resize(der_length);
unsigned char* der_buffer = public_key_der->data();
der_length = i2d_RSAPublicKey(rsa.get(), &der_buffer);
if (der_length < 0) {
LOG(ERROR) << "Failed to DER-encode public key.";
return false;
}
public_key_der->resize(der_length);
return true;
}
} // namespace
const unsigned char kDefaultSrkAuth[] = { };
const unsigned int kDefaultTpmRsaKeyBits = 2048;
const unsigned int kDefaultTpmRsaKeyFlag = TSS_KEY_SIZE_2048;
const unsigned int kDefaultDiscardableWrapPasswordLength = 32;
const char* kWellKnownSrkTmp = "1234567890";
const unsigned int kTpmConnectRetries = 10;
const unsigned int kTpmConnectIntervalMs = 100;
const unsigned int kTpmPCRLocality = 1;
const int kDelegateSecretSize = 20;
const size_t kPCRExtensionSize = 20; // SHA-1 digest size.
// This error is returned when an attempt is made to use the SRK but it does not
// yet exist because the TPM has not been owned.
const TSS_RESULT kKeyNotFoundError = (TSS_E_PS_KEY_NOTFOUND | TSS_LAYER_TCS);
TpmImpl::TpmImpl()
: initialized_(false),
srk_auth_(kDefaultSrkAuth, kDefaultSrkAuth + sizeof(kDefaultSrkAuth)),
owner_password_(),
password_sync_lock_(),
is_disabled_(true),
is_owned_(false),
is_being_owned_(false) {
TSS_HCONTEXT context_handle = ConnectContext();
if (context_handle) {
tpm_context_.reset(0, context_handle);
}
}
TpmImpl::~TpmImpl() { }
TSS_HCONTEXT TpmImpl::ConnectContext() {
TSS_RESULT result;
TSS_HCONTEXT context_handle = 0;
if (!OpenAndConnectTpm(&context_handle, &result)) {
return 0;
}
return context_handle;
}
bool TpmImpl::ConnectContextAsOwner(TSS_HCONTEXT* context, TSS_HTPM* tpm) {
*context = 0;
*tpm = 0;
SecureBlob owner_password;
if (!GetOwnerPassword(&owner_password)) {
LOG(ERROR) << "ConnectContextAsOwner requires an owner password";
return false;
}
if (!IsOwned() || IsBeingOwned()) {
LOG(ERROR) << "ConnectContextAsOwner: TPM is unowned or still being owned";
return false;
}
if ((*context = ConnectContext()) == 0) {
LOG(ERROR) << "ConnectContextAsOwner: Could not open the TPM";
return false;
}
if (!GetTpmWithAuth(*context, owner_password, tpm)) {
LOG(ERROR) << "ConnectContextAsOwner: failed to authorize as the owner";
Tspi_Context_Close(*context);
*context = 0;
*tpm = 0;
return false;
}
return true;
}
bool TpmImpl::ConnectContextAsUser(TSS_HCONTEXT* context, TSS_HTPM* tpm) {
*context = 0;
*tpm = 0;
if ((*context = ConnectContext()) == 0) {
LOG(ERROR) << "ConnectContextAsUser: Could not open the TPM";
return false;
}
if (!GetTpm(*context, tpm)) {
LOG(ERROR) << "ConnectContextAsUser: failed to get a TPM object";
Tspi_Context_Close(*context);
*context = 0;
*tpm = 0;
return false;
}
return true;
}
bool TpmImpl::ConnectContextAsDelegate(const Blob& delegate_blob,
const Blob& delegate_secret,
TSS_HCONTEXT* context,
TSS_HTPM* tpm_handle) {
*context = 0;
*tpm_handle = 0;
if (!IsOwned() || IsBeingOwned()) {
LOG(ERROR) << "ConnectContextAsDelegate: TPM is unowned.";
return false;
}
if ((*context = ConnectContext()) == 0) {
LOG(ERROR) << "ConnectContextAsDelegate: Could not open the TPM.";
return false;
}
if (!GetTpmWithDelegation(*context, delegate_blob, delegate_secret,
tpm_handle)) {
LOG(ERROR) << "ConnectContextAsDelegate: Failed to authorize.";
Tspi_Context_Close(*context);
*context = 0;
*tpm_handle = 0;
return false;
}
return true;
}
void TpmImpl::GetStatus(TpmKeyHandle key_handle,
TpmStatusInfo* status) {
memset(status, 0, sizeof(TpmStatusInfo));
status->this_instance_has_context = (tpm_context_.value() != 0);
status->this_instance_has_key_handle = (key_handle != 0);
ScopedTssContext context_handle;
// Check if we can connect
TSS_RESULT result;
if (!OpenAndConnectTpm(context_handle.ptr(), &result)) {
status->last_tpm_error = result;
return;
}
status->can_connect = true;
// Check the Storage Root Key
ScopedTssKey srk_handle(context_handle);
if (!LoadSrk(context_handle, srk_handle.ptr(), &result)) {
status->last_tpm_error = result;
return;
}
status->can_load_srk = true;
// Check the SRK public key
unsigned int public_srk_size;
ScopedTssMemory public_srk_bytes(context_handle);
if (TPM_ERROR(result = Tspi_Key_GetPubKey(srk_handle, &public_srk_size,
public_srk_bytes.ptr()))) {
status->last_tpm_error = result;
return;
}
status->can_load_srk_public_key = true;
// Perform ROCA vulnerability check.
crypto::ScopedRSA public_srk = ParseRsaFromTpmPubkeyBlob(Blob(
public_srk_bytes.value(), public_srk_bytes.value() + public_srk_size));
status->srk_vulnerable_roca =
public_srk && CryptoLib::TestRocaVulnerable(public_srk.get()->n);
// Check the Cryptohome key by using what we have been told.
status->has_cryptohome_key = (tpm_context_.value() != 0) && (key_handle != 0);
if (status->has_cryptohome_key) {
// Check encryption (we don't care about the contents, just whether or not
// there was an error)
SecureBlob data(16);
SecureBlob password(16);
SecureBlob salt(8);
SecureBlob data_out(16);
memset(data.data(), 'A', data.size());
memset(password.data(), 'B', password.size());
memset(salt.data(), 'C', salt.size());
memset(data_out.data(), 'D', data_out.size());
SecureBlob key;
CryptoLib::PasskeyToAesKey(password, salt, 13, &key, NULL);
if (EncryptBlob(key_handle, data, key, &data_out) != kTpmRetryNone) {
return;
}
status->can_encrypt = true;
// Check decryption (we don't care about the contents, just whether or not
// there was an error)
if (DecryptBlob(key_handle,
data_out, key,
std::map<uint32_t, std::string>(),
&data) != kTpmRetryNone) {
return;
}
status->can_decrypt = true;
}
}
bool TpmImpl::GetDictionaryAttackInfo(int* counter,
int* threshold,
bool* lockout,
int* seconds_remaining) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << __func__ << ": Failed to connect to the TPM.";
return false;
}
brillo::Blob capability_data;
if (!GetCapability(context_handle,
tpm_handle,
TSS_TPMCAP_DA_LOGIC,
TPM_ET_KEYHANDLE,
&capability_data,
NULL)) {
LOG(ERROR) << __func__ << ": Failed to query DA_LOGIC capability.";
return false;
}
if (static_cast<UINT16>(capability_data[1]) == TPM_TAG_DA_INFO) {
TPM_DA_INFO da_info;
UINT64 offset = 0;
Trspi_UnloadBlob_DA_INFO(&offset, capability_data.data(), &da_info);
VLOG(1) << "DA_INFO for TPM_ET_KEYHANDLE:";
VLOG(1) << " Active: " << static_cast<int>(da_info.state);
VLOG(1) << " Counter: " << da_info.currentCount;
VLOG(1) << " Threshold: " << da_info.thresholdCount;
VLOG(1) << " Action: " << da_info.actionAtThreshold.actions;
VLOG(1) << " Action Value: " << da_info.actionDependValue;
VLOG(1) << " Vendor Data Size: " << da_info.vendorDataSize;
if (da_info.vendorDataSize > 0) {
VLOG(1) << " Vendor Data: "
<< base::HexEncode(da_info.vendorData,
da_info.vendorDataSize);
}
*counter = da_info.currentCount;
*threshold = da_info.thresholdCount;
*lockout = (da_info.state == TPM_DA_STATE_ACTIVE);
*seconds_remaining = da_info.actionDependValue;
free(da_info.vendorData);
} else {
LOG(WARNING) << __func__ << ": Cannot read counter.";
}
// For Infineon, pull the counter out of vendor-specific data, and check if it
// matches the value in DA_INFO.
UINT32 manufacturer;
if (!GetCapability(context_handle,
tpm_handle,
TSS_TPMCAP_PROPERTY,
TSS_TPMCAP_PROP_MANUFACTURER,
NULL,
&manufacturer)) {
LOG(ERROR) << __func__ << ": Failed to query TSS_TPMCAP_PROP_MANUFACTURER.";
return false;
}
const UINT32 kInfineon = 0x49465800;
if (manufacturer == kInfineon) {
brillo::Blob capability_data;
if (!GetCapability(context_handle,
tpm_handle,
TSS_TPMCAP_MFR,
0x00000802, // Opaque vendor-specific bits.
&capability_data,
NULL)) {
LOG(ERROR) << __func__ << ": Failed to query MFR capability.";
return false;
}
const size_t kInfineonCounterOffset = 9;
if (capability_data.size() > kInfineonCounterOffset) {
if (*counter != capability_data[kInfineonCounterOffset]) {
LOG(WARNING) << __func__ << ": Counter mismatch: " << *counter << " vs "
<< capability_data[kInfineonCounterOffset];
*counter = std::max(*counter, static_cast<int>(
capability_data[kInfineonCounterOffset]));
}
VLOG(1) << __func__ << ": " << counter;
} else {
LOG(WARNING) << __func__ << ": Cannot read counter.";
}
}
return true;
}
bool TpmImpl::ResetDictionaryAttackMitigation(
const brillo::Blob& delegate_blob,
const brillo::Blob& delegate_secret) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsDelegate(delegate_blob, delegate_secret,
context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << __func__ << ": Failed to connect to the TPM.";
return false;
}
TSS_RESULT result = Tspi_TPM_SetStatus(tpm_handle,
TSS_TPMSTATUS_RESETLOCK,
true /* Will be ignored. */);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to reset lock.";
return false;
}
LOG(WARNING) << "Dictionary attack mitigation has been reset.";
return true;
}
bool TpmImpl::CreatePolicyWithRandomPassword(TSS_HCONTEXT context_handle,
TSS_FLAG policy_type,
TSS_HPOLICY* policy_handle) {
trousers::ScopedTssPolicy local_policy(context_handle);
TSS_RESULT result = TSS_SUCCESS;
if (TPM_ERROR(result = Tspi_Context_CreateObject(
context_handle, TSS_OBJECT_TYPE_POLICY, policy_type,
local_policy.ptr()))) {
TPM_LOG(ERROR, result) << "Error creating policy object";
return false;
}
SecureBlob migration_password(kDefaultDiscardableWrapPasswordLength);
CryptoLib::GetSecureRandom(migration_password.data(),
migration_password.size());
if (TPM_ERROR(result = Tspi_Policy_SetSecret(
local_policy, TSS_SECRET_MODE_PLAIN,
migration_password.size(), migration_password.data()))) {
TPM_LOG(ERROR, result) << "Error setting policy password";
return false;
}
*policy_handle = local_policy.release();
return true;
}
bool TpmImpl::CreateRsaPublicKeyObject(TSS_HCONTEXT context_handle,
const Blob& key_modulus,
TSS_FLAG key_flags,
UINT32 signature_scheme,
UINT32 encryption_scheme,
TSS_HKEY* key_handle) {
ScopedTssKey local_key(context_handle);
TSS_RESULT tss_result = Tspi_Context_CreateObject(
context_handle, TSS_OBJECT_TYPE_RSAKEY, key_flags, local_key.ptr());
if (TPM_ERROR(tss_result)) {
TPM_LOG(ERROR, tss_result) << __func__ << ": Error creating the key object";
return false;
}
tss_result = Tspi_SetAttribData(
local_key, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS,
key_modulus.size(), const_cast<BYTE*>(key_modulus.data()));
if (TPM_ERROR(tss_result)) {
TPM_LOG(ERROR, tss_result) << __func__ << ": Error setting the key modulus";
return false;
}
if (signature_scheme != TSS_SS_NONE) {
tss_result =
Tspi_SetAttribUint32(local_key, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_SIGSCHEME, signature_scheme);
if (TPM_ERROR(tss_result)) {
TPM_LOG(ERROR, tss_result)
<< __func__ << ": Error setting the key signing scheme";
return false;
}
}
if (encryption_scheme != TSS_ES_NONE) {
tss_result = Tspi_SetAttribUint32(local_key, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_ENCSCHEME,
encryption_scheme);
if (TPM_ERROR(tss_result)) {
TPM_LOG(ERROR, tss_result)
<< __func__ << ": Error setting the key encryption scheme";
return false;
}
}
*key_handle = local_key.release();
return true;
}
bool TpmImpl::OpenAndConnectTpm(TSS_HCONTEXT* context_handle,
TSS_RESULT* result) {
TSS_RESULT local_result;
ScopedTssContext local_context_handle;
if (TPM_ERROR(local_result = Tspi_Context_Create(
local_context_handle.ptr()))) {
TPM_LOG(ERROR, local_result) << "Error calling Tspi_Context_Create";
if (result)
*result = local_result;
return false;
}
for (unsigned int i = 0; i < kTpmConnectRetries; i++) {
if (TPM_ERROR(local_result = GetOveralls()->Ospi_Context_Connect(
local_context_handle, NULL))) {
// If there was a communications failure, try sleeping a bit here--it may
// be that tcsd is still starting
if (ERROR_CODE(local_result) == TSS_E_COMM_FAILURE) {
PlatformThread::Sleep(
base::TimeDelta::FromMilliseconds(kTpmConnectIntervalMs));
} else {
TPM_LOG(ERROR, local_result) << "Error calling Tspi_Context_Connect";
if (result)
*result = local_result;
return false;
}
} else {
break;
}
}
if (TPM_ERROR(local_result)) {
TPM_LOG(ERROR, local_result) << "Error calling Tspi_Context_Connect";
if (result)
*result = local_result;
return false;
}
*context_handle = local_context_handle.release();
if (result)
*result = local_result;
return (*context_handle != 0);
}
Tpm::TpmRetryAction TpmImpl::GetPublicKeyHash(TpmKeyHandle key_handle,
SecureBlob* hash) {
TSS_RESULT result = TSS_SUCCESS;
SecureBlob pubkey;
if (!GetPublicKeyBlob(tpm_context_.value(), key_handle, &pubkey, &result)) {
return ResultToRetryAction(result);
}
*hash = CryptoLib::Sha1(pubkey);
return kTpmRetryNone;
}
Tpm::TpmRetryAction TpmImpl::EncryptBlob(TpmKeyHandle key_handle,
const SecureBlob& plaintext,
const SecureBlob& key,
SecureBlob* ciphertext) {
TSS_RESULT result = TSS_SUCCESS;
TSS_FLAG init_flags = TSS_ENCDATA_SEAL;
ScopedTssKey enc_handle(tpm_context_.value());
if (TPM_ERROR(result = Tspi_Context_CreateObject(tpm_context_.value(),
TSS_OBJECT_TYPE_ENCDATA,
init_flags,
enc_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return ResultToRetryAction(result);
}
// TODO(fes): Check RSA key modulus size, return an error or block input
if (TPM_ERROR(result = Tspi_Data_Bind(
enc_handle,
key_handle,
plaintext.size(),
const_cast<BYTE*>(plaintext.data())))) {
TPM_LOG(ERROR, result) << __func__ << "Error calling Tspi_Data_Bind";
return ResultToRetryAction(result);
}
SecureBlob enc_data_blob;
Tpm::TpmRetryAction action = GetDataAttribute(tpm_context_.value(),
enc_handle,
TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB,
&enc_data_blob);
if (action != Tpm::kTpmRetryNone) {
LOG(ERROR) << __func__ << ": Failed to read encrypted blob.";
return action;
}
if (!CryptoLib::ObscureRSAMessage(enc_data_blob, key, ciphertext)) {
LOG(ERROR) << "Error obscuring message.";
return kTpmRetryFailNoRetry;
}
return kTpmRetryNone;
}
Tpm::TpmRetryAction TpmImpl::DecryptBlob(
TpmKeyHandle key_handle,
const SecureBlob& ciphertext,
const SecureBlob& key,
const std::map<uint32_t, std::string>& pcr_map,
SecureBlob* plaintext) {
TSS_RESULT result = TSS_SUCCESS;
SecureBlob local_data;
if (!CryptoLib::UnobscureRSAMessage(ciphertext, key, &local_data)) {
LOG(ERROR) << "Error unobscureing message.";
return kTpmRetryFailNoRetry;
}
TSS_FLAG init_flags = TSS_ENCDATA_SEAL;
ScopedTssKey enc_handle(tpm_context_.value());
if (TPM_ERROR(result = Tspi_Context_CreateObject(tpm_context_.value(),
TSS_OBJECT_TYPE_ENCDATA,
init_flags,
enc_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return ResultToRetryAction(result);
}
if (TPM_ERROR(result = Tspi_SetAttribData(enc_handle,
TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB,
local_data.size(),
local_data.data()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_SetAttribData";
return ResultToRetryAction(result);
}
ScopedTssMemory dec_data(tpm_context_.value());
UINT32 dec_data_length = 0;
if (TPM_ERROR(result = Tspi_Data_Unbind(enc_handle, key_handle,
&dec_data_length, dec_data.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Data_Unbind";
return ResultToRetryAction(result);
}
plaintext->resize(dec_data_length);
memcpy(plaintext->data(), dec_data.value(), dec_data_length);
brillo::SecureMemset(dec_data.value(), 0, dec_data_length);
return kTpmRetryNone;
}
bool TpmImpl::SetAuthValue(TSS_HCONTEXT context_handle,
ScopedTssKey* enc_handle,
TSS_HTPM tpm_handle,
const SecureBlob& auth_blob) {
// Create the enc_handle.
TSS_RESULT result;
if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_ENCDATA,
TSS_ENCDATA_SEAL,
enc_handle->ptr()))) {
TPM_LOG(ERROR, result)
<< "Error calling Tspi_Context_CreateObject";
return false;
}
// Get the TPM usage policy object and set the auth_value.
TSS_HPOLICY tpm_usage_policy;
if (TPM_ERROR(result = Tspi_GetPolicyObject(tpm_handle, TSS_POLICY_USAGE,
&tpm_usage_policy))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_GetPolicyObject";
return false;
}
if (TPM_ERROR(result = Tspi_Policy_SetSecret(
tpm_usage_policy, TSS_SECRET_MODE_PLAIN,
auth_blob.size(),
const_cast<BYTE*>(auth_blob.data())))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
if (TPM_ERROR(result = Tspi_Policy_AssignToObject(
tpm_usage_policy,
*enc_handle))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_AssignToObject";
return false;
}
return true;
}
Tpm::TpmRetryAction TpmImpl::SealToPcrWithAuthorization(
TpmKeyHandle key_handle,
const SecureBlob& plaintext,
const SecureBlob& auth_blob,
const std::map<uint32_t, std::string>& pcr_map,
SecureBlob* sealed_data) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "Failed to connect to the TPM.";
return Tpm::kTpmRetryFailNoRetry;
}
// Load the Storage Root Key.
TSS_RESULT result;
ScopedTssKey srk_handle(context_handle);
if (!LoadSrk(context_handle, srk_handle.ptr(), &result)) {
TPM_LOG(INFO, result) << "Failed to load SRK.";
return Tpm::kTpmRetryFailNoRetry;
}
// Create a PCRS object.
ScopedTssPcrs pcrs_handle(context_handle);
if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_PCRS,
TSS_PCRS_STRUCT_INFO,
pcrs_handle.ptr()))) {
TPM_LOG(ERROR, result)
<< "Error calling Tspi_Context_CreateObject";
return Tpm::kTpmRetryFailNoRetry;
}
// Process the data from pcr_map.
for (const auto& map_pair : pcr_map) {
uint32_t pcr_index = map_pair.first;
const std::string& digest = map_pair.second;
if (digest.empty()) {
UINT32 pcr_len = 0;
ScopedTssMemory pcr_value(context_handle);
if (TPM_ERROR(result = Tspi_TPM_PcrRead(tpm_handle, pcr_index,
&pcr_len, pcr_value.ptr()))) {
TPM_LOG(ERROR, result) << "Could not read PCR value";
return ResultToRetryAction(result);
}
Tspi_PcrComposite_SetPcrValue(
pcrs_handle, pcr_index, pcr_len, pcr_value.value());
} else {
Tspi_PcrComposite_SetPcrValue(
pcrs_handle, pcr_index, digest.size(),
reinterpret_cast<BYTE*>(const_cast<char*>(digest.data())));
}
}
ScopedTssKey enc_handle(context_handle);
if (!SetAuthValue(context_handle, &enc_handle, tpm_handle, auth_blob)) {
context_handle.reset();
return Tpm::kTpmRetryFailNoRetry;
}
// Seal the given value with the SRK.
if (TPM_ERROR(result = Tspi_Data_Seal(
enc_handle,
srk_handle,
plaintext.size(),
const_cast<BYTE*>(plaintext.data()),
pcrs_handle))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Data_Seal";
return Tpm::kTpmRetryFailNoRetry;
}
// Extract the sealed value.
ScopedTssMemory enc_data(context_handle);
UINT32 enc_data_length = 0;
if (TPM_ERROR(result = Tspi_GetAttribData(enc_handle,
TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB,
&enc_data_length,
enc_data.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_GetAttribData";
return Tpm::kTpmRetryFailNoRetry;
}
sealed_data->assign(&enc_data.value()[0],
&enc_data.value()[enc_data_length]);
return kTpmRetryNone;
}
Tpm::TpmRetryAction TpmImpl::UnsealWithAuthorization(
TpmKeyHandle key_handle,
const SecureBlob& sealed_data,
const SecureBlob& auth_blob,
const std::map<uint32_t, std::string>& pcr_map,
SecureBlob* plaintext) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "Failed to connect to the TPM.";
return Tpm::kTpmRetryFailNoRetry;
}
// Load the Storage Root Key.
TSS_RESULT result;
ScopedTssKey srk_handle(context_handle);
if (!LoadSrk(context_handle, srk_handle.ptr(), &result)) {
TPM_LOG(INFO, result) << "Failed to load SRK.";
return ResultToRetryAction(result);
}
// Create an ENCDATA object with the sealed value.
ScopedTssKey enc_handle(context_handle);
if (!SetAuthValue(context_handle, &enc_handle, tpm_handle, auth_blob)) {
context_handle.reset();
return Tpm::kTpmRetryFailNoRetry;
}
if (TPM_ERROR(result = Tspi_SetAttribData(enc_handle,
TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB,
sealed_data.size(),
const_cast<BYTE*>(sealed_data.data())))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_SetAttribData";
return ResultToRetryAction(result);
}
// Unseal using the SRK.
ScopedTssMemory dec_data(context_handle);
UINT32 dec_data_length = 0;
if (TPM_ERROR(result = Tspi_Data_Unseal(enc_handle,
srk_handle,
&dec_data_length,
dec_data.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Data_Unseal";
return ResultToRetryAction(result);
}
plaintext->assign(&dec_data.value()[0], &dec_data.value()[dec_data_length]);
brillo::SecureMemset(dec_data.value(), 0, dec_data_length);
return kTpmRetryNone;
}
bool TpmImpl::GetPublicKeyBlob(TSS_HCONTEXT context_handle,
TSS_HKEY key_handle,
SecureBlob* data_out,
TSS_RESULT* result) const {
*result = TSS_SUCCESS;
ScopedTssMemory blob(context_handle);
UINT32 blob_size;
if (TPM_ERROR(*result = Tspi_Key_GetPubKey(key_handle, &blob_size,
blob.ptr()))) {
TPM_LOG(ERROR, *result) << "Error calling Tspi_Key_GetPubKey";
return false;
}
SecureBlob local_data(blob_size);
memcpy(local_data.data(), blob.value(), blob_size);
brillo::SecureMemset(blob.value(), 0, blob_size);
data_out->swap(local_data);
return true;
}
bool TpmImpl::LoadSrk(TSS_HCONTEXT context_handle, TSS_HKEY* srk_handle,
TSS_RESULT* result) const {
*result = TSS_SUCCESS;
// Load the Storage Root Key
TSS_UUID SRK_UUID = TSS_UUID_SRK;
ScopedTssKey local_srk_handle(context_handle);
if (TPM_ERROR(*result = Tspi_Context_LoadKeyByUUID(context_handle,
TSS_PS_TYPE_SYSTEM,
SRK_UUID,
local_srk_handle.ptr()))) {
return false;
}
// Check if the SRK wants a password
UINT32 srk_authusage;
if (TPM_ERROR(*result = Tspi_GetAttribUint32(local_srk_handle,
TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_AUTHUSAGE,
&srk_authusage))) {
return false;
}
// Give it the password if needed
if (srk_authusage) {
TSS_HPOLICY srk_usage_policy;
if (TPM_ERROR(*result = Tspi_GetPolicyObject(local_srk_handle,
TSS_POLICY_USAGE,
&srk_usage_policy))) {
return false;
}
*result = Tspi_Policy_SetSecret(srk_usage_policy, TSS_SECRET_MODE_PLAIN,
srk_auth_.size(),
const_cast<BYTE*>(srk_auth_.data()));
if (TPM_ERROR(*result)) {
return false;
}
}
*srk_handle = local_srk_handle.release();
return true;
}
bool TpmImpl::CreateEndorsementKey() {
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpm(tpm_context_.value(), &tpm_handle)) {
return false;
}
ScopedTssKey local_key_handle(tpm_context_.value());
TSS_FLAG init_flags = TSS_KEY_TYPE_LEGACY | TSS_KEY_SIZE_2048;
if (TPM_ERROR(result = Tspi_Context_CreateObject(tpm_context_.value(),
TSS_OBJECT_TYPE_RSAKEY,
init_flags,
local_key_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return false;
}
if (TPM_ERROR(result = Tspi_TPM_CreateEndorsementKey(tpm_handle,
local_key_handle,
NULL))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_CreateEndorsementKey";
return false;
}
return true;
}
bool TpmImpl::IsEndorsementKeyAvailable() {
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpm(tpm_context_.value(), &tpm_handle)) {
return false;
}
ScopedTssKey local_key_handle(tpm_context_.value());
if (TPM_ERROR(result = Tspi_TPM_GetPubEndorsementKey(tpm_handle, false, NULL,
local_key_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetPubEndorsementKey";
return false;
}
return true;
}
bool TpmImpl::TakeOwnership(int max_timeout_tries,
const SecureBlob& owner_password) {
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpmWithAuth(tpm_context_.value(), owner_password, &tpm_handle)) {
return false;
}
ScopedTssKey srk_handle(tpm_context_.value());
TSS_FLAG init_flags = TSS_KEY_TSP_SRK | TSS_KEY_AUTHORIZATION;
if (TPM_ERROR(result = Tspi_Context_CreateObject(tpm_context_.value(),
TSS_OBJECT_TYPE_RSAKEY,
init_flags, srk_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return false;
}
TSS_HPOLICY srk_usage_policy;
if (TPM_ERROR(result = Tspi_GetPolicyObject(srk_handle, TSS_POLICY_USAGE,
&srk_usage_policy))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_GetPolicyObject";
return false;
}
if (TPM_ERROR(result = Tspi_Policy_SetSecret(srk_usage_policy,
TSS_SECRET_MODE_PLAIN,
strlen(kWellKnownSrkTmp),
const_cast<BYTE *>(reinterpret_cast<const BYTE *>(kWellKnownSrkTmp))))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
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) {
TPM_LOG(ERROR, result)
<< "Error calling Tspi_TPM_TakeOwnership, attempts: " << retry_count;
return false;
}
return true;
}
bool TpmImpl::InitializeSrk(const SecureBlob& owner_password) {
if (!ZeroSrkPassword(tpm_context_.value(), owner_password)) {
LOG(ERROR) << "Error Zero-ing SRK password.";
return false;
}
if (!UnrestrictSrk(tpm_context_.value(), owner_password)) {
LOG(ERROR) << "Error unrestricting SRK.";
return false;
}
return true;
}
bool TpmImpl::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;
}
ScopedTssKey srk_handle(context_handle);
TSS_UUID SRK_UUID = TSS_UUID_SRK;
if (TPM_ERROR(result = Tspi_Context_LoadKeyByUUID(context_handle,
TSS_PS_TYPE_SYSTEM,
SRK_UUID,
srk_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_LoadKeyByUUID";
return false;
}
ScopedTssPolicy policy_handle(context_handle);
if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_POLICY,
TSS_POLICY_USAGE,
policy_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return false;
}
BYTE new_password[0];
if (TPM_ERROR(result = Tspi_Policy_SetSecret(policy_handle,
TSS_SECRET_MODE_PLAIN,
0,
new_password))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
if (TPM_ERROR(result = Tspi_ChangeAuth(srk_handle,
tpm_handle,
policy_handle))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_ChangeAuth";
return false;
}
return true;
}
bool TpmImpl::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;
}
TSS_BOOL current_status = false;
if (TPM_ERROR(result = Tspi_TPM_GetStatus(tpm_handle,
TSS_TPMSTATUS_DISABLEPUBSRKREAD,
&current_status))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetStatus";
return false;
}
// If it is currently owner auth (true), set it to SRK auth
if (current_status) {
if (TPM_ERROR(result = Tspi_TPM_SetStatus(tpm_handle,
TSS_TPMSTATUS_DISABLEPUBSRKREAD,
false))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_SetStatus";
return false;
}
}
return true;
}
bool TpmImpl::ChangeOwnerPassword(const SecureBlob& previous_owner_password,
const SecureBlob& owner_password) {
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (!GetTpmWithAuth(tpm_context_.value(),
previous_owner_password,
&tpm_handle)) {
return false;
}
ScopedTssPolicy policy_handle(tpm_context_.value());
if (TPM_ERROR(result = Tspi_Context_CreateObject(tpm_context_.value(),
TSS_OBJECT_TYPE_POLICY,
TSS_POLICY_USAGE,
policy_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_CreateObject";
return false;
}
if (TPM_ERROR(result = Tspi_Policy_SetSecret(policy_handle,
TSS_SECRET_MODE_PLAIN,
owner_password.size(),
const_cast<BYTE *>(owner_password.data())))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
if (TPM_ERROR(result = Tspi_ChangeAuth(tpm_handle, 0, policy_handle))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_ChangeAuth";
return false;
}
return true;
}
bool TpmImpl::GetTpm(TSS_HCONTEXT context_handle, TSS_HTPM* tpm_handle) {
TSS_RESULT result;
TSS_HTPM local_tpm_handle;
if (TPM_ERROR(result = Tspi_Context_GetTpmObject(context_handle,
&local_tpm_handle))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_GetTpmObject";
return false;
}
*tpm_handle = local_tpm_handle;
return true;
}
bool TpmImpl::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)) {
return false;
}
TSS_HPOLICY tpm_usage_policy;
if (TPM_ERROR(result = Tspi_GetPolicyObject(local_tpm_handle,
TSS_POLICY_USAGE,
&tpm_usage_policy))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_GetPolicyObject";
return false;
}
if (TPM_ERROR(result = Tspi_Policy_SetSecret(
tpm_usage_policy,
TSS_SECRET_MODE_PLAIN,
owner_password.size(),
const_cast<BYTE*>(owner_password.data())))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
*tpm_handle = local_tpm_handle;
return true;
}
bool TpmImpl::GetTpmWithDelegation(TSS_HCONTEXT context_handle,
const brillo::Blob& delegate_blob,
const brillo::Blob& delegate_secret,
TSS_HTPM* tpm_handle) {
TSS_HTPM local_tpm_handle;
if (!GetTpm(context_handle, &local_tpm_handle)) {
return false;
}
TSS_RESULT result;
TSS_HPOLICY tpm_usage_policy;
if (TPM_ERROR(result = Tspi_GetPolicyObject(local_tpm_handle,
TSS_POLICY_USAGE,
&tpm_usage_policy))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_GetPolicyObject";
return false;
}
BYTE* secret_buffer = const_cast<BYTE*>(delegate_secret.data());
if (TPM_ERROR(result = Tspi_Policy_SetSecret(tpm_usage_policy,
TSS_SECRET_MODE_PLAIN,
delegate_secret.size(),
secret_buffer))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
if (TPM_ERROR(result = Tspi_SetAttribData(
tpm_usage_policy,
TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_OWNERBLOB,
delegate_blob.size(),
const_cast<BYTE*>(delegate_blob.data())))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_SetAttribData";
return false;
}
*tpm_handle = local_tpm_handle;
return true;
}
bool TpmImpl::TestTpmAuth(const brillo::SecureBlob& owner_password) {
TSS_HTPM tpm_handle;
if (!GetTpmWithAuth(tpm_context_.value(), owner_password, &tpm_handle)) {
LOG(ERROR) << "Error getting Tpm with supplied owner password.";
return false;
}
// Call Tspi_TPM_GetStatus to test the authentication
TSS_RESULT result;
TSS_BOOL current_status = false;
if (TPM_ERROR(result = Tspi_TPM_GetStatus(tpm_handle,
TSS_TPMSTATUS_DISABLED,
&current_status))) {
return false;
}
return true;
}
bool TpmImpl::GetOwnerPassword(brillo::SecureBlob* 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 TpmImpl::GetRandomDataBlob(size_t length, brillo::Blob* data) {
brillo::SecureBlob blob(length);
if (!this->GetRandomDataSecureBlob(length, &blob)) {
LOG(ERROR) << "GetRandomDataBlob failed";
return false;
}
data->assign(blob.begin(), blob.end());
return true;
}
bool TpmImpl::GetRandomDataSecureBlob(size_t length, brillo::SecureBlob* data) {
ScopedTssContext context_handle;
if ((*(context_handle.ptr()) = ConnectContext()) == 0) {
LOG(ERROR) << "Could not open the TPM";
return false;
}
TSS_HTPM tpm_handle;
if (!GetTpm(context_handle, &tpm_handle)) {
LOG(ERROR) << "Could not get a handle to the TPM";
return false;
}
TSS_RESULT result;
SecureBlob random(length);
ScopedTssMemory tpm_data(context_handle);
result = Tspi_TPM_GetRandom(tpm_handle, random.size(), tpm_data.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not get random data from the TPM";
return false;
}
memcpy(random.data(), tpm_data.value(), random.size());
brillo::SecureMemset(tpm_data.value(), 0, random.size());
data->swap(random);
return true;
}
bool TpmImpl::GetAlertsData(Tpm::AlertsData* alerts) {
return false;
}
bool TpmImpl::DestroyNvram(uint32_t index) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsOwner(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "Could not open the TPM";
return false;
}
if (!IsNvramDefinedForContext(context_handle, tpm_handle, index)) {
LOG(INFO) << "NVRAM index is already undefined.";
return true;
}
// Create an NVRAM store object handle.
TSS_RESULT result;
ScopedTssNvStore nv_handle(context_handle);
result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_NV,
0,
nv_handle.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not acquire an NVRAM object handle";
return false;
}
result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_INDEX,
0, index);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set index on NVRAM object: " << index;
return false;
}
if ((result = Tspi_NV_ReleaseSpace(nv_handle))) {
TPM_LOG(ERROR, result) << "Could not release NVRAM space: " << index;
return false;
}
return true;
}
bool TpmImpl::DefineNvram(uint32_t index, size_t length, uint32_t flags) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsOwner(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "DefineNvram failed to acquire authorization.";
return false;
}
TSS_RESULT result;
// Create a PCR object handle.
ScopedTssPcrs pcrs_handle(context_handle);
if (flags & kTpmNvramBindToPCR0) {
if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_PCRS,
TSS_PCRS_STRUCT_INFO_SHORT,
pcrs_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Could not acquire PCR object handle";
return false;
}
// Read PCR0.
UINT32 pcr_len;
ScopedTssMemory pcr_value(context_handle);
if (TPM_ERROR(result = Tspi_TPM_PcrRead(tpm_handle, kTpmBootPCR,
&pcr_len, pcr_value.ptr()))) {
TPM_LOG(ERROR, result) << "Could not read PCR0 value";
return false;
}
// Include PCR0 value in PcrComposite.
if (TPM_ERROR(result = Tspi_PcrComposite_SetPcrValue(pcrs_handle,
kTpmBootPCR,
pcr_len,
pcr_value.value()))) {
TPM_LOG(ERROR, result) << "Could not set value for PCR0 in PCR handle";
return false;
}
// Set locality.
if (TPM_ERROR(result = Tspi_PcrComposite_SetPcrLocality(pcrs_handle,
kTpmPCRLocality))) {
TPM_LOG(ERROR, result) << "Could not set locality for PCR0 in PCR handle";
return false;
}
}
// Create an NVRAM store object handle.
ScopedTssNvStore nv_handle(context_handle);
result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_NV,
0,
nv_handle.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not acquire an NVRAM object handle";
return false;
}
result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_INDEX,
0, index);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set index on NVRAM object: " << index;
return false;
}
result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_DATASIZE,
0, length);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set size on NVRAM object: " << length;
return false;
}
// Set appropriate permissions
uint32_t perms = 0;
if (flags & kTpmNvramWriteDefine) {
perms |= TPM_NV_PER_WRITEDEFINE;
} else {
TPM_LOG(ERROR, result) << "Unsupported permissions for NVRAM object";
return false;
}
result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_PERMISSIONS,
0, perms);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set permissions on NVRAM object";
return false;
}
if (TPM_ERROR(result = Tspi_NV_DefineSpace(nv_handle,
pcrs_handle, pcrs_handle))) {
TPM_LOG(ERROR, result) << "Could not define NVRAM space: " << index;
return false;
}
return true;
}
bool TpmImpl::IsNvramDefined(uint32_t index) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "Could not connect to the TPM";
return false;
}
return IsNvramDefinedForContext(context_handle, tpm_handle, index);
}
bool TpmImpl::IsNvramDefinedForContext(TSS_HCONTEXT context_handle,
TSS_HTPM tpm_handle,
uint32_t index) {
TSS_RESULT result;
UINT32 nv_list_data_length = 0;
ScopedTssMemory nv_list_data(context_handle);
if (TPM_ERROR(result = Tspi_TPM_GetCapability(tpm_handle,
TSS_TPMCAP_NV_LIST,
0,
NULL,
&nv_list_data_length,
nv_list_data.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetCapability";
return false;
}
// Walk the list and check if the index exists.
UINT32* nv_list = reinterpret_cast<UINT32*>(nv_list_data.value());
UINT32 nv_list_length = nv_list_data_length / sizeof(UINT32);
index = htonl(index); // TPM data is network byte order.
for (UINT32 i = 0; i < nv_list_length; ++i) {
// TODO(wad) add a NvramList method.
if (index == nv_list[i])
return true;
}
return false;
}
unsigned int TpmImpl::GetNvramSize(uint32_t index) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "Could not connect to the TPM";
return 0;
}
return GetNvramSizeForContext(context_handle, tpm_handle, index);
}
unsigned int TpmImpl::GetNvramSizeForContext(TSS_HCONTEXT context_handle,
TSS_HTPM tpm_handle,
uint32_t index) {
unsigned int count = 0;
TSS_RESULT result;
UINT32 nv_index_data_length = 0;
ScopedTssMemory nv_index_data(context_handle);
if (TPM_ERROR(result = Tspi_TPM_GetCapability(tpm_handle,
TSS_TPMCAP_NV_INDEX,
sizeof(index),
reinterpret_cast<BYTE*>(&index),
&nv_index_data_length,
nv_index_data.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetCapability";
return count;
}
if (nv_index_data_length == 0) {
return count;
}
// TPM_NV_DATA_PUBLIC->dataSize is the last element in the struct.
// Since packing the struct still doesn't eliminate inconsistencies between
// the API and the hardware, this is the safest way to extract the data.
uint32_t* nv_data_public = reinterpret_cast<uint32_t*>(
nv_index_data.value() + nv_index_data_length -
sizeof(UINT32));
return htonl(*nv_data_public);
}
bool TpmImpl::IsNvramLocked(uint32_t index) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "Could not connect to the TPM";
return 0;
}
return IsNvramLockedForContext(context_handle, tpm_handle, index);
}
bool TpmImpl::IsNvramLockedForContext(TSS_HCONTEXT context_handle,
TSS_HTPM tpm_handle,
uint32_t index) {
unsigned int count = 0;
TSS_RESULT result;
UINT32 nv_index_data_length = 0;
ScopedTssMemory nv_index_data(context_handle);
if (TPM_ERROR(result = Tspi_TPM_GetCapability(tpm_handle,
TSS_TPMCAP_NV_INDEX,
sizeof(index),
reinterpret_cast<BYTE*>(&index),
&nv_index_data_length,
nv_index_data.ptr()))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_TPM_GetCapability";
return count;
}
if (nv_index_data_length < sizeof(UINT32) + sizeof(TPM_BOOL)) {
return count;
}
// TPM_NV_DATA_PUBLIC->bWriteDefine is the second to last element in the
// struct. Since packing the struct still doesn't eliminate inconsistencies
// between the API and the hardware, this is the safest way to extract the
// data.
// TODO(wad) share data with GetNvramSize() to avoid extra calls.
uint32_t* nv_data_public = reinterpret_cast<uint32_t*>(
nv_index_data.value() + nv_index_data_length -
(sizeof(UINT32) + sizeof(TPM_BOOL)));
return (*nv_data_public != 0);
}
bool TpmImpl::ReadNvram(uint32_t index, SecureBlob* blob) {
// TODO(wad) longer term, add support for checking when a space is restricted
// and needs an authenticated handle.
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "Could not connect to the TPM";
return false;
}
return ReadNvramForContext(context_handle, tpm_handle, 0, index, blob);
}
bool TpmImpl::ReadNvramForContext(TSS_HCONTEXT context_handle,
TSS_HTPM tpm_handle,
TSS_HPOLICY policy_handle,
uint32_t index,
SecureBlob* blob) {
if (!IsNvramDefinedForContext(context_handle, tpm_handle, index)) {
LOG(ERROR) << "Cannot read from non-existent NVRAM space.";
return false;
}
// Create an NVRAM store object handle.
TSS_RESULT result;
ScopedTssNvStore nv_handle(context_handle);
result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_NV,
0,
nv_handle.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not acquire an NVRAM object handle";
return false;
}
result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_INDEX,
0, index);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set index on NVRAM object: " << index;
return false;
}
if (policy_handle) {
result = Tspi_Policy_AssignToObject(policy_handle, nv_handle);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set NVRAM object policy.";
return false;
}
}
UINT32 size = GetNvramSizeForContext(context_handle, tpm_handle, index);
if (size == 0) {
LOG(ERROR) << "NvramSize is too small.";
// TODO(wad) get attrs so we can explore more
return false;
}
blob->resize(size);
// Read from NVRAM in conservatively small chunks. This is a limitation of the
// TPM that is left for the application layer to deal with. The maximum size
// that is supported here can vary between vendors / models, so we'll be
// conservative. FWIW, the Infineon chips seem to handle up to 1024.
const UINT32 kMaxDataSize = 128;
UINT32 offset = 0;
while (offset < size) {
UINT32 chunk_size = size - offset;
if (chunk_size > kMaxDataSize)
chunk_size = kMaxDataSize;
ScopedTssMemory space_data(context_handle);
if ((result = Tspi_NV_ReadValue(nv_handle, offset, &chunk_size,
space_data.ptr()))) {
TPM_LOG(ERROR, result) << "Could not read from NVRAM space: " << index;
return false;
}
if (!space_data.value()) {
LOG(ERROR) << "No data read from NVRAM space: " << index;
return false;
}
CHECK(offset + chunk_size <= blob->size());
unsigned char* buffer = blob->data() + offset;
memcpy(buffer, space_data.value(), chunk_size);
offset += chunk_size;
}
return true;
}
bool TpmImpl::WriteNvram(uint32_t index, const SecureBlob& blob) {
// TODO(wad) longer term, add support for checking when a space is restricted
// and needs an authenticated handle.
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "Could not connect to the TPM";
return 0;
}
// Create an NVRAM store object handle.
TSS_RESULT result;
ScopedTssNvStore nv_handle(context_handle);
result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_NV,
0,
nv_handle.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not acquire an NVRAM object handle";
return false;
}
result = Tspi_SetAttribUint32(nv_handle, TSS_TSPATTRIB_NV_INDEX,
0, index);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not set index on NVRAM object: " << index;
return false;
}
std::unique_ptr<BYTE[]> nv_data(new BYTE[blob.size()]);
memcpy(nv_data.get(), blob.data(), blob.size());
result = Tspi_NV_WriteValue(nv_handle, 0, blob.size(), nv_data.get());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not write to NVRAM space: " << index;
return false;
}
return true;
}
bool TpmImpl::WriteLockNvram(uint32_t index) {
SecureBlob lock(0);
return WriteNvram(index, lock);
}
bool TpmImpl::PerformEnabledOwnedCheck(bool* enabled, bool* owned) {
*enabled = false;
*owned = false;
trousers::ScopedTssContext context(ConnectContext());
if (!context) {
return false;
}
TSS_HCONTEXT context_handle = context.context();
TSS_RESULT result;
TSS_HTPM tpm_handle;
if (TPM_ERROR(result = Tspi_Context_GetTpmObject(context_handle,
&tpm_handle))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Context_GetTpmObject";
return false;
}
UINT32 sub_cap = TSS_TPMCAP_PROP_OWNER;
UINT32 cap_length = 0;
trousers::ScopedTssMemory cap(context_handle);
if (TPM_ERROR(result = Tspi_TPM_GetCapability(tpm_handle, TSS_TPMCAP_PROPERTY,
sizeof(sub_cap),
reinterpret_cast<BYTE*>(&sub_cap),
&cap_length, cap.ptr())) == 0) {
if (cap_length >= (sizeof(TSS_BOOL))) {
*enabled = true;
*owned = ((*(reinterpret_cast<TSS_BOOL*>(cap.value()))) != 0);
}
} else if (ERROR_CODE(result) == TPM_E_DISABLED) {
*enabled = false;
}
return true;
}
Tpm::TpmRetryAction TpmImpl::GetEndorsementPublicKeyWithDelegate(
brillo::SecureBlob* ek_public_key,
const brillo::Blob& delegate_blob,
const brillo::Blob& delegate_secret) {
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
// Connect to the TPM as the owner delegate.
if (!ConnectContextAsDelegate(delegate_blob, delegate_secret,
context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) <<
"GetEndorsementPublicKeyFromDelegate: Could not connect to the TPM.";
}
return GetEndorsementPublicKeyInternal(
ek_public_key, context_handle.ptr(), &tpm_handle);
}
Tpm::TpmRetryAction TpmImpl::GetEndorsementPublicKey(
SecureBlob* ek_public_key) {
// Connect to the TPM as the owner if owned, user otherwise.
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (IsOwned()) {
if (!ConnectContextAsOwner(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "GetEndorsementPublicKey: Could not connect to the TPM.";
return Tpm::kTpmRetryFailNoRetry;
}
} else {
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "GetEndorsementPublicKey: Could not connect to the TPM.";
return Tpm::kTpmRetryFailNoRetry;
}
}
return GetEndorsementPublicKeyInternal(
ek_public_key, context_handle.ptr(), &tpm_handle);
}
Tpm::TpmRetryAction TpmImpl::GetEndorsementPublicKeyInternal(
brillo::SecureBlob* ek_public_key,
TSS_HCONTEXT* context_handle,
TSS_HTPM* tpm_handle) {
// Get a handle to the EK public key.
ScopedTssKey ek_public_key_object(*context_handle);
TSS_RESULT result = Tspi_TPM_GetPubEndorsementKey(
*tpm_handle, IsOwned(), NULL, ek_public_key_object.ptr());
if (TPM_ERROR(result)) {
return ResultToRetryActionWithMessage(result,
"GetEndorsementPublicKeyInternal: Failed to get public key.");
}
// Get the public key in TPM_PUBKEY form.
SecureBlob ek_public_key_blob;
Tpm::TpmRetryAction action = GetDataAttribute(*context_handle,
ek_public_key_object,
TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
&ek_public_key_blob);
if (action != Tpm::kTpmRetryNone) {
LOG(ERROR) << "GetEndorsementPublicKeyInternal: Failed to read public key.";
return action;
}
// Get the public key in DER encoded form.
if (!ConvertPublicKeyToDER(ek_public_key_blob, ek_public_key)) {
LOG(ERROR) << __func__ << ": Failed to DER encode public key.";
return Tpm::kTpmRetryLater;
}
return Tpm::kTpmRetryNone;
}
bool TpmImpl::GetEndorsementCredential(SecureBlob* credential) {
// Connect to the TPM as the owner.
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsOwner(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "GetEndorsementCredential: Could not connect to the TPM.";
return false;
}
// Use the owner secret to authorize reading the blob.
ScopedTssPolicy policy_handle(context_handle);
TSS_RESULT result;
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_POLICY,
TSS_POLICY_USAGE, policy_handle.ptr());
if (TPM_ERROR(result)) {
LOG(ERROR) << "GetEndorsementCredential: Could not create policy.";
return false;
}
// owner_password_ doesn't get set up if |TpmNewImpl| is used; however this
// function has no caller in the mentioned case.
result = Tspi_Policy_SetSecret(policy_handle, TSS_SECRET_MODE_PLAIN,
owner_password_.size(),
static_cast<BYTE*>(owner_password_.data()));
if (TPM_ERROR(result)) {
LOG(ERROR) << "GetEndorsementCredential: Could not set owner secret.";
return false;
}
// Read the EK cert from NVRAM.
SecureBlob nvram_value;
if (!ReadNvramForContext(context_handle, tpm_handle, policy_handle,
TSS_NV_DEFINED | TPM_NV_INDEX_EKCert,
&nvram_value)) {
LOG(ERROR) << "GetEndorsementCredential: Failed to read NVRAM.";
return false;
}
// Sanity check the contents of the data and extract the X.509 certificate.
// We are expecting data in the form of a TCG_PCCLIENT_STORED_CERT with an
// embedded TCG_FULL_CERT. Details can be found in the TCG PC Specific
// Implementation Specification v1.21 section 7.4.
const uint8_t kStoredCertHeader[] = {0x10, 0x01, 0x00};
const uint8_t kFullCertHeader[] = {0x10, 0x02};
const size_t kTotalHeaderBytes = 7;
const size_t kStoredCertHeaderOffset = 0;
const size_t kFullCertLengthOffset = 3;
const size_t kFullCertHeaderOffset = 5;
if (nvram_value.size() < kTotalHeaderBytes) {
LOG(ERROR) << "Malformed EK certificate: Bad header.";
return false;
}
if (memcmp(kStoredCertHeader,
&nvram_value[kStoredCertHeaderOffset],
arraysize(kStoredCertHeader)) != 0) {
LOG(ERROR) << "Malformed EK certificate: Bad PCCLIENT_STORED_CERT.";
return false;
}
if (memcmp(kFullCertHeader,
&nvram_value[kFullCertHeaderOffset],
arraysize(kFullCertHeader)) != 0) {
LOG(ERROR) << "Malformed EK certificate: Bad PCCLIENT_FULL_CERT.";
return false;
}
// The size value is represented by two bytes in network order.
size_t full_cert_size = (nvram_value[kFullCertLengthOffset] << 8) |
nvram_value[kFullCertLengthOffset + 1];
if (full_cert_size + kFullCertHeaderOffset > nvram_value.size()) {
LOG(ERROR) << "Malformed EK certificate: Bad size.";
return false;
}
// The X.509 certificate follows the header bytes.
size_t full_cert_end = kTotalHeaderBytes + full_cert_size -
arraysize(kFullCertHeader);
credential->assign(nvram_value.begin() + kTotalHeaderBytes,
nvram_value.begin() + full_cert_end);
return true;
}
bool TpmImpl::DecryptIdentityRequest(RSA* pca_key,
const SecureBlob& request,
SecureBlob* identity_binding,
SecureBlob* endorsement_credential,
SecureBlob* platform_credential,
SecureBlob* conformance_credential) {
// Parse the serialized TPM_IDENTITY_REQ structure.
UINT64 offset = 0;
BYTE* buffer = const_cast<BYTE*>(request.data());
TPM_IDENTITY_REQ request_parsed;
TSS_RESULT result = Trspi_UnloadBlob_IDENTITY_REQ(&offset, buffer,
&request_parsed);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Failed to parse identity request.";
return false;
}
ScopedByteArray scoped_asym_blob(request_parsed.asymBlob);
ScopedByteArray scoped_sym_blob(request_parsed.symBlob);
// Decrypt the symmetric key.
unsigned char key_buffer[kDefaultTpmRsaKeyBits / 8];
int key_length = RSA_private_decrypt(request_parsed.asymSize,
request_parsed.asymBlob,
key_buffer, pca_key, RSA_PKCS1_PADDING);
if (key_length == -1) {
LOG(ERROR) << "Failed to decrypt identity request key.";
return false;
}
TPM_SYMMETRIC_KEY symmetric_key;
offset = 0;
result = Trspi_UnloadBlob_SYMMETRIC_KEY(&offset, key_buffer, &symmetric_key);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Failed to parse symmetric key.";
return false;
}
ScopedByteArray scoped_sym_key(symmetric_key.data);
// Decrypt the request with the symmetric key.
SecureBlob proof_serial;
proof_serial.resize(request_parsed.symSize);
UINT32 proof_serial_length = proof_serial.size();
result = Trspi_SymDecrypt(symmetric_key.algId, TPM_ES_SYM_CBC_PKCS5PAD,
symmetric_key.data, NULL,
request_parsed.symBlob, request_parsed.symSize,
proof_serial.data(), &proof_serial_length);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Failed to decrypt identity request.";
return false;
}
// Parse the serialized TPM_IDENTITY_PROOF structure.
TPM_IDENTITY_PROOF proof;
offset = 0;
result = Trspi_UnloadBlob_IDENTITY_PROOF(&offset, proof_serial.data(),
&proof);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Failed to parse identity proof.";
return false;
}
ScopedByteArray scoped_label(proof.labelArea);
ScopedByteArray scoped_binding(proof.identityBinding);
ScopedByteArray scoped_endorsement(proof.endorsementCredential);
ScopedByteArray scoped_platform(proof.platformCredential);
ScopedByteArray scoped_conformance(proof.conformanceCredential);
ScopedByteArray scoped_key(proof.identityKey.pubKey.key);
ScopedByteArray scoped_parms(proof.identityKey.algorithmParms.parms);
identity_binding->assign(&proof.identityBinding[0],
&proof.identityBinding[proof.identityBindingSize]);
brillo::SecureMemset(proof.identityBinding, 0, proof.identityBindingSize);
endorsement_credential->assign(
&proof.endorsementCredential[0],
&proof.endorsementCredential[proof.endorsementSize]);
brillo::SecureMemset(proof.endorsementCredential, 0, proof.endorsementSize);
platform_credential->assign(&proof.platformCredential[0],
&proof.platformCredential[proof.platformSize]);
brillo::SecureMemset(proof.platformCredential, 0, proof.platformSize);
conformance_credential->assign(
&proof.conformanceCredential[0],
&proof.conformanceCredential[proof.conformanceSize]);
brillo::SecureMemset(proof.conformanceCredential, 0, proof.conformanceSize);
return true;
}
bool TpmImpl::MakeIdentity(SecureBlob* identity_public_key_der,
SecureBlob* identity_public_key,
SecureBlob* identity_key_blob,
SecureBlob* identity_binding,
SecureBlob* identity_label,
SecureBlob* pca_public_key,
SecureBlob* endorsement_credential,
SecureBlob* platform_credential,
SecureBlob* conformance_credential) {
CHECK(identity_public_key_der && identity_public_key && identity_key_blob &&
identity_binding && identity_label && pca_public_key &&
endorsement_credential && platform_credential &&
conformance_credential);
// Connect to the TPM as the owner.
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsOwner(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "MakeIdentity: Could not connect to the TPM.";
return false;
}
// Load the Storage Root Key.
TSS_RESULT result;
ScopedTssKey srk_handle(context_handle);
if (!LoadSrk(context_handle, srk_handle.ptr(), &result)) {
TPM_LOG(INFO, result) << "MakeIdentity: Cannot load SRK.";
return false;
}
crypto::ScopedRSA fake_pca_key(RSA_generate_key(kDefaultTpmRsaKeyBits,
kWellKnownExponent,
NULL,
NULL));
if (!fake_pca_key.get()) {
LOG(ERROR) << "MakeIdentity: Failed to generate local key pair.";
return false;
}
unsigned char modulus_buffer[kDefaultTpmRsaKeyBits/8];
BN_bn2bin(fake_pca_key.get()->n, modulus_buffer);
// Create a TSS object for the fake PCA public key.
ScopedTssKey pca_public_key_object(context_handle);
UINT32 pca_key_flags = kDefaultTpmRsaKeyFlag |
TSS_KEY_TYPE_LEGACY |
TSS_KEY_MIGRATABLE;
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_RSAKEY,
pca_key_flags,
pca_public_key_object.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "MakeIdentity: Cannot create PCA public key.";
return false;
}
result = Tspi_SetAttribData(pca_public_key_object,
TSS_TSPATTRIB_RSAKEY_INFO,
TSS_TSPATTRIB_KEYINFO_RSA_MODULUS,
arraysize(modulus_buffer), modulus_buffer);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "MakeIdentity: Cannot create PCA public key 2.";
return false;
}
result = Tspi_SetAttribUint32(pca_public_key_object,
TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_ENCSCHEME,
TSS_ES_RSAESPKCSV15);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "MakeIdentity: Cannot create PCA public key 3.";
return false;
}
// Get the fake PCA public key in serialized TPM_PUBKEY form.
if (GetDataAttribute(context_handle,
pca_public_key_object,
TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
pca_public_key) != Tpm::kTpmRetryNone) {
LOG(ERROR) << __func__ << ": Failed to read public key.";
return false;
}
// Construct an arbitrary unicode label.
const char* label_text = "ChromeOS_AIK_1BJNAMQDR4RH44F4ET2KPAOMJMO043K1";
BYTE* label_ascii =
const_cast<BYTE*>(reinterpret_cast<const BYTE*>(label_text));
unsigned int label_size = strlen(label_text);
ScopedByteArray label(Trspi_Native_To_UNICODE(label_ascii, &label_size));
if (!label.get()) {
LOG(ERROR) << "MakeIdentity: Failed to create AIK label.";
return false;
}
identity_label->assign(&label.get()[0],
&label.get()[label_size]);
// Initialize a key object to hold the new identity key.
ScopedTssKey identity_key(context_handle);
UINT32 identity_key_flags = kDefaultTpmRsaKeyFlag |
TSS_KEY_TYPE_IDENTITY |
TSS_KEY_VOLATILE |
TSS_KEY_NOT_MIGRATABLE;
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_RSAKEY,
identity_key_flags, identity_key.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "MakeIdentity: Failed to create key object.";
return false;
}
// Create the identity and receive the request intended for the PCA.
UINT32 request_length = 0;
ScopedTssMemory request(context_handle);
result = Tspi_TPM_CollateIdentityRequest(tpm_handle, srk_handle,
pca_public_key_object,
label_size, label.get(),
identity_key, TSS_ALG_3DES,
&request_length, request.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "MakeIdentity: Failed to make identity.";
return false;
}
// Decrypt and parse the identity request.
SecureBlob request_blob(request.value(), request.value() + request_length);
if (!DecryptIdentityRequest(fake_pca_key.get(), request_blob,
identity_binding, endorsement_credential,
platform_credential, conformance_credential)) {
LOG(ERROR) << "MakeIdentity: Failed to decrypt the identity request.";
return false;
}
brillo::SecureMemset(request.value(), 0, request_length);
// We need the endorsement credential. If CollateIdentityRequest does not
// provide it, read it manually.
if (endorsement_credential->size() == 0 &&
!GetEndorsementCredential(endorsement_credential)) {
LOG(ERROR) << "MakeIdentity: Failed to get endorsement credential.";
return false;
}
// Get the AIK public key.
if (GetDataAttribute(context_handle,
identity_key,
TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
identity_public_key) != Tpm::kTpmRetryNone) {
LOG(ERROR) << __func__ << ": Failed to read public key.";
return false;
}
if (!ConvertPublicKeyToDER(*identity_public_key, identity_public_key_der)) {
return false;
}
// Get the AIK blob so we can load it later.
if (GetDataAttribute(context_handle,
identity_key,
TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_BLOB,
identity_key_blob) != Tpm::kTpmRetryNone) {
LOG(ERROR) << __func__ << ": Failed to read key blob.";
return false;
}
return true;
}
bool TpmImpl::QuotePCR(uint32_t pcr_index,
const SecureBlob& identity_key_blob,
const SecureBlob& external_data,
Blob* pcr_value,
SecureBlob* quoted_data,
SecureBlob* quote) {
CHECK(pcr_value && quoted_data && quote);
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "QuotePCR: Failed to connect to the TPM.";
return false;
}
// Load the Storage Root Key.
TSS_RESULT result;
ScopedTssKey srk_handle(context_handle);
if (!LoadSrk(context_handle, srk_handle.ptr(), &result)) {
TPM_LOG(INFO, result) << "QuotePCR: Failed to load SRK.";
return false;
}
// Load the AIK (which is wrapped by the SRK).
ScopedTssKey identity_key(context_handle);
result = Tspi_Context_LoadKeyByBlob(
context_handle,
srk_handle,
identity_key_blob.size(),
const_cast<BYTE*>(identity_key_blob.data()),
identity_key.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "QuotePCR: Failed to load AIK.";
return false;
}
// Create a PCRS object and select the index.
ScopedTssPcrs pcrs(context_handle);
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_PCRS,
TSS_PCRS_STRUCT_INFO, pcrs.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "QuotePCR: Failed to create PCRS object.";
return false;
}
result = Tspi_PcrComposite_SelectPcrIndex(pcrs, pcr_index);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "QuotePCR: Failed to select PCR.";
return false;
}
// Generate the quote.
TSS_VALIDATION validation;
memset(&validation, 0, sizeof(validation));
validation.ulExternalDataLength = external_data.size();
validation.rgbExternalData = const_cast<BYTE*>(external_data.data());
result = Tspi_TPM_Quote(tpm_handle, identity_key, pcrs, &validation);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "QuotePCR: Failed to generate quote.";
return false;
}
ScopedTssMemory scoped_quoted_data(context_handle, validation.rgbData);
ScopedTssMemory scoped_quote(context_handle, validation.rgbValidationData);
// Get the PCR value that was quoted.
ScopedTssMemory pcr_value_buffer(context_handle);
UINT32 pcr_value_length = 0;
result = Tspi_PcrComposite_GetPcrValue(pcrs, pcr_index, &pcr_value_length,
pcr_value_buffer.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "QuotePCR: Failed to get PCR value.";
return false;
}
pcr_value->assign(&pcr_value_buffer.value()[0],
&pcr_value_buffer.value()[pcr_value_length]);
// Get the data that was quoted.
quoted_data->assign(&validation.rgbData[0],
&validation.rgbData[validation.ulDataLength]);
// Get the quote.
quote->assign(
&validation.rgbValidationData[0],
&validation.rgbValidationData[validation.ulValidationDataLength]);
return true;
}
bool TpmImpl::SealToPCR0(const brillo::SecureBlob& value,
brillo::SecureBlob* sealed_value) {
CHECK(sealed_value);
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "SealToPCR0: Failed to connect to the TPM.";
return false;
}
// Load the Storage Root Key.
TSS_RESULT result;
ScopedTssKey srk_handle(context_handle);
if (!LoadSrk(context_handle, srk_handle.ptr(), &result)) {
TPM_LOG(INFO, result) << "SealToPCR0: Failed to load SRK.";
return false;
}
// Check the SRK public key
unsigned int size_n = 0;
ScopedTssMemory public_srk(context_handle);
if (TPM_ERROR(result = Tspi_Key_GetPubKey(srk_handle, &size_n,
public_srk.ptr()))) {
TPM_LOG(ERROR, result) << "SealToPCR0: Unable to get the SRK public key";
return false;
}
// Create a PCRS object which holds the value of PCR0.
ScopedTssPcrs pcrs_handle(context_handle);
if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_PCRS,
TSS_PCRS_STRUCT_INFO,
pcrs_handle.ptr()))) {
TPM_LOG(ERROR, result)
<< "SealToPCR0: Error calling Tspi_Context_CreateObject";
return false;
}
// Create a ENCDATA object to receive the sealed data.
UINT32 pcr_len = 0;
ScopedTssMemory pcr_value(context_handle);
Tspi_TPM_PcrRead(tpm_handle, 0, &pcr_len, pcr_value.ptr());
Tspi_PcrComposite_SetPcrValue(pcrs_handle, 0, pcr_len, pcr_value.value());
ScopedTssKey enc_handle(context_handle);
if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_ENCDATA,
TSS_ENCDATA_SEAL,
enc_handle.ptr()))) {
TPM_LOG(ERROR, result)
<< "SealToPCR0: Error calling Tspi_Context_CreateObject";
return false;
}
// Seal the given value with the SRK.
if (TPM_ERROR(result = Tspi_Data_Seal(
enc_handle,
srk_handle,
value.size(),
const_cast<BYTE*>(value.data()),
pcrs_handle))) {
TPM_LOG(ERROR, result) << "SealToPCR0: Error calling Tspi_Data_Seal";
return false;
}
// Extract the sealed value.
ScopedTssMemory enc_data(context_handle);
UINT32 enc_data_length = 0;
if (TPM_ERROR(result = Tspi_GetAttribData(enc_handle,
TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB,
&enc_data_length,
enc_data.ptr()))) {
TPM_LOG(ERROR, result) << "SealToPCR0: Error calling Tspi_GetAttribData";
return false;
}
sealed_value->assign(&enc_data.value()[0],
&enc_data.value()[enc_data_length]);
return true;
}
bool TpmImpl::Unseal(const brillo::SecureBlob& sealed_value,
brillo::SecureBlob* value) {
CHECK(value);
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "Unseal: Failed to connect to the TPM.";
return false;
}
// Load the Storage Root Key.
TSS_RESULT result;
ScopedTssKey srk_handle(context_handle);
if (!LoadSrk(context_handle, srk_handle.ptr(), &result)) {
TPM_LOG(INFO, result) << "Unseal: Failed to load SRK.";
return false;
}
// Create an ENCDATA object with the sealed value.
ScopedTssKey enc_handle(context_handle);
if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle,
TSS_OBJECT_TYPE_ENCDATA,
TSS_ENCDATA_SEAL,
enc_handle.ptr()))) {
TPM_LOG(ERROR, result) << "Unseal: Error calling Tspi_Context_CreateObject";
return false;
}
if (TPM_ERROR(result = Tspi_SetAttribData(enc_handle,
TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB,
sealed_value.size(),
const_cast<BYTE*>(sealed_value.data())))) {
TPM_LOG(ERROR, result) << "Unseal: Error calling Tspi_SetAttribData";
return false;
}
// Unseal using the SRK.
ScopedTssMemory dec_data(context_handle);
UINT32 dec_data_length = 0;
if (TPM_ERROR(result = Tspi_Data_Unseal(enc_handle,
srk_handle,
&dec_data_length,
dec_data.ptr()))) {
TPM_LOG(ERROR, result) << "Unseal: Error calling Tspi_Data_Unseal";
return false;
}
value->assign(&dec_data.value()[0], &dec_data.value()[dec_data_length]);
brillo::SecureMemset(dec_data.value(), 0, dec_data_length);
return true;
}
bool TpmImpl::CreateCertifiedKey(const SecureBlob& identity_key_blob,
const SecureBlob& external_data,
SecureBlob* certified_public_key,
SecureBlob* certified_public_key_der,
SecureBlob* certified_key_blob,
SecureBlob* certified_key_info,
SecureBlob* certified_key_proof) {
CHECK(certified_public_key && certified_key_blob && certified_key_info &&
certified_key_proof);
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "CreateCertifiedKey: Failed to connect to the TPM.";
return false;
}
// Load the Storage Root Key.
TSS_RESULT result;
ScopedTssKey srk_handle(context_handle);
if (!LoadSrk(context_handle, srk_handle.ptr(), &result)) {
TPM_LOG(INFO, result) << "CreateCertifiedKey: Failed to load SRK.";
return false;
}
// Load the AIK (which is wrapped by the SRK).
ScopedTssKey identity_key(context_handle);
result = Tspi_Context_LoadKeyByBlob(
context_handle,
srk_handle,
identity_key_blob.size(),
const_cast<BYTE*>(identity_key_blob.data()),
identity_key.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateCertifiedKey: Failed to load AIK.";
return false;
}
// Create a non-migratable signing key.
ScopedTssKey signing_key(context_handle);
UINT32 init_flags = TSS_KEY_TYPE_SIGNING |
TSS_KEY_NOT_MIGRATABLE |
TSS_KEY_VOLATILE |
kDefaultTpmRsaKeyFlag;
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_RSAKEY,
init_flags, signing_key.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateCertifiedKey: Failed to create object.";
return false;
}
result = Tspi_SetAttribUint32(signing_key,
TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
TSS_SS_RSASSAPKCS1V15_DER);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateCertifiedKey: Failed to set signature "
<< "scheme.";
return false;
}
result = Tspi_Key_CreateKey(signing_key, srk_handle, 0);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateCertifiedKey: Failed to create key.";
return false;
}
result = Tspi_Key_LoadKey(signing_key, srk_handle);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateCertifiedKey: Failed to load key.";
return false;
}
// Certify the signing key.
TSS_VALIDATION validation;
memset(&validation, 0, sizeof(validation));
validation.ulExternalDataLength = external_data.size();
validation.rgbExternalData =
const_cast<BYTE*>(external_data.data());
result = Tspi_Key_CertifyKey(signing_key, identity_key, &validation);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateCertifiedKey: Failed to certify key.";
return false;
}
ScopedTssMemory scoped_certified_data(context_handle, validation.rgbData);
ScopedTssMemory scoped_proof(context_handle, validation.rgbValidationData);
// Get the certified public key.
if (GetDataAttribute(context_handle,
signing_key,
TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
certified_public_key) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "CreateCertifiedKey: Failed to read public key.";
return false;
}
if (!ConvertPublicKeyToDER(*certified_public_key, certified_public_key_der)) {
return false;
}
// Get the certified key blob so we can load it later.
if (GetDataAttribute(context_handle,
signing_key,
TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_BLOB,
certified_key_blob) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "CreateCertifiedKey: Failed to read key blob.";
return false;
}
// Get the data that was certified.
certified_key_info->assign(&validation.rgbData[0],
&validation.rgbData[validation.ulDataLength]);
// Get the certification proof.
certified_key_proof->assign(
&validation.rgbValidationData[0],
&validation.rgbValidationData[validation.ulValidationDataLength]);
return true;
}
bool TpmImpl::CreateDelegate(const std::set<uint32_t>& bound_pcrs,
uint8_t delegate_family_label,
uint8_t delegate_label,
Blob* delegate_blob,
Blob* delegate_secret) {
CHECK(delegate_blob && delegate_secret);
// Connect to the TPM as the owner.
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsOwner(context_handle.ptr(), &tpm_handle)) {
LOG(ERROR) << "CreateDelegate: Could not connect to the TPM.";
return false;
}
// Generate a delegate secret.
if (!GetRandomDataBlob(kDelegateSecretSize, delegate_secret)) {
return false;
}
// Create an owner delegation policy.
ScopedTssPolicy policy(context_handle);
TSS_RESULT result;
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_POLICY,
TSS_POLICY_USAGE, policy.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to create policy.";
return false;
}
result = Tspi_Policy_SetSecret(policy, TSS_SECRET_MODE_PLAIN,
delegate_secret->size(),
delegate_secret->data());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to set policy secret.";
return false;
}
result = Tspi_SetAttribUint32(policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_TYPE,
TSS_DELEGATIONTYPE_OWNER);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to set delegation type.";
return false;
}
// These are the privileged operations we will allow the delegate to perform.
const UINT32 permissions = TPM_DELEGATE_ActivateIdentity |
TPM_DELEGATE_DAA_Join |
TPM_DELEGATE_DAA_Sign |
TPM_DELEGATE_ResetLockValue |
TPM_DELEGATE_OwnerReadInternalPub |
TPM_DELEGATE_CMK_ApproveMA |
TPM_DELEGATE_CMK_CreateTicket |
TPM_DELEGATE_AuthorizeMigrationKey;
result = Tspi_SetAttribUint32(policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_PER1, permissions);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to set permissions.";
return false;
}
result = Tspi_SetAttribUint32(policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_PER2, 0);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "CreateDelegate: Failed to set permissions.";
return false;
}
// Bind the delegate to the specified PCRs. Note: it's crucial to pass a null
// TSS_HPCRS to Tspi_TPM_Delegate_CreateDelegation() when no PCR is selected,
// otherwise it will fail with TPM_E_BAD_PARAM_SIZE.
ScopedTssPcrs pcrs_handle(context_handle);