blob: a759dac48e43af2053fb1d1ffbd8bed00f254f65 [file] [log] [blame]
// Copyright 2020 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 "hwsec-test-utils/fake_pca_agent/tpm1_struct_utils.h"
#include <memory>
#include <string>
#include <arpa/inet.h>
#include <base/hash/sha1.h>
#include <base/memory/free_deleter.h>
#include <base/optional.h>
#include <base/sys_byteorder.h>
#include <crypto/scoped_openssl_types.h>
#include <trousers/trousers.h>
#include <trousers/tss.h>
#include "hwsec-test-utils/common/openssl_utility.h"
#define TPM_LOG(severity, result) \
LOG(severity) << "TPM error 0x" << std::hex << result << " (" \
<< Trspi_Error_String(result) << "): "
namespace hwsec_test_utils {
namespace fake_pca_agent {
namespace {
constexpr int kWellKnownExponent = 65537;
constexpr int kExpectedPcrLength = 20;
// The implementation of attestation service always selects 16 bits.
constexpr int kSelectBitmapSize = 2;
} // namespace
crypto::ScopedEVP_PKEY TpmPublicKeyToEVP(
const std::string& serialized_tpm_pubkey, std::string* public_key_digest) {
// Parse the serialized TPM_PUBKEY.
UINT64 offset = 0;
TPM_PUBKEY parsed = {};
TSS_RESULT result = Trspi_UnloadBlob_PUBKEY_s(
&offset,
reinterpret_cast<BYTE*>(const_cast<char*>(serialized_tpm_pubkey.data())),
serialized_tpm_pubkey.length(), &parsed);
if (result != TSS_SUCCESS) {
TPM_LOG(ERROR, result) << "Failed to parse TPM_PUBKEY.";
return nullptr;
}
// Prevent memory leak.
std::unique_ptr<BYTE, base::FreeDeleter> scoped_key(parsed.pubKey.key);
std::unique_ptr<BYTE, base::FreeDeleter> scoped_parms(
parsed.algorithmParms.parms);
if (offset != serialized_tpm_pubkey.length()) {
LOG(ERROR) << "Found garbage data after the TPM_PUBKEY.";
return nullptr;
}
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 nullptr;
}
if (parms_offset != parsed.algorithmParms.parmSize) {
LOG(ERROR) << "Find garbage data after the TPM_PUBKEY.";
return nullptr;
}
std::unique_ptr<BYTE, base::FreeDeleter> scoped_exponent(parms.exponent);
crypto::ScopedRSA rsa(RSA_new());
if (!rsa) {
LOG(ERROR) << "Failed to allocate RSA: " << GetOpenSSLError();
return nullptr;
}
crypto::ScopedBIGNUM e(BN_new()), n(BN_new());
if (!e || !n) {
LOG(ERROR) << "Failed to allocate RSA or BIGNUM.";
return nullptr;
}
// Get the public exponent.
if (parms.exponentSize == 0) {
if (!BN_set_word(e.get(), kWellKnownExponent)) {
LOG(ERROR) << "Failed to set exponent to WellKnownExponent.";
return nullptr;
}
} else {
if (!BN_bin2bn(parms.exponent, parms.exponentSize, e.get())) {
LOG(ERROR) << "Failed to convert exponent to BIGNUM.";
return nullptr;
}
}
// Get the modulus.
if (!BN_bin2bn(parsed.pubKey.key, parsed.pubKey.keyLength, n.get())) {
LOG(ERROR) << "Failed to convert public key to BIGNUM.";
return nullptr;
}
if (!RSA_set0_key(rsa.get(), n.release(), e.release(), nullptr)) {
LOG(ERROR) << ": Failed to set exponent or modulus.";
return nullptr;
}
crypto::ScopedEVP_PKEY key(EVP_PKEY_new());
if (!key) {
LOG(ERROR) << ": Failed to call EVP_PKEY_new: " << GetOpenSSLError();
return nullptr;
}
if (EVP_PKEY_set1_RSA(key.get(), rsa.get()) != 1) {
LOG(ERROR) << ": Failed to call EVP_PKEY_set1_RSA: " << GetOpenSSLError();
return nullptr;
}
// Calculate the public key digest if needed.
if (public_key_digest != nullptr) {
*public_key_digest = base::SHA1HashString(std::string(
parsed.pubKey.key, parsed.pubKey.key + parsed.pubKey.keyLength));
}
return key;
}
std::string ToPcrComposite(uint32_t pcr_index, const std::string& pcr_value) {
// Unfortunately trousers doesn't provide useful helpers to do this so we have
// to do it here.
CHECK_EQ(pcr_value.length(), kExpectedPcrLength);
struct __attribute__((packed)) {
// Corresponding to TPM_PCR_SELECTION.sizeOfSelect.
uint16_t select_size{htons(kSelectBitmapSize)};
// Corresponding to TPM_PCR_SELECTION.pcrSelect.
uint8_t select_bitmap[kSelectBitmapSize];
// Corresponding to TPM_PCR_COMPOSITE.valueSize.
uint32_t value_size{htonl(kExpectedPcrLength)};
} composite_header = {0};
static_assert(sizeof(composite_header) ==
sizeof(uint16_t) + kSelectBitmapSize + sizeof(uint32_t),
"Expect no padding between composite struct.");
// Sets upt the bitmap.
composite_header.select_bitmap[pcr_index / 8] = 1 << (pcr_index % 8);
composite_header.select_size = (htons(2u));
composite_header.select_bitmap[pcr_index / 8] = 1 << (pcr_index % 8);
composite_header.value_size = htonl(pcr_value.length());
const char* composite_header_buffer =
reinterpret_cast<const char*>(&composite_header);
return std::string(composite_header_buffer, sizeof(composite_header)) +
pcr_value;
}
std::string Serialize(TPM_ASYM_CA_CONTENTS* contents) {
std::unique_ptr<BYTE[]> blob(
std::make_unique<BYTE[]>(sizeof(*contents) + contents->sessionKey.size));
UINT64 offset = 0;
Trspi_LoadBlob_ASYM_CA_CONTENTS(&offset, blob.get(), contents);
return std::string(blob.get(), blob.get() + offset);
}
std::string Serialize(TPM_SYM_CA_ATTESTATION* contents) {
std::unique_ptr<BYTE[]> blob(
std::make_unique<BYTE[]>(sizeof(*contents) + contents->credSize));
UINT64 offset = 0;
Trspi_LoadBlob_SYM_CA_ATTESTATION(&offset, blob.get(), contents);
return std::string(blob.get(), blob.get() + offset);
}
base::Optional<std::string> ParseDigestFromTpmCertifyInfo(
const std::string& serialized) {
TPM_CERTIFY_INFO parsed{};
uint64_t offset = 0;
TSS_RESULT result = Trspi_UnloadBlob_CERTIFY_INFO(
&offset, reinterpret_cast<BYTE*>(const_cast<char*>(serialized.data())),
&parsed);
if (result != TSS_SUCCESS) {
TPM_LOG(ERROR, result) << "Failed to parse TPM_CERTIFY_INFO.";
return {};
}
// Prevent memory leak.
std::unique_ptr<BYTE, base::FreeDeleter> scoped_key(
parsed.algorithmParms.parms);
return std::string(
parsed.pubkeyDigest.digest,
parsed.pubkeyDigest.digest + sizeof(parsed.pubkeyDigest.digest));
}
} // namespace fake_pca_agent
} // namespace hwsec_test_utils