// // Copyright (C) 2020 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "encrypted_serializable.h" #include #include #include "host/commands/secure_env/tpm_auth.h" #include "host/commands/secure_env/tpm_encrypt_decrypt.h" #include "host/commands/secure_env/tpm_serialize.h" EncryptedSerializable::EncryptedSerializable( TpmResourceManager* resource_manager, std::function parent_key_fn, Serializable* wrapped) : resource_manager_(resource_manager), parent_key_fn_(parent_key_fn), wrapped_(wrapped) { } static bool CreateKey( TpmResourceManager* resource_manager, // in ESYS_TR parent_key, // in TPM2B_PUBLIC* key_public_out, // out TPM2B_PRIVATE* key_private_out, // out TpmObjectSlot* key_slot_out) { // out TPM2B_AUTH authValue = {}; auto rc = Esys_TR_SetAuth(resource_manager->Esys(), parent_key, &authValue); if (rc != TSS2_RC_SUCCESS) { LOG(ERROR) << "Esys_TR_SetAuth failed with return code " << rc << " (" << Tss2_RC_Decode(rc) << ")"; return false; } TPMT_PUBLIC public_area = { .type = TPM2_ALG_SYMCIPHER, .nameAlg = TPM2_ALG_SHA256, .objectAttributes = (TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_DECRYPT | TPMA_OBJECT_SIGN_ENCRYPT | TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_SENSITIVEDATAORIGIN), .authPolicy.size = 0, .parameters.symDetail.sym = { .algorithm = TPM2_ALG_AES, .keyBits.aes = 128, // The default maximum AES key size in the simulator. .mode.aes = TPM2_ALG_CFB, }, }; TPM2B_TEMPLATE public_template = {}; size_t offset = 0; rc = Tss2_MU_TPMT_PUBLIC_Marshal(&public_area, &public_template.buffer[0], sizeof(public_template.buffer), &offset); if (rc != TSS2_RC_SUCCESS) { LOG(ERROR) << "Tss2_MU_TPMT_PUBLIC_Marshal failed with return code " << rc << " (" << Tss2_RC_Decode(rc) << ")"; return false; } public_template.size = offset; TPM2B_SENSITIVE_CREATE in_sensitive = {}; auto key_slot = resource_manager->ReserveSlot(); if (!key_slot) { LOG(ERROR) << "No slots available"; return false; } ESYS_TR raw_handle; // TODO(b/154956668): Define better ACLs on these keys. TPM2B_PUBLIC* key_public = nullptr; TPM2B_PRIVATE* key_private = nullptr; // TODO(schuffelen): Use Esys_Create when key_slot is NULL rc = Esys_CreateLoaded( /* esysContext */ resource_manager->Esys(), /* primaryHandle */ parent_key, /* shandle1 */ ESYS_TR_PASSWORD, /* shandle2 */ ESYS_TR_NONE, /* shandle3 */ ESYS_TR_NONE, /* inSensitive */ &in_sensitive, /* inPublic */ &public_template, /* objectHandle */ &raw_handle, /* outPrivate */ &key_private, /* outPublic */ &key_public); if (rc != TSS2_RC_SUCCESS) { LOG(ERROR) << "Esys_CreateLoaded failed with return code " << rc << " (" << Tss2_RC_Decode(rc) << ")"; return false; } CHECK(key_public != nullptr) << "key_public was not assigned."; CHECK(key_private != nullptr) << "key_private was not assigned."; *key_public_out = *key_public; *key_private_out = *key_private; key_slot->set(raw_handle); Esys_Free(key_public); Esys_Free(key_private); if (key_slot_out) { rc = Esys_TR_SetAuth(resource_manager->Esys(), raw_handle, &authValue); if (rc != TSS2_RC_SUCCESS) { LOG(ERROR) << "Esys_TR_SetAuth failed with return code " << rc << " (" << Tss2_RC_Decode(rc) << ")"; return false; } } if (key_slot_out) { *key_slot_out = key_slot; } return true; } static TpmObjectSlot LoadKey( TpmResourceManager* resource_manager, ESYS_TR parent_key, const TPM2B_PUBLIC* key_public, const TPM2B_PRIVATE* key_private) { // TODO ESYS_TR raw_handle; auto key_slot = resource_manager->ReserveSlot(); if (!key_slot) { LOG(ERROR) << "No slots available"; return {}; } auto rc = Esys_Load( resource_manager->Esys(), parent_key, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, key_private, key_public, &raw_handle); if (rc != TSS2_RC_SUCCESS) { LOG(ERROR) << "Esys_Load failed with return code " << rc << " (" << Tss2_RC_Decode(rc) << ")"; return {}; } key_slot->set(raw_handle); return key_slot; } static constexpr uint32_t BLOCK_SIZE = 16; static uint32_t RoundUpToBlockSize(uint32_t num) { return num % BLOCK_SIZE == 0 ? num : num + (BLOCK_SIZE - (num % BLOCK_SIZE)); } size_t EncryptedSerializable::SerializedSize() const { TPM2B_PUBLIC key_public; TPM2B_PRIVATE key_private; auto parent = parent_key_fn_(resource_manager_); if (!CreateKey( resource_manager_, parent->get(), &key_public, &key_private, nullptr)) { LOG(ERROR) << "Unable to create key"; return 0; } // Assumes all created keys will have the same size. SerializeTpmKeyPublic serialize_public(&key_public); SerializeTpmKeyPrivate serialize_private(&key_private); auto encrypted_size = RoundUpToBlockSize(wrapped_->SerializedSize()); return serialize_public.SerializedSize() + serialize_private.SerializedSize() + sizeof(uint32_t) + sizeof(uint32_t) + encrypted_size; } uint8_t* EncryptedSerializable::Serialize( uint8_t* buf, const uint8_t* end) const { TPM2B_PUBLIC key_public; TPM2B_PRIVATE key_private; auto parent = parent_key_fn_(resource_manager_); if (!parent) { LOG(ERROR) << "Unable to load encryption parent key"; return buf; } TpmObjectSlot key_slot; if (!CreateKey( resource_manager_, parent->get(), &key_public, &key_private, &key_slot)) { LOG(ERROR) << "Unable to create key"; return buf; } auto wrapped_size = wrapped_->SerializedSize(); auto encrypted_size = RoundUpToBlockSize(wrapped_size); std::vector unencrypted(encrypted_size + 1, 0); auto unencrypted_buf = unencrypted.data(); auto unencrypted_buf_end = unencrypted_buf + unencrypted.size(); auto next_buf = wrapped_->Serialize(unencrypted_buf, unencrypted_buf_end); if (next_buf - unencrypted_buf != wrapped_size) { LOG(ERROR) << "Size mismatch on wrapped data"; return buf; } std::vector encrypted(encrypted_size, 0); if (!TpmEncrypt( resource_manager_->Esys(), key_slot->get(), TpmAuth(ESYS_TR_PASSWORD), unencrypted.data(), encrypted.data(), encrypted_size)) { LOG(ERROR) << "Encryption failed"; return buf; } SerializeTpmKeyPublic serialize_public(&key_public); SerializeTpmKeyPrivate serialize_private(&key_private); buf = serialize_public.Serialize(buf, end); buf = serialize_private.Serialize(buf, end); buf = keymaster::append_uint32_to_buf(buf, end, BLOCK_SIZE); buf = keymaster::append_uint32_to_buf(buf, end, wrapped_size); buf = keymaster::append_to_buf(buf, end, encrypted.data(), encrypted_size); return buf; } bool EncryptedSerializable::Deserialize( const uint8_t** buf_ptr, const uint8_t* end) { auto parent_key = parent_key_fn_(resource_manager_); if (!parent_key) { LOG(ERROR) << "Unable to load encryption parent key"; return false; } TPM2B_PUBLIC key_public; SerializeTpmKeyPublic serialize_public(&key_public); if (!serialize_public.Deserialize(buf_ptr, end)) { LOG(ERROR) << "Unable to deserialize key public part"; return false; } TPM2B_PRIVATE key_private; SerializeTpmKeyPrivate serialize_private(&key_private); if (!serialize_private.Deserialize(buf_ptr, end)) { LOG(ERROR) << "Unable to deserialize key private part"; return false; } auto key_slot = LoadKey(resource_manager_, parent_key->get(), &key_public, &key_private); if (!key_slot) { LOG(ERROR) << "Failed to load key into TPM"; return false; } uint32_t block_size = 0; if (!keymaster::copy_uint32_from_buf(buf_ptr, end, &block_size)) { LOG(ERROR) << "Failed to read block size"; return false; } if (block_size != BLOCK_SIZE) { LOG(ERROR) << "Unexpected block size: was " << block_size << ", expected " << BLOCK_SIZE; return false; } uint32_t wrapped_size = 0; if (!keymaster::copy_uint32_from_buf(buf_ptr, end, &wrapped_size)) { LOG(ERROR) << "Failed to read wrapped size"; return false; } uint32_t encrypted_size = RoundUpToBlockSize(wrapped_size); std::vector encrypted_data(encrypted_size, 0); if (!keymaster::copy_from_buf( buf_ptr, end, encrypted_data.data(), encrypted_size)) { LOG(ERROR) << "Failed to read encrypted data"; return false; } std::vector decrypted_data(encrypted_size, 0); if (!TpmDecrypt( resource_manager_->Esys(), key_slot->get(), TpmAuth(ESYS_TR_PASSWORD), encrypted_data.data(), decrypted_data.data(), encrypted_size)) { LOG(ERROR) << "Failed to decrypt encrypted data"; return false; } auto decrypted_buf = decrypted_data.data(); auto decrypted_buf_end = decrypted_data.data() + wrapped_size; if (!wrapped_->Deserialize( const_cast(&decrypted_buf), decrypted_buf_end)) { LOG(ERROR) << "Failed to deserialize wrapped type"; return false; } if (decrypted_buf != decrypted_buf_end) { LOG(ERROR) << "Inner type did not use all data"; return false; } return true; }