| // Copyright 2018 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 "cryptohome/signature_sealing_backend_tpm1_impl.h" |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <string> |
| #include <utility> |
| |
| #include <base/logging.h> |
| #include <base/memory/free_deleter.h> |
| #include <brillo/secure_blob.h> |
| #include <crypto/libcrypto-compat.h> |
| #include <crypto/scoped_openssl_types.h> |
| #include <openssl/bn.h> |
| #include <openssl/evp.h> |
| #include <openssl/rsa.h> |
| #include <openssl/x509.h> |
| #include <trousers/scoped_tss_type.h> |
| #include <trousers/tss.h> |
| #include <trousers/trousers.h> // NOLINT(build/include_alpha) - needs tss.h |
| |
| #include "cryptohome/cryptolib.h" |
| #include "cryptohome/key.pb.h" |
| #include "cryptohome/signature_sealed_data.pb.h" |
| #include "cryptohome/tpm1_static_utils.h" |
| #include "cryptohome/tpm_impl.h" |
| |
| using brillo::Blob; |
| using brillo::BlobFromString; |
| using brillo::BlobToString; |
| using brillo::CombineBlobs; |
| using brillo::SecureBlob; |
| using trousers::ScopedTssContext; |
| using trousers::ScopedTssKey; |
| using trousers::ScopedTssMemory; |
| using trousers::ScopedTssObject; |
| using trousers::ScopedTssPolicy; |
| |
| using ScopedByteArray = std::unique_ptr<BYTE, base::FreeDeleter>; |
| |
| namespace cryptohome { |
| |
| namespace { |
| |
| // Size, in bytes, of the secret value that is generated by |
| // SignatureSealingBackendTpm1Impl::CreateSealedSecret(). |
| // |
| // The choice of this constant is driven solely by the requirement to contain |
| // sufficient amount of entropy for whatever goal the SignatureSealingBackend is |
| // used. |
| constexpr int kSecretSizeBytes = 32; |
| |
| // Size of the AuthData blob to be randomly generated. |
| // |
| // The choice of this constant is dictated by the desire to provide sufficient |
| // amount of entropy as the authorization secret for the TPM_Seal command (but |
| // with taking into account that this authorization value is hashed by SHA-1 |
| // by Trousers anyway). |
| constexpr int kAuthDataSizeBytes = 32; |
| |
| // Size of the migration destination key to be generated. Note that the choice |
| // of this size is constrained by restrictions from the TPM 1.2 specs. |
| constexpr int kMigrationDestinationKeySizeBits = 2048; |
| constexpr int kMigrationDestinationKeySizeBytes = |
| kMigrationDestinationKeySizeBits / 8; |
| constexpr int kMigrationDestinationKeySizeFlag = TSS_KEY_SIZE_2048; |
| |
| // Size of the certified migratable key to be created. Note that the choice of |
| // this size is dictated by restrictions from the TPM 1.2 specs. |
| constexpr int kCmkKeySizeBits = 2048; |
| constexpr int kCmkKeySizeBytes = kCmkKeySizeBits / 8; |
| constexpr int kCmkPrivateKeySizeBytes = kCmkKeySizeBytes / 2; |
| constexpr int kCmkKeySizeFlag = TSS_KEY_SIZE_2048; |
| |
| // The RSA OAEP label parameter specified to be used by the TPM 1.2 specs (see |
| // TPM 1.2 Part 1 Section 31.1.1 "TPM_ES_RSAESOAEP_SHA1_MGF1"). |
| constexpr char kTpmRsaOaepLabel[] = {'T', 'C', 'P', 'A'}; |
| |
| // Sizes of the two parts of the migrated CMK private key blob: as described in |
| // TPM 1.2 Part 3 Section 11.9 ("TPM_CMK_CreateBlob"), one part goes into the |
| // OAEP seed and the rest goes into the TPM_MIGRATE_ASYMKEY struct. |
| constexpr int kMigratedCmkPrivateKeySeedPartSizeBytes = 16; |
| constexpr int kMigratedCmkPrivateKeyRestPartSizeBytes = 112; |
| static_assert(kMigratedCmkPrivateKeySeedPartSizeBytes == SHA_DIGEST_LENGTH - 4, |
| "Invalid private key seed part size constant"); |
| static_assert(kMigratedCmkPrivateKeySeedPartSizeBytes + |
| kMigratedCmkPrivateKeyRestPartSizeBytes == |
| kCmkPrivateKeySizeBytes, |
| "Invalid private key part size constants"); |
| |
| // Size of the TPM_MIGRATE_ASYMKEY structure containing the part of the migrated |
| // private key blob. |
| constexpr int kTpmMigrateAsymkeyBlobSize = |
| sizeof(TPM_PAYLOAD_TYPE) /* for payload */ + |
| SHA_DIGEST_LENGTH /* for usageAuth.authdata */ + |
| SHA_DIGEST_LENGTH /* for pubDataDigest.digest */ + |
| sizeof(UINT32) /* for partPrivKeyLen */ + |
| kMigratedCmkPrivateKeyRestPartSizeBytes /* for *partPrivKey */; |
| |
| // Scoped wrapper of the TPM_KEY12 struct. |
| class ScopedKey12 final { |
| public: |
| ScopedKey12() { memset(&value_, 0, sizeof(TPM_KEY12)); } |
| |
| ~ScopedKey12() { |
| free(value_.algorithmParms.parms); |
| free(value_.pubKey.key); |
| free(value_.encData); |
| free(value_.PCRInfo); |
| } |
| |
| const TPM_KEY12& operator*() const { return value_; } |
| const TPM_KEY12* operator->() const { return &value_; } |
| TPM_KEY12* ptr() { return &value_; } |
| |
| private: |
| TPM_KEY12 value_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedKey12); |
| }; |
| |
| class UnsealingSessionTpm1Impl final |
| : public SignatureSealingBackend::UnsealingSession { |
| public: |
| UnsealingSessionTpm1Impl(TpmImpl* tpm, |
| const Blob& srk_wrapped_cmk, |
| const Blob& cmk_wrapped_auth_data, |
| const Blob& pcr_bound_secret, |
| const Blob& public_key_spki_der, |
| const Blob& delegate_blob, |
| const Blob& delegate_secret, |
| const Blob& cmk_pubkey, |
| const Blob& protection_key_pubkey, |
| crypto::ScopedRSA migration_destination_rsa, |
| const Blob& migration_destination_key_pubkey); |
| ~UnsealingSessionTpm1Impl() override; |
| |
| // UnsealingSession: |
| ChallengeSignatureAlgorithm GetChallengeAlgorithm() override; |
| Blob GetChallengeValue() override; |
| bool Unseal(const Blob& signed_challenge_value, |
| SecureBlob* unsealed_value) override; |
| |
| private: |
| // Unowned. |
| TpmImpl* const tpm_; |
| // The blob of the CMK wrapped by the SRK. |
| const Blob srk_wrapped_cmk_; |
| // The AuthData blob encrypted by the CMK using the RSAES-OAEP MGF1 algorithm. |
| const Blob cmk_wrapped_auth_data_; |
| // The secret blob, which is bound to some PCR values with the AuthData value |
| // that is stored encrypted in |cmk_wrapped_auth_data_|. |
| const Blob pcr_bound_secret_; |
| // The DER-encoded Subject Public Key Info of the protection key. |
| const Blob public_key_spki_der_; |
| // The blob for the owner delegation. |
| const Blob delegate_blob_; |
| // The delegate secret for the delegate blob. |
| const Blob delegate_secret_; |
| // The TPM_PUBKEY blob of the CMK. |
| const Blob cmk_pubkey_; |
| // The SHA-1 digest of |cmk_pubkey_|. |
| const Blob cmk_pubkey_digest_; |
| // The TPM_PUBKEY blob of the protection key. |
| const Blob protection_key_pubkey_; |
| // The SHA-1 digest of |protection_key_pubkey_|. |
| const Blob protection_key_pubkey_digest_; |
| // The private RSA key of the migration destination key. |
| const crypto::ScopedRSA migration_destination_rsa_; |
| // The TPM_PUBKEY blob of the migration destination key. |
| const Blob migration_destination_key_pubkey_; |
| // The SHA-1 digest of |migration_destination_key_pubkey_|. |
| const Blob migration_destination_key_pubkey_digest_; |
| // The SHA-1 digest of the TPM_MSA_COMPOSITE structure containing a sole |
| // reference to |protection_key_pubkey_digest_|. |
| const Blob msa_composite_digest_; |
| |
| DISALLOW_COPY_AND_ASSIGN(UnsealingSessionTpm1Impl); |
| }; |
| |
| // Extracts the public modulus from the OpenSSL RSA struct. |
| bool GetRsaModulus(const RSA& rsa, Blob* modulus) { |
| modulus->resize(RSA_size(&rsa)); |
| const BIGNUM* n; |
| RSA_get0_key(&rsa, &n, nullptr, nullptr); |
| if (BN_bn2bin(n, modulus->data()) != modulus->size()) { |
| LOG(ERROR) << "Failed to extract RSA modulus: size mismatch"; |
| return false; |
| } |
| return true; |
| } |
| |
| // Parses the public key that is protecting the sealed data. The key size in |
| // bits is returned via |key_size_bits|, and the RSA key public modulus via |
| // |key_modulus|. |
| bool ParseProtectionKeySpki(const Blob& public_key_spki_der, |
| int* key_size_bits, |
| Blob* key_modulus) { |
| const unsigned char* asn1_ptr = public_key_spki_der.data(); |
| crypto::ScopedEVP_PKEY pkey( |
| d2i_PUBKEY(nullptr, &asn1_ptr, public_key_spki_der.size())); |
| if (!pkey) { |
| LOG(ERROR) << "Error parsing protection public key: Failed to parse " |
| "Subject Public Key Info DER"; |
| return false; |
| } |
| crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey.get())); |
| if (!rsa) { |
| LOG(ERROR) << "Error parsing protection public key: Non-RSA key"; |
| return false; |
| } |
| const BIGNUM* e; |
| RSA_get0_key(rsa.get(), nullptr, &e, nullptr); |
| const BN_ULONG key_exponent_word = BN_get_word(e); |
| if (key_exponent_word != kWellKnownExponent) { |
| // Trousers only supports the well-known exponent, failing internally on |
| // incorrect data serialization when other exponents are used. |
| LOG(ERROR) << "Error parsing protection public key: Exponent must be " |
| << kWellKnownExponent; |
| return false; |
| } |
| *key_size_bits = RSA_size(rsa.get()) * 8; |
| if (*key_size_bits != 1024 && *key_size_bits != 2048) { |
| LOG(ERROR) << "Error parsing protection public key: Unsupported key size"; |
| return false; |
| } |
| if (!GetRsaModulus(*rsa, key_modulus)) { |
| LOG(ERROR) |
| << "Error parsing protection public key: Failed to extract key modulus"; |
| return false; |
| } |
| return true; |
| } |
| |
| // Parses the public key that is protecting the sealed data and loads it into |
| // Trousers. The key size in bits is returned via |key_size_bits|, and the |
| // Trousers handle of the loaded public key via |key_handle|. |
| bool ParseAndLoadProtectionKey(TpmImpl* const tpm, |
| TSS_HCONTEXT tpm_context, |
| const Blob& public_key_spki_der, |
| int* key_size_bits, |
| TSS_HKEY* key_handle) { |
| Blob key_modulus; |
| if (!ParseProtectionKeySpki(public_key_spki_der, key_size_bits, |
| &key_modulus)) { |
| LOG(ERROR) << "Failed to parse protection public key"; |
| return false; |
| } |
| UINT32 key_size_flag = 0; |
| switch (*key_size_bits) { |
| case 1024: |
| key_size_flag = TSS_KEY_SIZE_1024; |
| break; |
| case 2048: |
| key_size_flag = TSS_KEY_SIZE_2048; |
| break; |
| default: |
| LOG(ERROR) << "Wrong size of protection public key"; |
| return false; |
| } |
| if (!tpm->CreateRsaPublicKeyObject( |
| tpm_context, key_modulus, |
| TSS_KEY_VOLATILE | TSS_KEY_TYPE_SIGNING | key_size_flag, |
| TSS_SS_RSASSAPKCS1V15_SHA1, TSS_ES_NONE, key_handle)) { |
| LOG(ERROR) << "Failed to load protection public key"; |
| return false; |
| } |
| return true; |
| } |
| |
| // Loads the migration destination public key into Trousers. The loaded key |
| // handle is returned via |key_handle|. |
| bool LoadMigrationDestinationPublicKey(TpmImpl* const tpm, |
| TSS_HCONTEXT tpm_context, |
| const RSA& migration_destination_rsa, |
| TSS_HKEY* key_handle) { |
| Blob key_modulus; |
| if (!GetRsaModulus(migration_destination_rsa, &key_modulus)) { |
| LOG(ERROR) << "Error loading migration destination public key: Failed to " |
| "extract key modulus"; |
| return false; |
| } |
| if (!tpm->CreateRsaPublicKeyObject(tpm_context, key_modulus, |
| TSS_KEY_VOLATILE | TSS_KEY_TYPE_STORAGE | |
| kMigrationDestinationKeySizeFlag, |
| TSS_SS_NONE, TSS_ES_RSAESOAEP_SHA1_MGF1, |
| key_handle)) { |
| LOG(ERROR) << "Error loading migration destination public key"; |
| return false; |
| } |
| return true; |
| } |
| |
| // Obtains via the TPM_AuthorizeMigrationKey command the migration authorization |
| // blob for the given migration destination key. Returns the authorization blob |
| // via |migration_authorization_blob|. |
| bool ObtainMigrationAuthorization(TSS_HCONTEXT tpm_context, |
| TSS_HTPM tpm_handle, |
| TSS_HKEY migration_destination_key_handle, |
| Blob* migration_authorization_blob) { |
| uint32_t migration_authorization_blob_buf_size = 0; |
| ScopedTssMemory migration_authorization_blob_buf(tpm_context); |
| TSS_RESULT tss_result = Tspi_TPM_AuthorizeMigrationTicket( |
| tpm_handle, migration_destination_key_handle, |
| TSS_MS_RESTRICT_APPROVE_DOUBLE, &migration_authorization_blob_buf_size, |
| migration_authorization_blob_buf.ptr()); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error obtaining the migration authorization: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| migration_authorization_blob->assign( |
| migration_authorization_blob_buf.value(), |
| migration_authorization_blob_buf.value() + |
| migration_authorization_blob_buf_size); |
| return true; |
| } |
| |
| // Obtains via the TPM_CMK_CreateTicket command the CMK migration signature |
| // ticket for the signature of the challenge. Returns the ticket via |
| // |cmk_migration_signature_ticket|. |
| bool ObtainCmkMigrationSignatureTicket( |
| TpmImpl* tpm, |
| TSS_HCONTEXT tpm_context, |
| TSS_HTPM tpm_handle, |
| TSS_HKEY protection_key_handle, |
| const Blob& migration_destination_key_pubkey, |
| const Blob& cmk_pubkey, |
| const Blob& protection_key_pubkey, |
| const Blob& signed_challenge_value, |
| Blob* cmk_migration_signature_ticket) { |
| ScopedTssObject<TSS_HMIGDATA> migdata_handle(tpm_context); |
| TSS_RESULT tss_result = Tspi_Context_CreateObject( |
| tpm_context, TSS_OBJECT_TYPE_MIGDATA, 0, migdata_handle.ptr()); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error creating the CMK migration data object: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| migdata_handle, TSS_MIGATTRIB_MIGRATIONBLOB, |
| TSS_MIGATTRIB_MIG_DESTINATION_PUBKEY_BLOB, |
| migration_destination_key_pubkey.size(), |
| const_cast<BYTE*>(migration_destination_key_pubkey.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the CMK migration destination public key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData(migdata_handle, TSS_MIGATTRIB_MIGRATIONBLOB, |
| TSS_MIGATTRIB_MIG_SOURCE_PUBKEY_BLOB, |
| cmk_pubkey.size(), |
| const_cast<BYTE*>(cmk_pubkey.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the CMK migration source public key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| migdata_handle, TSS_MIGATTRIB_MIGRATIONBLOB, |
| TSS_MIGATTRIB_MIG_AUTHORITY_PUBKEY_BLOB, protection_key_pubkey.size(), |
| const_cast<BYTE*>(protection_key_pubkey.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the CMK migration authority public key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| migdata_handle, TSS_MIGATTRIB_TICKET_DATA, TSS_MIGATTRIB_TICKET_SIG_VALUE, |
| signed_challenge_value.size(), |
| const_cast<BYTE*>(signed_challenge_value.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the CMK migration signed challenge data: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_TPM_CMKCreateTicket(tpm_handle, protection_key_handle, |
| migdata_handle); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error obtaining the CMK migration signature ticket: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| SecureBlob local_cmk_migration_signature_ticket; |
| if (tpm->GetDataAttribute( |
| tpm_context, migdata_handle, TSS_MIGATTRIB_TICKET_DATA, |
| TSS_MIGATTRIB_TICKET_SIG_TICKET, |
| &local_cmk_migration_signature_ticket) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) << "Error reading the CMK migration signature ticket"; |
| return false; |
| } |
| // TODO(emaxx): Replace with a direct usage of Blob for the attribute read. |
| cmk_migration_signature_ticket->assign( |
| local_cmk_migration_signature_ticket.begin(), |
| local_cmk_migration_signature_ticket.end()); |
| return true; |
| } |
| |
| // Performs the migration of the CMK, passed in |srk_wrapped_cmk|, onto the key |
| // specified by |migration_destination_key_pubkey|, using the migration |
| // authorization from |migration_authorization_blob| and the CMK migration |
| // signature ticket from |cmk_migration_signature_ticket| for authorizing the |
| // migration. Returns the TPM_KEY12 blob of the migrated CMK via |
| // |migrated_cmk_key12_blob|, and the migration random XOR mask via |
| // |migration_random_blob| (see ExtractCmkPrivateKeyFromMigratedBlob() for the |
| // details). |
| bool MigrateCmk(TpmImpl* tpm, |
| TSS_HCONTEXT tpm_context, |
| TSS_HTPM tpm_handle, |
| TSS_HKEY srk_handle, |
| const Blob& srk_wrapped_cmk, |
| const Blob& migration_destination_key_pubkey, |
| const Blob& cmk_pubkey, |
| const Blob& protection_key_pubkey, |
| const Blob& migration_authorization_blob, |
| const Blob& cmk_migration_signature_ticket, |
| Blob* migrated_cmk_key12_blob, |
| Blob* migration_random_blob) { |
| // Load the wrapped CMK into Trousers. |
| ScopedTssObject<TSS_HMIGDATA> wrapped_cmk_handle(tpm_context); |
| TSS_RESULT tss_result = Tspi_Context_CreateObject( |
| tpm_context, TSS_OBJECT_TYPE_RSAKEY, 0, wrapped_cmk_handle.ptr()); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error creating the wrapped certified migratable key object: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| wrapped_cmk_handle, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_BLOB, |
| srk_wrapped_cmk.size(), const_cast<BYTE*>(srk_wrapped_cmk.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the wrapped certified migratable key blob: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| // Prepare the parameters object for the migration command. |
| ScopedTssObject<TSS_HMIGDATA> migdata_handle(tpm_context); |
| tss_result = Tspi_Context_CreateObject(tpm_context, TSS_OBJECT_TYPE_MIGDATA, |
| 0, migdata_handle.ptr()); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error creating the CMK migration data object: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| migdata_handle, TSS_MIGATTRIB_MIGRATIONBLOB, |
| TSS_MIGATTRIB_MIG_DESTINATION_PUBKEY_BLOB, |
| migration_destination_key_pubkey.size(), |
| const_cast<BYTE*>(migration_destination_key_pubkey.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the CMK migration destination public key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData(migdata_handle, TSS_MIGATTRIB_MIGRATIONBLOB, |
| TSS_MIGATTRIB_MIG_SOURCE_PUBKEY_BLOB, |
| cmk_pubkey.size(), |
| const_cast<BYTE*>(cmk_pubkey.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the CMK migration source public key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| migdata_handle, TSS_MIGATTRIB_MIGRATIONBLOB, |
| TSS_MIGATTRIB_MIG_AUTHORITY_PUBKEY_BLOB, protection_key_pubkey.size(), |
| const_cast<BYTE*>(protection_key_pubkey.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the CMK migration authority public key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| migdata_handle, TSS_MIGATTRIB_MIGRATIONBLOB, |
| TSS_MIGATTRIB_MIG_MSALIST_PUBKEY_BLOB, protection_key_pubkey.size(), |
| const_cast<BYTE*>(protection_key_pubkey.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) |
| << "Error setting the CMK migration selection authority public key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| migdata_handle, TSS_MIGATTRIB_MIGRATIONTICKET, 0, |
| migration_authorization_blob.size(), |
| const_cast<BYTE*>(migration_authorization_blob.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the CMK migration authorization: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| migdata_handle, TSS_MIGATTRIB_TICKET_DATA, |
| TSS_MIGATTRIB_TICKET_SIG_TICKET, cmk_migration_signature_ticket.size(), |
| const_cast<BYTE*>(cmk_migration_signature_ticket.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting the CMK migration signature ticket: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| // Perform the migration and extract the resulting data. |
| UINT32 migration_random_buf_size = 0; |
| ScopedTssMemory migration_random_buf(tpm_context); |
| tss_result = Tspi_Key_CMKCreateBlob( |
| wrapped_cmk_handle, srk_handle, migdata_handle, |
| &migration_random_buf_size, migration_random_buf.ptr()); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error performing the certified migratable key migration: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| migration_random_blob->assign( |
| migration_random_buf.value(), |
| migration_random_buf.value() + migration_random_buf_size); |
| SecureBlob local_migrated_cmk_key12_blob; |
| if (tpm->GetDataAttribute( |
| tpm_context, migdata_handle, TSS_MIGATTRIB_MIGRATIONBLOB, |
| TSS_MIGATTRIB_MIG_XOR_BLOB, |
| &local_migrated_cmk_key12_blob) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) << "Failed to read the migrated key blob"; |
| return false; |
| } |
| // TODO(emaxx): Replace with a direct usage of Blob for the attribute read. |
| migrated_cmk_key12_blob->assign(local_migrated_cmk_key12_blob.begin(), |
| local_migrated_cmk_key12_blob.end()); |
| return true; |
| } |
| |
| // Returns the digest of the blob of the TPM_MSA_COMPOSITE structure containing |
| // a sole reference to the specified key (whose TPM_PUBKEY blob is passed via |
| // |msa_pubkey_digest|). |
| Blob BuildMsaCompositeDigest(const Blob& msa_pubkey_digest) { |
| // Build the structure. |
| DCHECK_EQ(TPM_SHA1_160_HASH_LEN, msa_pubkey_digest.size()); |
| TPM_DIGEST digest; |
| memcpy(digest.digest, msa_pubkey_digest.data(), msa_pubkey_digest.size()); |
| TPM_MSA_COMPOSITE msa_composite; |
| msa_composite.MSAlist = 1; |
| msa_composite.migAuthDigest = &digest; |
| // Serialize the structure. |
| UINT64 serializing_offset = 0; |
| Trspi_LoadBlob_MSA_COMPOSITE(&serializing_offset, nullptr, &msa_composite); |
| Blob msa_composite_blob(serializing_offset); |
| serializing_offset = 0; |
| Trspi_LoadBlob_MSA_COMPOSITE(&serializing_offset, msa_composite_blob.data(), |
| &msa_composite); |
| return CryptoLib::Sha1(msa_composite_blob); |
| } |
| |
| // Obtains via the TPM_CMK_ApproveMA command the migration authority approval |
| // ticket for the given TPM_MSA_COMPOSITE structure blob. Returns the ticket via |
| // |ma_approval_ticket|. |
| bool ObtainMaApprovalTicket(TpmImpl* const tpm, |
| TSS_HCONTEXT tpm_context, |
| TSS_HTPM tpm_handle, |
| const Blob& msa_composite_digest, |
| Blob* ma_approval_ticket) { |
| ScopedTssObject<TSS_HMIGDATA> migdata_handle(tpm_context); |
| TSS_RESULT tss_result = Tspi_Context_CreateObject( |
| tpm_context, TSS_OBJECT_TYPE_MIGDATA, 0, migdata_handle.ptr()); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error creating migration data object: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_SetAttribData( |
| migdata_handle, TSS_MIGATTRIB_AUTHORITY_DATA, |
| TSS_MIGATTRIB_AUTHORITY_DIGEST, msa_composite_digest.size(), |
| const_cast<BYTE*>(msa_composite_digest.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error setting migration selection authority: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| tss_result = Tspi_TPM_CMKApproveMA(tpm_handle, migdata_handle); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error obtaining migration authority approval ticket: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| SecureBlob local_ma_approval_ticket; |
| if (tpm->GetDataAttribute(tpm_context, migdata_handle, |
| TSS_MIGATTRIB_AUTHORITY_DATA, |
| TSS_MIGATTRIB_AUTHORITY_APPROVAL_HMAC, |
| &local_ma_approval_ticket) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) << "Error reading migration authority approval ticket"; |
| return false; |
| } |
| // TODO(emaxx): Replace with a direct usage of Blob for the attribute read. |
| ma_approval_ticket->assign(local_ma_approval_ticket.begin(), |
| local_ma_approval_ticket.end()); |
| return true; |
| } |
| |
| // Parses the TPM_KEY12 blob and returns its "encData" field blob. |
| bool ParseEncDataFromKey12Blob(const Blob& key12_blob, Blob* enc_data) { |
| ScopedKey12 key12; |
| UINT64 key12_parsing_offset = 0; |
| TSS_RESULT tss_result = Trspi_UnloadBlob_KEY12( |
| &key12_parsing_offset, const_cast<BYTE*>(key12_blob.data()), key12.ptr()); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Failed to parse the migrated key TPM_KEY12 blob: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| if (key12_parsing_offset != key12_blob.size()) { |
| LOG(ERROR) << "Failed to parse the migrated key TPM_KEY12 blob due to size " |
| "mismatch"; |
| return false; |
| } |
| enc_data->assign(key12->encData, key12->encData + key12->encSize); |
| return true; |
| } |
| |
| // Applies to the given blob the element-to-element bitwise XOR against the |
| // other blob. |
| void XorBytes(uint8_t* inplace_target_begin, |
| const uint8_t* other_begin, |
| size_t size) { |
| for (size_t index = 0; index < size; ++index) |
| inplace_target_begin[index] ^= other_begin[index]; |
| } |
| |
| // Obtains the value from its MGF1-masked representation in |masked_value|. The |
| // input value for the MGF1 mask is passed via |mgf_input_value|. Returns the |
| // result via |value|; its length, on success, is guaranteed to be the same as |
| // the |masked_value|'s one. |
| bool UnmaskWithMgf1(const SecureBlob& masked_value, |
| const SecureBlob& mgf_input_value, |
| SecureBlob* value) { |
| if (masked_value.empty()) { |
| LOG(ERROR) << "Bad MGF1-masked value"; |
| return false; |
| } |
| if (mgf_input_value.empty()) { |
| LOG(ERROR) << "Bad MGF1 input value"; |
| return false; |
| } |
| SecureBlob mask(masked_value.size()); |
| TSS_RESULT tss_result = Trspi_MGF1(TSS_HASH_SHA1, mgf_input_value.size(), |
| const_cast<BYTE*>(mgf_input_value.data()), |
| mask.size(), mask.data()); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Failed to generate the MGF1 mask: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| *value = masked_value; |
| XorBytes(value->data(), mask.data(), value->size()); |
| return true; |
| } |
| |
| // Performs the RSA OAEP MGF1 decoding of the encoded blob |encoded_blob| using |
| // the OAEP label parameter equal to |oaep_label|. The size |message_length| |
| // specifies the expected size of the returned message. |
| // Returns the decoded message via |message| and the OAEP seed via |seed|. |
| // Note that this custom implementation is used instead of the one from OpenSSL, |
| // because we need to get the seed back and OpenSSL doesn't return it. |
| bool DecodeOaepMgf1Encoding(const Blob& encoded_blob, |
| size_t message_length, |
| const Blob& oaep_label, |
| SecureBlob* seed, |
| SecureBlob* message) { |
| // The comments in this function below refer to the notation that corresponds |
| // to the "RSAES-OAEP Encryption Scheme" Algorithm specification and |
| // supporting documentation (2000), the "EME-OAEP-Decode" section. |
| // The correspondence between the function parameters and the terms in the |
| // specification is: |
| // * |encoded_blob| - "EM"; |
| // * |message_length| - "mLen"; |
| // * |oaep_label| - "P"; |
| // * |seed| - "seed"; |
| // * |message| - "M". |
| // Note that as the MGF1 mask is used which is based on SHA-1, the "hLen" term |
| // corresponds to |SHA_DIGEST_LENGTH|. |
| const size_t blob_size = encoded_blob.size(); |
| // Step #1 is omitted as not applicable to our implementation - the length of |
| // |oaep_label| can't realistically reach the size constraint of SHA-1. |
| // Step #2. The total length of the encoded message is formed by the length of |
| // "seed" (which is equal to "hLen"), the length of "pHash" (which is also |
| // equal to "hLen"), the length of the original message, and the length of the |
| // "01" octet (which is 1 byte). |
| const size_t minimum_blob_size = 2 * SHA_DIGEST_LENGTH + 1 + message_length; |
| if (blob_size < minimum_blob_size) { |
| LOG(ERROR) << "Failed to parse the blob: the size is too small"; |
| return false; |
| } |
| // Step #3. Split "EM" into "maskedSeed" and "maskedDB". |
| const SecureBlob masked_seed(encoded_blob.begin(), |
| encoded_blob.begin() + SHA_DIGEST_LENGTH); |
| const SecureBlob masked_padded_message( |
| encoded_blob.begin() + SHA_DIGEST_LENGTH, encoded_blob.end()); |
| // Steps ##4-5. Unmask "maskedSeed" to obtain "seed". |
| if (!UnmaskWithMgf1(masked_seed, masked_padded_message, seed)) { |
| LOG(ERROR) << "Failed to unmask the seed"; |
| return false; |
| } |
| // Steps ##6-7. Unmask "maskedDB" into "DB". |
| SecureBlob padded_message; |
| if (!UnmaskWithMgf1(masked_padded_message, *seed, &padded_message)) { |
| LOG(ERROR) << "Failed to unmask the message"; |
| return false; |
| } |
| // Steps ##8-10. Extract "M" from "DB", extract "pHash" from "DB" and check it |
| // against "P", and verify the zeros/ones padding that covers the rest. |
| const Blob obtained_label_digest(padded_message.begin(), |
| padded_message.begin() + SHA_DIGEST_LENGTH); |
| const Blob obtained_zeroes_ones_padding( |
| padded_message.begin() + SHA_DIGEST_LENGTH, |
| padded_message.end() - message_length); |
| message->assign(padded_message.end() - message_length, padded_message.end()); |
| DCHECK_EQ(padded_message.size(), obtained_label_digest.size() + |
| obtained_zeroes_ones_padding.size() + |
| message->size()); |
| if (obtained_label_digest != CryptoLib::Sha1(oaep_label)) { |
| LOG(ERROR) << "Incorrect OAEP label"; |
| return false; |
| } |
| const Blob expected_zeroes_ones_padding = |
| CombineBlobs({Blob(obtained_zeroes_ones_padding.size() - 1), Blob(1, 1)}); |
| if (obtained_zeroes_ones_padding != expected_zeroes_ones_padding) { |
| LOG(ERROR) << "Incorrect zeroes block in OAEP padding"; |
| return false; |
| } |
| return true; |
| } |
| |
| // Parses an unsigned four-byte integer from the given position in the blob in |
| // the TPM endianness. |
| uint32_t DecodeTpmUint32(const uint8_t* begin) { |
| UINT64 parsing_offset = 0; |
| uint32_t result = 0; |
| Trspi_UnloadBlob_UINT32(&parsing_offset, &result, const_cast<BYTE*>(begin)); |
| DCHECK_EQ(4, parsing_offset); |
| return result; |
| } |
| |
| // Parses the RSA secret prime from the TPM_MIGRATE_ASYMKEY blob and the seed |
| // blob. |
| bool ParseRsaSecretPrimeFromTpmMigrateAsymkeyBlob( |
| const SecureBlob& tpm_migrate_asymkey_blob, |
| const SecureBlob& tpm_migrate_asymkey_oaep_seed_blob, |
| SecureBlob* secret_prime_blob) { |
| DCHECK_EQ(SHA_DIGEST_LENGTH, tpm_migrate_asymkey_oaep_seed_blob.size()); |
| // The binary layout, as specified in TPM 1.2 Part 3 Section 11.9 |
| // ("TPM_CMK_CreateBlob"), is: |
| // * |tpm_migrate_asymkey_oaep_seed_blob| (called "K1" in the specification): |
| // is of |SHA_DIGEST_LENGTH| bytes length, and is structured as following: |
| // * the first 4 bytes contain a four-byte integer - the size of the private |
| // key in bytes (obtained from TPM_STORE_PRIVKEY.keyLength); |
| // * the rest are the first |kMigratedCmkPrivateKeySeedPartSizeBytes| bytes |
| // of the private key; |
| // * |tpm_migrate_asymkey_blob| (called "M1" in the specification): the binary |
| // dump of the TPM_MIGRATE_ASYMKEY structure, of which we are looking only |
| // at: |
| // * the first field |payload| of length 1 byte, which has to be equal to |
| // |TPM_PT_CMK_MIGRATE|; |
| // * the last field |partPrivKey|, which contains the last |
| // |kMigratedCmkPrivateKeyRestPartSizeBytes| bytes of the private key; |
| // * the last but one field |partPrivKeyLen| of length 4 bytes, which is a |
| // four-byte integer that has to be equal to |
| // |kMigratedCmkPrivateKeyRestPartSizeBytes|. |
| // We parse and validate this data below: |
| // Parse and validate the keyLength field of the TPM_STORE_PRIVKEY structure. |
| DCHECK_GE(tpm_migrate_asymkey_oaep_seed_blob.size(), 4); |
| const uint32_t tpm_store_privkey_key_length = |
| DecodeTpmUint32(tpm_migrate_asymkey_oaep_seed_blob.data()); |
| if (tpm_store_privkey_key_length != kCmkPrivateKeySizeBytes) { |
| LOG(ERROR) << "Wrong migrated private key size"; |
| return false; |
| } |
| // Extract the part of the private key from the OAEP seed. |
| const SecureBlob tpm_store_privkey_key_seed_part_blob( |
| tpm_migrate_asymkey_oaep_seed_blob.begin() + 4, |
| tpm_migrate_asymkey_oaep_seed_blob.end()); |
| DCHECK_EQ(kMigratedCmkPrivateKeySeedPartSizeBytes, |
| tpm_store_privkey_key_seed_part_blob.size()); |
| // Validate the TPM_MIGRATE_ASYMKEY blob size. |
| if (tpm_migrate_asymkey_blob.size() < |
| kMigratedCmkPrivateKeyRestPartSizeBytes + 4) { |
| LOG(ERROR) << "Wrong length of TPM_MIGRATE_ASYMKEY blob"; |
| return false; |
| } |
| // Parse and validate the payload field of the TPM_MIGRATE_ASYMKEY structure. |
| const int tpm_migrate_asymkey_payload = tpm_migrate_asymkey_blob[0]; |
| if (tpm_migrate_asymkey_payload != TPM_PT_CMK_MIGRATE) { |
| LOG(ERROR) << "Wrong migration payload type"; |
| return false; |
| } |
| // Extract the part of the private key from the TPM_MIGRATE_ASYMKEY blob. |
| const SecureBlob tpm_store_privkey_key_rest_part_blob( |
| tpm_migrate_asymkey_blob.end() - kMigratedCmkPrivateKeyRestPartSizeBytes, |
| tpm_migrate_asymkey_blob.end()); |
| // Parse and validate the partPrivKeyLen field of the TPM_MIGRATE_ASYMKEY |
| // structure. |
| const uint32_t tpm_migrate_asymkey_part_priv_key_length = DecodeTpmUint32( |
| &tpm_migrate_asymkey_blob[tpm_migrate_asymkey_blob.size() - |
| kMigratedCmkPrivateKeyRestPartSizeBytes - 4]); |
| if (tpm_migrate_asymkey_part_priv_key_length != |
| kMigratedCmkPrivateKeyRestPartSizeBytes) { |
| LOG(ERROR) << "Wrong size of the private key part in TPM_MIGRATE_ASYMKEY"; |
| return false; |
| } |
| // Assemble the resulting secret prime blob. |
| *secret_prime_blob = |
| SecureBlob::Combine(tpm_store_privkey_key_seed_part_blob, |
| tpm_store_privkey_key_rest_part_blob); |
| DCHECK_EQ(kCmkPrivateKeySizeBytes, secret_prime_blob->size()); |
| return true; |
| } |
| |
| // Generates the Certified Migratable Key, associated with the protection public |
| // key (via the TPM_MSA_COMPOSITE digest passed by |msa_composite_digest|). The |
| // |ma_approval_ticket| should contain ticket obtained from the |
| // TPM_CMK_ApproveMA command. Returns the CMK TPM_PUBKEY blob via |cmk_pubkey| |
| // and the wrapped CMK blob via |srk_wrapped_cmk|. |
| bool GenerateCmk(TpmImpl* const tpm, |
| TSS_HCONTEXT tpm_context, |
| TSS_HTPM tpm_handle, |
| TSS_HKEY srk_handle, |
| const Blob& msa_composite_digest, |
| const Blob& ma_approval_ticket, |
| Blob* cmk_pubkey, |
| Blob* srk_wrapped_cmk) { |
| // Create the Certified Migratable Key object. Note that the actual key |
| // generation isn't happening at this point yet. |
| ScopedTssKey cmk_handle(tpm_context); |
| TSS_RESULT tss_result = Tspi_Context_CreateObject( |
| tpm_context, TSS_OBJECT_TYPE_RSAKEY, |
| TSS_KEY_STRUCT_KEY12 | TSS_KEY_VOLATILE | TSS_KEY_TYPE_STORAGE | |
| TSS_KEY_AUTHORIZATION | TSS_KEY_MIGRATABLE | |
| TSS_KEY_CERTIFIED_MIGRATABLE | kCmkKeySizeFlag, |
| cmk_handle.ptr()); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Failed to create certified migratable key object: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| // Set the parameter to make the created CMK associated with the protection |
| // public key (via the TPM_MSA_COMPOSITE digest). |
| tss_result = Tspi_SetAttribData( |
| cmk_handle, TSS_TSPATTRIB_KEY_CMKINFO, |
| TSS_TSPATTRIB_KEYINFO_CMK_MA_DIGEST, msa_composite_digest.size(), |
| const_cast<BYTE*>(msa_composite_digest.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Failed to set migration authority digest: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| // Set the parameter to pass the migration authority approval ticket to the |
| // CMK creation procedure. |
| tss_result = Tspi_SetAttribData(cmk_handle, TSS_TSPATTRIB_KEY_CMKINFO, |
| TSS_TSPATTRIB_KEYINFO_CMK_MA_APPROVAL, |
| ma_approval_ticket.size(), |
| const_cast<BYTE*>(ma_approval_ticket.data())); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Failed to set migration authority approval ticket: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| // Add the usage policy to the CMK. The policy will effectively disallow the |
| // usage of the CMK for signing/decryption, as the policy's password is |
| // discarded. |
| ScopedTssPolicy usage_policy_handle(tpm_context); |
| if (!tpm->CreatePolicyWithRandomPassword(tpm_context, TSS_POLICY_USAGE, |
| usage_policy_handle.ptr())) { |
| LOG(ERROR) << "Failed to create the usage policy"; |
| return false; |
| } |
| tss_result = Tspi_Policy_AssignToObject(usage_policy_handle, cmk_handle); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Error assigning the usage policy to the key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| // Add the migration policy to the CMK. The policy will effectively disallow |
| // the usage of the CMK for non-certified migration, as the policy's password |
| // is discarded. |
| ScopedTssPolicy migration_policy_handle(tpm_context); |
| if (!tpm->CreatePolicyWithRandomPassword(tpm_context, TSS_POLICY_MIGRATION, |
| migration_policy_handle.ptr())) { |
| LOG(ERROR) << "Failed to create the usage policy"; |
| return false; |
| } |
| tss_result = Tspi_Policy_AssignToObject(migration_policy_handle, cmk_handle); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Failed to set the migration policy to the key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| // Trigger the CMK generation and extract the resulting blobs. |
| tss_result = |
| Tspi_Key_CreateKey(cmk_handle, srk_handle, 0 /* hPcrComposite */); |
| if (TPM_ERROR(tss_result)) { |
| LOG(ERROR) << "Failed to create the certified migratable key: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| SecureBlob local_cmk_pubkey; |
| if (tpm->GetDataAttribute(tpm_context, cmk_handle, TSS_TSPATTRIB_KEY_BLOB, |
| TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, |
| &local_cmk_pubkey) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) << "Failed to read the certified migratable public key"; |
| return false; |
| } |
| // TODO(emaxx): Replace with a direct usage of Blob for the attribute read. |
| cmk_pubkey->assign(local_cmk_pubkey.begin(), local_cmk_pubkey.end()); |
| SecureBlob local_srk_wrapped_cmk; |
| if (tpm->GetDataAttribute(tpm_context, cmk_handle, TSS_TSPATTRIB_KEY_BLOB, |
| TSS_TSPATTRIB_KEYBLOB_BLOB, |
| &local_srk_wrapped_cmk) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) << "Failed to read the certified migratable key"; |
| return false; |
| } |
| // TODO(emaxx): Replace with a direct usage of Blob for the attribute read. |
| srk_wrapped_cmk->assign(local_srk_wrapped_cmk.begin(), |
| local_srk_wrapped_cmk.end()); |
| return true; |
| } |
| |
| // Given the list of alternative sets of PCR-bound items, returns the one that |
| // is currently satisfied. Returns null if none is satisfied. |
| const SignatureSealedData_Tpm12PcrBoundItem* GetSatisfiedPcrRestriction( |
| const google::protobuf::RepeatedPtrField< |
| SignatureSealedData_Tpm12PcrBoundItem>& pcr_bound_items, |
| Tpm* tpm) { |
| std::map<uint32_t, Blob> current_pcr_values; |
| for (const auto& pcr_bound_item_proto : pcr_bound_items) { |
| bool is_satisfied = true; |
| for (const auto& pcr_value_proto : pcr_bound_item_proto.pcr_values()) { |
| const uint32_t pcr_index = pcr_value_proto.pcr_index(); |
| if (!current_pcr_values.count(pcr_index)) { |
| Blob pcr_value; |
| if (!tpm->ReadPCR(pcr_index, &pcr_value)) { |
| is_satisfied = false; |
| break; |
| } |
| current_pcr_values.emplace(pcr_index, pcr_value); |
| } |
| if (current_pcr_values[pcr_index] != |
| BlobFromString(pcr_value_proto.pcr_value())) { |
| is_satisfied = false; |
| break; |
| } |
| } |
| if (is_satisfied) |
| return &pcr_bound_item_proto; |
| } |
| return nullptr; |
| } |
| |
| UnsealingSessionTpm1Impl::UnsealingSessionTpm1Impl( |
| TpmImpl* tpm, |
| const Blob& srk_wrapped_cmk, |
| const Blob& cmk_wrapped_auth_data, |
| const Blob& pcr_bound_secret, |
| const Blob& public_key_spki_der, |
| const Blob& delegate_blob, |
| const Blob& delegate_secret, |
| const Blob& cmk_pubkey, |
| const Blob& protection_key_pubkey, |
| crypto::ScopedRSA migration_destination_rsa, |
| const Blob& migration_destination_key_pubkey) |
| : tpm_(tpm), |
| srk_wrapped_cmk_(srk_wrapped_cmk), |
| cmk_wrapped_auth_data_(cmk_wrapped_auth_data), |
| pcr_bound_secret_(pcr_bound_secret), |
| public_key_spki_der_(public_key_spki_der), |
| delegate_blob_(delegate_blob), |
| delegate_secret_(delegate_secret), |
| cmk_pubkey_(cmk_pubkey), |
| cmk_pubkey_digest_(CryptoLib::Sha1(cmk_pubkey_)), |
| protection_key_pubkey_(protection_key_pubkey), |
| protection_key_pubkey_digest_(CryptoLib::Sha1(protection_key_pubkey_)), |
| migration_destination_rsa_(std::move(migration_destination_rsa)), |
| migration_destination_key_pubkey_(migration_destination_key_pubkey), |
| migration_destination_key_pubkey_digest_( |
| CryptoLib::Sha1(migration_destination_key_pubkey_)), |
| msa_composite_digest_( |
| BuildMsaCompositeDigest(protection_key_pubkey_digest_)) {} |
| |
| UnsealingSessionTpm1Impl::~UnsealingSessionTpm1Impl() = default; |
| |
| ChallengeSignatureAlgorithm UnsealingSessionTpm1Impl::GetChallengeAlgorithm() { |
| return CHALLENGE_RSASSA_PKCS1_V1_5_SHA1; |
| } |
| |
| Blob UnsealingSessionTpm1Impl::GetChallengeValue() { |
| return CombineBlobs({protection_key_pubkey_digest_, |
| migration_destination_key_pubkey_digest_, |
| cmk_pubkey_digest_}); |
| } |
| |
| bool UnsealingSessionTpm1Impl::Unseal(const Blob& signed_challenge_value, |
| SecureBlob* unsealed_value) { |
| // Obtain the TPM context and handle with the required authorization. |
| ScopedTssContext tpm_context; |
| TSS_HTPM tpm_handle = 0; |
| if (!tpm_->ConnectContextAsDelegate(delegate_blob_, delegate_secret_, |
| tpm_context.ptr(), &tpm_handle)) { |
| LOG(ERROR) << "Failed to connect to the TPM"; |
| return false; |
| } |
| // Load the required keys into Trousers. |
| ScopedTssKey srk_handle(tpm_context); |
| TSS_RESULT tss_result = TSS_SUCCESS; |
| if (!tpm_->LoadSrk(tpm_context, srk_handle.ptr(), &tss_result)) { |
| LOG(ERROR) << "Failed to load the SRK: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| int protection_key_size_bits = 0; |
| ScopedTssKey protection_key_handle(tpm_context); |
| if (!ParseAndLoadProtectionKey(tpm_, tpm_context, public_key_spki_der_, |
| &protection_key_size_bits, |
| protection_key_handle.ptr())) { |
| LOG(ERROR) << "Failed to load the protection public key"; |
| return false; |
| } |
| ScopedTssKey migration_destination_key_handle(tpm_context); |
| if (!LoadMigrationDestinationPublicKey( |
| tpm_, tpm_context, *migration_destination_rsa_, |
| migration_destination_key_handle.ptr())) { |
| LOG(ERROR) << "Failed to load the migration destination key"; |
| return false; |
| } |
| // Sanity check the received signature blob. |
| if (signed_challenge_value.size() != protection_key_size_bits / 8) { |
| LOG(ERROR) << "Wrong size of challenge signature blob"; |
| return false; |
| } |
| // Obtain the migration authorization blob for the migration destination key. |
| Blob migration_authorization_blob; |
| if (!ObtainMigrationAuthorization(tpm_context, tpm_handle, |
| migration_destination_key_handle, |
| &migration_authorization_blob)) { |
| LOG(ERROR) << "Failed to obtain the migration authorization"; |
| return false; |
| } |
| // Obtain the CMK migration signature ticket for the signed challenge blob. |
| Blob cmk_migration_signature_ticket; |
| if (!ObtainCmkMigrationSignatureTicket( |
| tpm_, tpm_context, tpm_handle, protection_key_handle, |
| migration_destination_key_pubkey_, cmk_pubkey_, |
| protection_key_pubkey_, signed_challenge_value, |
| &cmk_migration_signature_ticket)) { |
| LOG(ERROR) << "Failed to obtain the CMK migration signature ticket"; |
| return false; |
| } |
| // Perform the migration of the CMK onto the migration destination key. |
| Blob migrated_cmk_key12_blob; |
| Blob migration_random_blob; |
| if (!MigrateCmk(tpm_, tpm_context, tpm_handle, srk_handle, srk_wrapped_cmk_, |
| migration_destination_key_pubkey_, cmk_pubkey_, |
| protection_key_pubkey_, migration_authorization_blob, |
| cmk_migration_signature_ticket, &migrated_cmk_key12_blob, |
| &migration_random_blob)) { |
| LOG(ERROR) << "Failed to migrate the certified migratable key"; |
| return false; |
| } |
| // Decrypt and decode the CMK private key. |
| crypto::ScopedRSA cmk_private_key = ExtractCmkPrivateKeyFromMigratedBlob( |
| migrated_cmk_key12_blob, migration_random_blob, cmk_pubkey_, |
| cmk_pubkey_digest_, msa_composite_digest_, |
| migration_destination_rsa_.get()); |
| if (!cmk_private_key) { |
| LOG(ERROR) << "Failed to extract the certified migratable private key"; |
| return false; |
| } |
| // Decrypt the AuthData value. |
| SecureBlob auth_data; |
| if (!CryptoLib::RsaOaepDecrypt(SecureBlob(cmk_wrapped_auth_data_), |
| SecureBlob() /* oaep_label */, |
| cmk_private_key.get(), &auth_data)) { |
| LOG(ERROR) << "Failed to decrypt the authorization data"; |
| return false; |
| } |
| // Unseal the secret value bound to PCRs and the AuthData value. |
| if (tpm_->UnsealWithAuthorization(srk_handle, SecureBlob(pcr_bound_secret_), |
| auth_data, {} /* pcr_map */, |
| unsealed_value) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) << "Failed to unseal the secret value"; |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| SignatureSealingBackendTpm1Impl::SignatureSealingBackendTpm1Impl(TpmImpl* tpm) |
| : tpm_(tpm) {} |
| |
| SignatureSealingBackendTpm1Impl::~SignatureSealingBackendTpm1Impl() = default; |
| |
| bool SignatureSealingBackendTpm1Impl::CreateSealedSecret( |
| const Blob& public_key_spki_der, |
| const std::vector<ChallengeSignatureAlgorithm>& key_algorithms, |
| const std::vector<std::map<uint32_t, brillo::Blob>>& pcr_restrictions, |
| const Blob& delegate_blob, |
| const Blob& delegate_secret, |
| brillo::SecureBlob* secret_value, |
| SignatureSealedData* sealed_secret_data) { |
| // TODO(chromium:806788,b:77799573): Support absence of PCR restrictions. |
| DCHECK(!pcr_restrictions.empty()); |
| // Only the |kRsassaPkcs1V15Sha1| algorithm is supported. |
| if (std::find(key_algorithms.begin(), key_algorithms.end(), |
| CHALLENGE_RSASSA_PKCS1_V1_5_SHA1) == key_algorithms.end()) { |
| LOG(ERROR) << "The key doesn't support RSASSA-PKCS1-v1_5 with SHA-1"; |
| return false; |
| } |
| // Obtain the TPM context and handle with the required authorization. |
| ScopedTssContext tpm_context; |
| TSS_HTPM tpm_handle = 0; |
| if (!tpm_->ConnectContextAsDelegate(delegate_blob, delegate_secret, |
| tpm_context.ptr(), &tpm_handle)) { |
| LOG(ERROR) << "Failed to connect to the TPM"; |
| return false; |
| } |
| // Load the protection public key into Trousers. Obtain its TPM_PUBKEY blob |
| // and build the blob of the TPM_MSA_COMPOSITE structure containing a sole |
| // reference to this key. |
| int protection_key_size_bits = 0; |
| ScopedTssKey protection_key_handle(tpm_context); |
| if (!ParseAndLoadProtectionKey(tpm_, tpm_context, public_key_spki_der, |
| &protection_key_size_bits, |
| protection_key_handle.ptr())) { |
| LOG(ERROR) << "Failed to load the protection public key"; |
| return false; |
| } |
| SecureBlob protection_key_pubkey; |
| if (tpm_->GetDataAttribute(tpm_context, protection_key_handle, |
| TSS_TSPATTRIB_KEY_BLOB, |
| TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, |
| &protection_key_pubkey) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) << "Failed to read the protection public key"; |
| return false; |
| } |
| const Blob protection_key_pubkey_digest = CryptoLib::Sha1( |
| Blob(protection_key_pubkey.begin(), protection_key_pubkey.end())); |
| const Blob msa_composite_digest = |
| BuildMsaCompositeDigest(protection_key_pubkey_digest); |
| // Obtain the migration authority approval ticket for the TPM_MSA_COMPOSITE |
| // structure. |
| Blob ma_approval_ticket; |
| if (!ObtainMaApprovalTicket(tpm_, tpm_context, tpm_handle, |
| msa_composite_digest, &ma_approval_ticket)) { |
| LOG(ERROR) << "Failed to obtain the migration authority approval ticket"; |
| return false; |
| } |
| // Load the SRK. |
| ScopedTssKey srk_handle(tpm_context); |
| TSS_RESULT tss_result = TSS_SUCCESS; |
| if (!tpm_->LoadSrk(tpm_context, srk_handle.ptr(), &tss_result)) { |
| LOG(ERROR) << "Failed to load the SRK: " |
| << FormatTrousersErrorCode(tss_result); |
| return false; |
| } |
| // Generate the Certified Migratable Key, associated with the protection |
| // public key (via the TPM_MSA_COMPOSITE digest). Obtain the resulting wrapped |
| // CMK blob and the TPM_PUBKEY blob. |
| Blob cmk_pubkey; |
| Blob srk_wrapped_cmk; |
| if (!GenerateCmk(tpm_, tpm_context, tpm_handle, srk_handle, |
| msa_composite_digest, ma_approval_ticket, &cmk_pubkey, |
| &srk_wrapped_cmk)) { |
| LOG(ERROR) << "Failed to generate the certified migratable key"; |
| return false; |
| } |
| // Generate the AuthData value randomly. |
| SecureBlob auth_data; |
| if (!tpm_->GetRandomDataSecureBlob(kAuthDataSizeBytes, &auth_data)) { |
| LOG(ERROR) << "Failed to generate the authorization data"; |
| return false; |
| } |
| DCHECK_EQ(auth_data.size(), kAuthDataSizeBytes); |
| // Encrypt the AuthData value. |
| crypto::ScopedRSA cmk_rsa = ParseRsaFromTpmPubkeyBlob(cmk_pubkey); |
| if (!cmk_rsa) { |
| LOG(ERROR) << "Failed to create OpenSSL public key object for the " |
| "certified migratable key"; |
| return false; |
| } |
| Blob cmk_wrapped_auth_data; |
| if (!CryptoLib::RsaOaepEncrypt(auth_data, cmk_rsa.get(), |
| &cmk_wrapped_auth_data)) { |
| LOG(ERROR) << "Failed to encrypt authorization data"; |
| return false; |
| } |
| // Generate the secret value randomly. |
| if (!tpm_->GetRandomDataSecureBlob(kSecretSizeBytes, secret_value)) { |
| LOG(ERROR) << "Error generating random secret"; |
| return false; |
| } |
| DCHECK_EQ(secret_value->size(), kSecretSizeBytes); |
| // Bind the secret value to each of the specified sets of PCR restrictions. |
| DCHECK(!pcr_restrictions.empty()); |
| std::vector<Blob> pcr_bound_secret_values; |
| for (const auto& pcr_values : pcr_restrictions) { |
| DCHECK(!pcr_values.empty()); |
| // Bind the secret value to the current set of PCR restrictions. |
| SecureBlob pcr_bound_secret_value; |
| std::map<uint32_t, std::string> pcr_values_strings; |
| for (const auto& pcr_index_and_value : pcr_values) { |
| pcr_values_strings[pcr_index_and_value.first] = |
| BlobToString(pcr_index_and_value.second); |
| } |
| if (tpm_->SealToPcrWithAuthorization( |
| kInvalidKeyHandle /* key_handle */, *secret_value, auth_data, |
| pcr_values_strings, |
| &pcr_bound_secret_value) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) |
| << "Error binding the secret value to PCRs and authorization data"; |
| return false; |
| } |
| pcr_bound_secret_values.push_back( |
| Blob(pcr_bound_secret_value.begin(), pcr_bound_secret_value.end())); |
| } |
| // Fill the resulting proto with data required for unsealing. |
| sealed_secret_data->Clear(); |
| SignatureSealedData_Tpm12CertifiedMigratableKeyData* const data_proto = |
| sealed_secret_data->mutable_tpm12_certified_migratable_key_data(); |
| data_proto->set_public_key_spki_der(BlobToString(public_key_spki_der)); |
| data_proto->set_srk_wrapped_cmk(BlobToString(srk_wrapped_cmk)); |
| data_proto->set_cmk_pubkey(BlobToString(cmk_pubkey)); |
| data_proto->set_cmk_wrapped_auth_data(BlobToString(cmk_wrapped_auth_data)); |
| DCHECK_EQ(pcr_restrictions.size(), pcr_bound_secret_values.size()); |
| for (size_t restriction_index = 0; |
| restriction_index < pcr_restrictions.size(); ++restriction_index) { |
| const auto& pcr_values = pcr_restrictions[restriction_index]; |
| SignatureSealedData_Tpm12PcrBoundItem* const pcr_bound_item_proto = |
| data_proto->add_pcr_bound_items(); |
| for (const auto& pcr_index_and_value : pcr_values) { |
| SignatureSealedData_PcrValue* const pcr_value_proto = |
| pcr_bound_item_proto->add_pcr_values(); |
| pcr_value_proto->set_pcr_index(pcr_index_and_value.first); |
| pcr_value_proto->set_pcr_value(BlobToString(pcr_index_and_value.second)); |
| } |
| pcr_bound_item_proto->set_bound_secret( |
| BlobToString(pcr_bound_secret_values[restriction_index])); |
| } |
| return true; |
| } |
| |
| std::unique_ptr<SignatureSealingBackend::UnsealingSession> |
| SignatureSealingBackendTpm1Impl::CreateUnsealingSession( |
| const SignatureSealedData& sealed_secret_data, |
| const Blob& public_key_spki_der, |
| const std::vector<ChallengeSignatureAlgorithm>& key_algorithms, |
| const Blob& delegate_blob, |
| const Blob& delegate_secret) { |
| // Validate the parameters. |
| if (!sealed_secret_data.has_tpm12_certified_migratable_key_data()) { |
| LOG(ERROR) << "Sealed data is empty or uses unexpected method"; |
| return nullptr; |
| } |
| const SignatureSealedData_Tpm12CertifiedMigratableKeyData& data_proto = |
| sealed_secret_data.tpm12_certified_migratable_key_data(); |
| if (data_proto.public_key_spki_der() != BlobToString(public_key_spki_der)) { |
| LOG(ERROR) << "Wrong subject public key info"; |
| return nullptr; |
| } |
| if (std::find(key_algorithms.begin(), key_algorithms.end(), |
| CHALLENGE_RSASSA_PKCS1_V1_5_SHA1) == key_algorithms.end()) { |
| LOG(ERROR) << "Failed to choose the algorithm: the key doesn't support " |
| "RSASSA-PKCS1-v1_5 with SHA-1"; |
| return nullptr; |
| } |
| // Determine the satisfied set of PCR restrictions. |
| const SignatureSealedData_Tpm12PcrBoundItem* const |
| satisfied_pcr_bound_item_proto = |
| GetSatisfiedPcrRestriction(data_proto.pcr_bound_items(), tpm_); |
| if (!satisfied_pcr_bound_item_proto) { |
| LOG(ERROR) << "None of PCR restrictions is satisfied"; |
| return nullptr; |
| } |
| // Obtain the TPM context and handle with the required authorization. |
| ScopedTssContext tpm_context; |
| TSS_HTPM tpm_handle = 0; |
| if (!tpm_->ConnectContextAsDelegate(delegate_blob, delegate_secret, |
| tpm_context.ptr(), &tpm_handle)) { |
| LOG(ERROR) << "Failed to connect to the TPM"; |
| return nullptr; |
| } |
| // Obtain the TPM_PUBKEY blob for the protection key. |
| int protection_key_size_bits = 0; |
| ScopedTssKey protection_key_handle(tpm_context); |
| if (!ParseAndLoadProtectionKey(tpm_, tpm_context, public_key_spki_der, |
| &protection_key_size_bits, |
| protection_key_handle.ptr())) { |
| LOG(ERROR) << "Failed to load the protection public key"; |
| return nullptr; |
| } |
| SecureBlob protection_key_pubkey; |
| if (tpm_->GetDataAttribute(tpm_context, protection_key_handle, |
| TSS_TSPATTRIB_KEY_BLOB, |
| TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, |
| &protection_key_pubkey) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) << "Failed to read the protection public key"; |
| return nullptr; |
| } |
| // Generate the migration destination RSA key. Onto this key the CMK private |
| // key will be migrated; to complete the unsealing, the decryption operation |
| // using the migration destination key will be performed. The security |
| // properties of the migration destination key aren't crucial, besides the |
| // reasonable amount of entropy, therefore generating using OpenSSL is fine. |
| // |
| // TODO(crbug.com/909700): Do the RSA key generation in background in advance. |
| crypto::ScopedRSA migration_destination_rsa(RSA_new()); |
| crypto::ScopedBIGNUM e(BN_new()); |
| if (!migration_destination_rsa || !e) { |
| LOG(ERROR) << "Failed to allocate the migration destination key"; |
| return nullptr; |
| } |
| if (!BN_set_word(e.get(), kWellKnownExponent) || |
| !RSA_generate_key_ex(migration_destination_rsa.get(), |
| kMigrationDestinationKeySizeBits, e.get(), |
| nullptr)) { |
| LOG(ERROR) << "Failed to generate the migration destination key"; |
| return nullptr; |
| } |
| // Obtain the TPM_PUBKEY blob for the migration destination key. |
| ScopedTssKey migration_destination_key_handle(tpm_context); |
| if (!LoadMigrationDestinationPublicKey( |
| tpm_, tpm_context, *migration_destination_rsa, |
| migration_destination_key_handle.ptr())) { |
| LOG(ERROR) << "Failed to load the migration destination key"; |
| return nullptr; |
| } |
| SecureBlob migration_destination_key_pubkey; |
| if (tpm_->GetDataAttribute( |
| tpm_context, migration_destination_key_handle, TSS_TSPATTRIB_KEY_BLOB, |
| TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, |
| &migration_destination_key_pubkey) != Tpm::kTpmRetryNone) { |
| LOG(ERROR) << "Failed to read the migration destination public key"; |
| return nullptr; |
| } |
| return std::make_unique<UnsealingSessionTpm1Impl>( |
| tpm_, BlobFromString(data_proto.srk_wrapped_cmk()), |
| BlobFromString(data_proto.cmk_wrapped_auth_data()), |
| BlobFromString(satisfied_pcr_bound_item_proto->bound_secret()), |
| public_key_spki_der, delegate_blob, delegate_secret, |
| BlobFromString(data_proto.cmk_pubkey()), |
| Blob(protection_key_pubkey.begin(), protection_key_pubkey.end()), |
| std::move(migration_destination_rsa), |
| Blob(migration_destination_key_pubkey.begin(), |
| migration_destination_key_pubkey.end())); |
| } |
| |
| crypto::ScopedRSA ExtractCmkPrivateKeyFromMigratedBlob( |
| const Blob& migrated_cmk_key12_blob, |
| const Blob& migration_random_blob, |
| const Blob& cmk_pubkey, |
| const Blob& cmk_pubkey_digest, |
| const Blob& msa_composite_digest, |
| RSA* migration_destination_rsa) { |
| // Load the encrypted TPM_MIGRATE_ASYMKEY blob from the TPM_KEY12 blob. |
| // Note that this encrypted TPM_MIGRATE_ASYMKEY blob was generated by taking |
| // the TPM_MIGRATE_ASYMKEY blob, applying the RSA OAEP *encoding* (not |
| // encryption), XOR'ing it with the migration random XOR-mask, applying the |
| // RSA OAEP *encryption* (not encoding). We'll unwind this to obtain the |
| // original TPM_MIGRATE_ASYMKEY blob below. |
| Blob encrypted_tpm_migrate_asymkey_blob; |
| if (!ParseEncDataFromKey12Blob(migrated_cmk_key12_blob, |
| &encrypted_tpm_migrate_asymkey_blob)) { |
| LOG(ERROR) << "Failed to parse the encrypted TPM_MIGRATE_ASYMKEY blob from " |
| "the TPM_KEY12 blob"; |
| return nullptr; |
| } |
| if (encrypted_tpm_migrate_asymkey_blob.size() != |
| kMigrationDestinationKeySizeBytes) { |
| LOG(ERROR) << "Failed to parse the encrypted TPM_MIGRATE_ASYMKEY blob due " |
| "to size mismatch"; |
| return nullptr; |
| } |
| // Perform the RSA OAEP decryption of the encrypted TPM_MIGRATE_ASYMKEY blob, |
| // using the custom OAEP label parameter as prescribed by the TPM 1.2 specs. |
| SecureBlob decrypted_tpm_migrate_asymkey_blob; |
| if (!CryptoLib::RsaOaepDecrypt( |
| SecureBlob(encrypted_tpm_migrate_asymkey_blob), |
| SecureBlob(std::begin(kTpmRsaOaepLabel), std::end(kTpmRsaOaepLabel)), |
| migration_destination_rsa, &decrypted_tpm_migrate_asymkey_blob)) { |
| LOG(ERROR) |
| << "Failed to RSA-decrypt the encrypted TPM_MIGRATE_ASYMKEY blob"; |
| return nullptr; |
| } |
| if (decrypted_tpm_migrate_asymkey_blob.size() != |
| migration_random_blob.size()) { |
| LOG(ERROR) |
| << "Failed to decrypt TPM_MIGRATE_ASYMKEY blob due to size mismatch"; |
| return nullptr; |
| } |
| // XOR the decrypted TPM_MIGRATE_ASYMKEY blob with the migration random |
| // XOR-mask. |
| DCHECK_EQ(decrypted_tpm_migrate_asymkey_blob.size(), |
| migration_random_blob.size()); |
| Blob xored_decrypted_tpm_migrate_asymkey_blob( |
| decrypted_tpm_migrate_asymkey_blob.begin(), |
| decrypted_tpm_migrate_asymkey_blob.end()); |
| XorBytes(xored_decrypted_tpm_migrate_asymkey_blob.data(), |
| migration_random_blob.data(), |
| xored_decrypted_tpm_migrate_asymkey_blob.size()); |
| // Perform the RSA OAEP decoding (not decryption) of the XOR'ed decrypted |
| // TPM_MIGRATE_ASYMKEY blob. |
| // The OAEP label parameter is equal to concatenation of |
| // |msa_composite_digest| and |cmk_pubkey_digest|. |
| // The OAEP seed parameter is extracted as well, because it contains a part of |
| // the private key data. |
| // Note that our own implementation of OAEP decoding is used instead of the |
| // OpenSSL's one, as the latter doesn't return the decoded seed. |
| const Blob tpm_migrate_asymkey_oaep_label_blob = |
| CombineBlobs({msa_composite_digest, cmk_pubkey_digest}); |
| SecureBlob tpm_migrate_asymkey_oaep_seed_blob; |
| SecureBlob tpm_migrate_asymkey_blob; |
| if (!DecodeOaepMgf1Encoding( |
| xored_decrypted_tpm_migrate_asymkey_blob, kTpmMigrateAsymkeyBlobSize, |
| tpm_migrate_asymkey_oaep_label_blob, |
| &tpm_migrate_asymkey_oaep_seed_blob, &tpm_migrate_asymkey_blob)) { |
| LOG(ERROR) << "Failed to perform RSA OAEP decoding of the XOR'ed decrypted " |
| "TPM_MIGRATE_ASYMKEY blob"; |
| return nullptr; |
| } |
| // Parse the resulting CMK's secret prime from the TPM_MIGRATE_ASYMKEY blob |
| // and the seed blob. |
| SecureBlob cmk_secret_prime; |
| if (!ParseRsaSecretPrimeFromTpmMigrateAsymkeyBlob( |
| tpm_migrate_asymkey_blob, tpm_migrate_asymkey_oaep_seed_blob, |
| &cmk_secret_prime)) { |
| LOG(ERROR) |
| << "Failed to parse the private key from the TPM_MIGRATE_ASYMKEY blob"; |
| return nullptr; |
| } |
| DCHECK_EQ(kCmkPrivateKeySizeBytes, cmk_secret_prime.size()); |
| // Build the OpenSSL RSA structure holding the private key. |
| crypto::ScopedRSA cmk_rsa = ParseRsaFromTpmPubkeyBlob(cmk_pubkey); |
| if (!cmk_rsa) { |
| LOG(ERROR) << "Failed to create OpenSSL public key object for the " |
| "certified migratable key"; |
| return nullptr; |
| } |
| if (!CryptoLib::FillRsaPrivateKeyFromSecretPrime(cmk_secret_prime, |
| cmk_rsa.get())) { |
| LOG(ERROR) << "Failed to create OpenSSL private key object for the " |
| "certified migratable key"; |
| return nullptr; |
| } |
| return cmk_rsa; |
| } |
| |
| } // namespace cryptohome |