blob: 1db5e879f3a9aef0a8f30ed75fa184dca544a6df [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/mount_encrypted/tpm.h"
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <type_traits>
#include <base/files/file.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/macros.h>
#include <base/strings/string_number_conversions.h>
#include <brillo/file_utils.h>
#include <brillo/process/process.h>
#include "cryptohome/cryptolib.h"
#include "cryptohome/mount_encrypted/mount_encrypted.h"
namespace mount_encrypted {
namespace {
const uint32_t kLockboxSaltOffset = 0x5;
// Attributes for the encstatful NVRAM space. Ideally, we'd set
// TPM_NV_PER_OWNERWRITE so the space gets automatically destroyed when the TPM
// gets cleared. That'd mean we'd have to recreate the NVRAM space on next boot
// though, which requires TPM ownership. Taking ownership is notoriously slow,
// so we can't afford to do this. Instead, we keep the space allocated and
// detect TPM clear to regenerate the system key.
const uint32_t kAttributes = TPM_NV_PER_WRITE_STCLEAR | TPM_NV_PER_READ_STCLEAR;
const uint32_t kAttributesMask =
TPM_NV_PER_READ_STCLEAR | TPM_NV_PER_AUTHREAD | TPM_NV_PER_OWNERREAD |
TPM_NV_PER_PPREAD | TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_WRITE_STCLEAR |
TPM_NV_PER_WRITEDEFINE | TPM_NV_PER_WRITEALL | TPM_NV_PER_AUTHWRITE |
TPM_NV_PER_OWNERWRITE | TPM_NV_PER_PPWRITE;
// Key derivation labels.
const char kLabelSystemKey[] = "system_key";
const char kLabelLockboxMAC[] = "lockbox_mac";
// This is the well-known secret (SHA-1 hash of 20 zero bytes) that TrouSerS
// sets by default when taking ownership. We use the same value here to simplify
// the logic in cryptohomed.
const uint8_t kWellKnownSecret[TPM_AUTH_DATA_LEN] = {
0x67, 0x68, 0x03, 0x3e, 0x21, 0x64, 0x68, 0x24, 0x7b, 0xd0,
0x31, 0xa0, 0xa2, 0xd9, 0x87, 0x6d, 0x79, 0x81, 0x8f, 0x8f,
};
// This struct defines the memory layout of the NVRAM area. Member sizes are
// chosen taking layout into consideration.
struct EncStatefulArea {
enum class Flag {
// The |lockbox_mac| field is valid and contains a MAC of the lockbox NVRAM
// area contents.
kLockboxMacValid = 0,
// We are expecting another TPM clear to take place for which preservation
// will be allowed. This is used to handle the TPM clear following a TPM
// firmware update.
kAnticipatingTPMClear = 1,
};
static constexpr uint32_t kMagic = 0x54504d31;
static constexpr size_t kVersionShift = 8;
static constexpr uint32_t kVersionMask = (1 << kVersionShift) - 1;
static constexpr uint32_t kCurrentVersion = 1;
uint32_t magic;
uint32_t ver_flags;
uint8_t key_material[DIGEST_LENGTH];
uint8_t lockbox_mac[DIGEST_LENGTH];
bool is_valid() const {
return magic == kMagic && (ver_flags & kVersionMask) == kCurrentVersion;
}
static uint32_t flag_value(Flag flag) {
return (1 << (static_cast<size_t>(flag) + kVersionShift));
}
bool test_flag(Flag flag) const { return ver_flags & flag_value(flag); }
void set_flag(Flag flag) { ver_flags |= flag_value(flag); }
void clear_flag(Flag flag) { ver_flags &= ~flag_value(flag); }
result_code Init(const brillo::SecureBlob& new_key_material) {
magic = kMagic;
ver_flags = kCurrentVersion;
size_t key_material_size = new_key_material.size();
if (key_material_size != sizeof(key_material)) {
LOG(ERROR) << "Invalid key material size " << key_material_size;
return RESULT_FAIL_FATAL;
}
memcpy(key_material, new_key_material.data(), key_material_size);
memset(lockbox_mac, 0, sizeof(lockbox_mac));
return RESULT_SUCCESS;
}
brillo::SecureBlob DeriveKey(const std::string& label) const {
return cryptohome::CryptoLib::HmacSha256(
brillo::SecureBlob(key_material, key_material + sizeof(key_material)),
brillo::Blob(label.data(), label.data() + label.size()));
}
};
// We're using casts to map the EncStatefulArea struct to NVRAM contents, so
// make sure to keep this a POD type.
static_assert(std::is_pod<EncStatefulArea>(),
"EncStatefulArea must be a POD type");
// Make sure that the EncStatefulArea struct fits the encstateful NVRAM space.
static_assert(kEncStatefulSize >= sizeof(EncStatefulArea),
"EncStatefulArea must fit within encstateful NVRAM space");
} // namespace
const uint8_t* kOwnerSecret = kWellKnownSecret;
const size_t kOwnerSecretSize = sizeof(kWellKnownSecret);
// System key loader implementation for TPM1 systems. This supports two
// different sources of obtaining system key material: A dedicated NVRAM space
// (called the "encstateful NVRAM space" below) and the "salt" in the lockbox
// space. We prefer the former if it is available.
class Tpm1SystemKeyLoader : public SystemKeyLoader {
public:
Tpm1SystemKeyLoader(Tpm* tpm, const base::FilePath& rootdir)
: tpm_(tpm), rootdir_(rootdir) {}
Tpm1SystemKeyLoader(const Tpm1SystemKeyLoader&) = delete;
Tpm1SystemKeyLoader& operator=(const Tpm1SystemKeyLoader&) = delete;
result_code Load(brillo::SecureBlob* key) override;
result_code Initialize(const brillo::SecureBlob& key_material,
brillo::SecureBlob* derived_system_key) override;
result_code Persist() override;
void Lock() override;
result_code SetupTpm() override;
result_code GenerateForPreservation(brillo::SecureBlob* previous_key,
brillo::SecureBlob* fresh_key) override;
result_code CheckLockbox(bool* valid) override;
bool UsingLockboxKey() override;
private:
// Gets a pointer to the EncStatefulArea structure backed by NVRAM.
result_code LoadEncStatefulArea(const EncStatefulArea** area);
// Loads the key from the encstateful NVRAM space.
result_code LoadEncStatefulKey(brillo::SecureBlob* key);
// Loads the key from the lockbox NVRAM space.
result_code LoadLockboxKey(brillo::SecureBlob* key);
// Validates the encstateful space is defined with correct parameters.
result_code IsEncStatefulSpaceProperlyDefined(bool* result);
// Obtains and formats TPM version info as key-value pairs.
std::string FormatVersionInfo();
// Obtains and formats IFX field upgrade status as key-value pairs.
std::string FormatIFXFieldUpgradeInfo();
// Check whether a TPM firmware update is pending. Returns true if there is an
// update, false if no pending update and on errors.
bool IsTPMFirmwareUpdatePending();
Tpm* tpm_ = nullptr;
base::FilePath rootdir_;
// Provisional space contents that get initialized by Generate() and written
// to the NVRAM space by Persist();
std::unique_ptr<brillo::SecureBlob> provisional_contents_;
// Whether we're using the lockbox salt as system key.
bool using_lockbox_key_ = false;
};
// TPM cases:
// - does not exist at all (disabled in test firmware or non-chrome device).
// - exists (below).
//
// TPM ownership cases:
// - unowned (OOBE):
// - expect modern lockbox.
// - owned: depends on NVRAM area (below).
//
// NVRAM area cases:
// - no NVRAM area at all:
// - interrupted install (cryptohome has the TPM password)
// - ancient device (cr48, cryptohome has thrown away TPM password)
// - broken device (cryptohome has thrown away/never had TPM password)
// - must expect worst-case: no lockbox ever.
// - defined NVRAM area, but not written to ("Finalized"); interrupted OOBE.
// - written ("Finalized") NVRAM area.
//
// In case of success: (NVRAM area found and used)
// - *system_key populated with NVRAM area entropy.
// In case of failure: (NVRAM missing or error)
// - *system_key untouched.
result_code Tpm1SystemKeyLoader::Load(brillo::SecureBlob* system_key) {
bool space_properly_defined = false;
result_code rc = IsEncStatefulSpaceProperlyDefined(&space_properly_defined);
if (rc != RESULT_SUCCESS) {
return rc;
}
// Prefer the encstateful space if it is set up correctly.
if (space_properly_defined) {
// Only load the key if we are sure that we have generated a fresh key after
// the last TPM clear. After a clear, the TPM has no owner. In unowned state
// we rely on a flag we store persistently in the TPM to indicate whether we
// have generated a key already (note that the TPM automatically clears the
// flag on TPM clear).
bool system_key_initialized = false;
rc = tpm_->HasSystemKeyInitializedFlag(&system_key_initialized);
if (rc != RESULT_SUCCESS) {
return rc;
}
if (system_key_initialized) {
rc = LoadEncStatefulKey(system_key);
if (rc == RESULT_SUCCESS) {
return RESULT_SUCCESS;
}
}
} else {
// The lockbox NVRAM space is created by cryptohomed and only valid after
// TPM ownership has been established.
bool owned = false;
result_code rc = tpm_->IsOwned(&owned);
if (rc != RESULT_SUCCESS) {
LOG(ERROR) << "Failed to determine TPM ownership.";
return rc;
}
if (owned) {
rc = LoadLockboxKey(system_key);
if (rc == RESULT_SUCCESS) {
using_lockbox_key_ = true;
return RESULT_SUCCESS;
}
}
}
return RESULT_FAIL_FATAL;
}
result_code Tpm1SystemKeyLoader::Initialize(
const brillo::SecureBlob& key_material,
brillo::SecureBlob* derived_system_key) {
provisional_contents_ =
std::make_unique<brillo::SecureBlob>(sizeof(EncStatefulArea));
EncStatefulArea* area =
reinterpret_cast<EncStatefulArea*>(provisional_contents_->data());
result_code rc = area->Init(key_material);
if (rc != RESULT_SUCCESS) {
return rc;
}
if (derived_system_key) {
*derived_system_key = area->DeriveKey(kLabelSystemKey);
}
return RESULT_SUCCESS;
}
result_code Tpm1SystemKeyLoader::Persist() {
CHECK(provisional_contents_);
bool space_defined_properly = false;
result_code rc = IsEncStatefulSpaceProperlyDefined(&space_defined_properly);
if (rc != RESULT_SUCCESS) {
return rc;
}
if (!space_defined_properly) {
return RESULT_FAIL_FATAL;
}
NvramSpace* encstateful_space = tpm_->GetEncStatefulSpace();
rc = encstateful_space->Write(*provisional_contents_);
if (rc != RESULT_SUCCESS) {
LOG(ERROR) << "Failed to write NVRAM area";
return rc;
}
rc = tpm_->SetSystemKeyInitializedFlag();
if (rc != RESULT_SUCCESS) {
LOG(ERROR) << "Failed to create placeholder delegation entry.";
return rc;
}
using_lockbox_key_ = false;
return RESULT_SUCCESS;
}
void Tpm1SystemKeyLoader::Lock() {
NvramSpace* encstateful_space = tpm_->GetEncStatefulSpace();
if (!encstateful_space->is_valid()) {
return;
}
if (encstateful_space->WriteLock() != RESULT_SUCCESS) {
LOG(ERROR) << "Failed to write-lock NVRAM area.";
}
if (encstateful_space->ReadLock() != RESULT_SUCCESS) {
LOG(ERROR) << "Failed to read-lock NVRAM area.";
}
}
result_code Tpm1SystemKeyLoader::SetupTpm() {
bool is_properly_defined = false;
result_code rc = IsEncStatefulSpaceProperlyDefined(&is_properly_defined);
if (rc != RESULT_SUCCESS) {
return rc;
}
if (is_properly_defined) {
return RESULT_SUCCESS;
}
// We need to take ownership and redefine the space.
LOG(INFO) << "Redefining encrypted stateful space.";
bool owned = false;
rc = tpm_->IsOwned(&owned);
if (rc != RESULT_SUCCESS) {
LOG(ERROR) << "Can't determine TPM ownership.";
return RESULT_FAIL_FATAL;
}
if (!owned) {
// Reset cryptohomed state so it re-initializes the TPM.
base::FilePath tpm_status_path =
rootdir_.AppendASCII(paths::cryptohome::kTpmStatus);
base::FilePath tpm_owned_path =
rootdir_.AppendASCII(paths::cryptohome::kTpmOwned);
base::FilePath shall_initialize_path =
rootdir_.AppendASCII(paths::cryptohome::kShallInitialize);
base::FilePath attestation_database_path =
rootdir_.AppendASCII(paths::cryptohome::kAttestationDatabase);
if (!base::DeleteFile(tpm_status_path) ||
!base::DeleteFile(tpm_owned_path) ||
!brillo::SyncFileOrDirectory(tpm_status_path.DirName(), true, false) ||
!brillo::WriteToFileAtomic(shall_initialize_path, nullptr, 0, 0644) ||
!brillo::SyncFileOrDirectory(shall_initialize_path.DirName(), true,
false) ||
!base::DeleteFile(attestation_database_path)) {
PLOG(ERROR) << "Failed to update cryptohomed state.";
return RESULT_FAIL_FATAL;
}
result_code rc = tpm_->TakeOwnership();
if (rc != RESULT_SUCCESS) {
LOG(ERROR) << "Failed to ensure TPM ownership.";
return rc;
}
}
uint32_t pcr_selection = (1 << kPCRBootMode);
rc = tpm_->GetEncStatefulSpace()->Define(kAttributes, sizeof(EncStatefulArea),
pcr_selection);
if (rc != RESULT_SUCCESS) {
LOG(ERROR) << "Failed to define encrypted stateful NVRAM space.";
return rc;
}
return RESULT_SUCCESS;
}
result_code Tpm1SystemKeyLoader::GenerateForPreservation(
brillo::SecureBlob* previous_key, brillo::SecureBlob* fresh_key) {
// Determine whether we may preserve the encryption key that was in use before
// the TPM got cleared. Preservation is allowed if either (1) a TPM firmware
// update is pending and has been requested for installation or (2) we've
// taken a note in NVRAM space flags to anticipate a TPM clear. Condition (2)
// covers the TPM clear that follows installation of the firmware update. We'd
// prefer to handle that case by testing whether we actually just went through
// an update, but there's no trustworthy post-factum signal to tell us.
const EncStatefulArea* area = nullptr;
bool tpm_firmware_update_pending = false;
result_code rc = LoadEncStatefulArea(&area);
if (rc != RESULT_SUCCESS ||
!area->test_flag(EncStatefulArea::Flag::kAnticipatingTPMClear)) {
tpm_firmware_update_pending = IsTPMFirmwareUpdatePending();
if (!tpm_firmware_update_pending) {
return RESULT_FAIL_FATAL;
}
}
// Load the previous system key.
rc = LoadEncStatefulKey(previous_key);
if (rc != RESULT_SUCCESS) {
rc = LoadLockboxKey(previous_key);
if (rc != RESULT_SUCCESS) {
return RESULT_FAIL_FATAL;
}
}
// Generate new encstateful contents.
provisional_contents_ =
std::make_unique<brillo::SecureBlob>(sizeof(EncStatefulArea));
EncStatefulArea* provisional_area =
reinterpret_cast<EncStatefulArea*>(provisional_contents_->data());
const auto key_material =
cryptohome::CryptoLib::CreateSecureRandomBlob(DIGEST_LENGTH);
rc = provisional_area->Init(key_material);
if (rc != RESULT_SUCCESS) {
return rc;
}
// Set the flag to anticipate another TPM clear for the case where we're
// preserving for the installation of a TPM firmware update.
if (tpm_firmware_update_pending) {
provisional_area->set_flag(EncStatefulArea::Flag::kAnticipatingTPMClear);
}
// We need to leave the TPM in a state with owner auth available. However,
// when preserving the state of the system, we must guarantee lockbox
// integrity. To achieve lockbox tamper evidence, we store a MAC of the
// lockbox space in the encstateful space, which gets locked to prevent
// further manipulation in Lock(). We can thus re-check lockbox contents are
// legit at next reboot by verifying the MAC.
provisional_area->set_flag(EncStatefulArea::Flag::kLockboxMacValid);
NvramSpace* lockbox_space = tpm_->GetLockboxSpace();
if (lockbox_space->is_valid()) {
brillo::SecureBlob mac = cryptohome::CryptoLib::HmacSha256(
provisional_area->DeriveKey(kLabelLockboxMAC),
lockbox_space->contents());
memcpy(provisional_area->lockbox_mac, mac.data(), mac.size());
}
*fresh_key = provisional_area->DeriveKey(kLabelSystemKey);
using_lockbox_key_ = false;
return RESULT_SUCCESS;
}
result_code Tpm1SystemKeyLoader::LoadEncStatefulArea(
const EncStatefulArea** area) {
NvramSpace* space = tpm_->GetEncStatefulSpace();
if (!space->is_valid()) {
LOG(ERROR) << "Invalid encstateful space.";
return RESULT_FAIL_FATAL;
}
*area = reinterpret_cast<const EncStatefulArea*>(space->contents().data());
if (!(*area)->is_valid()) {
LOG(ERROR) << "Invalid encstateful contents.";
return RESULT_FAIL_FATAL;
}
return RESULT_SUCCESS;
}
result_code Tpm1SystemKeyLoader::LoadEncStatefulKey(
brillo::SecureBlob* system_key) {
const EncStatefulArea* area = nullptr;
result_code rc = LoadEncStatefulArea(&area);
if (rc != RESULT_SUCCESS) {
return rc;
}
*system_key = area->DeriveKey(kLabelSystemKey);
return RESULT_SUCCESS;
}
result_code Tpm1SystemKeyLoader::LoadLockboxKey(
brillo::SecureBlob* system_key) {
brillo::SecureBlob key_material;
NvramSpace* lockbox_space = tpm_->GetLockboxSpace();
const brillo::SecureBlob& lockbox_contents = lockbox_space->contents();
if (!lockbox_space->is_valid()) {
return RESULT_FAIL_FATAL;
} else if (lockbox_contents.size() == kLockboxSizeV1) {
key_material = lockbox_contents;
} else if (kLockboxSaltOffset + DIGEST_LENGTH <= lockbox_contents.size()) {
const uint8_t* begin = lockbox_contents.data() + kLockboxSaltOffset;
key_material.assign(begin, begin + DIGEST_LENGTH);
} else {
LOG(INFO) << "Impossibly small NVRAM area size (" << lockbox_contents.size()
<< ").";
return RESULT_FAIL_FATAL;
}
*system_key = cryptohome::CryptoLib::Sha256(key_material);
return RESULT_SUCCESS;
}
result_code Tpm1SystemKeyLoader::IsEncStatefulSpaceProperlyDefined(
bool* result) {
*result = false;
NvramSpace* encstateful_space = tpm_->GetEncStatefulSpace();
if (!encstateful_space->is_valid() ||
encstateful_space->contents().size() < sizeof(EncStatefulArea)) {
LOG(ERROR) << "encstateful space contents absent or too short.";
return RESULT_SUCCESS;
}
uint32_t attributes;
result_code rc = encstateful_space->GetAttributes(&attributes);
if (rc != RESULT_SUCCESS) {
return rc;
}
if ((attributes & kAttributesMask) != kAttributes) {
LOG(ERROR) << "Bad encstateful space attributes.";
return rc;
}
uint32_t pcr_selection = (1 << kPCRBootMode);
bool pcr_binding_correct = false;
rc = encstateful_space->CheckPCRBinding(pcr_selection, &pcr_binding_correct);
if (rc != RESULT_SUCCESS) {
LOG(ERROR) << "Bad encstateful PCR binding.";
return rc;
}
*result = pcr_binding_correct;
return RESULT_SUCCESS;
}
std::string Tpm1SystemKeyLoader::FormatVersionInfo() {
uint32_t vendor;
uint64_t firmware_version;
std::vector<uint8_t> vendor_specific;
if (!tpm_->GetVersionInfo(&vendor, &firmware_version, &vendor_specific)) {
return std::string();
}
return base::StringPrintf(
"vendor %08x\nfirmware_version %016" PRIx64 "\nvendor_specific %s",
vendor, firmware_version,
base::HexEncode(vendor_specific.data(), vendor_specific.size()).c_str());
}
std::string Tpm1SystemKeyLoader::FormatIFXFieldUpgradeInfo() {
TPM_IFX_FIELDUPGRADEINFO info;
if (!tpm_->GetIFXFieldUpgradeInfo(&info)) {
return std::string();
}
auto format_fw_pkg = [](const TPM_IFX_FIRMWAREPACKAGE& firmware_package,
const char* prefix) {
return base::StringPrintf(
"%s_package_id %08x\n"
"%s_version %08x\n"
"%s_stale_version %08x\n",
prefix, firmware_package.FwPackageIdentifier, prefix,
firmware_package.Version, prefix, firmware_package.StaleVersion);
};
return base::StringPrintf(
"max_data_size %u\n"
"%s"
"%s"
"%s"
"status %04x\n"
"%s"
"field_upgrade_counter %u\n",
info.wMaxDataSize,
format_fw_pkg(info.sBootloaderFirmwarePackage, "bootloader").c_str(),
format_fw_pkg(info.sFirmwarePackages[0], "fw0").c_str(),
format_fw_pkg(info.sFirmwarePackages[1], "fw1").c_str(),
info.wSecurityModuleStatus,
format_fw_pkg(info.sProcessFirmwarePackage, "process_fw").c_str(),
info.wFieldUpgradeCounter);
}
bool Tpm1SystemKeyLoader::IsTPMFirmwareUpdatePending() {
// Make sure a TPM firmware upgrade has been requested.
if (!base::PathExists(rootdir_.AppendASCII(paths::kFirmwareUpdateRequest))) {
LOG(ERROR) << "TPM firmware update wasn't requested.";
return false;
}
// Obtain version and upgrade status information to pass to the locator tool.
std::string version_info = FormatVersionInfo();
std::string ifx_field_upgrade_info = FormatIFXFieldUpgradeInfo();
if (version_info.empty() || ifx_field_upgrade_info.empty()) {
return false;
}
// Launch the update locator script.
brillo::ProcessImpl tpm_firmware_update_locator;
tpm_firmware_update_locator.SetCloseUnusedFileDescriptors(true);
tpm_firmware_update_locator.RedirectUsingPipe(STDOUT_FILENO, false);
tpm_firmware_update_locator.AddArg(
rootdir_.AppendASCII(paths::kFirmwareUpdateLocator).value());
tpm_firmware_update_locator.AddArg(version_info);
tpm_firmware_update_locator.AddArg(ifx_field_upgrade_info);
if (!tpm_firmware_update_locator.Start()) {
LOG(ERROR) << "Failed to start update locator child process";
return false;
}
// Read the output.
{
base::File pipe(tpm_firmware_update_locator.GetPipe(STDOUT_FILENO));
char update_location[PATH_MAX];
int bytes_read =
pipe.ReadAtCurrentPos(update_location, sizeof(update_location));
if (bytes_read <= 0) {
LOG(ERROR) << "Failed to read update location from pipe.";
return false;
}
// Check that the update location file exists.
char* newline_pos = strchr(update_location, '\n');
if (newline_pos) {
*newline_pos = '\0';
}
base::FilePath update_path(update_location);
LOG(INFO) << "Checking whether "
<< rootdir_.AppendASCII(paths::kFirmwareDir) << " is a parent of "
<< update_path;
if (!rootdir_.AppendASCII(paths::kFirmwareDir).IsParent(update_path) ||
!base::PathExists(update_path)) {
LOG(ERROR) << "Failure locating TPM firmware update file.";
return false;
}
}
// Make sure the locator script terminated cleanly.
if (tpm_firmware_update_locator.Wait() != 0) {
LOG(ERROR) << "TPM firmware update locator utility failed.";
return false;
}
return true;
}
result_code Tpm1SystemKeyLoader::CheckLockbox(bool* valid) {
*valid = false;
bool space_properly_defined = false;
result_code rc = IsEncStatefulSpaceProperlyDefined(&space_properly_defined);
if (rc != RESULT_SUCCESS) {
return rc;
}
if (space_properly_defined) {
// Check whether the encstateful space contains a valid lockbox MAC. Check
// the actual lockbox contents against the MAC, reset the lockbox space to
// invalid so subsequent code won't use it (specifically, the lockbox space
// won't get exported for OS consumption).
//
// This addresses the scenario where the TPM is left in unowned state or
// owned with the well-known password after preservation. The requirement is
// that the lockbox contents may only change at full device reset (e.g.
// implying stateful file system loss). However, stateful preservation
// carries over state, so it needs to ensure the lockbox stays locked. Due
// to the TPM state, the lockbox space could get redefined and thus written
// to after preservation. The MAC check here doesn't disallow this, but it
// ensures tamper-evidence: Modified lockbox contents will cause MAC
// validation failure, so the lockbox will be considered invalid. Note that
// attempts at adjusting the MAC to match tampered lockbox contents are
// prevented by locking the encstateful space after boot.
const EncStatefulArea* area = nullptr;
rc = LoadEncStatefulArea(&area);
switch (rc) {
case RESULT_SUCCESS:
if (area->test_flag(EncStatefulArea::Flag::kLockboxMacValid)) {
NvramSpace* lockbox_space = tpm_->GetLockboxSpace();
if (lockbox_space->is_valid()) {
brillo::SecureBlob mac = cryptohome::CryptoLib::HmacSha256(
area->DeriveKey(kLabelLockboxMAC), lockbox_space->contents());
*valid = brillo::SecureMemcmp(area->lockbox_mac, mac.data(),
mac.size()) == 0;
return RESULT_SUCCESS;
}
}
break;
case RESULT_FAIL_FATAL:
// Encstateful contents invalid, so lockbox MAC doesn't apply.
break;
default:
return rc;
}
}
// In case there is no encstateful space, the lockbox space is only valid once
// cryptohomed has taken TPM ownership and recreated the space.
return tpm_->IsOwned(valid);
}
bool Tpm1SystemKeyLoader::UsingLockboxKey() {
return using_lockbox_key_;
}
std::unique_ptr<SystemKeyLoader> SystemKeyLoader::Create(
Tpm* tpm, const base::FilePath& rootdir) {
return std::make_unique<Tpm1SystemKeyLoader>(tpm, rootdir);
}
} // namespace mount_encrypted