| // 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/tpm2/deriving.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| |
| #include <base/functional/callback_helpers.h> |
| #include <brillo/secure_blob.h> |
| #include <libhwsec-foundation/crypto/big_num_util.h> |
| #include <libhwsec-foundation/crypto/elliptic_curve.h> |
| #include <libhwsec-foundation/crypto/sha.h> |
| #include <libhwsec-foundation/status/status_chain_macros.h> |
| #include <trunks/openssl_utility.h> |
| #include <trunks/tpm_utility.h> |
| |
| #include "libhwsec/error/tpm2_error.h" |
| #include "libhwsec/status.h" |
| |
| using brillo::Blob; |
| using brillo::SecureBlob; |
| using hwsec_foundation::CreateBigNumContext; |
| using hwsec_foundation::EllipticCurve; |
| using hwsec_foundation::ScopedBN_CTX; |
| using hwsec_foundation::SecureBlobToBigNum; |
| using hwsec_foundation::Sha256; |
| using hwsec_foundation::status::MakeStatus; |
| |
| namespace hwsec { |
| |
| namespace { |
| |
| constexpr uint32_t kDefaultTpmRsaModulusSize = 2048; |
| |
| constexpr int kMinDeriveBlobSize = 32; |
| |
| constexpr EllipticCurve::CurveType kDefaultCurve = |
| EllipticCurve::CurveType::kPrime256; |
| |
| hwsec::StatusOr<trunks::TPMS_ECC_POINT> DeriveTpmEccPointFromSeed( |
| const SecureBlob& seed) { |
| // Generate an ECC private key (scalar) based on the seed. |
| crypto::ScopedBIGNUM private_key = SecureBlobToBigNum(Sha256(seed)); |
| |
| ScopedBN_CTX context = CreateBigNumContext(); |
| if (!context.get()) { |
| return MakeStatus<TPMError>("Failed to allocate BN_CTX structure", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| std::optional<EllipticCurve> ec = |
| EllipticCurve::Create(kDefaultCurve, context.get()); |
| if (!ec) { |
| return MakeStatus<TPMError>("Failed to create EllipticCurve", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| if (!ec->IsScalarValid(*private_key)) { |
| // Generate another blob may resolve this issue. |
| return MakeStatus<TPMError>("ECC scalar out of range", |
| TPMRetryAction::kEllipticCurveScalarOutOfRange); |
| } |
| |
| crypto::ScopedEC_POINT public_point = |
| ec->MultiplyWithGenerator(*private_key, context.get()); |
| |
| if (!public_point) { |
| return MakeStatus<TPMError>("Failed to multiply with generator", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| trunks::TPMS_ECC_POINT out_point; |
| |
| if (!trunks::OpensslToTpmEccPoint(*ec->GetGroup(), *public_point, |
| ec->AffineCoordinateSizeInBytes(), |
| &out_point)) { |
| return MakeStatus<TPMError>("Error converting OpenSSL to TPM ECC point", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| return out_point; |
| } |
| |
| } // namespace |
| |
| StatusOr<Blob> DerivingTpm2::Derive(Key key, const Blob& blob) { |
| ASSIGN_OR_RETURN( |
| const SecureBlob& result, |
| SecureDerive(std::move(key), SecureBlob(blob.begin(), blob.end())), |
| _.WithStatus<TPMError>("Failed to derive secure blob")); |
| return Blob(result.begin(), result.end()); |
| } |
| |
| StatusOr<SecureBlob> DerivingTpm2::SecureDerive(Key key, |
| const SecureBlob& blob) { |
| ASSIGN_OR_RETURN(const KeyTpm2& key_data, key_management_.GetKeyData(key)); |
| |
| switch (key_data.cache.public_area.type) { |
| case trunks::TPM_ALG_RSA: |
| return DeriveRsaKey(key_data, blob); |
| |
| case trunks::TPM_ALG_ECC: |
| return DeriveEccKey(key_data, blob); |
| |
| default: |
| return MakeStatus<TPMError>("Unknown algorithm", |
| TPMRetryAction::kNoRetry); |
| } |
| } |
| |
| StatusOr<SecureBlob> DerivingTpm2::DeriveRsaKey(const KeyTpm2& key_data, |
| const SecureBlob& blob) { |
| if (blob.size() != kDefaultTpmRsaModulusSize / 8) { |
| return MakeStatus<TPMError>( |
| "Unexpected blob size: " + std::to_string(blob.size()), |
| TPMRetryAction::kNoRetry); |
| } |
| |
| // To guarantee that pass_blob is lower that public key modulus, just set the |
| // first byte to 0. |
| std::string value_to_decrypt = blob.to_string(); |
| value_to_decrypt[0] = 0; |
| |
| // Cleanup the data from secure blob. |
| base::ScopedClosureRunner cleanup_value_to_decrypt(base::BindOnce( |
| brillo::SecureClearContainer<std::string>, std::ref(value_to_decrypt))); |
| |
| ASSIGN_OR_RETURN( |
| ConfigTpm2::TrunksSession session, |
| config_.GetTrunksSession(key_data.cache.policy, |
| SessionSecuritySetting::kNoEncrypted), |
| _.WithStatus<TPMError>("Failed to get session for policy")); |
| |
| std::string decrypted_value; |
| |
| RETURN_IF_ERROR( |
| MakeStatus<TPM2Error>(context_.GetTpmUtility().AsymmetricDecrypt( |
| key_data.key_handle, trunks::TPM_ALG_NULL, trunks::TPM_ALG_NULL, |
| value_to_decrypt, session.delegate, &decrypted_value))) |
| .WithStatus<TPMError>("Failed to decrypt blob"); |
| |
| return Sha256(SecureBlob(decrypted_value)); |
| } |
| |
| StatusOr<SecureBlob> DerivingTpm2::DeriveEccKey(const KeyTpm2& key_data, |
| const SecureBlob& blob) { |
| if (blob.size() < kMinDeriveBlobSize) { |
| return MakeStatus<TPMError>( |
| "Unexpected blob size: " + std::to_string(blob.size()), |
| TPMRetryAction::kNoRetry); |
| } |
| |
| ASSIGN_OR_RETURN( |
| const trunks::TPMS_ECC_POINT& ecc_point, DeriveTpmEccPointFromSeed(blob), |
| _.WithStatus<TPMError>("Failed to derive TPM ECC point from seed")); |
| |
| trunks::TPM2B_ECC_POINT in_point = trunks::Make_TPM2B_ECC_POINT(ecc_point); |
| trunks::TPM2B_ECC_POINT z_point; |
| |
| ASSIGN_OR_RETURN( |
| ConfigTpm2::TrunksSession session, |
| config_.GetTrunksSession(key_data.cache.policy, |
| SessionSecuritySetting::kNoEncrypted), |
| _.WithStatus<TPMError>("Failed to get session for policy")); |
| |
| RETURN_IF_ERROR( |
| MakeStatus<TPM2Error>(context_.GetTpmUtility().ECDHZGen( |
| key_data.key_handle, in_point, session.delegate, &z_point))) |
| .WithStatus<TPMError>("Failed to ECDH ZGen"); |
| |
| if (z_point.point.x.size > sizeof(z_point.point.x.buffer)) { |
| return MakeStatus<TPMError>("Z point overflow", TPMRetryAction::kNoRetry); |
| } |
| |
| return Sha256(SecureBlob(StringFrom_TPM2B_ECC_PARAMETER(z_point.point.x))); |
| } |
| |
| } // namespace hwsec |