blob: c76e3c7aaa6d7b98bc9ca384b1efe7bae2272f2f [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "libhwsec/backend/tpm1/config.h"
#include <cstdint>
#include <map>
#include <string>
#include <base/containers/contains.h>
#include <base/no_destructor.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <crypto/sha2.h>
#include <libhwsec-foundation/crypto/sha.h>
#include <libhwsec-foundation/status/status_chain_macros.h>
#include <openssl/sha.h>
#include "libhwsec/backend/tpm1/static_utils.h"
#include "libhwsec/error/tpm1_error.h"
#include "libhwsec/overalls/overalls.h"
#include "libhwsec/status.h"
using brillo::BlobFromString;
using brillo::BlobToString;
using hwsec_foundation::Sha1;
using hwsec_foundation::status::MakeStatus;
using Mode = hwsec::DeviceConfigSettings::BootModeSetting::Mode;
namespace hwsec {
const int kCurrentUserPcrTpm1 = USE_TPM_DYNAMIC ? 11 : 4;
namespace {
constexpr int kBootModePcr = 0;
constexpr int kDeviceModelPcr = 1;
constexpr DeviceConfig kSupportConfigs[] = {
DeviceConfig::kBootMode,
DeviceConfig::kDeviceModel,
DeviceConfig::kCurrentUser,
};
StatusOr<int> DeviceConfigToPcr(DeviceConfig config) {
switch (config) {
case DeviceConfig::kBootMode:
return kBootModePcr;
case DeviceConfig::kDeviceModel:
return kDeviceModelPcr;
case DeviceConfig::kCurrentUser:
return kCurrentUserPcrTpm1;
case DeviceConfig::kBootCmdline:
// Not supported.
break;
}
return MakeStatus<TPMError>("Unknown device config",
TPMRetryAction::kNoRetry);
}
// The mapping that maps pcr value to corresponding boot mode.
const std::map<brillo::Blob, Mode>& BootModeMapping() {
static const base::NoDestructor<std::map<brillo::Blob, Mode>> mapping([] {
std::map<brillo::Blob, Mode> mapping;
// 3-byte boot mode:
// - byte 0: 1 if in developer mode, 0 otherwise,
// - byte 1: 1 if in recovery mode, 0 otherwise,
// - byte 2: 1 if verified firmware, 0 if developer firmware.
// Iterating through all possible combination of modes.
for (int i = 0; i < (1 << 3); ++i) {
Mode mode = {
.developer_mode = i & 1,
.recovery_mode = i & 2,
.verified_firmware = i & 4,
};
mapping.emplace(GetTpm1PCRValueForMode(mode), mode);
}
return mapping;
}());
return *mapping;
}
} // namespace
StatusOr<OperationPolicy> ConfigTpm1::ToOperationPolicy(
const OperationPolicySetting& policy) {
DeviceConfigs configs;
const DeviceConfigSettings& settings = policy.device_config_settings;
if (settings.boot_mode.has_value()) {
configs[DeviceConfig::kBootMode] = true;
}
if (settings.device_model.has_value()) {
configs[DeviceConfig::kDeviceModel] = true;
}
if (settings.current_user.has_value()) {
configs[DeviceConfig::kCurrentUser] = true;
}
return OperationPolicy{
.device_configs = configs,
.permission = policy.permission,
};
}
Status ConfigTpm1::SetCurrentUser(const std::string& current_user) {
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
ASSIGN_OR_RETURN(TSS_HTPM tpm_handle, tss_helper_.GetTpmHandle());
brillo::Blob extention = Sha1(brillo::BlobFromString(current_user));
uint32_t new_pcr_value_length = 0;
ScopedTssMemory new_pcr_value(overalls_, context);
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_TPM_PcrExtend(
tpm_handle, kCurrentUserPcrTpm1, extention.size(), extention.data(),
nullptr, &new_pcr_value_length, new_pcr_value.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_TPM_PcrExtend");
return OkStatus();
}
StatusOr<bool> ConfigTpm1::IsCurrentUserSet() {
ASSIGN_OR_RETURN(brillo::Blob && value, ReadPcr(kCurrentUserPcrTpm1),
_.WithStatus<TPMError>("Failed to read current user PCR"));
return value != brillo::Blob(SHA_DIGEST_LENGTH, 0);
}
StatusOr<Mode> ConfigTpm1::GetCurrentBootMode() {
ASSIGN_OR_RETURN(const brillo::Blob& value, ReadPcr(kBootModePcr),
_.WithStatus<TPMError>("Failed to read boot mode PCR"));
return ToBootMode(value);
}
StatusOr<Mode> ConfigTpm1::ToBootMode(const brillo::Blob& value) {
const std::map<brillo::Blob, Mode>& mapping = BootModeMapping();
if (auto it = mapping.find(value); it != mapping.end()) {
return it->second;
}
return MakeStatus<TPMError>("Encountered invalid boot mode value: " +
base::HexEncode(value.data(), value.size()),
TPMRetryAction::kNoRetry);
}
StatusOr<ConfigTpm1::PcrMap> ConfigTpm1::ToPcrMap(
const DeviceConfigs& device_config) {
PcrMap result;
for (DeviceConfig config : kSupportConfigs) {
if (device_config[config]) {
ASSIGN_OR_RETURN(int pcr, DeviceConfigToPcr(config),
_.WithStatus<TPMError>("Failed to convert to PCR"));
result[pcr] = brillo::Blob();
}
}
return result;
}
StatusOr<ConfigTpm1::PcrMap> ConfigTpm1::ToCurrentPcrValueMap(
const DeviceConfigs& device_config) {
PcrMap result;
for (DeviceConfig config : kSupportConfigs) {
if (device_config[config]) {
ASSIGN_OR_RETURN(int pcr, DeviceConfigToPcr(config),
_.WithStatus<TPMError>("Failed to convert to PCR"));
ASSIGN_OR_RETURN(result[pcr], ReadPcr(pcr),
_.WithStatus<TPMError>(base::StringPrintf(
"Failed to read PCR %d value", pcr)));
}
}
return result;
}
StatusOr<brillo::Blob> ConfigTpm1::ReadPcr(uint32_t pcr_index) {
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
ASSIGN_OR_RETURN(TSS_HTPM tpm_handle, tss_helper_.GetTpmHandle());
uint32_t length = 0;
ScopedTssMemory buffer(overalls_, context);
RETURN_IF_ERROR(MakeStatus<TPM1Error>(overalls_.Ospi_TPM_PcrRead(
tpm_handle, pcr_index, &length, buffer.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_TPM_PcrRead");
return brillo::Blob(buffer.value(), buffer.value() + length);
}
StatusOr<ConfigTpm1::PcrMap> ConfigTpm1::ToSettingsPcrMap(
const DeviceConfigSettings& settings) {
PcrMap result;
if (settings.boot_mode.has_value()) {
const auto& mode = settings.boot_mode->mode;
if (mode.has_value()) {
result[kBootModePcr] = GetTpm1PCRValueForMode(*mode);
} else {
ASSIGN_OR_RETURN(brillo::Blob && value, ReadPcr(kBootModePcr),
_.WithStatus<TPMError>("Failed to read boot mode PCR"));
result[kBootModePcr] = std::move(value);
}
}
if (settings.device_model.has_value()) {
const auto& hardware_id = settings.device_model->hardware_id;
if (hardware_id.has_value()) {
return MakeStatus<TPMError>("Unsupported settings",
TPMRetryAction::kNoRetry);
} else {
ASSIGN_OR_RETURN(
brillo::Blob && value, ReadPcr(kDeviceModelPcr),
_.WithStatus<TPMError>("Failed to read device model PCR"));
result[kDeviceModelPcr] = std::move(value);
}
}
if (settings.current_user.has_value()) {
const auto& username = settings.current_user->username;
brillo::Blob digest_value(SHA_DIGEST_LENGTH, 0);
if (username.has_value()) {
digest_value = Sha1(brillo::CombineBlobs(
{digest_value, Sha1(BlobFromString(username.value()))}));
}
result[kCurrentUserPcrTpm1] = digest_value;
}
return result;
}
StatusOr<ScopedTssPcrs> ConfigTpm1::ToPcrSelection(
const DeviceConfigs& device_configs) {
ASSIGN_OR_RETURN(const PcrMap& pcr_map, ToPcrMap(device_configs));
ASSIGN_OR_RETURN(TSS_HCONTEXT context, tss_helper_.GetTssContext());
ScopedTssPcrs pcrs(overalls_, context);
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(overalls_.Ospi_Context_CreateObject(
context, TSS_OBJECT_TYPE_PCRS, TSS_PCRS_STRUCT_INFO, pcrs.ptr())))
.WithStatus<TPMError>("Failed to call Ospi_Context_CreateObject");
for (const PcrMap::value_type& pcr : pcr_map) {
RETURN_IF_ERROR(
MakeStatus<TPM1Error>(
overalls_.Ospi_PcrComposite_SelectPcrIndex(pcrs, pcr.first)))
.WithStatus<TPMError>(
"Failed to call Ospi_PcrComposite_SelectPcrIndex");
}
return pcrs;
}
StatusOr<std::string> ConfigTpm1::GetHardwareID() {
auto property = crossystem_.VbGetSystemPropertyString("hwid");
if (!property.has_value()) {
return MakeStatus<TPMError>("Failed to read hwid property",
TPMRetryAction::kNoRetry);
}
return *property;
}
} // namespace hwsec