blob: 88b9b91da3d6aebf5984ab9865a5272f101d10c1 [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 <utility>
#include <vector>
#include <arpa/inet.h>
#include <base/check.h>
#include <base/check_op.h>
#include <base/logging.h>
#include <base/memory/free_deleter.h>
#include <base/notreached.h>
#include <base/stl_util.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/libcrypto-compat.h>
#include <crypto/scoped_openssl_types.h>
#include <libhwsec/overalls/overalls_api.h>
#include <libhwsec/error/tpm1_error.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <tpm_manager-client/tpm_manager/dbus-constants.h>
#include <trousers/scoped_tss_type.h>
#include <trousers/tss.h>
#include <trousers/trousers.h> // NOLINT(build/include_alpha) - needs tss.h
#include "cryptohome/crypto/aes.h"
#include "cryptohome/crypto/rsa.h"
#include "cryptohome/crypto/secure_blob_util.h"
#include "cryptohome/crypto/sha.h"
#include "cryptohome/cryptohome_metrics.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::error::TPM1Error;
using hwsec::error::TPMError;
using hwsec::error::TPMErrorBase;
using hwsec::error::TPMRetryAction;
using hwsec::overalls::GetOveralls;
using hwsec_foundation::error::CreateError;
using hwsec_foundation::error::WrapError;
using trousers::ScopedTssContext;
using trousers::ScopedTssKey;
using trousers::ScopedTssMemory;
using trousers::ScopedTssNvStore;
using trousers::ScopedTssObject;
using trousers::ScopedTssPcrs;
using trousers::ScopedTssPolicy;
namespace cryptohome {
// Returns the corresponding TpmResult enum value to be used to report a
// "Cryptohome.TpmResults" histogram sample.
TpmResult GetTpmResultSample(TSS_RESULT result);
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}};
// 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;
}
std::string OwnerDependencyEnumClassToString(
Tpm::TpmOwnerDependency dependency) {
switch (dependency) {
case Tpm::TpmOwnerDependency::kInstallAttributes:
return tpm_manager::kTpmOwnerDependency_Nvram;
case Tpm::TpmOwnerDependency::kAttestation:
return tpm_manager::kTpmOwnerDependency_Attestation;
}
NOTREACHED() << __func__ << ": Unexpected enum class value: "
<< static_cast<int>(dependency);
return "";
}
} // namespace
const unsigned char kDefaultSrkAuth[] = {};
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()
: srk_auth_(kDefaultSrkAuth, kDefaultSrkAuth + sizeof(kDefaultSrkAuth)),
owner_password_() {
TSS_HCONTEXT context_handle = ConnectContext();
if (context_handle) {
tpm_context_.reset(0, context_handle);
}
}
TpmImpl::~TpmImpl() {}
void TpmImpl::SetTpmManagerUtilityForTesting(
tpm_manager::TpmManagerUtility* tpm_manager_utility) {
tpm_manager_utility_ = tpm_manager_utility;
}
TSS_HCONTEXT TpmImpl::ConnectContext() {
TSS_HCONTEXT context_handle = 0;
if (TPM1Error err = OpenAndConnectTpm(&context_handle)) {
LOG(ERROR) << "Failed to OpenAndConnectTpm: " << *err;
return 0;
}
return context_handle;
}
bool TpmImpl::ConnectContextAsOwner(TSS_HCONTEXT* context, TSS_HTPM* tpm) {
*context = 0;
*tpm = 0;
SecureBlob owner_password;
if (TPMErrorBase err = GetOwnerPassword(&owner_password)) {
LOG(ERROR) << "ConnectContextAsOwner requires an owner password: " << *err;
return false;
}
if (!IsOwned()) {
LOG(ERROR) << "ConnectContextAsOwner: TPM is unowned";
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()) {
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(base::Optional<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.has_value();
ScopedTssContext context_handle;
// Check if we can connect
if (TPM1Error err = OpenAndConnectTpm(context_handle.ptr())) {
status->last_tpm_error = err->ErrorCode();
return;
}
status->can_connect = true;
// Check the Storage Root Key
ScopedTssKey srk_handle(context_handle);
if (TPM1Error err = LoadSrk(context_handle, srk_handle.ptr())) {
status->last_tpm_error = err->ErrorCode();
return;
}
status->can_load_srk = true;
// Check the SRK public key
unsigned int public_srk_size;
ScopedTssMemory public_srk_bytes(context_handle);
if (auto err = CreateError<TPM1Error>(Tspi_Key_GetPubKey(
srk_handle, &public_srk_size, public_srk_bytes.ptr()))) {
LOG(ERROR) << "Failed to get public key: " << *err;
status->last_tpm_error = err->ErrorCode();
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));
if (public_srk) {
const BIGNUM* n;
RSA_get0_key(public_srk.get(), &n, nullptr, nullptr);
status->srk_vulnerable_roca = TestRocaVulnerable(n);
} else {
status->srk_vulnerable_roca = false;
}
// Check the Cryptohome key by using what we have been told.
status->has_cryptohome_key =
(tpm_context_.value() != 0) && key_handle.has_value();
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;
PasskeyToAesKey(password, salt, 13, &key, NULL);
if (TPMErrorBase err =
EncryptBlob(key_handle.value(), data, key, &data_out)) {
LOG(ERROR) << __func__ << ": Failed to encrypt blob: " << *err;
return;
}
status->can_encrypt = true;
// Check decryption (we don't care about the contents, just whether or not
// there was an error)
if (TPMErrorBase err =
DecryptBlob(key_handle.value(), data_out, key,
std::map<uint32_t, std::string>(), &data)) {
LOG(ERROR) << __func__ << ": Failed to decrypt blob: " << *err;
return;
}
status->can_decrypt = true;
}
}
TPMErrorBase TpmImpl::IsSrkRocaVulnerable(bool* result) {
if (!tpm_context_) {
return CreateError<TPMError>("No TPM context", TPMRetryAction::kNoRetry);
}
ScopedTssKey srk_handle(tpm_context_);
if (TPM1Error err = LoadSrk(tpm_context_, srk_handle.ptr())) {
return WrapError<TPMError>(std::move(err), "Failed to load SRK");
}
unsigned public_srk_size;
ScopedTssMemory public_srk_bytes(tpm_context_);
if (auto err = CreateError<TPM1Error>(Tspi_Key_GetPubKey(
srk_handle, &public_srk_size, public_srk_bytes.ptr()))) {
return WrapError<TPMError>(std::move(err), "Failed to get SRK public key");
}
crypto::ScopedRSA public_srk = ParseRsaFromTpmPubkeyBlob(Blob(
public_srk_bytes.value(), public_srk_bytes.value() + public_srk_size));
if (!public_srk) {
return CreateError<TPMError>("Failed to parse RSA public key",
TPMRetryAction::kNoRetry);
}
const BIGNUM* n = nullptr;
RSA_get0_key(public_srk.get(), &n, nullptr, nullptr);
*result = TestRocaVulnerable(n);
return nullptr;
}
bool TpmImpl::GetDictionaryAttackInfo(int* counter,
int* threshold,
bool* lockout,
int* seconds_remaining) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": failed to initialize |TpmManagerUtility|.";
return false;
}
return tpm_manager_utility_->GetDictionaryAttackInfo(
counter, threshold, lockout, seconds_remaining);
}
bool TpmImpl::ResetDictionaryAttackMitigation(const brillo::Blob&,
const brillo::Blob&) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": failed to initialize |TpmManagerUtility|.";
return false;
}
return tpm_manager_utility_->ResetDictionaryAttackLock();
}
bool TpmImpl::CreatePolicyWithRandomPassword(TSS_HCONTEXT context_handle,
TSS_FLAG policy_type,
TSS_HPOLICY* policy_handle) {
trousers::ScopedTssPolicy local_policy(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_POLICY,
policy_type, local_policy.ptr()))) {
LOG(ERROR) << "Error creating policy object: " << *err;
return false;
}
auto migration_password =
CreateSecureRandomBlob(kDefaultDiscardableWrapPasswordLength);
if (auto err = CreateError<TPM1Error>(Tspi_Policy_SetSecret(
local_policy, TSS_SECRET_MODE_PLAIN, migration_password.size(),
migration_password.data()))) {
LOG(ERROR) << "Error setting policy password: " << *err;
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);
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_RSAKEY,
key_flags, local_key.ptr()))) {
LOG(ERROR) << __func__ << ": Error creating the key object: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(Tspi_SetAttribData(
local_key, TSS_TSPATTRIB_RSAKEY_INFO,
TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, key_modulus.size(),
const_cast<BYTE*>(key_modulus.data())))) {
LOG(ERROR) << __func__ << ": Error setting the key modulus: " << *err;
return false;
}
if (signature_scheme != TSS_SS_NONE) {
if (auto err = CreateError<TPM1Error>(Tspi_SetAttribUint32(
local_key, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
signature_scheme))) {
LOG(ERROR) << __func__
<< ": Error setting the key signing scheme: " << *err;
return false;
}
}
if (encryption_scheme != TSS_ES_NONE) {
if (auto err = CreateError<TPM1Error>(Tspi_SetAttribUint32(
local_key, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_ENCSCHEME,
encryption_scheme))) {
LOG(ERROR) << __func__
<< ": Error setting the key encryption scheme: " << *err;
return false;
}
}
*key_handle = local_key.release();
return true;
}
TPM1Error TpmImpl::OpenAndConnectTpm(TSS_HCONTEXT* context_handle) {
ScopedTssContext local_context_handle;
if (auto err = CreateError<TPM1Error>(
Tspi_Context_Create(local_context_handle.ptr()))) {
LOG(ERROR) << "Error calling Tspi_Context_Create: " << *err;
return err;
}
for (unsigned int i = 0; i < kTpmConnectRetries; i++) {
if (auto err = CreateError<TPM1Error>(
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 (err->ToTPMRetryAction() == TPMRetryAction::kCommunication &&
i + 1 != kTpmConnectRetries) {
PlatformThread::Sleep(
base::TimeDelta::FromMilliseconds(kTpmConnectIntervalMs));
} else {
LOG(ERROR) << "Error calling Tspi_Context_Connect: " << *err;
return err;
}
} else {
break;
}
}
*context_handle = local_context_handle.release();
return nullptr;
}
TPMErrorBase TpmImpl::GetPublicKeyHash(TpmKeyHandle key_handle,
SecureBlob* hash) {
SecureBlob pubkey;
if (TPM1Error err =
GetPublicKeyBlob(tpm_context_.value(), key_handle, &pubkey)) {
return WrapError<TPMError>(std::move(err),
"Failed to get TPM public key hash");
}
*hash = Sha1(pubkey);
return nullptr;
}
TPMErrorBase TpmImpl::EncryptBlob(TpmKeyHandle key_handle,
const SecureBlob& plaintext,
const SecureBlob& key,
SecureBlob* ciphertext) {
TSS_FLAG init_flags = TSS_ENCDATA_SEAL;
ScopedTssKey enc_handle(tpm_context_.value());
if (auto err = CreateError<TPM1Error>(Tspi_Context_CreateObject(
tpm_context_.value(), TSS_OBJECT_TYPE_ENCDATA, init_flags,
enc_handle.ptr()))) {
return WrapError<TPMError>(std::move(err),
"Error calling Tspi_Context_CreateObject");
}
// TODO(fes): Check RSA key modulus size, return an error or block input
if (auto err = CreateError<TPM1Error>(
Tspi_Data_Bind(enc_handle, key_handle, plaintext.size(),
const_cast<BYTE*>(plaintext.data())))) {
return WrapError<TPMError>(std::move(err), "Error calling Tspi_Data_Bind");
}
SecureBlob enc_data_blob;
if (TPM1Error err = GetDataAttribute(
tpm_context_.value(), enc_handle, TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB, &enc_data_blob)) {
return WrapError<TPMError>(std::move(err), "Failed to read encrypted blob");
}
if (!ObscureRsaMessage(enc_data_blob, key, ciphertext)) {
return CreateError<TPMError>("Error obscuring message",
TPMRetryAction::kNoRetry);
}
return nullptr;
}
TPMErrorBase TpmImpl::DecryptBlob(
TpmKeyHandle key_handle,
const SecureBlob& ciphertext,
const SecureBlob& key,
const std::map<uint32_t, std::string>& pcr_map,
SecureBlob* plaintext) {
SecureBlob local_data;
if (!UnobscureRsaMessage(ciphertext, key, &local_data)) {
return CreateError<TPMError>("Error unobscureing message",
TPMRetryAction::kNoRetry);
}
TSS_FLAG init_flags = TSS_ENCDATA_SEAL;
ScopedTssKey enc_handle(tpm_context_.value());
if (auto err = CreateError<TPM1Error>(Tspi_Context_CreateObject(
tpm_context_.value(), TSS_OBJECT_TYPE_ENCDATA, init_flags,
enc_handle.ptr()))) {
return WrapError<TPMError>(std::move(err),
"Error calling Tspi_Context_CreateObject");
}
if (auto err = CreateError<TPM1Error>(
Tspi_SetAttribData(enc_handle, TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB, local_data.size(),
local_data.data()))) {
return WrapError<TPMError>(std::move(err),
"Error calling Tspi_SetAttribData");
}
ScopedTssMemory dec_data(tpm_context_.value());
UINT32 dec_data_length = 0;
if (auto err = CreateError<TPM1Error>(Tspi_Data_Unbind(
enc_handle, key_handle, &dec_data_length, dec_data.ptr()))) {
return WrapError<TPMError>(std::move(err),
"Error calling Tspi_Data_Unbind");
}
plaintext->resize(dec_data_length);
memcpy(plaintext->data(), dec_data.value(), dec_data_length);
brillo::SecureClearBytes(dec_data.value(), dec_data_length);
return nullptr;
}
bool TpmImpl::SetAuthValue(TSS_HCONTEXT context_handle,
ScopedTssKey* enc_handle,
TSS_HTPM tpm_handle,
const SecureBlob& auth_value) {
// Create the enc_handle.
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_ENCDATA,
TSS_ENCDATA_SEAL, enc_handle->ptr()))) {
LOG(ERROR) << "Error calling Tspi_Context_CreateObject: " << *err;
return false;
}
// Get the TPM usage policy object and set the auth_value.
TSS_HPOLICY tpm_usage_policy;
if (auto err = CreateError<TPM1Error>(Tspi_GetPolicyObject(
tpm_handle, TSS_POLICY_USAGE, &tpm_usage_policy))) {
LOG(ERROR) << "Error calling Tspi_GetPolicyObject: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(Tspi_Policy_SetSecret(
tpm_usage_policy, TSS_SECRET_MODE_PLAIN, auth_value.size(),
const_cast<BYTE*>(auth_value.data())))) {
LOG(ERROR) << "Error calling Tspi_Policy_SetSecret: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(
Tspi_Policy_AssignToObject(tpm_usage_policy, *enc_handle))) {
LOG(ERROR) << "Error calling Tspi_Policy_AssignToObject: " << *err;
return false;
}
return true;
}
TPMErrorBase TpmImpl::SealToPcrWithAuthorization(
const SecureBlob& plaintext,
const SecureBlob& auth_value,
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)) {
return CreateError<TPMError>("Failed to connect to the TPM",
TPMRetryAction::kCommunication);
}
// Load the Storage Root Key.
ScopedTssKey srk_handle(context_handle);
if (TPM1Error err = LoadSrk(context_handle, srk_handle.ptr())) {
return WrapError<TPMError>(std::move(err), "Failed to load SRK",
TPMRetryAction::kNoRetry);
}
// Create a PCRS object.
ScopedTssPcrs pcrs_handle(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_PCRS,
TSS_PCRS_STRUCT_INFO, pcrs_handle.ptr()))) {
return WrapError<TPMError>(std::move(err),
"Error calling Tspi_Context_CreateObject",
TPMRetryAction::kNoRetry);
}
// 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 (auto err = CreateError<TPM1Error>(Tspi_TPM_PcrRead(
tpm_handle, pcr_index, &pcr_len, pcr_value.ptr()))) {
return WrapError<TPMError>(std::move(err), "Could not read PCR value");
}
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_value)) {
context_handle.reset();
return CreateError<TPMError>("Failed to SetAuthValue",
TPMRetryAction::kNoRetry);
}
// Seal the given value with the SRK.
if (auto err = CreateError<TPM1Error>(
Tspi_Data_Seal(enc_handle, srk_handle, plaintext.size(),
const_cast<BYTE*>(plaintext.data()), pcrs_handle))) {
return WrapError<TPMError>(std::move(err), "Error calling Tspi_Data_Seal",
TPMRetryAction::kNoRetry);
}
// Extract the sealed value.
ScopedTssMemory enc_data(context_handle);
UINT32 enc_data_length = 0;
if (auto err = CreateError<TPM1Error>(Tspi_GetAttribData(
enc_handle, TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB, &enc_data_length, enc_data.ptr()))) {
return WrapError<TPMError>(std::move(err),
"Error calling Tspi_GetAttribData",
TPMRetryAction::kNoRetry);
}
sealed_data->assign(&enc_data.value()[0], &enc_data.value()[enc_data_length]);
return nullptr;
}
TPMErrorBase TpmImpl::PreloadSealedData(const brillo::SecureBlob& sealed_data,
ScopedKeyHandle* preload_handle) {
// No effect for TPM 1.2.
return nullptr;
}
TPMErrorBase TpmImpl::UnsealWithAuthorization(
base::Optional<TpmKeyHandle> preload_handle,
const SecureBlob& sealed_data,
const SecureBlob& auth_value,
const std::map<uint32_t, std::string>& pcr_map,
SecureBlob* plaintext) {
if (preload_handle) {
LOG(DFATAL) << "TPM1.2 doesn't support preload_handle.";
return CreateError<TPMError>("TPM1.2 doesn't support preload_handle",
TPMRetryAction::kNoRetry);
}
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsUser(context_handle.ptr(), &tpm_handle)) {
return CreateError<TPMError>("Failed to connect to the TPM",
TPMRetryAction::kCommunication);
}
// Load the Storage Root Key.
ScopedTssKey srk_handle(context_handle);
if (TPM1Error err = LoadSrk(context_handle, srk_handle.ptr())) {
return WrapError<TPMError>(std::move(err), "Failed to load SRK",
TPMRetryAction::kNoRetry);
}
// Create an ENCDATA object with the sealed value.
ScopedTssKey enc_handle(context_handle);
if (!SetAuthValue(context_handle, &enc_handle, tpm_handle, auth_value)) {
context_handle.reset();
return CreateError<TPMError>("Failed to SetAuthValue",
TPMRetryAction::kNoRetry);
}
if (auto err = CreateError<TPM1Error>(
Tspi_SetAttribData(enc_handle, TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB, sealed_data.size(),
const_cast<BYTE*>(sealed_data.data())))) {
return WrapError<TPMError>(std::move(err),
"Error calling Tspi_SetAttribData");
}
// Unseal using the SRK.
ScopedTssMemory dec_data(context_handle);
UINT32 dec_data_length = 0;
if (auto err = CreateError<TPM1Error>(Tspi_Data_Unseal(
enc_handle, srk_handle, &dec_data_length, dec_data.ptr()))) {
return WrapError<TPMError>(std::move(err),
"Error calling Tspi_Data_Unseal");
}
plaintext->assign(&dec_data.value()[0], &dec_data.value()[dec_data_length]);
brillo::SecureClearBytes(dec_data.value(), dec_data_length);
return nullptr;
}
TPM1Error TpmImpl::GetPublicKeyBlob(TSS_HCONTEXT context_handle,
TSS_HKEY key_handle,
SecureBlob* data_out) const {
ScopedTssMemory blob(context_handle);
UINT32 blob_size;
if (auto err = CreateError<TPM1Error>(
Tspi_Key_GetPubKey(key_handle, &blob_size, blob.ptr()))) {
LOG(ERROR) << "Error calling Tspi_Key_GetPubKey: " << *err;
return err;
}
SecureBlob local_data(blob_size);
memcpy(local_data.data(), blob.value(), blob_size);
brillo::SecureClearBytes(blob.value(), blob_size);
data_out->swap(local_data);
return nullptr;
}
TPM1Error TpmImpl::LoadSrk(TSS_HCONTEXT context_handle, TSS_HKEY* srk_handle) {
// We shouldn't load the SRK if the TPM have been fully owned.
if (!IsOwned()) {
return CreateError<TPM1Error>(TSS_LAYER_TCS | TSS_E_FAIL);
}
// Load the Storage Root Key
TSS_UUID SRK_UUID = TSS_UUID_SRK;
ScopedTssKey local_srk_handle(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Context_LoadKeyByUUID(context_handle, TSS_PS_TYPE_SYSTEM,
SRK_UUID, local_srk_handle.ptr()))) {
return err;
}
// Check if the SRK wants a password
UINT32 srk_authusage;
if (auto err = CreateError<TPM1Error>(Tspi_GetAttribUint32(
local_srk_handle, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_AUTHUSAGE, &srk_authusage))) {
return err;
}
// Give it the password if needed
if (srk_authusage) {
TSS_HPOLICY srk_usage_policy;
if (auto err = CreateError<TPM1Error>(Tspi_GetPolicyObject(
local_srk_handle, TSS_POLICY_USAGE, &srk_usage_policy))) {
return err;
}
if (auto err = CreateError<TPM1Error>(Tspi_Policy_SetSecret(
srk_usage_policy, TSS_SECRET_MODE_PLAIN, srk_auth_.size(),
const_cast<BYTE*>(srk_auth_.data())))) {
return err;
}
}
*srk_handle = local_srk_handle.release();
return nullptr;
}
bool TpmImpl::CreateEndorsementKey() {
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 (auto err = CreateError<TPM1Error>(Tspi_Context_CreateObject(
tpm_context_.value(), TSS_OBJECT_TYPE_RSAKEY, init_flags,
local_key_handle.ptr()))) {
LOG(ERROR) << "Error calling Tspi_Context_CreateObject: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(
Tspi_TPM_CreateEndorsementKey(tpm_handle, local_key_handle, NULL))) {
LOG(ERROR) << "Error calling Tspi_TPM_CreateEndorsementKey: " << *err;
return false;
}
return true;
}
bool TpmImpl::IsEndorsementKeyAvailable() {
TSS_HTPM tpm_handle;
if (!GetTpm(tpm_context_.value(), &tpm_handle)) {
return false;
}
ScopedTssKey local_key_handle(tpm_context_.value());
if (auto err = CreateError<TPM1Error>(Tspi_TPM_GetPubEndorsementKey(
tpm_handle, false, NULL, local_key_handle.ptr()))) {
LOG(ERROR) << "Error calling Tspi_TPM_GetPubEndorsementKey: " << *err;
return false;
}
return true;
}
bool TpmImpl::TakeOwnership(int, const SecureBlob&) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
if (IsOwned()) {
LOG(INFO) << __func__ << ": TPM is already owned.";
return true;
}
return tpm_manager_utility_->TakeOwnership();
}
bool TpmImpl::GetTpm(TSS_HCONTEXT context_handle, TSS_HTPM* tpm_handle) {
TSS_HTPM local_tpm_handle;
if (auto err = CreateError<TPM1Error>(
Tspi_Context_GetTpmObject(context_handle, &local_tpm_handle))) {
LOG(ERROR) << "Error calling Tspi_Context_GetTpmObject: " << *err;
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_HTPM local_tpm_handle;
if (!GetTpm(context_handle, &local_tpm_handle)) {
return false;
}
TSS_HPOLICY tpm_usage_policy;
if (auto err = CreateError<TPM1Error>(Tspi_GetPolicyObject(
local_tpm_handle, TSS_POLICY_USAGE, &tpm_usage_policy))) {
LOG(ERROR) << "Error calling Tspi_GetPolicyObject: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(Tspi_Policy_SetSecret(
tpm_usage_policy, TSS_SECRET_MODE_PLAIN, owner_password.size(),
const_cast<BYTE*>(owner_password.data())))) {
LOG(ERROR) << "Error calling Tspi_Policy_SetSecret: " << *err;
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_HPOLICY tpm_usage_policy;
if (auto err = CreateError<TPM1Error>(Tspi_GetPolicyObject(
local_tpm_handle, TSS_POLICY_USAGE, &tpm_usage_policy))) {
LOG(ERROR) << "Error calling Tspi_GetPolicyObject: " << *err;
return false;
}
BYTE* secret_buffer = const_cast<BYTE*>(delegate_secret.data());
if (auto err = CreateError<TPM1Error>(
Tspi_Policy_SetSecret(tpm_usage_policy, TSS_SECRET_MODE_PLAIN,
delegate_secret.size(), secret_buffer))) {
LOG(ERROR) << "Error calling Tspi_Policy_SetSecret: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(Tspi_SetAttribData(
tpm_usage_policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_OWNERBLOB, delegate_blob.size(),
const_cast<BYTE*>(delegate_blob.data())))) {
LOG(ERROR) << "Error calling Tspi_SetAttribData: " << *err;
return false;
}
*tpm_handle = local_tpm_handle;
return true;
}
TPMErrorBase TpmImpl::GetOwnerPassword(brillo::SecureBlob* owner_password) {
if (IsOwned()) {
*owner_password =
brillo::SecureBlob(last_tpm_manager_data_.owner_password());
if (owner_password->empty()) {
return CreateError<TPMError>(
"Trying to get owner password after it is cleared",
TPMRetryAction::kNoRetry);
}
} else {
owner_password->clear();
return CreateError<TPMError>(
"Cannot get owner password until TPM is confirmed to be owned",
TPMRetryAction::kNoRetry);
}
if (owner_password->empty()) {
return CreateError<TPMError>("Empty owner password",
TPMRetryAction::kNoRetry);
}
return nullptr;
}
TPMErrorBase TpmImpl::GetRandomDataBlob(size_t length, brillo::Blob* data) {
brillo::SecureBlob blob(length);
if (TPMErrorBase err = GetRandomDataSecureBlob(length, &blob)) {
return WrapError<TPMError>(std::move(err), "GetRandomDataBlob failed");
}
data->assign(blob.begin(), blob.end());
return nullptr;
}
TPMErrorBase TpmImpl::GetRandomDataSecureBlob(size_t length,
brillo::SecureBlob* data) {
ScopedTssContext context_handle;
if ((*(context_handle.ptr()) = ConnectContext()) == 0) {
return CreateError<TPMError>("Could not open the TPM",
TPMRetryAction::kNoRetry);
}
TSS_HTPM tpm_handle;
if (!GetTpm(context_handle, &tpm_handle)) {
return CreateError<TPMError>("Could not get a handle to the TPM",
TPMRetryAction::kNoRetry);
}
SecureBlob random(length);
ScopedTssMemory tpm_data(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_TPM_GetRandom(tpm_handle, random.size(), tpm_data.ptr()))) {
return WrapError<TPMError>(std::move(err),
"Could not get random data from the TPM");
}
memcpy(random.data(), tpm_data.value(), random.size());
brillo::SecureClearBytes(tpm_data.value(), random.size());
data->swap(random);
return nullptr;
}
TPMErrorBase TpmImpl::GetAlertsData(Tpm::AlertsData* alerts) {
return CreateError<TPMError>("Not supported", TPMRetryAction::kNoRetry);
}
bool TpmImpl::DestroyNvram(uint32_t index) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
return tpm_manager_utility_->DestroySpace(index);
}
bool TpmImpl::DefineNvram(uint32_t index, size_t length, uint32_t flags) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
const bool write_define = flags & Tpm::kTpmNvramWriteDefine;
const bool bind_to_pcr0 = flags & Tpm::kTpmNvramBindToPCR0;
const bool firmware_readable = flags & Tpm::kTpmNvramFirmwareReadable;
return tpm_manager_utility_->DefineSpace(index, length, write_define,
bind_to_pcr0, firmware_readable);
}
bool TpmImpl::IsNvramDefined(uint32_t index) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
std::vector<uint32_t> spaces;
if (!tpm_manager_utility_->ListSpaces(&spaces)) {
return false;
}
for (uint32_t space : spaces) {
if (index == space) {
return true;
}
}
return false;
}
unsigned int TpmImpl::GetNvramSize(uint32_t index) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
uint32_t size;
bool is_read_locked;
bool is_write_locked;
if (!tpm_manager_utility_->GetSpaceInfo(index, &size, &is_read_locked,
&is_write_locked,
/*attributes=*/nullptr)) {
return 0;
}
return size;
}
bool TpmImpl::IsNvramLocked(uint32_t index) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
uint32_t size;
bool is_read_locked;
bool is_write_locked;
if (!tpm_manager_utility_->GetSpaceInfo(index, &size, &is_read_locked,
&is_write_locked,
/*attributes=*/nullptr)) {
return false;
}
return is_write_locked;
}
bool TpmImpl::ReadNvram(uint32_t index, SecureBlob* blob) {
if (!InitializeTpmManagerUtility()) {
return false;
}
std::string output;
const bool result = tpm_manager_utility_->ReadSpace(index, false, &output);
brillo::SecureBlob tmp(output);
blob->swap(tmp);
return result;
}
bool TpmImpl::WriteNvram(uint32_t index, const SecureBlob& blob) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
return tpm_manager_utility_->WriteSpace(index, blob.to_string(),
/*use_owner_auth=*/false);
}
bool TpmImpl::OwnerWriteNvram(uint32_t index, const SecureBlob& blob) {
// Not implemented in TPM 1.2.
// Note that technically the implementation should be the same as
// `Tpm2Impl::OwnerWriteNvram()`; however, because 1. there is no demand by
// cryptohome and 2. there is no active consumption of OWNERWRITE case for
// TPM1.2, it is unnecessary and confusing to implement this block.
return false;
}
bool TpmImpl::WriteLockNvram(uint32_t index) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
return tpm_manager_utility_->LockSpace(index);
}
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.
ScopedTssKey srk_handle(context_handle);
if (TPM1Error err = LoadSrk(context_handle, srk_handle.ptr())) {
LOG(ERROR) << __func__ << ": Failed to load SRK: " << *err;
return false;
}
// Check the SRK public key
unsigned int size_n = 0;
ScopedTssMemory public_srk(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Key_GetPubKey(srk_handle, &size_n, public_srk.ptr()))) {
LOG(ERROR) << __func__ << ": Unable to get the SRK public key: " << *err;
return false;
}
// Create a PCRS object which holds the value of PCR0.
ScopedTssPcrs pcrs_handle(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_PCRS,
TSS_PCRS_STRUCT_INFO, pcrs_handle.ptr()))) {
LOG(ERROR) << __func__
<< ": Error calling Tspi_Context_CreateObject: " << *err;
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 (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_ENCDATA,
TSS_ENCDATA_SEAL, enc_handle.ptr()))) {
LOG(ERROR) << __func__
<< ": Error calling Tspi_Context_CreateObject: " << *err;
return false;
}
// Seal the given value with the SRK.
if (auto err = CreateError<TPM1Error>(
Tspi_Data_Seal(enc_handle, srk_handle, value.size(),
const_cast<BYTE*>(value.data()), pcrs_handle))) {
LOG(ERROR) << __func__ << ": Error calling Tspi_Data_Seal: " << *err;
return false;
}
// Extract the sealed value.
ScopedTssMemory enc_data(context_handle);
UINT32 enc_data_length = 0;
if (auto err = CreateError<TPM1Error>(Tspi_GetAttribData(
enc_handle, TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB, &enc_data_length, enc_data.ptr()))) {
LOG(ERROR) << __func__ << ": Error calling Tspi_GetAttribData: " << *err;
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.
ScopedTssKey srk_handle(context_handle);
if (TPM1Error err = LoadSrk(context_handle, srk_handle.ptr())) {
LOG(ERROR) << __func__ << ": Failed to load SRK: " << *err;
return false;
}
// Create an ENCDATA object with the sealed value.
ScopedTssKey enc_handle(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_ENCDATA,
TSS_ENCDATA_SEAL, enc_handle.ptr()))) {
LOG(ERROR) << __func__
<< ": Error calling Tspi_Context_CreateObject: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(Tspi_SetAttribData(
enc_handle, TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB, sealed_value.size(),
const_cast<BYTE*>(sealed_value.data())))) {
LOG(ERROR) << __func__ << ": Error calling Tspi_SetAttribData: " << *err;
return false;
}
// Unseal using the SRK.
ScopedTssMemory dec_data(context_handle);
UINT32 dec_data_length = 0;
if (auto err = CreateError<TPM1Error>(Tspi_Data_Unseal(
enc_handle, srk_handle, &dec_data_length, dec_data.ptr()))) {
LOG(ERROR) << __func__ << ": Error calling Tspi_Data_Unseal: " << *err;
return false;
}
value->assign(&dec_data.value()[0], &dec_data.value()[dec_data_length]);
brillo::SecureClearBytes(dec_data.value(), dec_data_length);
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) << __func__ << ": Could not connect to the TPM.";
return false;
}
// Generate a delegate secret.
if (TPMErrorBase err =
GetRandomDataBlob(kDelegateSecretSize, delegate_secret)) {
LOG(ERROR) << __func__ << ": Failed to get random data blob: " << *err;
return false;
}
// Create an owner delegation policy.
ScopedTssPolicy policy(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_POLICY,
TSS_POLICY_USAGE, policy.ptr()))) {
LOG(ERROR) << __func__ << ": Failed to create policy: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(Tspi_Policy_SetSecret(
policy, TSS_SECRET_MODE_PLAIN, delegate_secret->size(),
delegate_secret->data()))) {
LOG(ERROR) << __func__ << ": Failed to set policy secret: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(Tspi_SetAttribUint32(
policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_TYPE, TSS_DELEGATIONTYPE_OWNER))) {
LOG(ERROR) << __func__ << ": Failed to set delegation type: " << *err;
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;
if (auto err = CreateError<TPM1Error>(
Tspi_SetAttribUint32(policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_PER1, permissions))) {
LOG(ERROR) << __func__ << ": Failed to set permissions: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(
Tspi_SetAttribUint32(policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_PER2, 0))) {
LOG(ERROR) << __func__ << ": Failed to set permissions: " << *err;
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);
if (!bound_pcrs.empty()) {
if (auto err = CreateError<TPM1Error>(Tspi_Context_CreateObject(
context_handle, TSS_OBJECT_TYPE_PCRS, TSS_PCRS_STRUCT_INFO_SHORT,
pcrs_handle.ptr()))) {
LOG(ERROR) << __func__ << ": Failed to create PCRS object: " << *err;
return false;
}
for (auto bound_pcr : bound_pcrs) {
UINT32 pcr_len = 0;
ScopedTssMemory pcr_value(context_handle);
if (auto err = CreateError<TPM1Error>(Tspi_TPM_PcrRead(
tpm_handle, bound_pcr, &pcr_len, pcr_value.ptr()))) {
LOG(ERROR) << __func__ << ": Could not read PCR value: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(Tspi_PcrComposite_SetPcrValue(
pcrs_handle, bound_pcr, pcr_len, pcr_value.value()))) {
LOG(ERROR) << __func__
<< ": Could not set value for PCR in PCRS handle: " << *err;
return false;
}
}
if (auto err = CreateError<TPM1Error>(
Tspi_PcrComposite_SetPcrLocality(pcrs_handle, kTpmPCRLocality))) {
LOG(ERROR) << __func__
<< ": Could not set locality for PCRs in PCRS handle: "
<< *err;
return false;
}
}
// Create a delegation family.
ScopedTssObject<TSS_HDELFAMILY> family(context_handle);
if (auto err = CreateError<TPM1Error>(Tspi_TPM_Delegate_AddFamily(
tpm_handle, delegate_family_label, family.ptr()))) {
LOG(ERROR) << __func__ << ": Failed to create family: " << *err;
return false;
}
// Create the delegation.
if (auto err = CreateError<TPM1Error>(Tspi_TPM_Delegate_CreateDelegation(
tpm_handle, delegate_label, 0, pcrs_handle, family, policy))) {
LOG(ERROR) << __func__ << ": Failed to create delegation: " << *err;
return false;
}
// Enable the delegation family.
if (auto err = CreateError<TPM1Error>(
Tspi_SetAttribUint32(family, TSS_TSPATTRIB_DELFAMILY_STATE,
TSS_TSPATTRIB_DELFAMILYSTATE_ENABLED, TRUE))) {
LOG(ERROR) << __func__ << ": Failed to enable family: " << *err;
return false;
}
// Save the delegation blob for later.
SecureBlob delegate;
if (TPM1Error err = GetDataAttribute(
context_handle, policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_OWNERBLOB, &delegate)) {
LOG(ERROR) << __func__ << ": Failed to get delegate blob: " << *err;
return false;
}
delegate_blob->assign(delegate.begin(), delegate.end());
is_delegate_bound_to_pcr_ = !bound_pcrs.empty();
has_reset_lock_permissions_ = true;
return true;
}
bool TpmImpl::Sign(const SecureBlob& key_blob,
const SecureBlob& input,
uint32_t bound_pcr_index,
SecureBlob* signature) {
CHECK(signature);
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;
}
// Load the Storage Root Key.
ScopedTssKey srk_handle(context_handle);
if (TPM1Error err = LoadSrk(context_handle, srk_handle.ptr())) {
LOG(ERROR) << __func__ << ": Failed to load SRK: " << *err;
return false;
}
// Load the key (which should be wrapped by the SRK).
ScopedTssKey key_handle(context_handle);
if (auto err = CreateError<TPM1Error>(Tspi_Context_LoadKeyByBlob(
context_handle, srk_handle, key_blob.size(),
const_cast<BYTE*>(key_blob.data()), key_handle.ptr()))) {
LOG(ERROR) << __func__ << ": Failed to load key: " << *err;
return false;
}
// Create a hash object to hold the input.
ScopedTssObject<TSS_HHASH> hash_handle(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_HASH,
TSS_HASH_OTHER, hash_handle.ptr()))) {
LOG(ERROR) << __func__ << ": Failed to create hash object: " << *err;
return false;
}
// Create the DER encoded input.
SecureBlob der_header(std::begin(kSha256DigestInfo),
std::end(kSha256DigestInfo));
SecureBlob der_encoded_input = SecureBlob::Combine(der_header, Sha256(input));
// Don't hash anything, just push the input data into the hash object.
if (auto err = CreateError<TPM1Error>(Tspi_Hash_SetHashValue(
hash_handle, der_encoded_input.size(),
const_cast<BYTE*>(der_encoded_input.data())))) {
LOG(ERROR) << __func__ << ": Failed to set hash data: " << *err;
return false;
}
UINT32 length = 0;
ScopedTssMemory buffer(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Hash_Sign(hash_handle, key_handle, &length, buffer.ptr()))) {
LOG(ERROR) << __func__ << ": Failed to generate signature: " << *err;
return false;
}
SecureBlob tmp(buffer.value(), buffer.value() + length);
brillo::SecureClearBytes(buffer.value(), length);
signature->swap(tmp);
return true;
}
bool TpmImpl::CreatePCRBoundKey(const std::map<uint32_t, std::string>& pcr_map,
AsymmetricKeyUsage key_type,
brillo::SecureBlob* key_blob,
brillo::SecureBlob* public_key_der,
brillo::SecureBlob* creation_blob) {
CHECK(creation_blob) << "Error no creation_blob.";
creation_blob->clear();
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;
}
// Load the Storage Root Key.
ScopedTssKey srk_handle(context_handle);
if (TPM1Error err = LoadSrk(context_handle, srk_handle.ptr())) {
LOG(ERROR) << __func__ << ": Failed to load SRK: " << *err;
return false;
}
// Create a PCRS object to hold pcr_index and pcr_value.
ScopedTssPcrs pcrs(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_PCRS,
TSS_PCRS_STRUCT_INFO, pcrs.ptr()))) {
LOG(ERROR) << __func__ << ": Failed to create PCRS object: " << *err;
return false;
}
for (const auto& map_pair : pcr_map) {
uint32_t pcr_index = map_pair.first;
Blob pcr_value(BlobFromString(map_pair.second));
if (pcr_value.empty()) {
if (!ReadPCR(pcr_index, &pcr_value)) {
LOG(ERROR) << __func__ << ": Failed to read PCR.";
return false;
}
}
BYTE* pcr_value_buffer = const_cast<BYTE*>(pcr_value.data());
Tspi_PcrComposite_SetPcrValue(pcrs, pcr_index, pcr_value.size(),
pcr_value_buffer);
}
// Create a non-migratable key restricted to |pcrs|.
ScopedTssKey pcr_bound_key(context_handle);
TSS_FLAG init_flags =
TSS_KEY_VOLATILE | TSS_KEY_NOT_MIGRATABLE | kDefaultTpmRsaKeyFlag;
switch (key_type) {
case AsymmetricKeyUsage::kDecryptKey:
// In this case, the key is not decrypt only. It can be used to sign the
// data too. No easy way to make a decrypt only key here.
init_flags |= TSS_KEY_TYPE_LEGACY;
break;
case AsymmetricKeyUsage::kSignKey:
init_flags |= TSS_KEY_TYPE_SIGNING;
break;
case AsymmetricKeyUsage::kDecryptAndSignKey:
init_flags |= TSS_KEY_TYPE_LEGACY;
break;
}
if (auto err = CreateError<TPM1Error>(
Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_RSAKEY,
init_flags, pcr_bound_key.ptr()))) {
LOG(ERROR) << __func__ << ": Failed to create object: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(Tspi_SetAttribUint32(
pcr_bound_key, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_SIGSCHEME, TSS_SS_RSASSAPKCS1V15_DER))) {
LOG(ERROR) << __func__ << ": Failed to set signature scheme: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(
Tspi_Key_CreateKey(pcr_bound_key, srk_handle, pcrs))) {
LOG(ERROR) << __func__ << ": Failed to create key: " << *err;
return false;
}
if (auto err =
CreateError<TPM1Error>(Tspi_Key_LoadKey(pcr_bound_key, srk_handle))) {
LOG(ERROR) << __func__ << ": Failed to load key: " << *err;
return false;
}
// Get the public key.
SecureBlob public_key;
if (TPM1Error err = GetDataAttribute(
context_handle, pcr_bound_key, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, &public_key)) {
LOG(ERROR) << __func__ << ": Failed to read public key: " << *err;
return false;
}
if (!ConvertPublicKeyToDER(public_key, public_key_der)) {
return false;
}
// Get the key blob so we can load it later.
if (TPM1Error err = GetDataAttribute(context_handle, pcr_bound_key,
TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_BLOB, key_blob)) {
LOG(ERROR) << __func__ << ": Failed to read key blob: " << *err;
return false;
}
return true;
}
bool TpmImpl::VerifyPCRBoundKey(const std::map<uint32_t, std::string>& pcr_map,
const brillo::SecureBlob& key_blob,
const brillo::SecureBlob& creation_blob) {
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;
}
ScopedTssKey srk_handle(context_handle);
if (TPM1Error err = LoadSrk(context_handle, srk_handle.ptr())) {
LOG(ERROR) << __func__ << ": Failed to load SRK: " << *err;
return false;
}
ScopedTssKey key(context_handle);
if (auto err = CreateError<TPM1Error>(Tspi_Context_LoadKeyByBlob(
context_handle, srk_handle, key_blob.size(),
const_cast<BYTE*>(key_blob.data()), key.ptr()))) {
LOG(ERROR) << __func__ << ": Failed to load key: " << *err;
return false;
}
// Check that |pcr_index| is selected.
SecureBlob pcr_selection_blob;
if (TPM1Error err = GetDataAttribute(
context_handle, key, TSS_TSPATTRIB_KEY_PCR,
TSS_TSPATTRIB_KEYPCR_SELECTION, &pcr_selection_blob)) {
LOG(ERROR) << __func__
<< ": Failed to read PCR selection for key: " << *err;
return false;
}
UINT64 trspi_offset = 0;
TPM_PCR_SELECTION pcr_selection;
Trspi_UnloadBlob_PCR_SELECTION(&trspi_offset, pcr_selection_blob.data(),
&pcr_selection);
if (!pcr_selection.pcrSelect) {
LOG(ERROR) << __func__ << ": No PCR selected.";
return false;
}
const Blob pcr_bitmap(pcr_selection.pcrSelect,
pcr_selection.pcrSelect + pcr_selection.sizeOfSelect);
free(pcr_selection.pcrSelect);
std::string concatenated_pcr_values;
for (const auto& map_pair : pcr_map) {
uint32_t pcr_index = map_pair.first;
const std::string pcr_value = map_pair.second;
size_t offset = pcr_index / 8;
unsigned char mask = 1 << (pcr_index % 8);
if (pcr_bitmap.size() <= offset || (pcr_bitmap[offset] & mask) == 0) {
LOG(ERROR) << __func__ << ": Invalid PCR selection.";
return false;
}
concatenated_pcr_values += pcr_value;
}
// Compute the PCR composite hash we're expecting. Basically, we want to do
// the equivalent of hashing a TPM_PCR_COMPOSITE structure.
trspi_offset = 0;
UINT32 pcr_value_length = concatenated_pcr_values.size();
Blob pcr_value_length_blob(sizeof(UINT32));
Trspi_LoadBlob_UINT32(&trspi_offset, pcr_value_length,
pcr_value_length_blob.data());
const SecureBlob pcr_hash = Sha1ToSecureBlob(CombineBlobs(
{Blob(pcr_selection_blob.begin(), pcr_selection_blob.end()),
pcr_value_length_blob, BlobFromString(concatenated_pcr_values)}));
// Check that the PCR value matches the key creation PCR value.
SecureBlob pcr_at_creation;
if (TPM1Error err = GetDataAttribute(
context_handle, key, TSS_TSPATTRIB_KEY_PCR,
TSS_TSPATTRIB_KEYPCR_DIGEST_ATCREATION, &pcr_at_creation)) {
LOG(ERROR) << __func__ << ": Failed to read PCR value at key creation"
<< *err;
return false;
}
if (pcr_at_creation != pcr_hash) {
LOG(ERROR) << __func__ << ": Invalid key creation PCR.";
return false;
}
// Check that the PCR value matches the PCR value required to use the key.
SecureBlob pcr_at_release;
if (TPM1Error err = GetDataAttribute(
context_handle, key, TSS_TSPATTRIB_KEY_PCR,
TSS_TSPATTRIB_KEYPCR_DIGEST_ATRELEASE, &pcr_at_release)) {
LOG(ERROR) << __func__
<< ": Failed to read PCR value for key usage: " << *err;
return false;
}
if (pcr_at_release != pcr_hash) {
LOG(ERROR) << __func__ << ": Invalid key usage PCR.";
return false;
}
return true;
}
bool TpmImpl::ExtendPCR(uint32_t pcr_index, const brillo::Blob& extension) {
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;
}
CHECK_EQ(extension.size(), kPCRExtensionSize);
Blob mutable_extension = extension;
UINT32 new_pcr_value_length = 0;
ScopedTssMemory new_pcr_value(context_handle);
if (auto err = CreateError<TPM1Error>(Tspi_TPM_PcrExtend(
tpm_handle, pcr_index, extension.size(), mutable_extension.data(),
NULL, &new_pcr_value_length, new_pcr_value.ptr()))) {
LOG(ERROR) << "Failed to extend PCR " << pcr_index << ": " << *err;
return false;
}
return true;
}
bool TpmImpl::ReadPCR(uint32_t pcr_index, brillo::Blob* pcr_value) {
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;
}
UINT32 pcr_len = 0;
ScopedTssMemory pcr_value_buffer(context_handle);
if (auto err = CreateError<TPM1Error>(Tspi_TPM_PcrRead(
tpm_handle, pcr_index, &pcr_len, pcr_value_buffer.ptr()))) {
LOG(ERROR) << "Could not read PCR " << pcr_index << ": " << *err;
return false;
}
pcr_value->assign(pcr_value_buffer.value(),
pcr_value_buffer.value() + pcr_len);
return true;
}
TPM1Error TpmImpl::GetDataAttribute(TSS_HCONTEXT context,
TSS_HOBJECT object,
TSS_FLAG flag,
TSS_FLAG sub_flag,
SecureBlob* data) const {
UINT32 length = 0;
ScopedTssMemory buf(context);
if (auto err = CreateError<TPM1Error>(
Tspi_GetAttribData(object, flag, sub_flag, &length, buf.ptr()))) {
LOG(ERROR) << "Failed to read object attribute: " << *err;
return err;
}
SecureBlob tmp(buf.value(), buf.value() + length);
brillo::SecureClearBytes(buf.value(), length);
data->swap(tmp);
return nullptr;
}
bool TpmImpl::IsEnabled() {
if (!is_enabled_) {
if (!CacheTpmManagerStatus()) {
LOG(ERROR) << __func__
<< ": Failed to update TPM status from tpm manager.";
return false;
}
}
return is_enabled_;
}
bool TpmImpl::IsOwned() {
if (!is_owned_) {
if (!UpdateLocalDataFromTpmManager()) {
LOG(ERROR) << __func__
<< ": Failed to call |UpdateLocalDataFromTpmManager|.";
return false;
}
}
return is_owned_;
}
bool TpmImpl::IsOwnerPasswordPresent() {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": failed to initialize |TpmManagerUtility|.";
return false;
}
bool is_owner_password_present = false;
if (!tpm_manager_utility_->GetTpmNonsensitiveStatus(
nullptr, nullptr, &is_owner_password_present, nullptr)) {
LOG(ERROR) << __func__ << ": Failed to get |is_owner_password_present|.";
return false;
}
return is_owner_password_present;
}
bool TpmImpl::HasResetLockPermissions() {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": failed to initialize |TpmManagerUtility|.";
return false;
}
bool has_reset_lock_permissions = false;
if (!tpm_manager_utility_->GetTpmNonsensitiveStatus(
nullptr, nullptr, nullptr, &has_reset_lock_permissions)) {
LOG(ERROR) << __func__ << ": Failed to get |has_reset_lock_permissions|.";
return false;
}
return has_reset_lock_permissions;
}
bool TpmImpl::WrapRsaKey(const SecureBlob& public_modulus,
const SecureBlob& prime_factor,
SecureBlob* wrapped_key) {
// Load the Storage Root Key
trousers::ScopedTssKey srk_handle(tpm_context_.value());
if (TPM1Error err = LoadSrk(tpm_context_.value(), srk_handle.ptr())) {
if (err->ErrorCode() != kKeyNotFoundError) {
LOG(ERROR) << __func__ << ": Failed to load SRK: " << *err;
}
return false;
}
// Make sure we can get the public key for the SRK. If not, then the TPM
// is not available.
unsigned int size_n;
trousers::ScopedTssMemory public_srk(tpm_context_.value());
if (auto err = CreateError<TPM1Error>(
Tspi_Key_GetPubKey(srk_handle, &size_n, public_srk.ptr()))) {
LOG(ERROR) << __func__ << ": Cannot load SRK pub key: " << *err;
return false;
}
// Create the key object
TSS_FLAG init_flags = TSS_KEY_TYPE_LEGACY | TSS_KEY_VOLATILE |
TSS_KEY_MIGRATABLE | kDefaultTpmRsaKeyFlag;
trousers::ScopedTssKey local_key_handle(tpm_context_.value());
if (auto err = CreateError<TPM1Error>(Tspi_Context_CreateObject(
tpm_context_.value(), TSS_OBJECT_TYPE_RSAKEY, init_flags,
local_key_handle.ptr()))) {
LOG(ERROR) << __func__
<< ": Error calling Tspi_Context_CreateObject: " << *err;
return false;
}
// Set the attributes
UINT32 sig_scheme = TSS_SS_RSASSAPKCS1V15_DER;
if (auto err = CreateError<TPM1Error>(
Tspi_SetAttribUint32(local_key_handle, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_SIGSCHEME, sig_scheme))) {
LOG(ERROR) << __func__ << ": Error calling Tspi_SetAttribUint32: " << *err;
return false;
}
UINT32 enc_scheme = TSS_ES_RSAESPKCSV15;
if (auto err = CreateError<TPM1Error>(
Tspi_SetAttribUint32(local_key_handle, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_ENCSCHEME, enc_scheme))) {
LOG(ERROR) << __func__ << ": Error calling Tspi_SetAttribUint32: " << *err;
return false;
}
// Set a random migration policy password, and discard it. The key will not
// be migrated, but to create the key outside of the TPM, we have to do it
// this way.
trousers::ScopedTssPolicy policy_handle(tpm_context_);
if (!CreatePolicyWithRandomPassword(tpm_context_, TSS_POLICY_MIGRATION,
policy_handle.ptr())) {
LOG(ERROR) << __func__ << ": Error creating policy object";
return false;
}
if (auto err = CreateError<TPM1Error>(
Tspi_Policy_AssignToObject(policy_handle, local_key_handle))) {
LOG(ERROR) << __func__ << ": Error assigning migration policy: " << *err;
return false;
}
SecureBlob mutable_modulus(public_modulus.begin(), public_modulus.end());
BYTE* public_modulus_buffer = static_cast<BYTE*>(mutable_modulus.data());
if (auto err = CreateError<TPM1Error>(
Tspi_SetAttribData(local_key_handle, TSS_TSPATTRIB_RSAKEY_INFO,
TSS_TSPATTRIB_KEYINFO_RSA_MODULUS,
public_modulus.size(), public_modulus_buffer))) {
LOG(ERROR) << __func__ << ": Error setting RSA modulus: " << *err;
return false;
}
SecureBlob mutable_factor(prime_factor.begin(), prime_factor.end());
BYTE* prime_factor_buffer = static_cast<BYTE*>(mutable_factor.data());
if (auto err = CreateError<TPM1Error>(
Tspi_SetAttribData(local_key_handle, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PRIVATE_KEY,
prime_factor.size(), prime_factor_buffer))) {
LOG(ERROR) << __func__ << ": Error setting private key: " << *err;
return false;
}
if (auto err = CreateError<TPM1Error>(
Tspi_Key_WrapKey(local_key_handle, srk_handle, 0))) {
LOG(ERROR) << __func__ << ": Error wrapping RSA key: " << *err;
return false;
}
if (TPM1Error err =
GetKeyBlob(tpm_context_.value(), local_key_handle, wrapped_key)) {
LOG(ERROR) << "Failed to GetKeyBlob: " << *err;
return false;
}
return true;
}
TPM1Error TpmImpl::GetKeyBlob(TSS_HCONTEXT context_handle,
TSS_HKEY key_handle,
SecureBlob* data_out) const {
if (TPM1Error err =
GetDataAttribute(context_handle, key_handle, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_BLOB, data_out)) {
LOG(ERROR) << __func__ << ": Failed to get key blob " << *err;
return err;
}
return nullptr;
}
TPMErrorBase TpmImpl::LoadWrappedKey(const brillo::SecureBlob& wrapped_key,
ScopedKeyHandle* key_handle) {
CHECK(key_handle);
// Load the Storage Root Key
trousers::ScopedTssKey srk_handle(tpm_context_.value());
if (TPM1Error err = LoadSrk(tpm_context_.value(), srk_handle.ptr())) {
if (err->ErrorCode() != kKeyNotFoundError) {
ReportCryptohomeError(kCannotLoadTpmSrk);
}
return WrapError<TPMError>(std::move(err), "Failed to load SRK");
}
// Make sure we can get the public key for the SRK. If not, then the TPM
// is not available.
{
SecureBlob pubkey;
if (TPM1Error err =
GetPublicKeyBlob(tpm_context_.value(), srk_handle, &pubkey)) {
ReportCryptohomeError(kCannotReadTpmSrkPublic);
return WrapError<TPMError>(std::move(err), "Cannot load SRK public key");
}
}
TpmKeyHandle local_key_handle;
if (auto err = CreateError<TPM1Error>(Tspi_Context_LoadKeyByBlob(
tpm_context_.value(), srk_handle, wrapped_key.size(),
const_cast<BYTE*>(wrapped_key.data()), &local_key_handle))) {
ReportCryptohomeError(kCannotLoadTpmKey);
if (err->ErrorCode() == TPM_E_BAD_KEY_PROPERTY) {
ReportCryptohomeError(kTpmBadKeyProperty);
}
return WrapError<TPMError>(std::move(err), "Cannot load key from blob");
}
SecureBlob pub_key;
// Make sure that we can get the public key
if (TPM1Error err =
GetPublicKeyBlob(tpm_context_.value(), local_key_handle, &pub_key)) {
ReportCryptohomeError(kCannotReadTpmPublicKey);
Tspi_Context_CloseObject(tpm_context_.value(), local_key_handle);
return WrapError<TPMError>(std::move(err),
"Cannot get public key from blob");
}
key_handle->reset(this, local_key_handle);
return nullptr;
}
bool TpmImpl::LegacyLoadCryptohomeKey(ScopedKeyHandle* key_handle,
brillo::SecureBlob* key_blob) {
CHECK(key_handle);
TpmKeyHandle local_key_handle;
if (auto err = CreateError<TPM1Error>(Tspi_Context_LoadKeyByUUID(
tpm_context_.value(), TSS_PS_TYPE_SYSTEM, kCryptohomeWellKnownUuid,
&local_key_handle))) {
LOG(ERROR) << __func__ << ": failed LoadKeyByUUID: " << *err;
return false;
}
if (key_blob) {
if (TPM1Error err =
GetKeyBlob(tpm_context_.value(), local_key_handle, key_blob)) {
LOG(ERROR) << __func__ << ": failed to GetKeyBlob: " << *err;
Tspi_Context_CloseObject(tpm_context_.value(), local_key_handle);
return false;
}
}
key_handle->reset(this, local_key_handle);
return true;
}
void TpmImpl::CloseHandle(TpmKeyHandle key_handle) {
Tspi_Context_CloseObject(tpm_context_.value(), key_handle);
}
bool TpmImpl::RemoveOwnerDependency(Tpm::TpmOwnerDependency dependency) {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": failed to initialize |TpmManagerUtility|.";
return false;
}
return tpm_manager_utility_->RemoveOwnerDependency(
OwnerDependencyEnumClassToString(dependency));
}
bool TpmImpl::ClearStoredPassword() {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": failed to initialize |TpmManagerUtility|.";
return false;
}
return tpm_manager_utility_->ClearStoredOwnerPassword();
}
bool TpmImpl::GetVersionInfo(TpmVersionInfo* version_info) {
if (!version_info) {
LOG(ERROR) << __func__ << "version_info is not initialized.";
return false;
}
// Version info on a device never changes. Returns from cache directly if we
// have the cache.
if (version_info_) {
*version_info = *version_info_;
return true;
}
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": failed to initialize |TpmManagerUtility|.";
return false;
}
if (!tpm_manager_utility_->GetVersionInfo(
&version_info->family, &version_info->spec_level,
&version_info->manufacturer, &version_info->tpm_model,
&version_info->firmware_version, &version_info->vendor_specific)) {
LOG(ERROR) << __func__ << ": failed to get version info from tpm_manager.";
return false;
}
version_info_ = *version_info;
return true;
}
static void ParseIFXFirmwarePackage(
uint64_t* offset,
uint8_t* buffer,
Tpm::IFXFieldUpgradeInfo::FirmwarePackage* firmware_package) {
Trspi_UnloadBlob_UINT32(offset, &firmware_package->package_id, buffer);
Trspi_UnloadBlob_UINT32(offset, &firmware_package->version, buffer);
Trspi_UnloadBlob_UINT32(offset, &firmware_package->stale_version, buffer);
}
bool TpmImpl::GetIFXFieldUpgradeInfo(IFXFieldUpgradeInfo* info) {
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;
}
uint8_t kRequest[] = {0x11, 0x00, 0x00};
uint32_t response_size;
ScopedTssMemory response(context_handle);
if (auto err = CreateError<TPM1Error>(
Tspi_TPM_FieldUpgrade(tpm_handle, sizeof(kRequest), kRequest,
&response_size, response.ptr()))) {
LOG(ERROR) << "Error calling Tspi_TPM_FieldUpgrade: " << *err;
return false;
}
const uint32_t kFieldUpgradeInfo2Size = 106;
const uint32_t kFieldUpgradeResponseSize =
kFieldUpgradeInfo2Size + sizeof(uint16_t);
if (response_size < kFieldUpgradeResponseSize) {
LOG(ERROR) << "FieldUpgrade response too short";
return false;
}
// Parse the response.
uint64_t offset = 0;
uint16_t size = 0;
Trspi_UnloadBlob_UINT16(&offset, &size, response.value());
if (size != kFieldUpgradeInfo2Size) {
LOG(ERROR) << "FieldUpgrade response size too short";
return false;
}
Trspi_UnloadBlob_UINT16(&offset, NULL, response.value());
Trspi_UnloadBlob_UINT16(&offset, &info->max_data_size, response.value());
Trspi_UnloadBlob_UINT16(&offset, NULL, response.value());
Trspi_UnloadBlob_UINT32(&offset, NULL, response.value());
offset += 34;
ParseIFXFirmwarePackage(&offset, response.value(), &info->bootloader);
Trspi_UnloadBlob_UINT16(&offset, NULL, response.value());
ParseIFXFirmwarePackage(&offset, response.value(), &info->firmware[0]);
ParseIFXFirmwarePackage(&offset, response.value(), &info->firmware[1]);
Trspi_UnloadBlob_UINT16(&offset, &info->status, response.value());
ParseIFXFirmwarePackage(&offset, response.value(), &info->process_fw);
Trspi_UnloadBlob_UINT16(&offset, NULL, response.value());
offset += 6;
Trspi_UnloadBlob_UINT16(&offset, &info->field_upgrade_counter,
response.value());
CHECK_EQ(offset, kFieldUpgradeResponseSize);
return true;
}
bool TpmImpl::SetDelegateData(const brillo::Blob& delegate_blob,
bool has_reset_lock_permissions) {
if (delegate_blob.size() == 0) {
LOG(ERROR) << __func__ << ": Empty blob.";
return false;
}
has_reset_lock_permissions_ = has_reset_lock_permissions;
UINT64 offset = 0;
TPM_DELEGATE_OWNER_BLOB ownerBlob;
// TODO(b/169392230): Fix the potential memory leak while migrating to tpm
// manager.
if (auto err =
CreateError<TPM1Error>(Trspi_UnloadBlob_TPM_DELEGATE_OWNER_BLOB(
&offset, const_cast<BYTE*>(delegate_blob.data()), &ownerBlob))) {
LOG(ERROR) << __func__ << ": Failed to unload delegate blob: " << *err;
return false;
}
if (ownerBlob.pub.pcrInfo.pcrSelection.sizeOfSelect > 1 &&
ownerBlob.pub.pcrInfo.pcrSelection.pcrSelect != nullptr) {
is_delegate_bound_to_pcr_ =
(ownerBlob.pub.pcrInfo.pcrSelection.pcrSelect[0] != 0) ||
(ownerBlob.pub.pcrInfo.pcrSelection.pcrSelect[1] != 0);
} else {
LOG(WARNING) << __func__ << ": Unexpected PCR information: "
<< ownerBlob.pub.pcrInfo.pcrSelection.sizeOfSelect << " (at "
<< ownerBlob.pub.pcrInfo.pcrSelection.pcrSelect << ").";
return false;
}
return true;
}
TPMErrorBase TpmImpl::IsDelegateBoundToPcr(bool* result) {
if (!SetDelegateDataFromTpmManager()) {
LOG(WARNING) << __func__
<< ": failed to call |SetDelegateDataFromTpmManager|.";
return CreateError<TPMError>(
"failed to call |SetDelegateDataFromTpmManager|",
TPMRetryAction::kNoRetry);
}
*result = is_delegate_bound_to_pcr_;
return nullptr;
}
bool TpmImpl::DelegateCanResetDACounter() {
if (!SetDelegateDataFromTpmManager()) {
LOG(WARNING) << __func__
<< ": failed to call |SetDelegateDataFromTpmManager|.";
}
return has_reset_lock_permissions_;
}
bool TpmImpl::GetRsuDeviceId(std::string* device_id) {
// Not supported for TPM 1.2.
return false;
}
LECredentialBackend* TpmImpl::GetLECredentialBackend() {
// Not implemented in TPM 1.2.
return nullptr;
}
SignatureSealingBackend* TpmImpl::GetSignatureSealingBackend() {
return &signature_sealing_backend_;
}
bool TpmImpl::GetDelegate(brillo::Blob* blob,
brillo::Blob* secret,
bool* has_reset_lock_permissions) {
blob->clear();
secret->clear();
if (last_tpm_manager_data_.owner_delegate().blob().empty() ||
last_tpm_manager_data_.owner_delegate().secret().empty()) {
if (!CacheTpmManagerStatus()) {
LOG(ERROR) << __func__
<< ": Failed to call |UpdateLocalDataFromTpmManager|.";
return false;
}
}
const auto& owner_delegate = last_tpm_manager_data_.owner_delegate();
*blob = brillo::BlobFromString(owner_delegate.blob());
*secret = brillo::BlobFromString(owner_delegate.secret());
*has_reset_lock_permissions = owner_delegate.has_reset_lock_permissions();
return !blob->empty() && !secret->empty();
}
std::map<uint32_t, std::string> TpmImpl::GetPcrMap(
const std::string& obfuscated_username, bool use_extended_pcr) const {
std::map<uint32_t, std::string> pcr_map;
if (use_extended_pcr) {
SecureBlob starting_value(SHA_DIGEST_LENGTH, 0);
SecureBlob digest_value = Sha1(SecureBlob::Combine(
starting_value, Sha1(SecureBlob(obfuscated_username))));
pcr_map[kTpmSingleUserPCR] = digest_value.to_string();
} else {
pcr_map[kTpmSingleUserPCR] = std::string(SHA_DIGEST_LENGTH, 0);
}
return pcr_map;
}
bool TpmImpl::InitializeTpmManagerUtility() {
if (!tpm_manager_utility_) {
tpm_manager_utility_ = tpm_manager::TpmManagerUtility::GetSingleton();
if (!tpm_manager_utility_) {
LOG(ERROR) << __func__ << ": Failed to get TpmManagerUtility singleton!";
}
}
return tpm_manager_utility_ && tpm_manager_utility_->Initialize();
}
bool TpmImpl::CacheTpmManagerStatus() {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
return tpm_manager_utility_->GetTpmStatus(&is_enabled_, &is_owned_,
&last_tpm_manager_data_);
}
bool TpmImpl::UpdateLocalDataFromTpmManager() {
if (!InitializeTpmManagerUtility()) {
LOG(ERROR) << __func__ << ": Failed to initialize |TpmManagerUtility|.";
return false;
}
bool is_successful = false;
bool has_received = false;
// Repeats data copy into |last_tpm_manager_data_|; reasonable trade-off due
// to low ROI to avoid that.
bool is_connected = tpm_manager_utility_->GetOwnershipTakenSignalStatus(
&is_successful, &has_received, &last_tpm_manager_data_);
// When we need explicitly query tpm status either because the signal is not
// ready for any reason, or because the signal is not received yet so we need
// to run it once in case the signal is sent by tpm_manager before already.
if (!is_connected || !is_successful ||
(!has_received && shall_cache_tpm_manager_status_)) {
// Retains |shall_cache_tpm_manager_status_| to be |true| if the signal
// cannot be relied on (yet). Actually |!is_successful| suffices to update
// |shall_cache_tpm_manager_status_|; by design, uses the redundancy just to
// avoid confusion.
shall_cache_tpm_manager_status_ &= (!is_connected || !is_successful);
return CacheTpmManagerStatus();
} else if (has_received) {
is_enabled_ = true;
is_owned_ = true;
}
return true;
}
bool TpmImpl::SetDelegateDataFromTpmManager() {
if (has_set_delegate_data_) {
return true;
}
brillo::Blob blob, unused_secret;
bool has_reset_lock_permissions = false;
if (GetDelegate(&blob, &unused_secret, &has_reset_lock_permissions)) {
// Don't log the error at this level but by the called function and the
// functions that call it.
has_set_delegate_data_ |= SetDelegateData(blob, has_reset_lock_permissions);
}
return has_set_delegate_data_;
}
TPMErrorBase TpmImpl::GetAuthValue(base::Optional<TpmKeyHandle> key_handle,
const SecureBlob& pass_blob,
SecureBlob* auth_value) {
// For TPM1.2, the |auth_value| should be the same as |pass_blob|.
*auth_value = pass_blob;
return nullptr;
}
} // namespace cryptohome