blob: 3845212c50255a14803dfdaa1c08de8402a27999 [file] [log] [blame]
//
// 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 <vector>
#include <android-base/logging.h>
#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<TpmObjectSlot(TpmResourceManager&)> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<const uint8_t **>(&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;
}