| // Copyright 2023 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/tpm2/version_attestation.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include <libhwsec-foundation/status/status_chain_macros.h> |
| |
| #include "base/files/file_path.h" |
| #include "brillo/secure_blob.h" |
| #include "libhwsec/backend/tpm2/static_utils.h" |
| #include "libhwsec/error/tpm2_error.h" |
| #include "libhwsec/status.h" |
| #include "libhwsec/structures/device_config.h" |
| |
| using hwsec_foundation::status::MakeStatus; |
| using std::string; |
| |
| namespace hwsec { |
| |
| namespace { |
| |
| constexpr char kLsbReleasePath[] = "/etc/lsb-release"; |
| constexpr char kProcCmdlinePath[] = "/proc/cmdline"; |
| |
| } // namespace |
| |
| StatusOr<arc_attestation::CrOSVersionAttestationBlob> |
| VersionAttestationTpm2::AttestVersion(Key key, |
| const std::string& cert, |
| const brillo::Blob& challenge) { |
| arc_attestation::CrOSVersionAttestationBlob result; |
| result.set_version(arc_attestation::CrOSVersionAttestationVersion:: |
| CROS_BLOB_VERSION_TPM2_FORMAT_1); |
| result.set_tpm_certifying_key_cert(cert); |
| |
| // Populate the lsb-release and /proc/cmdline content. |
| std::string lsb_release_content; |
| if (!platform_.ReadFileToString(base::FilePath(kLsbReleasePath), |
| &lsb_release_content)) { |
| return MakeStatus<TPMError>("Unable to read lsb-release", |
| TPMRetryAction::kNoRetry); |
| } |
| result.set_lsb_release_content(lsb_release_content); |
| |
| std::string cmdline_content; |
| if (!platform_.ReadFileToString(base::FilePath(kProcCmdlinePath), |
| &cmdline_content)) { |
| return MakeStatus<TPMError>("Unable to read /proc/cmdline", |
| TPMRetryAction::kNoRetry); |
| } |
| result.set_kernel_cmdline_content(cmdline_content); |
| |
| // Quote the PCR. |
| std::string pcr_signature, pcr_quoted; |
| VersionAttestationTpm2::PcrQuoteResult quote; |
| ASSIGN_OR_RETURN(quote, |
| QuotePCRInternal(key, brillo::BlobToString(challenge))); |
| |
| result.set_kernel_cmdline_quote(quote.quoted); |
| result.set_kernel_cmdline_quote_signature(quote.signature); |
| |
| return result; |
| } |
| |
| StatusOr<VersionAttestationTpm2::PcrQuoteResult> |
| VersionAttestationTpm2::QuotePCRInternal(Key& key, |
| const std::string& challenge) { |
| // Check that the key is ECC. |
| ASSIGN_OR_RETURN(const KeyTpm2& key_data, key_management_.GetKeyData(key)); |
| if (key_data.cache.public_area.type != trunks::TPM_ALG_ECC) { |
| return MakeStatus<TPMError>( |
| "Non-ECC key not supported for Version Attestation", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| // Prepare the PCR selection. |
| ASSIGN_OR_RETURN( |
| trunks::TPMS_PCR_SELECTION pcr_selection, |
| config_.ToPcrSelection(DeviceConfigs{ |
| DeviceConfig::kBootCmdline, |
| }), |
| _.WithStatus<TPMError>("Boot cmdline required for version attestation " |
| "unavailable on current device")); |
| |
| trunks::TPML_PCR_SELECTION pcr_select; |
| pcr_select.count = 1; |
| pcr_select.pcr_selections[0] = pcr_selection; |
| |
| // Prepare the key. |
| const trunks::TPM_HANDLE& key_handle = key_data.key_handle; |
| std::string key_name; |
| RETURN_IF_ERROR(MakeStatus<TPM2Error>(context_.GetTpmUtility().GetKeyName( |
| key_handle, &key_name))) |
| .WithStatus<TPMError>("Failed to get key name"); |
| |
| // Prepare the delegate. |
| std::unique_ptr<trunks::AuthorizationDelegate> delegate = |
| context_.GetTrunksFactory().GetPasswordAuthorization(""); |
| |
| // Prepare the scheme. |
| trunks::TPMT_SIG_SCHEME scheme; |
| scheme.details.any.hash_alg = trunks::TPM_ALG_SHA256; |
| scheme.scheme = trunks::TPM_ALG_ECDSA; |
| trunks::TPM2B_ATTEST quoted_struct; |
| trunks::TPMT_SIGNATURE signature; |
| RETURN_IF_ERROR( |
| MakeStatus<TPM2Error>(context_.GetTrunksFactory().GetTpm()->QuoteSync( |
| key_handle, key_name, |
| /*qualifying_data=*/trunks::Make_TPM2B_DATA(challenge), scheme, |
| pcr_select, "ed_struct, &signature, delegate.get()))) |
| .WithStatus<TPMError>("Failed to quote"); |
| |
| ASSIGN_OR_RETURN(const std::string& serialized_signature, |
| SerializeFromTpmSignature(signature)); |
| const std::string& quoted = StringFrom_TPM2B_ATTEST(quoted_struct); |
| return VersionAttestationTpm2::PcrQuoteResult{ |
| .signature = serialized_signature, .quoted = quoted}; |
| } |
| |
| } // namespace hwsec |