| // 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/static_utils.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include <base/hash/sha1.h> |
| #include <brillo/secure_blob.h> |
| #include <crypto/scoped_openssl_types.h> |
| #include <crypto/sha2.h> |
| #include <libhwsec-foundation/status/status_chain_macros.h> |
| #include <libhwsec-foundation/crypto/openssl.h> |
| #include <tpm_manager/proto_bindings/tpm_manager.pb.h> |
| #include <tpm_manager-client/tpm_manager/dbus-constants.h> |
| #include <tpm_manager-client/tpm_manager/dbus-proxies.h> |
| #include <trunks/tpm_generated.h> |
| |
| #include "libhwsec/error/tpm2_error.h" |
| #include "libhwsec/error/tpm_manager_error.h" |
| #include "libhwsec/proxy/proxy.h" |
| #include "libhwsec/status.h" |
| |
| using hwsec_foundation::status::MakeStatus; |
| |
| namespace hwsec { |
| |
| namespace { |
| |
| constexpr uint32_t kMaxPasswordLength = sizeof(trunks::TPMU_HA); |
| |
| StatusOr<crypto::ScopedBIGNUM> StringToBignum(const std::string& big_integer) { |
| if (big_integer.empty()) { |
| return MakeStatus<TPMError>("Input string is empty", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| crypto::ScopedBIGNUM bn(BN_new()); |
| if (!bn) { |
| return MakeStatus<TPMError>("Failed to allocate BIGNUM", |
| TPMRetryAction::kNoRetry); |
| } |
| if (!BN_bin2bn(reinterpret_cast<const uint8_t*>(big_integer.data()), |
| big_integer.length(), bn.get())) { |
| return MakeStatus<TPMError>("Failed to convert string to BIGNUM", |
| TPMRetryAction::kNoRetry); |
| } |
| return bn; |
| } |
| |
| StatusOr<crypto::ScopedECDSA_SIG> CreateEcdsaSigFromRS(const std::string& r, |
| const std::string& s) { |
| ASSIGN_OR_RETURN(crypto::ScopedBIGNUM r_bn, StringToBignum(r)); |
| ASSIGN_OR_RETURN(crypto::ScopedBIGNUM s_bn, StringToBignum(s)); |
| |
| crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new()); |
| if (!sig) { |
| return MakeStatus<TPMError>("Failed to allocate ECDSA", |
| TPMRetryAction::kNoRetry); |
| } |
| if (!ECDSA_SIG_set0(sig.get(), r_bn.release(), s_bn.release())) { |
| return MakeStatus<TPMError>("Failed to set ECDSA SIG parameters", |
| TPMRetryAction::kNoRetry); |
| } |
| return sig; |
| } |
| |
| } // namespace |
| |
| StatusOr<std::string> SerializeFromTpmSignature( |
| const trunks::TPMT_SIGNATURE& signature) { |
| switch (signature.sig_alg) { |
| case trunks::TPM_ALG_RSASSA: |
| if (signature.signature.rsassa.sig.size > |
| sizeof(signature.signature.rsassa.sig.buffer)) { |
| return MakeStatus<TPMError>("RSASSA signature overflow", |
| TPMRetryAction::kNoRetry); |
| } |
| return StringFrom_TPM2B_PUBLIC_KEY_RSA(signature.signature.rsassa.sig); |
| case trunks::TPM_ALG_ECDSA: { |
| if (signature.signature.ecdsa.signature_r.size > |
| sizeof(signature.signature.ecdsa.signature_r.buffer) || |
| signature.signature.ecdsa.signature_s.size > |
| sizeof(signature.signature.ecdsa.signature_s.buffer)) { |
| return MakeStatus<TPMError>("ECDSA signature overflow", |
| TPMRetryAction::kNoRetry); |
| } |
| ASSIGN_OR_RETURN( |
| crypto::ScopedECDSA_SIG sig, |
| CreateEcdsaSigFromRS(StringFrom_TPM2B_ECC_PARAMETER( |
| signature.signature.ecdsa.signature_r), |
| StringFrom_TPM2B_ECC_PARAMETER( |
| signature.signature.ecdsa.signature_s)), |
| _.WithStatus<TPMError>("Failed to create ECDSA SIG")); |
| std::string result = hwsec_foundation::ECDSASignatureToString(sig); |
| if (result.empty()) { |
| return MakeStatus<TPMError>("Failed to convert ECDSA signature", |
| TPMRetryAction::kNoRetry); |
| } |
| return result; |
| } |
| default: |
| return MakeStatus<TPMError>("Unkown TPM 2.0 signature type", |
| TPMRetryAction::kNoRetry); |
| } |
| } |
| |
| std::string GetTpm2PCRValueForMode( |
| const DeviceConfigSettings::BootModeSetting::Mode& mode) { |
| char boot_modes[3] = {mode.developer_mode, mode.recovery_mode, |
| mode.verified_firmware}; |
| std::string mode_str(std::begin(boot_modes), std::end(boot_modes)); |
| std::string mode_digest = base::SHA1HashString(mode_str); |
| mode_digest.resize(SHA256_DIGEST_LENGTH); |
| const std::string pcr_initial_value(SHA256_DIGEST_LENGTH, 0); |
| return crypto::SHA256HashString(pcr_initial_value + mode_digest); |
| } |
| |
| StatusOr<brillo::SecureBlob> GetEndorsementPassword( |
| org::chromium::TpmManagerProxyInterface& tpm_manager) { |
| tpm_manager::GetTpmStatusRequest status_request; |
| tpm_manager::GetTpmStatusReply status_reply; |
| if (brillo::ErrorPtr err; !tpm_manager.GetTpmStatus( |
| status_request, &status_reply, &err, Proxy::kDefaultDBusTimeoutMs)) { |
| return MakeStatus<TPMError>(TPMRetryAction::kCommunication) |
| .Wrap(std::move(err)); |
| } |
| RETURN_IF_ERROR(MakeStatus<TPMManagerError>(status_reply.status())); |
| |
| brillo::SecureBlob password(status_reply.local_data().endorsement_password()); |
| if (password.empty()) { |
| return MakeStatus<TPMError>("Empty endorsement password", |
| TPMRetryAction::kLater); |
| } |
| if (password.size() > kMaxPasswordLength) { |
| return MakeStatus<TPMError>("Endorsement password too large", |
| TPMRetryAction::kLater); |
| } |
| return password; |
| } |
| |
| } // namespace hwsec |