blob: ea4f44a4b638fb9ba9dc2d71e1122c89ac39c003 [file] [log] [blame]
// 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/bootlockbox/nvram_boot_lockbox.h"
#include <string>
#include <vector>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <brillo/file_utils.h>
#include <crypto/secure_hash.h>
#include <crypto/sha2.h>
#include <trunks/tpm_constants.h>
#include <trunks/tpm_utility.h>
#include "cryptohome/bootlockbox/tpm2_nvspace_utility.h"
#include "cryptohome/bootlockbox/tpm_nvspace_interface.h"
#include "cryptohome/cryptolib.h"
#include "cryptohome/platform.h"
namespace cryptohome {
NVRamBootLockbox::NVRamBootLockbox(
TPMNVSpaceUtilityInterface* tpm_nvspace_utility)
: boot_lockbox_filepath_(base::FilePath(kNVRamBootLockboxFilePath)),
tpm_nvspace_utility_(tpm_nvspace_utility) {}
NVRamBootLockbox::NVRamBootLockbox(
TPMNVSpaceUtilityInterface* tpm_nvspace_utility,
const base::FilePath& bootlockbox_file_path)
: boot_lockbox_filepath_(bootlockbox_file_path),
tpm_nvspace_utility_(tpm_nvspace_utility) {}
NVRamBootLockbox::~NVRamBootLockbox() {}
bool NVRamBootLockbox::Store(const std::string& key,
const std::string& digest,
BootLockboxErrorCode* error) {
// Returns nvspace state to client.
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_NOT_SET;
if (nvspace_state_ == NVSpaceState::kNVSpaceWriteLocked) {
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_WRITE_LOCKED;
return false;
}
if (nvspace_state_ == NVSpaceState::kNVSpaceUndefined) {
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_NVSPACE_UNDEFINED;
return false;
}
if (nvspace_state_ == NVSpaceState::kNVSpaceError) {
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_NVSPACE_OTHER;
return false;
}
// A temporaray key value map for writing.
KeyValueMap updated_key_value_map = key_value_store_;
updated_key_value_map[key] = digest;
if (!FlushAndUpdate(updated_key_value_map)) {
LOG(ERROR) << "Store Failed: Cannot flush to file.";
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_WRITE_FAILED;
return false;
}
return true;
}
bool NVRamBootLockbox::Read(const std::string& key,
std::string* digest,
BootLockboxErrorCode* error) {
// Returns nvspace state to client.
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_NOT_SET;
if (nvspace_state_ == NVSpaceState::kNVSpaceUndefined) {
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_NVSPACE_UNDEFINED;
return false;
}
if (nvspace_state_ == NVSpaceState::kNVSpaceUninitialized) {
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_NVSPACE_UNINITIALIZED;
return false;
}
if (nvspace_state_ == NVSpaceState::kNVSpaceError) {
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_NVSPACE_OTHER;
return false;
}
KeyValueMap::const_iterator it = key_value_store_.find(key);
if (it == key_value_store_.end()) {
*error = BootLockboxErrorCode::BOOTLOCKBOX_ERROR_MISSING_KEY;
return false;
}
*digest = it->second;
return true;
}
bool NVRamBootLockbox::Finalize() {
if (nvspace_state_ == NVSpaceState::kNVSpaceUndefined) {
return false;
}
if (tpm_nvspace_utility_->LockNVSpace()) {
nvspace_state_ = NVSpaceState::kNVSpaceWriteLocked;
return true;
}
nvspace_state_ = NVSpaceState::kNVSpaceError;
return false;
}
bool NVRamBootLockbox::DefineSpace() {
if (tpm_nvspace_utility_->DefineNVSpace()) {
nvspace_state_ = NVSpaceState::kNVSpaceUninitialized;
return true;
}
nvspace_state_ = NVSpaceState::kNVSpaceUndefined;
return false;
}
bool NVRamBootLockbox::Load() {
if (!tpm_nvspace_utility_->ReadNVSpace(&root_digest_, &nvspace_state_)) {
LOG(ERROR) << "Failed to read NVRAM space.";
return false;
}
brillo::Blob data;
if (!Platform().ReadFile(boot_lockbox_filepath_, &data)) {
LOG(ERROR) << "Failed to read boot lockbox file.";
return false;
}
brillo::Blob digest_blob = CryptoLib::Sha256(data);
std::string digest(digest_blob.begin(), digest_blob.end());
if (digest != root_digest_) {
LOG(ERROR) << "The nvram boot lockbox file verification failed.";
return false;
}
SerializedKeyValueMap message;
if (!message.ParseFromArray(data.data(), data.size())) {
LOG(ERROR) << "Failed to parse boot lockbox file.";
return false;
}
if (!message.has_version() || message.version() != kVersion) {
LOG(ERROR) << "Unsupported version " << message.version();
return false;
}
KeyValueMap tmp(message.keyvals().begin(), message.keyvals().end());
key_value_store_.swap(tmp);
return true;
}
bool NVRamBootLockbox::FlushAndUpdate(const KeyValueMap& keyvals) {
SerializedKeyValueMap message;
message.set_version(kVersion);
auto mutable_map = message.mutable_keyvals();
KeyValueMap::const_iterator it;
for (it = keyvals.begin(); it != keyvals.end(); ++it) {
(*mutable_map)[it->first] = it->second;
}
brillo::Blob content(message.ByteSizeLong());
message.SerializeWithCachedSizesToArray(content.data());
brillo::Blob digest_blob = CryptoLib::Sha256(content);
std::string digest(digest_blob.begin(), digest_blob.end());
// It is hard to make this atomic. In the case the file digest
// and NVRAM space content are inconsistent, the file is deleted and NVRAM
// space is updated on write.
if (!brillo::WriteBlobToFileAtomic(boot_lockbox_filepath_, content, 0600)) {
LOG(ERROR) << "Failed to write to boot lockbox file";
return false;
}
// Update tpm_nvram.
if (!tpm_nvspace_utility_->WriteNVSpace(digest)) {
LOG(ERROR) << "Failed to write boot lockbox NVRAM space";
return false;
}
brillo::SyncFileOrDirectory(boot_lockbox_filepath_, false /* is directory */,
true /* data sync */);
// Update in memory information.
key_value_store_ = keyvals;
root_digest_ = digest;
nvspace_state_ = NVSpaceState::kNVSpaceNormal;
return true;
}
NVSpaceState NVRamBootLockbox::GetState() {
return nvspace_state_;
}
void NVRamBootLockbox::SetState(const NVSpaceState state) {
nvspace_state_ = state;
}
} // namespace cryptohome