| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cryptohome/signature_sealing_backend_tpm2_impl.h" |
| |
| #include <cstring> |
| #include <optional> |
| #include <set> |
| #include <stdint.h> |
| #include <string> |
| #include <utility> |
| |
| #include <base/check.h> |
| #include <base/check_op.h> |
| #include <base/logging.h> |
| #include <base/memory/ptr_util.h> |
| #include <base/notreached.h> |
| #include <base/numerics/safe_conversions.h> |
| #include <base/threading/thread_checker.h> |
| #include <brillo/secure_blob.h> |
| #include <cryptohome/proto_bindings/key.pb.h> |
| #include <google/protobuf/repeated_field.h> |
| #include <libhwsec/error/tpm2_error.h> |
| #include <libhwsec/status.h> |
| #include <trunks/error_codes.h> |
| #include <trunks/policy_session.h> |
| #include <trunks/tpm_generated.h> |
| #include <trunks/trunks_factory_impl.h> |
| #include <trunks/hmac_session.h> |
| #include <trunks/tpm_utility.h> |
| #include <trunks/authorization_delegate.h> |
| |
| #include "cryptohome/signature_sealed_data.pb.h" |
| #include "cryptohome/tpm2_impl.h" |
| |
| using brillo::Blob; |
| using brillo::BlobFromString; |
| using brillo::BlobToString; |
| using brillo::CombineBlobs; |
| using brillo::SecureBlob; |
| using hwsec::TPM2Error; |
| using hwsec::TPMError; |
| using hwsec::TPMErrorBase; |
| using hwsec::TPMRetryAction; |
| using hwsec_foundation::error::CreateError; |
| using hwsec_foundation::error::WrapError; |
| using trunks::GetErrorString; |
| using trunks::TPM_ALG_ID; |
| using trunks::TPM_ALG_NULL; |
| using trunks::TPM_RC; |
| using trunks::TPM_RC_SUCCESS; |
| |
| namespace cryptohome { |
| |
| namespace { |
| |
| // Size, in bytes, of the secret value that is generated by |
| // SignatureSealingBackendTpm2Impl::CreateSealedSecret(). |
| constexpr int kSecretSizeBytes = 32; |
| |
| class UnsealingSessionTpm2Impl final |
| : public SignatureSealingBackend::UnsealingSession { |
| public: |
| UnsealingSessionTpm2Impl( |
| Tpm2Impl* tpm, |
| Tpm2Impl::TrunksClientContext* trunks, |
| const Blob& srk_wrapped_secret, |
| const Blob& public_key_spki_der, |
| structure::ChallengeSignatureAlgorithm algorithm, |
| TPM_ALG_ID scheme, |
| TPM_ALG_ID hash_alg, |
| std::unique_ptr<trunks::PolicySession> policy_session, |
| const Blob& policy_session_tpm_nonce); |
| UnsealingSessionTpm2Impl(const UnsealingSessionTpm2Impl&) = delete; |
| UnsealingSessionTpm2Impl& operator=(const UnsealingSessionTpm2Impl&) = delete; |
| |
| ~UnsealingSessionTpm2Impl() override; |
| |
| // UnsealingSession: |
| structure::ChallengeSignatureAlgorithm GetChallengeAlgorithm() override; |
| Blob GetChallengeValue() override; |
| hwsec::Status Unseal(const Blob& signed_challenge_value, |
| SecureBlob* unsealed_value) override; |
| |
| private: |
| // Unowned. |
| Tpm2Impl* const tpm_; |
| // Unowned. |
| Tpm2Impl::TrunksClientContext* const trunks_; |
| const Blob srk_wrapped_secret_; |
| const Blob public_key_spki_der_; |
| const structure::ChallengeSignatureAlgorithm algorithm_; |
| const TPM_ALG_ID scheme_; |
| const TPM_ALG_ID hash_alg_; |
| const std::unique_ptr<trunks::PolicySession> policy_session_; |
| const Blob policy_session_tpm_nonce_; |
| base::ThreadChecker thread_checker_; |
| }; |
| |
| // Obtains the TPM 2.0 signature scheme and hashing algorithms that correspond |
| // to the provided challenge signature algorithm. |
| bool GetAlgIdsByAlgorithm(structure::ChallengeSignatureAlgorithm algorithm, |
| TPM_ALG_ID* scheme, |
| TPM_ALG_ID* hash_alg) { |
| switch (algorithm) { |
| case structure::ChallengeSignatureAlgorithm::kRsassaPkcs1V15Sha1: |
| *scheme = trunks::TPM_ALG_RSASSA; |
| *hash_alg = trunks::TPM_ALG_SHA1; |
| return true; |
| case structure::ChallengeSignatureAlgorithm::kRsassaPkcs1V15Sha256: |
| *scheme = trunks::TPM_ALG_RSASSA; |
| *hash_alg = trunks::TPM_ALG_SHA256; |
| return true; |
| case structure::ChallengeSignatureAlgorithm::kRsassaPkcs1V15Sha384: |
| *scheme = trunks::TPM_ALG_RSASSA; |
| *hash_alg = trunks::TPM_ALG_SHA384; |
| return true; |
| case structure::ChallengeSignatureAlgorithm::kRsassaPkcs1V15Sha512: |
| *scheme = trunks::TPM_ALG_RSASSA; |
| *hash_alg = trunks::TPM_ALG_SHA512; |
| return true; |
| } |
| NOTREACHED(); |
| return false; |
| } |
| |
| UnsealingSessionTpm2Impl::UnsealingSessionTpm2Impl( |
| Tpm2Impl* tpm, |
| Tpm2Impl::TrunksClientContext* trunks, |
| const Blob& srk_wrapped_secret, |
| const Blob& public_key_spki_der, |
| structure::ChallengeSignatureAlgorithm algorithm, |
| TPM_ALG_ID scheme, |
| TPM_ALG_ID hash_alg, |
| std::unique_ptr<trunks::PolicySession> policy_session, |
| const Blob& policy_session_tpm_nonce) |
| : tpm_(tpm), |
| trunks_(trunks), |
| srk_wrapped_secret_(srk_wrapped_secret), |
| public_key_spki_der_(public_key_spki_der), |
| algorithm_(algorithm), |
| scheme_(scheme), |
| hash_alg_(hash_alg), |
| policy_session_(std::move(policy_session)), |
| policy_session_tpm_nonce_(policy_session_tpm_nonce) {} |
| |
| UnsealingSessionTpm2Impl::~UnsealingSessionTpm2Impl() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| |
| structure::ChallengeSignatureAlgorithm |
| UnsealingSessionTpm2Impl::GetChallengeAlgorithm() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return algorithm_; |
| } |
| |
| Blob UnsealingSessionTpm2Impl::GetChallengeValue() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| const Blob expiration_blob(4); // zero expiration (4-byte integer) |
| return CombineBlobs({policy_session_tpm_nonce_, expiration_blob}); |
| } |
| |
| hwsec::Status UnsealingSessionTpm2Impl::Unseal( |
| const Blob& signed_challenge_value, SecureBlob* unsealed_value) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| // Start a TPM authorization session. |
| std::unique_ptr<trunks::HmacSession> session = |
| trunks_->factory->GetHmacSession(); |
| if (auto err = CreateError<TPM2Error>( |
| trunks_->tpm_utility->StartSession(session.get()))) { |
| return WrapError<TPMError>(std::move(err), "Error starting hmac session"); |
| } |
| // Load the protection public key onto the TPM. |
| ScopedKeyHandle key_handle; |
| if (!tpm_->LoadPublicKeyFromSpki( |
| public_key_spki_der_, AsymmetricKeyUsage::kSignKey, scheme_, |
| hash_alg_, session->GetDelegate(), &key_handle)) { |
| return CreateError<TPMError>("Error loading protection key", |
| TPMRetryAction::kNoRetry); |
| } |
| std::string key_name; |
| if (auto err = CreateError<TPM2Error>( |
| trunks_->tpm_utility->GetKeyName(key_handle.value(), &key_name))) { |
| return WrapError<TPMError>(std::move(err), "Failed to get key name"); |
| } |
| // Update the policy with the signature. |
| trunks::TPMT_SIGNATURE signature; |
| memset(&signature, 0, sizeof(trunks::TPMT_SIGNATURE)); |
| signature.sig_alg = scheme_; |
| signature.signature.rsassa.hash = hash_alg_; |
| signature.signature.rsassa.sig = |
| trunks::Make_TPM2B_PUBLIC_KEY_RSA(BlobToString(signed_challenge_value)); |
| if (auto err = CreateError<TPM2Error>(policy_session_->PolicySigned( |
| key_handle.value(), key_name, BlobToString(policy_session_tpm_nonce_), |
| std::string() /* cp_hash */, std::string() /* policy_ref */, |
| 0 /* expiration */, signature, session->GetDelegate()))) { |
| return WrapError<TPMError>( |
| std::move(err), |
| "Error restricting policy to signature with the public key"); |
| } |
| // Obtain the resulting policy digest. |
| std::string policy_digest; |
| if (auto err = |
| CreateError<TPM2Error>(policy_session_->GetDigest(&policy_digest))) { |
| return WrapError<TPMError>(std::move(err), "Error getting policy digest"); |
| } |
| // Unseal the secret value. |
| std::string unsealed_value_string; |
| if (auto err = CreateError<TPM2Error>(trunks_->tpm_utility->UnsealData( |
| BlobToString(srk_wrapped_secret_), policy_session_->GetDelegate(), |
| &unsealed_value_string))) { |
| return WrapError<TPMError>(std::move(err), "Error unsealing object"); |
| } |
| *unsealed_value = SecureBlob(unsealed_value_string); |
| return nullptr; |
| } |
| |
| std::map<uint32_t, std::string> ToStrPcrMap( |
| const std::map<uint32_t, brillo::Blob>& pcr_map) { |
| std::map<uint32_t, std::string> str_pcr_map; |
| for (const auto& [index, value] : pcr_map) { |
| str_pcr_map[index] = brillo::BlobToString(value); |
| } |
| return str_pcr_map; |
| } |
| |
| hwsec::Status GetPcrPolicyDigest( |
| trunks::PolicySession* policy_session, |
| const std::map<uint32_t, brillo::Blob>& pcr_map, |
| std::string* pcr_policy_digest) { |
| std::map<uint32_t, std::string> str_pcr_map = ToStrPcrMap(pcr_map); |
| |
| // Run PolicyPCR against the PCR set. |
| if (auto err = |
| CreateError<TPM2Error>(policy_session->PolicyPCR(str_pcr_map))) { |
| return WrapError<TPMError>(std::move(err), |
| "Error restricting policy to PCRs"); |
| } |
| // Remember the policy digest for the PCR set. |
| if (auto err = CreateError<TPM2Error>( |
| policy_session->GetDigest(pcr_policy_digest))) { |
| return WrapError<TPMError>(std::move(err), "Error getting policy digest"); |
| } |
| |
| // Restart the policy session. |
| if (auto err = CreateError<TPM2Error>(policy_session->PolicyRestart())) { |
| return WrapError<TPMError>(std::move(err), |
| "Error restarting the policy session"); |
| } |
| return nullptr; |
| } |
| |
| } // namespace |
| |
| SignatureSealingBackendTpm2Impl::SignatureSealingBackendTpm2Impl(Tpm2Impl* tpm) |
| : tpm_(tpm) {} |
| |
| SignatureSealingBackendTpm2Impl::~SignatureSealingBackendTpm2Impl() = default; |
| |
| hwsec::Status SignatureSealingBackendTpm2Impl::CreateSealedSecret( |
| const Blob& public_key_spki_der, |
| const std::vector<structure::ChallengeSignatureAlgorithm>& key_algorithms, |
| const std::string& obfuscated_username, |
| const Blob& /* delegate_blob */, |
| const Blob& /* delegate_secret */, |
| SecureBlob* secret_value, |
| structure::SignatureSealedData* sealed_secret_data) { |
| // Choose the algorithm. Respect the input's algorithm prioritization, with |
| // the exception of considering SHA-1 as the least preferred option. |
| TPM_ALG_ID scheme = TPM_ALG_NULL; |
| TPM_ALG_ID hash_alg = TPM_ALG_NULL; |
| for (auto algorithm : key_algorithms) { |
| TPM_ALG_ID current_scheme = TPM_ALG_NULL; |
| TPM_ALG_ID current_hash_alg = TPM_ALG_NULL; |
| if (GetAlgIdsByAlgorithm(algorithm, ¤t_scheme, ¤t_hash_alg)) { |
| scheme = current_scheme; |
| hash_alg = current_hash_alg; |
| if (hash_alg != trunks::TPM_ALG_SHA1) |
| break; |
| } |
| } |
| if (scheme == TPM_ALG_NULL) { |
| return CreateError<TPMError>("Error choosing the signature algorithm", |
| TPMRetryAction::kNoRetry); |
| } |
| // Start a TPM authorization session. |
| Tpm2Impl::TrunksClientContext* trunks = nullptr; |
| if (!tpm_->GetTrunksContext(&trunks)) { |
| return CreateError<TPMError>("Failed to get trunks context", |
| TPMRetryAction::kNoRetry); |
| } |
| std::unique_ptr<trunks::HmacSession> session = |
| trunks->factory->GetHmacSession(); |
| if (auto err = CreateError<TPM2Error>( |
| trunks->tpm_utility->StartSession(session.get()))) { |
| return WrapError<TPMError>(std::move(err), "Error starting hmac session"); |
| } |
| // Load the protection public key onto the TPM. |
| ScopedKeyHandle key_handle; |
| if (!tpm_->LoadPublicKeyFromSpki( |
| public_key_spki_der, AsymmetricKeyUsage::kSignKey, scheme, hash_alg, |
| session->GetDelegate(), &key_handle)) { |
| return CreateError<TPMError>("Error loading protection key", |
| TPMRetryAction::kNoRetry); |
| } |
| std::string key_name; |
| if (auto err = CreateError<TPM2Error>( |
| trunks->tpm_utility->GetKeyName(key_handle.value(), &key_name))) { |
| return WrapError<TPMError>(std::move(err), "Failed to get key name"); |
| } |
| // Start a trial policy session for sealing the secret value. |
| std::unique_ptr<trunks::PolicySession> policy_session = |
| trunks->factory->GetTrialSession(); |
| if (auto err = CreateError<TPM2Error>( |
| policy_session->StartUnboundSession(true, false))) { |
| return WrapError<TPMError>(std::move(err), |
| "Error starting a trial session"); |
| } |
| |
| std::map<uint32_t, brillo::Blob> default_pcr_map = |
| tpm_->GetPcrMap(obfuscated_username, /*use_extended_pcr=*/false); |
| std::map<uint32_t, brillo::Blob> extended_pcr_map = |
| tpm_->GetPcrMap(obfuscated_username, /*use_extended_pcr=*/true); |
| |
| // Calculate policy digests for each of the sets of PCR restrictions |
| // separately. Rewind each time the policy session back to the initial state. |
| std::vector<std::string> pcr_policy_digests; |
| |
| std::string default_pcr_policy_digest; |
| if (hwsec::Status err = GetPcrPolicyDigest( |
| policy_session.get(), default_pcr_map, &default_pcr_policy_digest)) { |
| return WrapError<TPMError>(std::move(err), |
| "Error getting default PCR policy digest"); |
| } |
| pcr_policy_digests.push_back(default_pcr_policy_digest); |
| |
| std::string extended_pcr_policy_digest; |
| if (hwsec::Status err = GetPcrPolicyDigest( |
| policy_session.get(), default_pcr_map, &extended_pcr_policy_digest)) { |
| return WrapError<TPMError>(std::move(err), |
| "Error getting default PCR policy digest"); |
| } |
| pcr_policy_digests.push_back(extended_pcr_policy_digest); |
| |
| // Apply PolicyOR for restricting to the disjunction of the specified sets of |
| // PCR restrictions. |
| if (auto err = CreateError<TPM2Error>( |
| policy_session->PolicyOR(pcr_policy_digests))) { |
| return WrapError<TPMError>( |
| std::move(err), |
| "Error restricting policy to logical disjunction of PCRs"); |
| } |
| |
| // Update the policy with an empty signature that refers to the public key. |
| trunks::TPMT_SIGNATURE signature; |
| memset(&signature, 0, sizeof(trunks::TPMT_SIGNATURE)); |
| signature.sig_alg = scheme; |
| signature.signature.rsassa.hash = hash_alg; |
| signature.signature.rsassa.sig = |
| trunks::Make_TPM2B_PUBLIC_KEY_RSA(std::string()); |
| if (auto err = CreateError<TPM2Error>(policy_session->PolicySigned( |
| key_handle.value(), key_name, std::string() /* nonce */, |
| std::string() /* cp_hash */, std::string() /* policy_ref */, |
| 0 /* expiration */, signature, session->GetDelegate()))) { |
| return WrapError<TPMError>( |
| std::move(err), |
| "Error restricting policy to signature with the public key"); |
| } |
| // Obtain the resulting policy digest. |
| std::string policy_digest; |
| if (auto err = |
| CreateError<TPM2Error>(policy_session->GetDigest(&policy_digest))) { |
| return WrapError<TPMError>(std::move(err), "Error getting policy digest"); |
| } |
| if (policy_digest.size() != SHA256_DIGEST_SIZE) { |
| return CreateError<TPMError>("Unexpected policy digest size", |
| TPMRetryAction::kNoRetry); |
| } |
| // Generate the secret value randomly. |
| if (auto err = |
| tpm_->GetRandomDataSecureBlob(kSecretSizeBytes, secret_value)) { |
| return WrapError<TPMError>(std::move(err), |
| "Error generating random secret"); |
| } |
| DCHECK_EQ(secret_value->size(), kSecretSizeBytes); |
| // Seal the secret value. |
| std::string sealed_value; |
| if (auto err = CreateError<TPM2Error>(trunks->tpm_utility->SealData( |
| secret_value->to_string(), policy_digest, "", |
| /*require_admin_with_policy=*/true, session->GetDelegate(), |
| &sealed_value))) { |
| return WrapError<TPMError>(std::move(err), "Error sealing secret data"); |
| } |
| // Fill the resulting proto with data required for unsealing. |
| structure::Tpm2PolicySignedData data; |
| data.public_key_spki_der = public_key_spki_der; |
| data.srk_wrapped_secret = BlobFromString(sealed_value); |
| data.scheme = scheme; |
| data.hash_alg = hash_alg; |
| data.default_pcr_policy_digest = BlobFromString(default_pcr_policy_digest); |
| data.extended_pcr_policy_digest = BlobFromString(extended_pcr_policy_digest); |
| *sealed_secret_data = data; |
| return nullptr; |
| } |
| |
| hwsec::Status SignatureSealingBackendTpm2Impl::CreateUnsealingSession( |
| const structure::SignatureSealedData& sealed_secret_data, |
| const Blob& public_key_spki_der, |
| const std::vector<structure::ChallengeSignatureAlgorithm>& key_algorithms, |
| const std::set<uint32_t>& pcr_set, |
| const Blob& /* delegate_blob */, |
| const Blob& /* delegate_secret */, |
| bool /* locked_to_single_user */, |
| std::unique_ptr<SignatureSealingBackend::UnsealingSession>* |
| unsealing_session) { |
| // Validate the parameters. |
| auto* sealed_secret_data_ptr = |
| std::get_if<structure::Tpm2PolicySignedData>(&sealed_secret_data); |
| if (!sealed_secret_data_ptr) { |
| return CreateError<TPMError>( |
| "Sealed data is empty or uses unexpected method", |
| TPMRetryAction::kNoRetry); |
| } |
| const structure::Tpm2PolicySignedData& data = *sealed_secret_data_ptr; |
| |
| if (data.public_key_spki_der.empty()) { |
| return CreateError<TPMError>("Empty public key", TPMRetryAction::kNoRetry); |
| } |
| if (data.srk_wrapped_secret.empty()) { |
| return CreateError<TPMError>("Empty SRK wrapped secret", |
| TPMRetryAction::kNoRetry); |
| } |
| if (!data.scheme.has_value()) { |
| return CreateError<TPMError>("Empty scheme", TPMRetryAction::kNoRetry); |
| } |
| if (!data.hash_alg.has_value()) { |
| return CreateError<TPMError>("Empty hash algorithm", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| if (data.public_key_spki_der != public_key_spki_der) { |
| return CreateError<TPMError>("Wrong subject public key info", |
| TPMRetryAction::kNoRetry); |
| } |
| if (!base::IsValueInRangeForNumericType<TPM_ALG_ID>(data.scheme.value())) { |
| return CreateError<TPMError>("Error parsing signature scheme", |
| TPMRetryAction::kNoRetry); |
| } |
| const TPM_ALG_ID scheme = static_cast<TPM_ALG_ID>(data.scheme.value()); |
| if (!base::IsValueInRangeForNumericType<TPM_ALG_ID>(data.hash_alg.value())) { |
| return CreateError<TPMError>("Error parsing signature hash algorithm", |
| TPMRetryAction::kNoRetry); |
| } |
| const TPM_ALG_ID hash_alg = static_cast<TPM_ALG_ID>(data.hash_alg.value()); |
| std::optional<structure::ChallengeSignatureAlgorithm> chosen_algorithm; |
| for (auto algorithm : key_algorithms) { |
| TPM_ALG_ID current_scheme = TPM_ALG_NULL; |
| TPM_ALG_ID current_hash_alg = TPM_ALG_NULL; |
| if (GetAlgIdsByAlgorithm(algorithm, ¤t_scheme, ¤t_hash_alg) && |
| current_scheme == scheme && current_hash_alg == hash_alg) { |
| chosen_algorithm = algorithm; |
| break; |
| } |
| } |
| if (!chosen_algorithm) { |
| return CreateError<TPMError>("Key doesn't support required algorithm", |
| TPMRetryAction::kNoRetry); |
| } |
| // Obtain the trunks context to be used for the whole unsealing session. |
| Tpm2Impl::TrunksClientContext* trunks = nullptr; |
| if (!tpm_->GetTrunksContext(&trunks)) { |
| return CreateError<TPMError>("Failed to get trunks context", |
| TPMRetryAction::kNoRetry); |
| } |
| // Start a policy session that will be used for obtaining the TPM nonce and |
| // unsealing the secret value. |
| std::unique_ptr<trunks::PolicySession> policy_session = |
| trunks->factory->GetPolicySession(); |
| if (auto err = CreateError<TPM2Error>( |
| policy_session->StartUnboundSession(true, false))) { |
| return WrapError<TPMError>(std::move(err), |
| "Error starting a policy session"); |
| } |
| |
| if (data.default_pcr_policy_digest.empty() || |
| data.extended_pcr_policy_digest.empty()) { |
| return CreateError<TPMError>("Empty PCR policy digests", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| if (data.default_pcr_policy_digest.size() != SHA256_DIGEST_SIZE || |
| data.extended_pcr_policy_digest.size() != SHA256_DIGEST_SIZE) { |
| return CreateError<TPMError>("Invalid policy digest size", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| // The PCR map with empty user name. |
| std::map<uint32_t, std::string> pcr_map; |
| |
| // Create a PCR map with empty strings to use current PCR values. |
| for (uint32_t pcr_index : pcr_set) { |
| pcr_map[pcr_index] = std::string(); |
| } |
| |
| if (auto err = CreateError<TPM2Error>(policy_session->PolicyPCR(pcr_map))) { |
| return WrapError<TPMError>(std::move(err), |
| "Error restricting policy to PCRs"); |
| } |
| |
| // Update the policy with the disjunction of their policy digests. |
| // Note: The order of items in this vector is important, it must match the |
| // order used in the CreateSealedSecret() function, and should never change |
| // due to backwards compatibility. |
| std::vector<std::string> pcr_policy_digests; |
| pcr_policy_digests.push_back(BlobToString(data.default_pcr_policy_digest)); |
| pcr_policy_digests.push_back(BlobToString(data.extended_pcr_policy_digest)); |
| |
| if (auto err = CreateError<TPM2Error>( |
| policy_session->PolicyOR(pcr_policy_digests))) { |
| return WrapError<TPMError>( |
| std::move(err), |
| "Error restricting policy to logical disjunction of PCRs"); |
| } |
| |
| // Obtain the TPM nonce. |
| std::string tpm_nonce; |
| if (!policy_session->GetDelegate()->GetTpmNonce(&tpm_nonce)) { |
| return CreateError<TPMError>("Error obtaining TPM nonce", |
| TPMRetryAction::kNoRetry); |
| } |
| |
| // Create the unsealing session that will keep the required state. |
| *unsealing_session = std::make_unique<UnsealingSessionTpm2Impl>( |
| tpm_, trunks, data.srk_wrapped_secret, public_key_spki_der, |
| *chosen_algorithm, scheme, hash_alg, std::move(policy_session), |
| BlobFromString(tpm_nonce)); |
| return nullptr; |
| } |
| |
| } // namespace cryptohome |