blob: 42f925fe54335220f095a77600a635c84e41919c [file] [log] [blame]
// Copyright 2019 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 "tpm_manager/server/local_data_migration.h"
#include <attestation/proto_bindings/attestation_ca.pb.h>
#include <base/check.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <brillo/secure_blob.h>
#include <crypto/scoped_openssl_types.h>
#include <crypto/secure_util.h>
#include <libtpmcrypto/tpm.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <algorithm>
#include <limits>
#include <memory>
#include "tpm_manager/proto_bindings/tpm_manager.pb.h"
namespace {
constexpr size_t kAesBlockSize = 16;
// The following few functions are simplied to minimum from
// "attestation/common/crypto_utility_impl.cc" so we can decrypt the encrypted
// database. Modifications are also made in order to replace |std::string| in
// the function signature and get rid of reinterpret_cast.
//
// TODO(cylai): replace this with calls to commmon library once we have a
// library shared across all hwsec daemons.
std::string GetOpenSSLError() {
BIO* bio = BIO_new(BIO_s_mem());
ERR_print_errors(bio);
char* data = nullptr;
int data_len = BIO_get_mem_data(bio, &data);
std::string error_string(data, data_len);
BIO_free(bio);
return error_string;
}
unsigned char* SecureBlobAsSSLBuffer(const brillo::SecureBlob& blob) {
return static_cast<unsigned char*>(
const_cast<brillo::SecureBlob::value_type*>(blob.data()));
}
brillo::SecureBlob HmacSha512(const brillo::SecureBlob& key,
const brillo::SecureBlob& data) {
brillo::SecureBlob mac;
mac.resize(SHA512_DIGEST_LENGTH);
HMAC(EVP_sha512(), SecureBlobAsSSLBuffer(key), key.size(),
SecureBlobAsSSLBuffer(data), data.size(), mac.data(), nullptr);
return mac;
}
// Decrypts |encrypted_data| using |key|, |iv|, and the given |cipher|.
// Returns true on success.
bool AesDecrypt(const EVP_CIPHER* cipher,
const brillo::SecureBlob& encrypted_data,
const brillo::SecureBlob& key,
const brillo::SecureBlob& iv,
brillo::SecureBlob* data) {
if (key.size() != static_cast<size_t>(EVP_CIPHER_key_length(cipher)) ||
iv.size() != kAesBlockSize) {
return false;
}
if (encrypted_data.size() >
static_cast<size_t>(std::numeric_limits<int>::max())) {
// EVP_DecryptUpdate takes a signed int.
return false;
}
unsigned char* input_buffer = SecureBlobAsSSLBuffer(encrypted_data);
unsigned char* key_buffer = SecureBlobAsSSLBuffer(key);
unsigned char* iv_buffer = SecureBlobAsSSLBuffer(iv);
// Allocate enough space for the output.
data->resize(encrypted_data.size());
unsigned char* output_buffer = SecureBlobAsSSLBuffer(*data);
int output_size = 0;
crypto::ScopedEVP_CIPHER_CTX decryption_context(EVP_CIPHER_CTX_new());
if (!decryption_context) {
LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
return false;
}
if (!EVP_DecryptInit_ex(decryption_context.get(), cipher, nullptr, key_buffer,
iv_buffer)) {
LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
return false;
}
if (!EVP_DecryptUpdate(decryption_context.get(), output_buffer, &output_size,
input_buffer, encrypted_data.size())) {
LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
return false;
}
size_t total_size = output_size;
output_buffer += output_size;
output_size = 0;
if (!EVP_DecryptFinal_ex(decryption_context.get(), output_buffer,
&output_size)) {
LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
return false;
}
total_size += output_size;
data->resize(total_size);
return true;
}
// Decrypts |input| using |key|. On success populates
// |decrypted| and returns |true|.
bool Decrypt(const attestation::EncryptedData& input,
const brillo::SecureBlob& key,
brillo::SecureBlob* decrypted) {
const brillo::SecureBlob expected_mac =
HmacSha512(key, brillo::SecureBlob(input.iv() + input.encrypted_data()));
if (expected_mac.size() != input.mac().length()) {
LOG(ERROR) << __func__ << ": MAC length mismatch." << ' '
<< expected_mac.size() << ' ' << input.mac().length();
return false;
}
if (!crypto::SecureMemEqual(expected_mac.data(), input.mac().data(),
input.mac().length())) {
LOG(ERROR) << __func__ << ": MAC mismatch.";
return false;
}
if (!AesDecrypt(EVP_aes_256_cbc(), brillo::SecureBlob(input.encrypted_data()),
key, brillo::SecureBlob(input.iv()), decrypted)) {
return false;
}
return true;
}
// Parses and decrypts |encrypted_database| into a |LegacyAttestationDatabase|.
// |tpm| is used to unseal the wrapped key in the |encrypted_database|.
bool DecryptAttestationDatabase(
const brillo::SecureBlob& encrypted_database,
tpmcrypto::Tpm* tpm,
tpm_manager::LegacyAttestationDatabase* database) {
attestation::EncryptedData encrypted_data;
if (!encrypted_data.ParseFromString(encrypted_database.to_string())) {
LOG(ERROR) << __func__
<< ": Failed to parse data into |EncryptedData| message.";
return false;
}
brillo::SecureBlob key;
if (!tpm->Unseal(brillo::SecureBlob(encrypted_data.wrapped_key()), &key)) {
LOG(ERROR) << __func__ << ": Failed to unseal the encrypting key.";
return false;
}
brillo::SecureBlob decrypted_database_blob;
if (!Decrypt(encrypted_data, key, &decrypted_database_blob)) {
LOG(ERROR) << __func__ << ": Failed to decrypt the database blob.";
return false;
}
if (!database->ParseFromArray(decrypted_database_blob.data(),
decrypted_database_blob.size())) {
LOG(ERROR) << __func__ << ": Failed to parse attestation database.";
return false;
}
return true;
}
tpm_manager::AuthDelegate LegacyDelegationToAuthDelegate(
const tpm_manager::LegacyDelegation& old_delegate) {
tpm_manager::AuthDelegate new_delegate;
new_delegate.mutable_blob()->assign(old_delegate.blob());
new_delegate.mutable_secret()->assign(old_delegate.secret());
new_delegate.set_has_reset_lock_permissions(
old_delegate.has_reset_lock_permissions());
return new_delegate;
}
} // namespace
namespace tpm_manager {
bool MigrateAuthDelegate(const brillo::SecureBlob& sealed_database,
tpmcrypto::Tpm* tpm,
tpm_manager::AuthDelegate* delegate) {
CHECK(delegate != nullptr);
CHECK(tpm != nullptr);
LegacyAttestationDatabase old_database;
if (!DecryptAttestationDatabase(sealed_database, tpm, &old_database)) {
LOG(ERROR) << __func__ << ":Failed to unseal attestation database.";
return false;
}
*delegate = LegacyDelegationToAuthDelegate(old_database.delegate());
return true;
}
bool UnsealOwnerPasswordFromSerializedTpmStatus(
const brillo::SecureBlob& serialized_tpm_status,
tpmcrypto::Tpm* tpm,
brillo::SecureBlob* owner_password) {
CHECK(owner_password != nullptr);
CHECK(tpm != nullptr);
LegacyTpmStatus tpm_status;
if (!tpm_status.ParseFromArray(serialized_tpm_status.data(),
serialized_tpm_status.size())) {
LOG(ERROR) << __func__ << ": Failed to parse tpm status.";
return false;
}
if (!tpm_status.owner_password().empty() &&
!tpm->Unseal(brillo::SecureBlob(tpm_status.owner_password()),
owner_password)) {
LOG(ERROR) << __func__ << ": Failed to unseal owner password.";
return false;
}
return true;
}
bool LocalDataMigrator::MigrateAuthDelegateIfNeeded(
const base::FilePath& database_path,
tpmcrypto::Tpm* tpm,
LocalData* local_data,
bool* has_migrated) {
CHECK(tpm != nullptr);
CHECK(local_data != nullptr);
CHECK(has_migrated != nullptr);
*has_migrated = false;
auto auth_delegate = local_data->mutable_owner_delegate();
if ((!auth_delegate->blob().empty() && !auth_delegate->secret().empty()) ||
!PathExists(database_path)) {
return true;
}
std::string sealed_database;
if (!ReadFileToString(database_path, &sealed_database)) {
LOG(ERROR) << __func__ << ": Failed to read attestation database from "
<< database_path.value();
return false;
}
if (!MigrateAuthDelegate(brillo::SecureBlob(sealed_database), tpm,
auth_delegate)) {
LOG(ERROR) << __func__ << ": Failed to migrate auth delegate from "
<< database_path.value();
return false;
}
*has_migrated = !local_data->owner_delegate().blob().empty() &&
!local_data->owner_delegate().secret().empty();
return true;
}
bool LocalDataMigrator::MigrateOwnerPasswordIfNeeded(
const base::FilePath& tpm_status_path,
tpmcrypto::Tpm* tpm,
LocalData* local_data,
bool* has_migrated) {
CHECK(local_data != nullptr);
CHECK(has_migrated != nullptr);
*has_migrated = false;
if (!local_data->owner_password().empty() || !PathExists(tpm_status_path)) {
return true;
}
std::string serialized_tpm_status;
if (!ReadFileToString(tpm_status_path, &serialized_tpm_status)) {
LOG(ERROR) << __func__ << ": Failed to read tpm status from "
<< tpm_status_path.value();
return false;
}
brillo::SecureBlob owner_password;
if (!UnsealOwnerPasswordFromSerializedTpmStatus(
brillo::SecureBlob(serialized_tpm_status), tpm, &owner_password)) {
LOG(ERROR) << __func__ << ": Failed to parse tpm status from "
<< tpm_status_path.value();
return false;
}
local_data->set_owner_password(owner_password.data(), owner_password.size());
*has_migrated = !local_data->owner_password().empty();
return true;
}
bool LocalDataMigrator::PathExists(const base::FilePath& path) {
return base::PathExists(path);
}
bool LocalDataMigrator::ReadFileToString(const base::FilePath& path,
std::string* content) {
return base::ReadFileToString(path, content);
}
} // namespace tpm_manager