// // Copyright (C) 2015 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "trunks/trunks_client_test.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trunks/authorization_delegate.h" #include "trunks/error_codes.h" #include "trunks/hmac_session.h" #include "trunks/policy_session.h" #include "trunks/scoped_key_handle.h" #include "trunks/tpm_constants.h" #include "trunks/tpm_generated.h" #include "trunks/tpm_state.h" #include "trunks/tpm_utility.h" #include "trunks/trunks_factory_impl.h" namespace { std::string GetOpenSSLError() { BIO* bio = BIO_new(BIO_s_mem()); ERR_print_errors(bio); char* data = nullptr; int data_len = BIO_get_mem_data(bio, &data); std::string error_string(data, data_len); BIO_free(bio); return error_string; } } // namespace namespace trunks { TrunksClientTest::TrunksClientTest(const TrunksFactory& factory) : factory_(factory) { crypto::EnsureOpenSSLInit(); } TrunksClientTest::~TrunksClientTest() {} bool TrunksClientTest::RNGTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr session = factory_.GetHmacSession(); if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session."; return false; } std::string entropy_data("entropy_data"); std::string random_data; size_t num_bytes = 70; TPM_RC result = utility->StirRandom(entropy_data, session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error stirring TPM RNG: " << GetErrorString(result); return false; } result = utility->GenerateRandom(num_bytes, session->GetDelegate(), &random_data); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error getting random bytes from TPM: " << GetErrorString(result); return false; } if (num_bytes != random_data.size()) { LOG(ERROR) << "Error not enough random bytes received."; return false; } return true; } bool TrunksClientTest::SignTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr session = factory_.GetHmacSession(); if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session."; return false; } std::string key_authorization("sign"); std::string key_blob; TPM_RC result = utility->CreateRSAKeyPair( TpmUtility::AsymmetricKeyUsage::kSignKey, 2048, 0x10001, key_authorization, "", false, // use_only_policy_authorization kNoCreationPCR, session->GetDelegate(), &key_blob, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error creating signing key: " << GetErrorString(result); return false; } TPM_HANDLE signing_key; result = utility->LoadKey(key_blob, session->GetDelegate(), &signing_key); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error loading signing key: " << GetErrorString(result); } ScopedKeyHandle scoped_key(factory_, signing_key); session->SetEntityAuthorizationValue(key_authorization); std::string signature; result = utility->Sign(signing_key, TPM_ALG_NULL, TPM_ALG_NULL, std::string(32, 'a'), session->GetDelegate(), &signature); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to sign: " << GetErrorString(result); return false; } result = utility->Verify(signing_key, TPM_ALG_NULL, TPM_ALG_NULL, std::string(32, 'a'), signature, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to verify: " << GetErrorString(result); return false; } return true; } bool TrunksClientTest::DecryptTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr session = factory_.GetHmacSession(); if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session."; return false; } std::string key_authorization("decrypt"); std::string key_blob; TPM_RC result = utility->CreateRSAKeyPair( TpmUtility::AsymmetricKeyUsage::kDecryptKey, 2048, 0x10001, key_authorization, "", false, // use_only_policy_authorization kNoCreationPCR, session->GetDelegate(), &key_blob, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error creating decrypt key: " << GetErrorString(result); return false; } TPM_HANDLE decrypt_key; result = utility->LoadKey(key_blob, session->GetDelegate(), &decrypt_key); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error loading decrypt key: " << GetErrorString(result); } ScopedKeyHandle scoped_key(factory_, decrypt_key); return PerformRSAEncrpytAndDecrpyt(scoped_key.get(), key_authorization, session.get()); } bool TrunksClientTest::ImportTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr session = factory_.GetHmacSession(); if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session."; return false; } std::string modulus; std::string prime_factor; GenerateRSAKeyPair(&modulus, &prime_factor, nullptr); std::string key_blob; std::string key_authorization("import"); TPM_RC result = utility->ImportRSAKey( TpmUtility::AsymmetricKeyUsage::kDecryptAndSignKey, modulus, 0x10001, prime_factor, key_authorization, session->GetDelegate(), &key_blob); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error importing key into TPM: " << GetErrorString(result); return false; } TPM_HANDLE key_handle; result = utility->LoadKey(key_blob, session->GetDelegate(), &key_handle); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error loading key into TPM: " << GetErrorString(result); return false; } ScopedKeyHandle scoped_key(factory_, key_handle); return PerformRSAEncrpytAndDecrpyt(scoped_key.get(), key_authorization, session.get()); } bool TrunksClientTest::AuthChangeTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr session = factory_.GetHmacSession(); if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session."; return false; } std::string key_authorization("new_pass"); std::string key_blob; TPM_RC result = utility->CreateRSAKeyPair( TpmUtility::AsymmetricKeyUsage::kDecryptKey, 2048, 0x10001, "old_pass", "", false, // use_only_policy_authorization kNoCreationPCR, session->GetDelegate(), &key_blob, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error creating change auth key: " << GetErrorString(result); return false; } TPM_HANDLE key_handle; result = utility->LoadKey(key_blob, session->GetDelegate(), &key_handle); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error loading change auth key: " << GetErrorString(result); } ScopedKeyHandle scoped_key(factory_, key_handle); session->SetEntityAuthorizationValue("old_pass"); result = utility->ChangeKeyAuthorizationData( key_handle, key_authorization, session->GetDelegate(), &key_blob); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error changing auth data: " << GetErrorString(result); return false; } session->SetEntityAuthorizationValue(""); result = utility->LoadKey(key_blob, session->GetDelegate(), &key_handle); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error reloading key: " << GetErrorString(result); return false; } scoped_key.reset(key_handle); return PerformRSAEncrpytAndDecrpyt(scoped_key.get(), key_authorization, session.get()); } bool TrunksClientTest::VerifyKeyCreationTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr session = factory_.GetHmacSession(); if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session."; return false; } std::string key_blob; std::string creation_blob; session->SetEntityAuthorizationValue(""); TPM_RC result = utility->CreateRSAKeyPair( TpmUtility::AsymmetricKeyUsage::kDecryptKey, 2048, 0x10001, "", "", false, // use_only_policy_authorization kNoCreationPCR, session->GetDelegate(), &key_blob, &creation_blob); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error creating certify key: " << GetErrorString(result); return false; } std::string alternate_key_blob; result = utility->CreateRSAKeyPair( TpmUtility::AsymmetricKeyUsage::kDecryptKey, 2048, 0x10001, "", "", false, // use_only_policy_authorization kNoCreationPCR, session->GetDelegate(), &alternate_key_blob, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error creating alternate key: " << GetErrorString(result); return false; } TPM_HANDLE key_handle; result = utility->LoadKey(key_blob, session->GetDelegate(), &key_handle); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error loading certify key: " << GetErrorString(result); return false; } TPM_HANDLE alternate_key_handle; result = utility->LoadKey(alternate_key_blob, session->GetDelegate(), &alternate_key_handle); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error loading alternate key: " << GetErrorString(result); return false; } ScopedKeyHandle certify_key(factory_, key_handle); ScopedKeyHandle alternate_key(factory_, alternate_key_handle); result = utility->CertifyCreation(certify_key.get(), creation_blob); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error certifying key: " << GetErrorString(result); return false; } result = utility->CertifyCreation(alternate_key.get(), creation_blob); if (result == TPM_RC_SUCCESS) { LOG(ERROR) << "Error alternate key certified with wrong creation data."; return false; } return true; } bool TrunksClientTest::SealedDataTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr session = factory_.GetHmacSession(); if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session."; return false; } int pcr_index = 5; std::string policy_digest; TPM_RC result = utility->GetPolicyDigestForPcrValue(pcr_index, "", &policy_digest); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error getting policy_digest: " << GetErrorString(result); return false; } std::string data_to_seal("seal_data"); std::string sealed_data; result = utility->SealData(data_to_seal, policy_digest, session->GetDelegate(), &sealed_data); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error creating Sealed Object: " << GetErrorString(result); return false; } std::unique_ptr policy_session = factory_.GetPolicySession(); result = policy_session->StartUnboundSession(false); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting policy session: " << GetErrorString(result); return false; } result = policy_session->PolicyPCR(pcr_index, ""); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy to pcr value: " << GetErrorString(result); return false; } std::string unsealed_data; result = utility->UnsealData(sealed_data, policy_session->GetDelegate(), &unsealed_data); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error unsealing object: " << GetErrorString(result); return false; } if (data_to_seal != unsealed_data) { LOG(ERROR) << "Error unsealed data from TPM does not match original data."; return false; } result = utility->ExtendPCR(pcr_index, "extend", session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error extending pcr: " << GetErrorString(result); return false; } result = policy_session->PolicyPCR(pcr_index, ""); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy to pcr value: " << GetErrorString(result); return false; } result = utility->UnsealData(sealed_data, policy_session->GetDelegate(), &unsealed_data); if (result == TPM_RC_SUCCESS) { LOG(ERROR) << "Error object was unsealed with wrong policy_digest."; return false; } return true; } bool TrunksClientTest::PCRTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr session = factory_.GetHmacSession(); if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session."; return false; } // We are using PCR 2 because it is currently not used by ChromeOS. uint32_t pcr_index = 2; std::string extend_data("data"); std::string old_data; TPM_RC result = utility->ReadPCR(pcr_index, &old_data); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error reading from PCR: " << GetErrorString(result); return false; } result = utility->ExtendPCR(pcr_index, extend_data, session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error extending PCR value: " << GetErrorString(result); return false; } std::string pcr_data; result = utility->ReadPCR(pcr_index, &pcr_data); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error reading from PCR: " << GetErrorString(result); return false; } std::string hashed_extend_data = crypto::SHA256HashString(extend_data); std::string expected_pcr_data = crypto::SHA256HashString(old_data + hashed_extend_data); if (pcr_data.compare(expected_pcr_data) != 0) { LOG(ERROR) << "PCR data does not match expected value."; return false; } return true; } bool TrunksClientTest::PolicyAuthValueTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr trial_session = factory_.GetTrialSession(); TPM_RC result; result = trial_session->StartUnboundSession(true); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting policy session: " << GetErrorString(result); return false; } result = trial_session->PolicyAuthValue(); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy to auth value knowledge: " << GetErrorString(result); return false; } std::string policy_digest; result = trial_session->GetDigest(&policy_digest); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error getting policy digest: " << GetErrorString(result); return false; } // Now that we have the digest, we can close the trial session and use hmac. trial_session.reset(); std::unique_ptr hmac_session = factory_.GetHmacSession(); result = hmac_session->StartUnboundSession(true); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session: " << GetErrorString(result); return false; } std::string key_blob; result = utility->CreateRSAKeyPair( TpmUtility::AsymmetricKeyUsage::kDecryptAndSignKey, 2048, 0x10001, "password", policy_digest, true, // use_only_policy_authorization kNoCreationPCR, hmac_session->GetDelegate(), &key_blob, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error creating RSA key: " << GetErrorString(result); return false; } TPM_HANDLE key_handle; result = utility->LoadKey(key_blob, hmac_session->GetDelegate(), &key_handle); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error loading RSA key: " << GetErrorString(result); return false; } ScopedKeyHandle scoped_key(factory_, key_handle); // Now we can reset the hmac_session. hmac_session.reset(); std::unique_ptr policy_session = factory_.GetPolicySession(); result = policy_session->StartUnboundSession(false); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting policy session: " << GetErrorString(result); return false; } result = policy_session->PolicyAuthValue(); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy to auth value knowledge: " << GetErrorString(result); return false; } std::string signature; policy_session->SetEntityAuthorizationValue("password"); result = utility->Sign(scoped_key.get(), TPM_ALG_NULL, TPM_ALG_NULL, std::string(32, 0), policy_session->GetDelegate(), &signature); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error signing using RSA key: " << GetErrorString(result); return false; } result = utility->Verify(scoped_key.get(), TPM_ALG_NULL, TPM_ALG_NULL, std::string(32, 0), signature, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error verifying using RSA key: " << GetErrorString(result); return false; } std::string ciphertext; result = utility->AsymmetricEncrypt(scoped_key.get(), TPM_ALG_NULL, TPM_ALG_NULL, "plaintext", nullptr, &ciphertext); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error encrypting using RSA key: " << GetErrorString(result); return false; } result = policy_session->PolicyAuthValue(); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy to auth value knowledge: " << GetErrorString(result); return false; } std::string plaintext; policy_session->SetEntityAuthorizationValue("password"); result = utility->AsymmetricDecrypt( scoped_key.get(), TPM_ALG_NULL, TPM_ALG_NULL, ciphertext, policy_session->GetDelegate(), &plaintext); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error encrypting using RSA key: " << GetErrorString(result); return false; } if (plaintext.compare("plaintext") != 0) { LOG(ERROR) << "Plaintext changed after encrypt + decrypt."; return false; } return true; } bool TrunksClientTest::PolicyAndTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr trial_session = factory_.GetTrialSession(); TPM_RC result; result = trial_session->StartUnboundSession(true); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting policy session: " << GetErrorString(result); return false; } result = trial_session->PolicyCommandCode(TPM_CC_Sign); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } uint32_t pcr_index = 2; std::string pcr_value; result = utility->ReadPCR(pcr_index, &pcr_value); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error reading pcr: " << GetErrorString(result); return false; } std::string pcr_extend_data("extend"); std::string next_pcr_value; std::string hashed_extend_data = crypto::SHA256HashString(pcr_extend_data); next_pcr_value = crypto::SHA256HashString(pcr_value + hashed_extend_data); result = trial_session->PolicyPCR(pcr_index, next_pcr_value); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } std::string policy_digest; result = trial_session->GetDigest(&policy_digest); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error getting policy digest: " << GetErrorString(result); return false; } // Now that we have the digest, we can close the trial session and use hmac. trial_session.reset(); std::unique_ptr hmac_session = factory_.GetHmacSession(); result = hmac_session->StartUnboundSession(true); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session: " << GetErrorString(result); return false; } std::string key_authorization("password"); std::string key_blob; // This key is created with a policy that dictates it can only be used // when pcr 2 remains unchanged, and when the command is TPM2_Sign. result = utility->CreateRSAKeyPair( TpmUtility::AsymmetricKeyUsage::kDecryptAndSignKey, 2048, 0x10001, key_authorization, policy_digest, true, // use_only_policy_authorization kNoCreationPCR, hmac_session->GetDelegate(), &key_blob, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error creating RSA key: " << GetErrorString(result); return false; } TPM_HANDLE key_handle; result = utility->LoadKey(key_blob, hmac_session->GetDelegate(), &key_handle); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error loading RSA key: " << GetErrorString(result); return false; } ScopedKeyHandle scoped_key(factory_, key_handle); // Now we can reset the hmac_session. hmac_session.reset(); std::unique_ptr policy_session = factory_.GetPolicySession(); result = policy_session->StartUnboundSession(false); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting policy session: " << GetErrorString(result); return false; } result = policy_session->PolicyCommandCode(TPM_CC_Sign); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } result = policy_session->PolicyPCR(pcr_index, ""); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } std::string signature; policy_session->SetEntityAuthorizationValue(key_authorization); // Signing with this key when pcr 2 is unchanged fails. result = utility->Sign(scoped_key.get(), TPM_ALG_NULL, TPM_ALG_NULL, std::string(32, 'a'), policy_session->GetDelegate(), &signature); if (GetFormatOneError(result) != TPM_RC_POLICY_FAIL) { LOG(ERROR) << "Error using key to sign: " << GetErrorString(result); return false; } std::unique_ptr delegate = factory_.GetPasswordAuthorization(""); result = utility->ExtendPCR(pcr_index, pcr_extend_data, delegate.get()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error extending pcr: " << GetErrorString(result); return false; } // we have to restart the session because we changed the pcr values. result = policy_session->StartUnboundSession(false); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting policy session: " << GetErrorString(result); return false; } result = policy_session->PolicyCommandCode(TPM_CC_Sign); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } result = policy_session->PolicyPCR(pcr_index, ""); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } policy_session->SetEntityAuthorizationValue(key_authorization); // Signing with this key when pcr 2 is changed succeeds. result = utility->Sign(scoped_key.get(), TPM_ALG_NULL, TPM_ALG_NULL, std::string(32, 'a'), policy_session->GetDelegate(), &signature); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to sign: " << GetErrorString(result); return false; } result = utility->Verify(scoped_key.get(), TPM_ALG_NULL, TPM_ALG_NULL, std::string(32, 'a'), signature, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to verify: " << GetErrorString(result); return false; } std::string ciphertext; result = utility->AsymmetricEncrypt(key_handle, TPM_ALG_NULL, TPM_ALG_NULL, "plaintext", nullptr, &ciphertext); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to encrypt: " << GetErrorString(result); return false; } result = policy_session->PolicyCommandCode(TPM_CC_Sign); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } result = policy_session->PolicyPCR(pcr_index, ""); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } std::string plaintext; policy_session->SetEntityAuthorizationValue(key_authorization); // This call is not authorized with the policy, because its command code // is not TPM_CC_SIGN. It should fail with TPM_RC_POLICY_CC. result = utility->AsymmetricDecrypt(key_handle, TPM_ALG_NULL, TPM_ALG_NULL, ciphertext, policy_session->GetDelegate(), &plaintext); if (GetFormatOneError(result) != TPM_RC_POLICY_CC) { LOG(ERROR) << "Error: " << GetErrorString(result); return false; } return true; } bool TrunksClientTest::PolicyOrTest() { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr trial_session = factory_.GetTrialSession(); TPM_RC result; // Specify a policy that asserts either TPM_CC_RSA_Encrypt or // TPM_CC_RSA_Decrypt. A key created under this policy can only be used // to encrypt or decrypt. result = trial_session->StartUnboundSession(true); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting policy session: " << GetErrorString(result); return false; } result = trial_session->PolicyCommandCode(TPM_CC_Sign); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } std::string sign_digest; result = trial_session->GetDigest(&sign_digest); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error getting policy digest: " << GetErrorString(result); return false; } result = trial_session->StartUnboundSession(true); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting policy session: " << GetErrorString(result); return false; } result = trial_session->PolicyCommandCode(TPM_CC_RSA_Decrypt); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } std::string decrypt_digest; result = trial_session->GetDigest(&decrypt_digest); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error getting policy digest: " << GetErrorString(result); return false; } std::vector digests; digests.push_back(sign_digest); digests.push_back(decrypt_digest); result = trial_session->PolicyOR(digests); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } std::string policy_digest; result = trial_session->GetDigest(&policy_digest); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error getting policy digest: " << GetErrorString(result); return false; } // Now that we have the digest, we can close the trial session and use hmac. trial_session.reset(); std::unique_ptr hmac_session = factory_.GetHmacSession(); result = hmac_session->StartUnboundSession(true); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session: " << GetErrorString(result); return false; } std::string key_authorization("password"); std::string key_blob; // This key is created with a policy that specifies that it can only be used // for encrypt and decrypt operations. result = utility->CreateRSAKeyPair( TpmUtility::AsymmetricKeyUsage::kDecryptAndSignKey, 2048, 0x10001, key_authorization, policy_digest, true, // use_only_policy_authorization kNoCreationPCR, hmac_session->GetDelegate(), &key_blob, nullptr); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error creating RSA key: " << GetErrorString(result); return false; } TPM_HANDLE key_handle; result = utility->LoadKey(key_blob, hmac_session->GetDelegate(), &key_handle); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error loading RSA key: " << GetErrorString(result); return false; } ScopedKeyHandle scoped_key(factory_, key_handle); // Now we can reset the hmac_session. hmac_session.reset(); std::unique_ptr policy_session = factory_.GetPolicySession(); result = policy_session->StartUnboundSession(false); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting policy session: " << GetErrorString(result); return false; } std::string ciphertext; result = utility->AsymmetricEncrypt(key_handle, TPM_ALG_NULL, TPM_ALG_NULL, "plaintext", nullptr, &ciphertext); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to encrypt: " << GetErrorString(result); return false; } result = policy_session->PolicyCommandCode(TPM_CC_RSA_Decrypt); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } result = policy_session->PolicyOR(digests); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } std::string plaintext; policy_session->SetEntityAuthorizationValue(key_authorization); // We can freely use the key for decryption. result = utility->AsymmetricDecrypt(key_handle, TPM_ALG_NULL, TPM_ALG_NULL, ciphertext, policy_session->GetDelegate(), &plaintext); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to decrypt: " << GetErrorString(result); return false; } if (plaintext.compare("plaintext") != 0) { LOG(ERROR) << "Plaintext changed after encrypt + decrypt."; return false; } result = policy_session->PolicyCommandCode(TPM_CC_Sign); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } result = policy_session->PolicyOR(digests); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error restricting policy: " << GetErrorString(result); return false; } std::string signature; policy_session->SetEntityAuthorizationValue(key_authorization); // However signing with a key only authorized for encrypt/decrypt should // fail with TPM_RC_POLICY_CC. result = utility->Sign(scoped_key.get(), TPM_ALG_NULL, TPM_ALG_NULL, std::string(32, 'a'), policy_session->GetDelegate(), &signature); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to sign: " << GetErrorString(result); return false; } return true; } bool TrunksClientTest::NvramTest(const std::string& owner_password) { std::unique_ptr utility = factory_.GetTpmUtility(); std::unique_ptr session = factory_.GetHmacSession(); TPM_RC result = session->StartUnboundSession(true /* enable encryption */); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session: " << GetErrorString(result); return false; } uint32_t index = 1; session->SetEntityAuthorizationValue(owner_password); std::string nv_data("nv_data"); TPMA_NV attributes = TPMA_NV_OWNERWRITE | TPMA_NV_AUTHREAD | TPMA_NV_WRITE_STCLEAR | TPMA_NV_READ_STCLEAR; result = utility->DefineNVSpace(index, nv_data.size(), attributes, "", "", session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error defining nvram: " << GetErrorString(result); return false; } // Setup auto-cleanup of the NVRAM space. auto cleanup = [](HmacSession* session, const std::string& owner_password, TpmUtility* utility, uint32_t index) { session->SetEntityAuthorizationValue(owner_password); TPM_RC result = utility->DestroyNVSpace(index, session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error destroying nvram: " << GetErrorString(result); } }; class Scoper { public: explicit Scoper(const base::Closure& callback) : callback_(callback) {} ~Scoper() { if (!cancel_) callback_.Run(); } void Cancel() { cancel_ = true; } private: base::Closure callback_; bool cancel_ = false; } scoper(base::Bind(cleanup, base::Unretained(session.get()), owner_password, base::Unretained(utility.get()), index)); session->SetEntityAuthorizationValue(owner_password); result = utility->WriteNVSpace(index, 0, nv_data, true /*owner*/, false /*extend*/, session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error writing nvram: " << GetErrorString(result); return false; } std::string new_nvdata; session->SetEntityAuthorizationValue(""); result = utility->ReadNVSpace(index, 0, nv_data.size(), false /*owner*/, &new_nvdata, session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error reading nvram: " << GetErrorString(result); return false; } if (nv_data.compare(new_nvdata) != 0) { LOG(ERROR) << "NV space had different data than was written."; return false; } session->SetEntityAuthorizationValue(owner_password); result = utility->LockNVSpace(index, false /*lock_read*/, true /*lock_write*/, false /*owner*/, session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error locking nvram write: " << GetErrorString(result); return false; } session->SetEntityAuthorizationValue(""); result = utility->ReadNVSpace(index, 0, nv_data.size(), false /*owner*/, &new_nvdata, session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error reading nvram: " << GetErrorString(result); return false; } if (nv_data.compare(new_nvdata) != 0) { LOG(ERROR) << "NV space had different data than was written."; return false; } session->SetEntityAuthorizationValue(owner_password); result = utility->WriteNVSpace(index, 0, nv_data, true /*owner*/, false /*extend*/, session->GetDelegate()); if (result == TPM_RC_SUCCESS) { LOG(ERROR) << "Wrote nvram after locking!"; return false; } result = utility->LockNVSpace(index, true /*lock_read*/, false /*lock_write*/, true /*owner*/, session->GetDelegate()); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error locking nvram read: " << GetErrorString(result); return false; } result = utility->ReadNVSpace(index, 0, nv_data.size(), false /*owner*/, &new_nvdata, session->GetDelegate()); if (result == TPM_RC_SUCCESS) { LOG(ERROR) << "Read nvram after locking!"; return false; } return true; } bool TrunksClientTest::ManyKeysTest() { const size_t kNumKeys = 20; std::vector> key_handles; std::map public_key_map; for (size_t i = 0; i < kNumKeys; ++i) { std::unique_ptr key_handle(new ScopedKeyHandle(factory_)); std::string public_key; if (!LoadSigningKey(key_handle.get(), &public_key)) { LOG(ERROR) << "Error loading key " << i << " into TPM."; } public_key_map[key_handle->get()] = public_key; key_handles.push_back(std::move(key_handle)); } CHECK_EQ(key_handles.size(), kNumKeys); CHECK_EQ(public_key_map.size(), kNumKeys); std::unique_ptr delegate = factory_.GetPasswordAuthorization(""); for (size_t i = 0; i < kNumKeys; ++i) { const ScopedKeyHandle& key_handle = *key_handles[i]; const std::string& public_key = public_key_map[key_handle.get()]; if (!SignAndVerify(key_handle, public_key, delegate.get())) { LOG(ERROR) << "Error signing with key " << i; } } std::random_shuffle(key_handles.begin(), key_handles.end()); for (size_t i = 0; i < kNumKeys; ++i) { const ScopedKeyHandle& key_handle = *key_handles[i]; const std::string& public_key = public_key_map[key_handle.get()]; if (!SignAndVerify(key_handle, public_key, delegate.get())) { LOG(ERROR) << "Error signing with shuffled key " << i; } } return true; } bool TrunksClientTest::ManySessionsTest() { const size_t kNumSessions = 20; std::unique_ptr utility = factory_.GetTpmUtility(); std::vector> sessions; for (size_t i = 0; i < kNumSessions; ++i) { std::unique_ptr session(factory_.GetHmacSession().release()); TPM_RC result = session->StartUnboundSession(true /* enable encryption */); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error starting hmac session " << i << ": " << GetErrorString(result); return false; } sessions.push_back(std::move(session)); } CHECK_EQ(sessions.size(), kNumSessions); ScopedKeyHandle key_handle(factory_); std::string public_key; if (!LoadSigningKey(&key_handle, &public_key)) { return false; } for (size_t i = 0; i < kNumSessions; ++i) { if (!SignAndVerify(key_handle, public_key, sessions[i]->GetDelegate())) { LOG(ERROR) << "Error signing with hmac session " << i; } } std::random_shuffle(sessions.begin(), sessions.end()); for (size_t i = 0; i < kNumSessions; ++i) { if (!SignAndVerify(key_handle, public_key, sessions[i]->GetDelegate())) { LOG(ERROR) << "Error signing with shuffled hmac session " << i; } } return true; } bool TrunksClientTest::PerformRSAEncrpytAndDecrpyt( TPM_HANDLE key_handle, const std::string& key_authorization, HmacSession* session) { std::unique_ptr utility = factory_.GetTpmUtility(); std::string ciphertext; session->SetEntityAuthorizationValue(""); TPM_RC result = utility->AsymmetricEncrypt( key_handle, TPM_ALG_NULL, TPM_ALG_NULL, "plaintext", session->GetDelegate(), &ciphertext); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to encrypt: " << GetErrorString(result); return false; } std::string plaintext; session->SetEntityAuthorizationValue(key_authorization); result = utility->AsymmetricDecrypt(key_handle, TPM_ALG_NULL, TPM_ALG_NULL, ciphertext, session->GetDelegate(), &plaintext); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Error using key to decrypt: " << GetErrorString(result); return false; } if (plaintext.compare("plaintext") != 0) { LOG(ERROR) << "Plaintext changed after encrypt + decrypt."; return false; } return true; } void TrunksClientTest::GenerateRSAKeyPair(std::string* modulus, std::string* prime_factor, std::string* public_key) { #if defined(OPENSSL_IS_BORINGSSL) crypto::ScopedRSA rsa(RSA_new()); crypto::ScopedBIGNUM exponent(BN_new()); CHECK(BN_set_word(exponent.get(), RSA_F4)); CHECK(RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr)) << "Failed to generate RSA key: " << GetOpenSSLError(); #else crypto::ScopedRSA rsa(RSA_generate_key(2048, 0x10001, nullptr, nullptr)); CHECK(rsa.get()); #endif modulus->resize(BN_num_bytes(rsa.get()->n), 0); BN_bn2bin(rsa.get()->n, reinterpret_cast(string_as_array(modulus))); prime_factor->resize(BN_num_bytes(rsa.get()->p), 0); BN_bn2bin(rsa.get()->p, reinterpret_cast(string_as_array(prime_factor))); if (public_key) { unsigned char* buffer = NULL; int length = i2d_RSAPublicKey(rsa.get(), &buffer); CHECK_GT(length, 0); crypto::ScopedOpenSSLBytes scoped_buffer(buffer); public_key->assign(reinterpret_cast(buffer), length); } } bool TrunksClientTest::VerifyRSASignature(const std::string& public_key, const std::string& data, const std::string& signature) { auto asn1_ptr = reinterpret_cast(public_key.data()); crypto::ScopedRSA rsa( d2i_RSAPublicKey(nullptr, &asn1_ptr, public_key.size())); CHECK(rsa.get()); std::string digest = crypto::SHA256HashString(data); auto digest_buffer = reinterpret_cast(digest.data()); std::string mutable_signature(signature); unsigned char* signature_buffer = reinterpret_cast(string_as_array(&mutable_signature)); return (RSA_verify(NID_sha256, digest_buffer, digest.size(), signature_buffer, signature.size(), rsa.get()) == 1); } bool TrunksClientTest::LoadSigningKey(ScopedKeyHandle* key_handle, std::string* public_key) { std::string modulus; std::string prime_factor; GenerateRSAKeyPair(&modulus, &prime_factor, public_key); std::string key_blob; std::unique_ptr utility = factory_.GetTpmUtility(); TPM_RC result = utility->ImportRSAKey( TpmUtility::AsymmetricKeyUsage::kSignKey, modulus, 0x10001, prime_factor, "", // password factory_.GetPasswordAuthorization("").get(), &key_blob); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "ImportRSAKey: " << GetErrorString(result); return false; } TPM_HANDLE raw_key_handle; result = utility->LoadKey( key_blob, factory_.GetPasswordAuthorization("").get(), &raw_key_handle); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "LoadKey: " << GetErrorString(result); return false; } key_handle->reset(raw_key_handle); return true; } bool TrunksClientTest::SignAndVerify(const ScopedKeyHandle& key_handle, const std::string& public_key, AuthorizationDelegate* delegate) { std::string signature; std::string data_to_sign("sign_this"); std::unique_ptr utility = factory_.GetTpmUtility(); TPM_RC result = utility->Sign(key_handle.get(), TPM_ALG_RSASSA, TPM_ALG_SHA256, data_to_sign, delegate, &signature); if (result != TPM_RC_SUCCESS) { LOG(ERROR) << "Sign: " << GetErrorString(result); return false; } if (!VerifyRSASignature(public_key, data_to_sign, signature)) { LOG(ERROR) << "Signature verification failed: " << GetOpenSSLError(); return false; } return true; } } // namespace trunks