| // 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 |