blob: d90c8aa9c0c2fe2f8dd0db0c5a266b6aaa619e99 [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "attestation/common/tpm_utility_v1.h"
#include <arpa/inet.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/hash/sha1.h>
#include <base/logging.h>
#include <base/memory/free_deleter.h>
#include <base/stl_util.h>
#include <brillo/secure_blob.h>
#include <crypto/libcrypto-compat.h>
#include <crypto/scoped_openssl_types.h>
#include <crypto/sha2.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <trousers/scoped_tss_type.h>
#include <trousers/trousers.h>
#include <trousers/tss.h>
#include <algorithm>
#include <cstdint>
#include <memory>
#include <vector>
#define TPM_LOG(severity, result) \
LOG(severity) << "TPM error 0x" << std::hex << result << " (" \
<< Trspi_Error_String(result) << "): "
using trousers::ScopedTssContext;
using trousers::ScopedTssKey;
using trousers::ScopedTssMemory;
using trousers::ScopedTssPcrs;
using trousers::ScopedTssPolicy;
namespace {
using ScopedByteArray = std::unique_ptr<BYTE, base::FreeDeleter>;
using ScopedTssEncryptedData = trousers::ScopedTssObject<TSS_HENCDATA>;
using ScopedTssHash = trousers::ScopedTssObject<TSS_HHASH>;
constexpr unsigned int kDigestSize = sizeof(TPM_DIGEST);
constexpr unsigned int kDefaultTpmRsaKeyBits = 2048;
constexpr unsigned int kDefaultTpmRsaKeyFlag = TSS_KEY_SIZE_2048;
constexpr unsigned int kWellKnownExponent = 65537;
constexpr unsigned char kSha256DigestInfo[] = {
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
constexpr size_t kSelectBitmapSize = 2;
BYTE* StringAsTSSBuffer(std::string* s) {
return reinterpret_cast<BYTE*>(base::data(*s));
}
BYTE* StringAsTSSBuffer(const std::string* s) {
return StringAsTSSBuffer(const_cast<std::string*>(s));
}
std::string TSSBufferAsString(const BYTE* buffer, size_t length) {
return std::string(reinterpret_cast<const char*>(buffer), length);
}
// Builds the seciralized TPM_PCR_COMPOSITE stream, where |pcr_index| is the PCR
// index, and |quoted_pcr_value| is the value of the register.
std::string buildPcrComposite(uint32_t pcr_index,
const std::string& quoted_pcr_value) {
CHECK_LT(pcr_index, kSelectBitmapSize * 8);
struct __attribute__((packed)) {
// Corresponding to TPM_PCR_SELECTION.sizeOfSelect.
uint16_t select_size;
// Corresponding to TPM_PCR_SELECTION.pcrSelect.
uint8_t select_bitmap[kSelectBitmapSize];
// Corresponding to TPM_PCR_COMPOSITE.valueSize.
uint32_t value_size;
} composite_header = {0};
static_assert(sizeof(composite_header) ==
sizeof(uint16_t) + kSelectBitmapSize + sizeof(uint32_t),
"Expect no padding between composite struct.");
// Sets to 2 bytes.
composite_header.select_size = (htons(2u));
composite_header.select_bitmap[pcr_index / 8] = 1 << (pcr_index % 8);
composite_header.value_size = htonl(quoted_pcr_value.length());
const char* composite_header_buffer =
reinterpret_cast<const char*>(&composite_header);
return std::string(composite_header_buffer, sizeof(composite_header)) +
quoted_pcr_value;
}
// Checks if `delegate_blob`'s flag `TPM_DELEGATE_OwnerReadInternalPub` is set.
// In case of empty input or failed parsing, returns `false`; otherwise, set
// `can_read` and returns `true`.
bool CanDelegateReadInternalPub(const std::string& delegate_blob,
bool* can_read) {
if (delegate_blob.empty()) {
LOG(ERROR) << __func__ << ": Empty blob.";
return false;
}
UINT64 offset = 0;
// Make sure the parsing will be successful first.
TSS_RESULT result = Trspi_UnloadBlob_TPM_DELEGATE_OWNER_BLOB(
&offset,
const_cast<BYTE*>(reinterpret_cast<const BYTE*>(delegate_blob.data())),
nullptr);
if (offset != delegate_blob.size()) {
TPM_LOG(ERROR, result) << __func__ << ": Bad delegate blob.";
return false;
}
offset = 0;
TPM_DELEGATE_OWNER_BLOB ownerBlob = {};
// TODO(b/169392230): Fix the potential memory leak while migrating to tpm
// manager.
result = Trspi_UnloadBlob_TPM_DELEGATE_OWNER_BLOB(
&offset,
const_cast<BYTE*>(reinterpret_cast<const BYTE*>(delegate_blob.data())),
&ownerBlob);
if (result != TSS_SUCCESS) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to unload delegate blob.";
return false;
}
*can_read =
ownerBlob.pub.permissions.per1 & TPM_DELEGATE_OwnerReadInternalPub;
return true;
}
} // namespace
namespace attestation {
TpmUtilityV1::~TpmUtilityV1() {}
bool TpmUtilityV1::Initialize() {
if (!TpmUtilityCommon::Initialize()) {
LOG(ERROR) << __func__ << ": Cannot initialize TpmUtilityCommon.";
return false;
}
if (!InitializeContextHandle(__func__)) {
LOG(WARNING) << __func__
<< ": Failed to connect to the TPM during initialization.";
}
if (!IsTpmReady()) {
LOG(WARNING) << __func__ << ": TPM is not owned; attestation services will "
<< "not be available until ownership is taken.";
}
return true;
}
bool TpmUtilityV1::ActivateIdentity(const std::string& identity_key_blob,
const std::string& asym_ca_contents,
const std::string& sym_ca_attestation,
std::string* credential) {
CHECK(credential);
if (!SetupSrk()) {
LOG(ERROR) << "SRK is not ready.";
return false;
}
// Connect to the TPM as the owner delegate.
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsDelegate(delegate_blob_, delegate_secret_,
&context_handle, &tpm_handle)) {
LOG(ERROR) << __func__ << ": 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)) {
LOG(ERROR) << __func__ << ": Failed to load SRK.";
return false;
}
// Load the AIK (which is wrapped by the SRK).
std::string mutable_identity_key_blob(identity_key_blob);
BYTE* identity_key_blob_buffer =
StringAsTSSBuffer(&mutable_identity_key_blob);
ScopedTssKey identity_key(context_handle);
result = Tspi_Context_LoadKeyByBlob(
context_handle, srk_handle, identity_key_blob.size(),
identity_key_blob_buffer, identity_key.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to load AIK.";
return false;
}
std::string mutable_asym_ca_contents(asym_ca_contents);
BYTE* asym_ca_contents_buffer = StringAsTSSBuffer(&mutable_asym_ca_contents);
std::string mutable_sym_ca_attestation(sym_ca_attestation);
BYTE* sym_ca_attestation_buffer =
StringAsTSSBuffer(&mutable_sym_ca_attestation);
UINT32 credential_length = 0;
ScopedTssMemory credential_buffer(context_handle);
result = Tspi_TPM_ActivateIdentity(
tpm_handle, identity_key, asym_ca_contents.size(),
asym_ca_contents_buffer, sym_ca_attestation.size(),
sym_ca_attestation_buffer, &credential_length, credential_buffer.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to activate identity.";
return false;
}
credential->assign(
TSSBufferAsString(credential_buffer.value(), credential_length));
return true;
}
bool TpmUtilityV1::ActivateIdentityForTpm2(
KeyType key_type,
const std::string& identity_key_blob,
const std::string& encrypted_seed,
const std::string& credential_mac,
const std::string& wrapped_credential,
std::string* credential) {
LOG(ERROR) << __func__ << ": Not implemented.";
return false;
}
bool TpmUtilityV1::CreateCertifiedKey(KeyType key_type,
KeyUsage key_usage,
const std::string& identity_key_blob,
const std::string& external_data,
std::string* key_blob,
std::string* public_key,
std::string* public_key_tpm_format,
std::string* key_info,
std::string* proof) {
CHECK(key_blob && public_key && public_key_tpm_format && key_info && proof);
if (!InitializeContextHandle(__func__)) {
return false;
}
if (!SetupSrk()) {
LOG(ERROR) << "SRK is not ready.";
return false;
}
if (key_type != KEY_TYPE_RSA) {
LOG(ERROR) << "Only RSA supported on TPM v1.2.";
return false;
}
// Load the AIK (which is wrapped by the SRK).
ScopedTssKey identity_key(context_handle_);
if (!LoadKeyFromBlob(identity_key_blob, context_handle_, srk_handle_,
&identity_key)) {
LOG(ERROR) << __func__ << "Failed to load AIK.";
return false;
}
// Create a non-migratable RSA key.
ScopedTssKey key(context_handle_);
UINT32 tss_key_type =
(key_usage == KEY_USAGE_SIGN) ? TSS_KEY_TYPE_SIGNING : TSS_KEY_TYPE_BIND;
UINT32 init_flags = tss_key_type | TSS_KEY_NOT_MIGRATABLE | TSS_KEY_VOLATILE |
TSS_KEY_NO_AUTHORIZATION | TSS_KEY_SIZE_2048;
TSS_RESULT result = Tspi_Context_CreateObject(
context_handle_, TSS_OBJECT_TYPE_RSAKEY, init_flags, key.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to create object.";
return false;
}
if (key_usage == KEY_USAGE_SIGN) {
result = Tspi_SetAttribUint32(key, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
TSS_SS_RSASSAPKCS1V15_DER);
} else {
result = Tspi_SetAttribUint32(key, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_ENCSCHEME,
TSS_ES_RSAESOAEP_SHA1_MGF1);
}
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to set scheme.";
return false;
}
result = Tspi_Key_CreateKey(key, srk_handle_, 0);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to create key.";
return false;
}
result = Tspi_Key_LoadKey(key, srk_handle_);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to load key.";
return false;
}
// Certify the key.
TSS_VALIDATION validation;
memset(&validation, 0, sizeof(validation));
validation.ulExternalDataLength = external_data.size();
std::string mutable_external_data(external_data);
validation.rgbExternalData = StringAsTSSBuffer(&mutable_external_data);
result = Tspi_Key_CertifyKey(key, identity_key, &validation);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": 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_, key, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
public_key_tpm_format)) {
LOG(ERROR) << __func__ << ": Failed to read public key.";
return false;
}
if (!GetRSAPublicKeyFromTpmPublicKey(*public_key_tpm_format, public_key)) {
return false;
}
// Get the certified key blob so we can load it later.
if (!GetDataAttribute(context_handle_, key, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_BLOB, key_blob)) {
LOG(ERROR) << __func__ << ": Failed to read key blob.";
return false;
}
// Get the data that was certified.
key_info->assign(
TSSBufferAsString(validation.rgbData, validation.ulDataLength));
// Get the certification proof.
proof->assign(TSSBufferAsString(validation.rgbValidationData,
validation.ulValidationDataLength));
return true;
}
bool TpmUtilityV1::SealToPCR0(const std::string& data,
std::string* sealed_data) {
CHECK(sealed_data);
if (!InitializeContextHandle(__func__)) {
return false;
}
if (!SetupSrk()) {
LOG(ERROR) << "SRK is not ready.";
return false;
}
// Create a PCRS object which holds the value of PCR0.
ScopedTssPcrs pcrs_handle(context_handle_);
TSS_RESULT result;
if (TPM_ERROR(result = Tspi_Context_CreateObject(
context_handle_, TSS_OBJECT_TYPE_PCRS, TSS_PCRS_STRUCT_INFO,
pcrs_handle.ptr()))) {
TPM_LOG(ERROR, result) << __func__
<< ": Error calling Tspi_Context_CreateObject";
return false;
}
UINT32 pcr_length = 0;
ScopedTssMemory pcr_value(context_handle_);
Tspi_TPM_PcrRead(tpm_handle_, 0, &pcr_length, pcr_value.ptr());
Tspi_PcrComposite_SetPcrValue(pcrs_handle, 0, pcr_length, pcr_value.value());
// Create a ENCDATA object to receive the sealed data.
ScopedTssKey encrypted_data_handle(context_handle_);
if (TPM_ERROR(result = Tspi_Context_CreateObject(
context_handle_, TSS_OBJECT_TYPE_ENCDATA, TSS_ENCDATA_SEAL,
encrypted_data_handle.ptr()))) {
TPM_LOG(ERROR, result) << __func__
<< ": Error calling Tspi_Context_CreateObject";
return false;
}
// Seal the given value with the SRK.
std::string mutable_data(data);
BYTE* data_buffer = StringAsTSSBuffer(&mutable_data);
if (TPM_ERROR(result =
Tspi_Data_Seal(encrypted_data_handle, srk_handle_,
data.size(), data_buffer, pcrs_handle))) {
TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_Data_Seal";
return false;
}
// Extract the sealed value.
ScopedTssMemory encrypted_data(context_handle_);
UINT32 encrypted_data_length = 0;
if (TPM_ERROR(result = Tspi_GetAttribData(
encrypted_data_handle, TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB, &encrypted_data_length,
encrypted_data.ptr()))) {
TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_GetAttribData";
return false;
}
sealed_data->assign(
TSSBufferAsString(encrypted_data.value(), encrypted_data_length));
return true;
}
bool TpmUtilityV1::Unseal(const std::string& sealed_data, std::string* data) {
CHECK(data);
if (!SetupSrk()) {
LOG(ERROR) << "SRK is not ready.";
return false;
}
// Create an ENCDATA object with the sealed value.
ScopedTssKey encrypted_data_handle(context_handle_);
TSS_RESULT result;
if (TPM_ERROR(result = Tspi_Context_CreateObject(
context_handle_, TSS_OBJECT_TYPE_ENCDATA, TSS_ENCDATA_SEAL,
encrypted_data_handle.ptr()))) {
TPM_LOG(ERROR, result) << __func__
<< ": Error calling Tspi_Context_CreateObject";
return false;
}
std::string mutable_sealed_data(sealed_data);
BYTE* sealed_data_buffer = StringAsTSSBuffer(&mutable_sealed_data);
if (TPM_ERROR(result = Tspi_SetAttribData(
encrypted_data_handle, TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB, sealed_data.size(),
sealed_data_buffer))) {
TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_SetAttribData";
return false;
}
// Unseal using the SRK.
ScopedTssMemory decrypted_data(context_handle_);
UINT32 decrypted_data_length = 0;
if (TPM_ERROR(result = Tspi_Data_Unseal(encrypted_data_handle, srk_handle_,
&decrypted_data_length,
decrypted_data.ptr()))) {
TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_Data_Unseal";
return false;
}
data->assign(
TSSBufferAsString(decrypted_data.value(), decrypted_data_length));
return true;
}
bool TpmUtilityV1::GetEndorsementPublicKey(KeyType key_type,
std::string* public_key_der) {
if (key_type != KEY_TYPE_RSA) {
return false;
}
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
bool is_ready = IsTpmReady();
bool can_read_ek = false;
if (!CanDelegateReadInternalPub(delegate_blob_, &can_read_ek)) {
LOG(ERROR) << __func__ << ": Cannot check permission.";
}
LOG_IF(WARNING, !can_read_ek)
<< __func__ << ": owner delegate cannot read ek.";
// Rationality check of auth values, if necessary.
if (is_ready && !can_read_ek && owner_password_.empty()) {
LOG(ERROR) << __func__ << ": No valid auth.";
return false;
}
if (!is_ready) {
if (!ConnectContextAsUser(&context_handle, &tpm_handle)) {
LOG(ERROR) << __func__ << ": Could not connect to the TPM as user.";
return false;
}
} else if (can_read_ek) {
if (!ConnectContextAsDelegate(delegate_blob_, delegate_secret_,
&context_handle, &tpm_handle)) {
LOG(ERROR) << __func__ << ": Could not connect to the TPM as delegate.";
return false;
}
} else if (!owner_password_.empty()) {
if (!ConnectContextAsOwner(owner_password_, &context_handle, &tpm_handle)) {
LOG(ERROR) << __func__ << ": Could not connect to the TPM as owner.";
return false;
}
} else {
NOTREACHED();
return false;
}
// Get a handle to the EK public key.
ScopedTssKey ek_public_key_object(context_handle);
TSS_RESULT result = Tspi_TPM_GetPubEndorsementKey(
tpm_handle, is_ready, nullptr, ek_public_key_object.ptr());
if (TPM_ERROR(result)) {
if (!is_ready && IsTpmReady()) {
LOG(INFO) << " ownership taken during retrieval of EK. Retry.";
return GetEndorsementPublicKey(key_type, public_key_der);
}
TPM_LOG(ERROR, result) << __func__ << ": Failed to get key.";
return false;
}
// Get the public key in TPM_PUBKEY form.
std::string ek_public_key_blob;
if (!GetDataAttribute(
context_handle, ek_public_key_object, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, &ek_public_key_blob)) {
LOG(ERROR) << __func__ << ": Failed to read public key.";
return false;
}
// Get the public key in DER encoded form.
if (!GetRSAPublicKeyFromTpmPublicKey(ek_public_key_blob, public_key_der)) {
return false;
}
return true;
}
bool TpmUtilityV1::GetEndorsementCertificate(KeyType key_type,
std::string* certificate) {
// Connect to the TPM as the owner.
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsOwner(owner_password_, &context_handle, &tpm_handle)) {
LOG(ERROR) << __func__ << " 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) << __func__ << " Could not create policy.";
return false;
}
result = Tspi_Policy_SetSecret(
policy_handle, TSS_SECRET_MODE_PLAIN, owner_password_.size(),
reinterpret_cast<BYTE*>(
const_cast<std::string::value_type*>(owner_password_.data())));
if (TPM_ERROR(result)) {
LOG(ERROR) << __func__ << " Could not set owner secret.";
return false;
}
// Read the EK cert from NVRAM.
std::string nvram_value;
if (!ReadNvram(context_handle, tpm_handle, policy_handle,
TSS_NV_DEFINED | TPM_NV_INDEX_EKCert, &nvram_value)) {
LOG(ERROR) << __func__ << " 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.
constexpr uint8_t kStoredCertHeader[] = {0x10, 0x01, 0x00};
constexpr uint8_t kFullCertHeader[] = {0x10, 0x02};
constexpr size_t kTotalHeaderBytes = 7;
constexpr size_t kStoredCertHeaderOffset = 0;
constexpr size_t kFullCertLengthOffset = 3;
constexpr size_t kFullCertHeaderOffset = 5;
if (nvram_value.size() < kTotalHeaderBytes) {
LOG(ERROR) << "Malformed EK certificate: Bad header.";
return false;
}
if (memcmp(kStoredCertHeader, &nvram_value[kStoredCertHeaderOffset],
base::size(kStoredCertHeader)) != 0) {
LOG(ERROR) << "Malformed EK certificate: Bad PCCLIENT_STORED_CERT.";
return false;
}
if (memcmp(kFullCertHeader, &nvram_value[kFullCertHeaderOffset],
base::size(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 =
(size_t(uint8_t(nvram_value[kFullCertLengthOffset])) << 8) |
uint8_t(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 - base::size(kFullCertHeader);
certificate->assign(nvram_value.begin() + kTotalHeaderBytes,
nvram_value.begin() + full_cert_end);
return true;
}
bool TpmUtilityV1::Unbind(const std::string& key_blob,
const std::string& bound_data,
std::string* data) {
CHECK(data);
if (!InitializeContextHandle(__func__)) {
return false;
}
if (!SetupSrk()) {
LOG(ERROR) << "SRK is not ready.";
return false;
}
ScopedTssKey key_handle(context_handle_);
if (!LoadKeyFromBlob(key_blob, context_handle_, srk_handle_, &key_handle)) {
return false;
}
TSS_RESULT result;
ScopedTssEncryptedData data_handle(context_handle_);
if (TPM_ERROR(result = Tspi_Context_CreateObject(
context_handle_, TSS_OBJECT_TYPE_ENCDATA, TSS_ENCDATA_BIND,
data_handle.ptr()))) {
TPM_LOG(ERROR, result) << __func__ << ": Tspi_Context_CreateObject failed.";
return false;
}
std::string mutable_bound_data(bound_data);
if (TPM_ERROR(result = Tspi_SetAttribData(
data_handle, TSS_TSPATTRIB_ENCDATA_BLOB,
TSS_TSPATTRIB_ENCDATABLOB_BLOB, bound_data.size(),
StringAsTSSBuffer(&mutable_bound_data)))) {
TPM_LOG(ERROR, result) << __func__ << ": Tspi_SetAttribData failed.";
return false;
}
ScopedTssMemory decrypted_data(context_handle_);
UINT32 length = 0;
if (TPM_ERROR(result = Tspi_Data_Unbind(data_handle, key_handle, &length,
decrypted_data.ptr()))) {
TPM_LOG(ERROR, result) << __func__ << ": Tspi_Data_Unbind failed.";
return false;
}
data->assign(TSSBufferAsString(decrypted_data.value(), length));
return true;
}
bool TpmUtilityV1::Sign(const std::string& key_blob,
const std::string& data_to_sign,
std::string* signature) {
CHECK(signature);
if (!InitializeContextHandle(__func__)) {
return false;
}
if (!SetupSrk()) {
LOG(ERROR) << "SRK is not ready.";
return false;
}
ScopedTssKey key_handle(context_handle_);
if (!LoadKeyFromBlob(key_blob, context_handle_, srk_handle_, &key_handle)) {
return false;
}
// Construct an ASN.1 DER DigestInfo.
std::string digest_to_sign(std::begin(kSha256DigestInfo),
std::end(kSha256DigestInfo));
digest_to_sign += crypto::SHA256HashString(data_to_sign);
// Create a hash object to hold the digest.
ScopedTssHash hash_handle(context_handle_);
TSS_RESULT result = Tspi_Context_CreateObject(
context_handle_, TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, hash_handle.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to create hash object.";
return false;
}
result = Tspi_Hash_SetHashValue(hash_handle, digest_to_sign.size(),
StringAsTSSBuffer(&digest_to_sign));
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to set hash data.";
return false;
}
UINT32 length = 0;
ScopedTssMemory buffer(context_handle_);
result = Tspi_Hash_Sign(hash_handle, key_handle, &length, buffer.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to generate signature.";
return false;
}
signature->assign(TSSBufferAsString(buffer.value(), length));
return true;
}
bool TpmUtilityV1::QuotePCR(uint32_t pcr_index,
const std::string& key_blob,
std::string* quoted_pcr_value,
std::string* quoted_data,
std::string* quote) {
if (!InitializeContextHandle(__func__)) {
return false;
}
// Load the Storage Root Key.
TSS_RESULT result;
if (!SetupSrk()) {
LOG(ERROR) << __func__ << ": Failed to setup SRK.";
return false;
}
// Load the AIK (which is wrapped by the SRK).
ScopedTssKey identity_key(context_handle_);
BYTE* key_blob_ptr = StringAsTSSBuffer(&key_blob);
result =
Tspi_Context_LoadKeyByBlob(context_handle_, srk_handle_, key_blob.size(),
key_blob_ptr, identity_key.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": 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) << __func__ << ": Failed to create PCRS object.";
return false;
}
result = Tspi_PcrComposite_SelectPcrIndex(pcrs, pcr_index);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to select PCR.";
return false;
}
// Generate the quote.
TSS_VALIDATION validation = {};
// it's a difference from |TpmImpl| in |cryptohomed|, which uses OpenSSL to
// generate the random number. Here we use well-known string value for
// consistency with |TpmUtilityV2|, which doesn't supply any qualifying data
// from caller while in TPM 1.2 it's required to have non-empty external data.
BYTE well_known_external_data[kDigestSize] = {};
validation.ulExternalDataLength = kDigestSize;
validation.rgbExternalData = well_known_external_data;
result = Tspi_TPM_Quote(tpm_handle_, identity_key, pcrs, &validation);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": 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) << __func__ << ": Failed to get PCR value.";
return false;
}
*quoted_pcr_value =
TSSBufferAsString(pcr_value_buffer.value(), pcr_value_length);
// Get the data that was quoted.
*quoted_data = TSSBufferAsString(validation.rgbData, validation.ulDataLength);
// Get the quote.
*quote = TSSBufferAsString(validation.rgbValidationData,
validation.ulValidationDataLength);
return true;
}
bool TpmUtilityV1::IsQuoteForPCR(const std::string& quoted_pcr_value,
const std::string& quoted_data,
const std::string& quote,
uint32_t pcr_index) const {
// Checks that the quoted value matches the given PCR value by reconstructing
// the TPM_PCR_COMPOSITE structure the TPM would create.
const std::string pcr_digest =
base::SHA1HashString(buildPcrComposite(pcr_index, quoted_pcr_value));
// The PCR digest should appear starting at 8th byte of the quoted data. See
// the TPM_QUOTE_INFO structure.
if (quoted_data.length() < pcr_digest.length() + 8) {
LOG(ERROR) << __func__ << ": Quoted data too short.";
return false;
}
if (!std::equal(pcr_digest.begin(), pcr_digest.end(),
quoted_data.begin() + 8)) {
LOG(ERROR) << __func__ << "PCR value mismatch.";
return false;
}
return true;
}
bool TpmUtilityV1::ReadPCR(uint32_t pcr_index, std::string* pcr_value) {
if (!InitializeContextHandle(__func__)) {
return false;
}
UINT32 pcr_len = 0;
ScopedTssMemory pcr_value_buffer(context_handle_);
TSS_RESULT result = Tspi_TPM_PcrRead(tpm_handle_, pcr_index, &pcr_len,
pcr_value_buffer.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Could not read PCR " << pcr_index << " value";
return false;
}
pcr_value->assign(pcr_value_buffer.value(),
pcr_value_buffer.value() + pcr_len);
return true;
}
bool TpmUtilityV1::GetNVDataSize(uint32_t nv_index, uint16_t* nv_size) const {
LOG(ERROR) << __func__ << ": Not implemented.";
return false;
}
bool TpmUtilityV1::CertifyNV(uint32_t nv_index,
int nv_size,
const std::string& key_blob,
std::string* quoted_data,
std::string* quote) {
LOG(ERROR) << __func__ << ": Not implemented.";
return false;
}
bool TpmUtilityV1::ConnectContextAsUser(ScopedTssContext* context,
TSS_HTPM* tpm) {
*tpm = 0;
TSS_RESULT result;
if (TPM_ERROR(result = Tspi_Context_Create(context->ptr()))) {
TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_Context_Create";
return false;
}
if (TPM_ERROR(result = Tspi_Context_Connect(*context, nullptr))) {
TPM_LOG(ERROR, result) << __func__
<< ": Error calling Tspi_Context_Connect";
return false;
}
if (TPM_ERROR(result = Tspi_Context_GetTpmObject(*context, tpm))) {
TPM_LOG(ERROR, result) << __func__
<< ": Error calling Tspi_Context_GetTpmObject";
return false;
}
return true;
}
bool TpmUtilityV1::ConnectContextAsOwner(const std::string& owner_password,
trousers::ScopedTssContext* context,
TSS_HTPM* tpm) {
*tpm = 0;
if (owner_password.empty()) {
LOG(ERROR) << __func__ << ": requires an owner password";
return false;
}
if (!ConnectContextAsUser(context, tpm)) {
LOG(ERROR) << __func__ << ": Could not open the TPM";
return false;
}
if (!SetTpmOwnerAuth(owner_password, context->context(), *tpm)) {
LOG(ERROR) << __func__ << ": failed to authorize as the owner";
Tspi_Context_Close(*context);
context->reset();
*tpm = 0;
return false;
}
return true;
}
bool TpmUtilityV1::ConnectContextAsDelegate(const std::string& delegate_blob,
const std::string& delegate_secret,
ScopedTssContext* context,
TSS_HTPM* tpm) {
if (delegate_blob.empty() || delegate_secret.empty()) {
LOG(ERROR) << __func__
<< ": requires a delegate blob and a delegate secret.";
return false;
}
*tpm = 0;
if (!ConnectContextAsUser(context, tpm)) {
return false;
}
TSS_RESULT result;
TSS_HPOLICY tpm_usage_policy;
if (TPM_ERROR(result = Tspi_GetPolicyObject(*tpm, TSS_POLICY_USAGE,
&tpm_usage_policy))) {
TPM_LOG(ERROR, result) << __func__
<< ": Error calling Tspi_GetPolicyObject";
return false;
}
std::string mutable_delegate_secret(delegate_secret);
BYTE* secret_buffer = StringAsTSSBuffer(&mutable_delegate_secret);
if (TPM_ERROR(result = Tspi_Policy_SetSecret(
tpm_usage_policy, TSS_SECRET_MODE_PLAIN,
delegate_secret.size(), secret_buffer))) {
TPM_LOG(ERROR, result) << __func__
<< ": Error calling Tspi_Policy_SetSecret";
return false;
}
std::string mutable_delegate_blob(delegate_blob);
BYTE* blob_buffer = StringAsTSSBuffer(&mutable_delegate_blob);
if (TPM_ERROR(result = Tspi_SetAttribData(
tpm_usage_policy, TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
TSS_TSPATTRIB_POLDEL_OWNERBLOB, delegate_blob.size(),
blob_buffer))) {
TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_SetAttribData";
return false;
}
return true;
}
bool TpmUtilityV1::SetTpmOwnerAuth(const std::string& owner_password,
TSS_HCONTEXT context_handle,
TSS_HTPM tpm_handle) {
TSS_RESULT result;
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;
}
BYTE* owner_password_buffer = StringAsTSSBuffer(&owner_password);
if (TPM_ERROR(result = Tspi_Policy_SetSecret(
tpm_usage_policy, TSS_SECRET_MODE_PLAIN,
owner_password.size(), owner_password_buffer))) {
TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
return false;
}
return true;
}
bool TpmUtilityV1::ReadNvram(TSS_HCONTEXT context_handle,
TSS_HTPM tpm_handle,
TSS_HPOLICY policy_handle,
uint32_t index,
std::string* blob) {
UINT32 size = GetNvramSize(context_handle, tpm_handle, index);
if (size == 0) {
if (!IsNvramDefined(context_handle, tpm_handle, index)) {
LOG(ERROR) << "Cannot read from non-existent NVRAM space.";
} else {
LOG(ERROR) << "Cannot get nvram size.";
}
return false;
}
blob->resize(size);
// Create an NVRAM store object handle.
TSS_RESULT result;
trousers::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;
}
}
// 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;
trousers::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());
// not for TSS APIs but BYTE* is also suitable here.
BYTE* buffer = StringAsTSSBuffer(blob) + offset;
memcpy(buffer, space_data.value(), chunk_size);
offset += chunk_size;
}
return true;
}
bool TpmUtilityV1::IsNvramDefined(TSS_HCONTEXT context_handle,
TSS_HTPM tpm_handle,
uint32_t index) {
TSS_RESULT result;
UINT32 nv_list_data_length = 0;
trousers::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 TpmUtilityV1::GetNvramSize(TSS_HCONTEXT context_handle,
TSS_HTPM tpm_handle,
uint32_t index) {
TSS_RESULT result;
UINT32 nv_index_data_length = 0;
trousers::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 0u;
}
if (nv_index_data_length == 0) {
return 0u;
}
// 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 TpmUtilityV1::SetupSrk() {
if (!IsTpmReady()) {
return false;
}
if (srk_handle_) {
return true;
}
if (!InitializeContextHandle(__func__)) {
return false;
}
srk_handle_.reset(context_handle_, 0);
if (!LoadSrk(context_handle_, &srk_handle_)) {
LOG(ERROR) << __func__ << ": Failed to load SRK.";
return false;
}
// In order to wrap a key with the SRK we need access to the SRK public key
// and we need to get it manually. Once it's in the key object, we don't need
// to do this again.
UINT32 length = 0;
ScopedTssMemory buffer(context_handle_);
TSS_RESULT result;
result = Tspi_Key_GetPubKey(srk_handle_, &length, buffer.ptr());
if (result != TSS_SUCCESS) {
TPM_LOG(INFO, result) << __func__ << ": Failed to read SRK public key.";
return false;
}
return true;
}
bool TpmUtilityV1::LoadSrk(TSS_HCONTEXT context_handle,
ScopedTssKey* srk_handle) {
TSS_RESULT result;
TSS_UUID uuid = TSS_UUID_SRK;
if (TPM_ERROR(result = Tspi_Context_LoadKeyByUUID(context_handle,
TSS_PS_TYPE_SYSTEM, uuid,
srk_handle->ptr()))) {
TPM_LOG(ERROR, result) << __func__
<< ": Error calling Tspi_Context_LoadKeyByUUID";
return false;
}
// Check if the SRK wants a password.
UINT32 auth_usage;
if (TPM_ERROR(result = Tspi_GetAttribUint32(
*srk_handle, TSS_TSPATTRIB_KEY_INFO,
TSS_TSPATTRIB_KEYINFO_AUTHUSAGE, &auth_usage))) {
TPM_LOG(ERROR, result) << __func__
<< ": Error calling Tspi_GetAttribUint32";
return false;
}
if (auth_usage) {
// Give it an empty password if needed.
TSS_HPOLICY usage_policy;
if (TPM_ERROR(result = Tspi_GetPolicyObject(*srk_handle, TSS_POLICY_USAGE,
&usage_policy))) {
TPM_LOG(ERROR, result)
<< __func__ << ": Error calling Tspi_GetPolicyObject";
return false;
}
BYTE empty_password[] = {};
if (TPM_ERROR(result =
Tspi_Policy_SetSecret(usage_policy, TSS_SECRET_MODE_PLAIN,
0, empty_password))) {
TPM_LOG(ERROR, result)
<< __func__ << ": Error calling Tspi_Policy_SetSecret";
return false;
}
}
return true;
}
bool TpmUtilityV1::LoadKeyFromBlob(const std::string& key_blob,
TSS_HCONTEXT context_handle,
TSS_HKEY parent_key_handle,
ScopedTssKey* key_handle) {
std::string mutable_key_blob(key_blob);
BYTE* key_blob_buffer = StringAsTSSBuffer(&mutable_key_blob);
TSS_RESULT result = Tspi_Context_LoadKeyByBlob(
context_handle, parent_key_handle, key_blob.size(), key_blob_buffer,
key_handle->ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << ": Failed to load key by blob.";
return false;
}
return true;
}
bool TpmUtilityV1::GetDataAttribute(TSS_HCONTEXT context,
TSS_HOBJECT object,
TSS_FLAG flag,
TSS_FLAG sub_flag,
std::string* data) {
UINT32 length = 0;
ScopedTssMemory buffer(context);
TSS_RESULT result =
Tspi_GetAttribData(object, flag, sub_flag, &length, buffer.ptr());
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << __func__ << "Failed to read object attribute.";
return false;
}
data->assign(TSSBufferAsString(buffer.value(), length));
return true;
}
bool TpmUtilityV1::DecryptIdentityRequest(RSA* pca_key,
const std::string& request,
std::string* identity_binding) {
// Parse the serialized TPM_IDENTITY_REQ structure.
UINT64 offset = 0;
BYTE* buffer = reinterpret_cast<BYTE*>(
const_cast<typename std::string::value_type*>(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.
brillo::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::SecureClear(proof.identityBinding, proof.identityBindingSize);
return true;
}
bool TpmUtilityV1::GetRSAPublicKeyFromTpmPublicKey(
const std::string& tpm_public_key_object, std::string* public_key_der) {
// Parse the serialized TPM_PUBKEY.
UINT64 offset = 0;
std::string mutable_public_key(tpm_public_key_object);
BYTE* buffer = StringAsTSSBuffer(&mutable_public_key);
TPM_PUBKEY parsed;
TSS_RESULT result = Trspi_UnloadBlob_PUBKEY_s(
&offset, buffer, mutable_public_key.length(), &parsed);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Failed to parse TPM_PUBKEY.";
return false;
}
ScopedByteArray scoped_key(parsed.pubKey.key);
ScopedByteArray scoped_parms(parsed.algorithmParms.parms);
if (offset != mutable_public_key.length()) {
LOG(ERROR) << "Found garbage data after the TPM_PUBKEY.";
return false;
}
TPM_RSA_KEY_PARMS parms;
UINT64 parms_offset = 0;
result = Trspi_UnloadBlob_RSA_KEY_PARMS_s(
&parms_offset, parsed.algorithmParms.parms,
parsed.algorithmParms.parmSize, &parms);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result) << "Failed to parse RSA_KEY_PARMS.";
return false;
}
if (parms_offset != parsed.algorithmParms.parmSize) {
LOG(ERROR) << "Find garbage data after the TPM_PUBKEY.";
return false;
}
ScopedByteArray scoped_exponent(parms.exponent);
crypto::ScopedRSA rsa(RSA_new());
crypto::ScopedBIGNUM e(BN_new()), n(BN_new());
if (!rsa || !e || !n) {
LOG(ERROR) << "Failed to allocate RSA or BIGNUM.";
return false;
}
// Get the public exponent.
if (parms.exponentSize == 0) {
if (!BN_set_word(e.get(), kWellKnownExponent)) {
LOG(ERROR) << "Failed to set exponent to WellKnownExponent.";
return false;
}
} else {
if (!BN_bin2bn(parms.exponent, parms.exponentSize, e.get())) {
LOG(ERROR) << "Failed to convert exponent to BIGNUM.";
return false;
}
}
// Get the modulus.
if (!BN_bin2bn(parsed.pubKey.key, parsed.pubKey.keyLength, n.get())) {
LOG(ERROR) << "Failed to convert public key to BIGNUM.";
return false;
}
if (!RSA_set0_key(rsa.get(), n.release(), e.release(), nullptr)) {
LOG(ERROR) << ": Failed to set exponent or modulus.";
return false;
}
// DER encode.
int der_length = i2d_RSAPublicKey(rsa.get(), nullptr);
if (der_length < 0) {
LOG(ERROR) << "Failed to DER-encode public key.";
return false;
}
public_key_der->resize(der_length);
unsigned char* der_buffer =
reinterpret_cast<unsigned char*>(base::data(*public_key_der));
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;
}
bool TpmUtilityV1::GetEndorsementPublicKeyModulus(KeyType key_type,
std::string* ekm) {
if (key_type != KEY_TYPE_RSA) {
LOG(ERROR) << __func__ << ": Only RSA supported on TPM1.2.";
return false;
}
std::string ek_public_key;
if (!GetEndorsementPublicKey(key_type, &ek_public_key)) {
LOG(ERROR) << __func__ << ": Failed to get EK public key.";
return false;
}
// Extracts the modulus from the public key.
const unsigned char* asn1_ptr =
reinterpret_cast<const unsigned char*>(ek_public_key.data());
crypto::ScopedRSA public_key(
d2i_RSAPublicKey(nullptr, &asn1_ptr, ek_public_key.size()));
if (!public_key.get()) {
LOG(ERROR) << __func__ << ": Failed to decode public endorsement key.";
return false;
}
int modulus_bytes_length = RSA_size(public_key.get());
if (modulus_bytes_length <= 0) {
LOG(ERROR) << __func__
<< ": Failed to get public endorsement key modulus length.";
return false;
}
std::vector<uint8_t> modulus_bytes(modulus_bytes_length);
const BIGNUM* n;
RSA_get0_key(public_key.get(), &n, nullptr, nullptr);
int output_length = BN_bn2bin(n, modulus_bytes.data());
if (output_length != modulus_bytes_length) {
LOG(ERROR) << __func__ << ": Bad length returned by BN_bn2bin: got "
<< output_length << " while it should be "
<< modulus_bytes_length;
return false;
}
ekm->assign(modulus_bytes.begin(), modulus_bytes.end());
return true;
}
bool TpmUtilityV1::CreateIdentity(KeyType key_type,
AttestationDatabase::Identity* identity) {
if (KEY_TYPE_RSA != key_type) {
LOG(ERROR) << __func__ << ": Identity key only supports RSA key type.";
return false;
}
// fill the fields in identity
auto binding_pb = identity->mutable_identity_binding();
auto key_pb = identity->mutable_identity_key();
if (!MakeIdentity(key_pb->mutable_identity_public_key_der(),
binding_pb->mutable_identity_public_key_tpm_format(),
key_pb->mutable_identity_key_blob(),
binding_pb->mutable_identity_binding(),
binding_pb->mutable_identity_label(),
binding_pb->mutable_pca_public_key())) {
LOG(ERROR) << __func__ << ": Failed to make identity.";
return false;
}
key_pb->set_identity_key_type(key_type);
binding_pb->set_identity_public_key_der(key_pb->identity_public_key_der());
return true;
}
bool TpmUtilityV1::MakeIdentity(std::string* identity_public_key_der,
std::string* identity_public_key,
std::string* identity_key_blob,
std::string* identity_binding,
std::string* identity_label,
std::string* pca_public_key) {
CHECK(identity_public_key_der && identity_public_key && identity_key_blob &&
identity_binding && identity_label && pca_public_key);
// Connect to the TPM as the owner.
ScopedTssContext context_handle;
TSS_HTPM tpm_handle;
if (!ConnectContextAsOwner(owner_password_, &context_handle, &tpm_handle)) {
LOG(ERROR) << __func__ << " 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)) {
LOG(ERROR) << "MakeIdentity: Cannot load SRK.";
return false;
}
crypto::ScopedRSA fake_pca_key(RSA_new());
crypto::ScopedBIGNUM e(BN_new());
if (!fake_pca_key || !e) {
LOG(ERROR) << "MakeIdentity: Failed to allocate RSA or BIGNUM.";
return false;
}
if (!BN_set_word(e.get(), kWellKnownExponent) ||
!RSA_generate_key_ex(fake_pca_key.get(), kDefaultTpmRsaKeyBits, e.get(),
nullptr)) {
LOG(ERROR) << "MakeIdentity: Failed to generate local key pair.";
return false;
}
unsigned char modulus_buffer[kDefaultTpmRsaKeyBits / 8];
const BIGNUM* n;
RSA_get0_key(fake_pca_key.get(), &n, NULL, NULL);
if (BN_bn2bin(n, modulus_buffer) != RSA_size(fake_pca_key.get())) {
LOG(ERROR) << "MakeIdentity: Failed to convert modulus from BIGNUM.";
return false;
}
// Create a TSS object for the fake PCA public key.
ScopedTssKey pca_public_key_object(context_handle);
constexpr UINT32 kPcaKeyFlags =
kDefaultTpmRsaKeyFlag | TSS_KEY_TYPE_LEGACY | TSS_KEY_MIGRATABLE;
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_RSAKEY,
kPcaKeyFlags, 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,
base::size(modulus_buffer), modulus_buffer);
if (TPM_ERROR(result)) {
TPM_LOG(ERROR, result)
<< "MakeIdentity: Cannot set modulus to PCA public key.";
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 set encoding scheme "
"attribute to PCA public key.";
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)) {
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);
constexpr UINT32 kIdentityKeyFlags =
kDefaultTpmRsaKeyFlag | TSS_KEY_TYPE_IDENTITY | TSS_KEY_VOLATILE |
TSS_KEY_NOT_MIGRATABLE;
result = Tspi_Context_CreateObject(context_handle, TSS_OBJECT_TYPE_RSAKEY,
kIdentityKeyFlags, 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.
std::string request_blob(request.value(), request.value() + request_length);
if (!DecryptIdentityRequest(fake_pca_key.get(), request_blob,
identity_binding)) {
LOG(ERROR) << "MakeIdentity: Failed to decrypt the identity request.";
return false;
}
brillo::SecureClear(request.value(), request_length);
// Get the AIK public key.
if (!GetDataAttribute(context_handle, identity_key, TSS_TSPATTRIB_KEY_BLOB,
TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
identity_public_key)) {
LOG(ERROR) << __func__ << ": Failed to read public key.";
return false;
}
if (!GetRSAPublicKeyFromTpmPublicKey(*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)) {
LOG(ERROR) << __func__ << ": Failed to read key blob.";
return false;
}
return true;
}
bool TpmUtilityV1::GetRsuDeviceId(std::string* rsu_device_id) {
LOG(ERROR) << __func__ << ": Not implemented.";
return false;
}
std::string TpmUtilityV1::GetPCRValueForMode(const std::string& mode) {
const std::string mode_digest = base::SHA1HashString(mode);
// PCR0 value immediately after power on.
const std::string pcr_initial_value(base::kSHA1Length, 0);
return base::SHA1HashString(pcr_initial_value + mode_digest);
}
bool TpmUtilityV1::InitializeContextHandle(const std::string& consumer_name) {
if (!static_cast<TSS_HCONTEXT>(context_handle_) || !tpm_handle_) {
context_handle_.reset();
if (!ConnectContextAsUser(&context_handle_, &tpm_handle_)) {
LOG(ERROR) << __func__ << ": Failed to connect to the TPM.";
return false;
}
}
return true;
}
} // namespace attestation