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