blob: c64ea4676e2fdf2888cdf79350b53a26dfac353b [file] [log] [blame]
// Copyright 2015 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 "trunks/trunks_client_test.h"
#include <base/logging.h>
#include <base/stl_util.h>
#include <crypto/scoped_openssl_types.h>
#include <crypto/sha2.h>
#include <openssl/bn.h>
#include <openssl/rsa.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_generated.h"
#include "trunks/tpm_state.h"
#include "trunks/tpm_utility.h"
#include "trunks/trunks_factory_impl.h"
namespace trunks {
TrunksClientTest::TrunksClientTest() : factory_(new TrunksFactoryImpl()) {}
TrunksClientTest::TrunksClientTest(scoped_ptr<TrunksFactory> factory)
: factory_(factory.Pass()) {}
TrunksClientTest::~TrunksClientTest() {}
bool TrunksClientTest::RNGTest() {
scoped_ptr<TpmUtility> utility = factory_->GetTpmUtility();
scoped_ptr<HmacSession> session = factory_->GetHmacSession();
std::string entropy_data("entropy_data");
std::string random_data;
size_t num_bytes = 70;
TPM_RC result = session->StartUnboundSession(true /* enable encryption */);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting hmac session: " << GetErrorString(result);
return false;
}
session->SetEntityAuthorizationValue("");
result = utility->StirRandom(entropy_data, session->GetDelegate());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error stirring TPM RNG: " << GetErrorString(result);
return false;
}
session->SetEntityAuthorizationValue("");
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() {
scoped_ptr<TpmUtility> utility = factory_->GetTpmUtility();
scoped_ptr<HmacSession> 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;
}
std::string key_authorization("sign");
std::string key_blob;
session->SetEntityAuthorizationValue("");
result = utility->CreateRSAKeyPair(TpmUtility::AsymmetricKeyUsage::kSignKey,
2048, 0x10001, key_authorization, "",
session->GetDelegate(), &key_blob);
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_.get(), signing_key);
std::string signature;
session->SetEntityAuthorizationValue(key_authorization);
result = utility->Sign(scoped_key.get(), 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(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;
}
return true;
}
bool TrunksClientTest::DecryptTest() {
scoped_ptr<TpmUtility> utility = factory_->GetTpmUtility();
scoped_ptr<HmacSession> 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;
}
std::string key_authorization("decrypt");
std::string key_blob;
session->SetEntityAuthorizationValue("");
result = utility->CreateRSAKeyPair(
TpmUtility::AsymmetricKeyUsage::kDecryptKey, 2048, 0x10001,
key_authorization, "", session->GetDelegate(), &key_blob);
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_.get(), decrypt_key);
return PerformRSAEncrpytAndDecrpyt(scoped_key.get(),
key_authorization,
session.get());
}
bool TrunksClientTest::ImportTest() {
scoped_ptr<TpmUtility> utility = factory_->GetTpmUtility();
scoped_ptr<HmacSession> 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;
}
crypto::ScopedRSA rsa(RSA_generate_key(2048, 0x10001, nullptr, nullptr));
CHECK(rsa.get());
std::string modulus(BN_num_bytes(rsa.get()->n), 0);
BN_bn2bin(rsa.get()->n,
reinterpret_cast<unsigned char*>(string_as_array(&modulus)));
std::string prime_factor(BN_num_bytes(rsa.get()->p), 0);
BN_bn2bin(rsa.get()->p,
reinterpret_cast<unsigned char*>(string_as_array(&prime_factor)));
std::string key_blob;
std::string key_authorization("import");
session->SetEntityAuthorizationValue("");
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_.get(), key_handle);
return PerformRSAEncrpytAndDecrpyt(scoped_key.get(), key_authorization,
session.get());
}
bool TrunksClientTest::AuthChangeTest() {
scoped_ptr<TpmUtility> utility = factory_->GetTpmUtility();
scoped_ptr<HmacSession> 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;
}
std::string key_authorization("new_pass");
std::string key_blob;
session->SetEntityAuthorizationValue("");
result = utility->CreateRSAKeyPair(
TpmUtility::AsymmetricKeyUsage::kDecryptKey, 2048, 0x10001,
"old_pass", "", session->GetDelegate(), &key_blob);
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_.get(), 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::SimplePolicyTest() {
scoped_ptr<TpmUtility> utility = factory_->GetTpmUtility();
scoped_ptr<PolicySession> policy_session = factory_->GetPolicySession();
TPM_RC result;
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 policy_digest(32, 0);
result = policy_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 policy session and use hmac.
policy_session.reset();
scoped_ptr<HmacSession> hmac_session = factory_->GetHmacSession();
result = hmac_session->StartUnboundSession(false);
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, hmac_session->GetDelegate(),
&key_blob);
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_.get(), key_handle);
// Now we can reset the hmac_session.
hmac_session.reset();
policy_session = factory_->GetPolicySession();
result = policy_session->StartUnboundSession(true);
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;
}
// TODO(usanghi): Investigate resetting of policy_digest. crbug.com/486185.
result = policy_session->PolicyAuthValue();
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error restricting policy to auth value knowledge: "
<< GetErrorString(result);
return false;
}
std::string ciphertext;
policy_session->SetEntityAuthorizationValue("");
result = utility->AsymmetricEncrypt(scoped_key.get(), TPM_ALG_NULL,
TPM_ALG_NULL, "plaintext",
policy_session->GetDelegate(),
&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::PCRTest() {
scoped_ptr<TpmUtility> utility = factory_->GetTpmUtility();
scoped_ptr<HmacSession> 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;
}
// 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;
session->SetEntityAuthorizationValue("");
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::NvramTest(const std::string& owner_password) {
scoped_ptr<TpmUtility> utility = factory_->GetTpmUtility();
scoped_ptr<HmacSession> 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");
result = utility->DefineNVSpace(index, nv_data.size(),
session->GetDelegate());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error defining nvram: " << GetErrorString(result);
return false;
}
session->SetEntityAuthorizationValue(owner_password);
result = utility->WriteNVSpace(index, 0, nv_data, 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(),
&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;
}
result = utility->LockNVSpace(index, session->GetDelegate());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error locking nvram: " << GetErrorString(result);
return false;
}
result = utility->ReadNVSpace(index, 0, nv_data.size(),
&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, session->GetDelegate());
if (result == TPM_RC_SUCCESS) {
LOG(ERROR) << "Wrote nvram after locking: " << GetErrorString(result);
return false;
}
session->SetEntityAuthorizationValue(owner_password);
result = utility->DestroyNVSpace(index, session->GetDelegate());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error destroying nvram: " << GetErrorString(result);
return false;
}
return true;
}
bool TrunksClientTest::PerformRSAEncrpytAndDecrpyt(
TPM_HANDLE key_handle,
const std::string& key_authorization,
HmacSession* session) {
scoped_ptr<TpmUtility> 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;
}
} // namespace trunks