blob: 65c6a0694e19616a0afb22ed485f7e60d551e87f [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 <algorithm>
#include <map>
#include <memory>
#include <random>
#include <string>
#include <vector>
#include <base/bind.h>
#include <base/callback.h>
#include <base/callback_helpers.h>
#include <base/logging.h>
#include <base/rand_util.h>
#include <base/stl_util.h>
#include <crypto/openssl_util.h>
#include <crypto/libcrypto-compat.h>
#include <crypto/scoped_openssl_types.h>
#include <crypto/sha2.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#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<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> 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<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> 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
std::vector<uint32_t>(), 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_RSASSA, TPM_ALG_SHA256,
std::string(32, 'a'), true /* generate_hash */,
session->GetDelegate(), &signature);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error using key to sign: " << GetErrorString(result);
return false;
}
std::string public_key;
if (!GetRSAPublicKeyFromHandle(scoped_key, &public_key,
session->GetDelegate())) {
LOG(ERROR) << "Error fetching the public key to verify: "
<< GetErrorString(result);
return false;
}
return VerifyRSASignature(public_key, std::string(32, 'a'), signature);
}
bool TrunksClientTest::DecryptTest() {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> 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
std::vector<uint32_t>(), 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 PerformRSAEncryptAndDecrypt(scoped_key.get(), key_authorization,
session.get());
}
bool TrunksClientTest::ImportTest() {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> 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 PerformRSAEncryptAndDecrypt(scoped_key.get(), key_authorization,
session.get());
}
bool TrunksClientTest::AuthChangeTest() {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> 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
std::vector<uint32_t>(), 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 PerformRSAEncryptAndDecrypt(scoped_key.get(), key_authorization,
session.get());
}
bool TrunksClientTest::VerifyKeyCreationTest() {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> 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
std::vector<uint32_t>(), 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
std::vector<uint32_t>(), 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<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> session = factory_.GetHmacSession();
if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting hmac session.";
return false;
}
uint32_t pcr_index = 5;
std::string policy_digest;
TPM_RC result = utility->GetPolicyDigestForPcrValues(
std::map<uint32_t, std::string>({{pcr_index, ""}}),
true /* use_auth_value */, &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 auth_value("auth_value");
std::string sealed_data;
result = utility->SealData(data_to_seal, policy_digest, auth_value,
session->GetDelegate(), &sealed_data);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error creating Sealed Object: " << GetErrorString(result);
return false;
}
std::unique_ptr<PolicySession> policy_session = factory_.GetPolicySession();
result = policy_session->StartUnboundSession(true, 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) << __func__ << ": Error setting session to use auth_value: "
<< GetErrorString(result);
return result;
}
result = policy_session->PolicyPCR(
std::map<uint32_t, std::string>({{pcr_index, ""}}));
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error restricting policy to pcr value: "
<< GetErrorString(result);
return false;
}
// Check fail scenario when no authorization value is given.
std::string unsealed_data;
result = utility->UnsealData(sealed_data, policy_session->GetDelegate(),
&unsealed_data);
if (result == TPM_RC_SUCCESS && data_to_seal == unsealed_data) {
LOG(ERROR) << "Error: unseal succeeded without authorization.";
return false;
}
// Check success scenario.
policy_session->SetEntityAuthorizationValue(auth_value);
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(
std::map<uint32_t, std::string>({{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::SealedToMultiplePCRDataTest() {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> session = factory_.GetHmacSession();
if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting hmac session.";
return false;
}
uint32_t pcr_index1 = 0;
uint32_t pcr_index2 = 2;
// Build policy digest.
std::string policy_digest;
TPM_RC result = utility->GetPolicyDigestForPcrValues(
std::map<uint32_t, std::string>({{pcr_index1, ""}, {pcr_index2, ""}}),
false /* use_auth_value */, &policy_digest);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error getting policy_digest: " << GetErrorString(result);
return false;
}
// Seal the data.
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<PolicySession> policy_session = factory_.GetPolicySession();
result = policy_session->StartUnboundSession(true, false);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting policy session: " << GetErrorString(result);
return false;
}
result = policy_session->PolicyPCR(
std::map<uint32_t, std::string>({{pcr_index1, ""}, {pcr_index2, ""}}));
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error restricting policy to pcr value: "
<< GetErrorString(result);
return false;
}
// Unseal the data under the same PCR.
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;
}
// Extend the PCR, thus making the data impossible to unseal.
result = utility->ExtendPCR(pcr_index1, "extend", session->GetDelegate());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error extending pcr: " << GetErrorString(result);
return false;
}
result = policy_session->PolicyPCR(
std::map<uint32_t, std::string>({{pcr_index1, ""}, {pcr_index2, ""}}));
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error restricting policy to pcr value: "
<< GetErrorString(result);
return false;
}
// Try to unseal the data, after PCR change. It should fail.
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<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> 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<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<PolicySession> trial_session = factory_.GetTrialSession();
TPM_RC result;
result = trial_session->StartUnboundSession(true, 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<HmacSession> hmac_session = factory_.GetHmacSession();
result = hmac_session->StartUnboundSession(true, 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
std::vector<uint32_t>(), 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<PolicySession> policy_session = factory_.GetPolicySession();
result = policy_session->StartUnboundSession(true, 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_RSASSA, TPM_ALG_SHA256,
std::string(32, 0), true /* generate_hash */,
policy_session->GetDelegate(), &signature);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error signing using RSA key: " << GetErrorString(result);
return false;
}
std::string public_key;
if (!GetRSAPublicKeyFromHandle(scoped_key, &public_key,
policy_session->GetDelegate())) {
LOG(ERROR) << "Error fetching the public key to verify: "
<< GetErrorString(result);
return false;
}
if (!VerifyRSASignature(public_key, std::string(32, 0), signature)) {
LOG(ERROR) << "Error verifying using RSA key: " << GetErrorString(result);
return false;
}
std::string ciphertext;
result =
utility->AsymmetricEncrypt(scoped_key.get(), TPM_ALG_OAEP, TPM_ALG_SHA256,
"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_OAEP, TPM_ALG_SHA256, 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<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<PolicySession> trial_session = factory_.GetTrialSession();
TPM_RC result;
result = trial_session->StartUnboundSession(true, 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(
std::map<uint32_t, std::string>({{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<HmacSession> hmac_session = factory_.GetHmacSession();
result = hmac_session->StartUnboundSession(true, 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
std::vector<uint32_t>(), 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<PolicySession> policy_session = factory_.GetPolicySession();
result = policy_session->StartUnboundSession(true, 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(
std::map<uint32_t, std::string>({{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_RSASSA, TPM_ALG_SHA256,
std::string(32, 'a'), true /* generate_hash */,
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<AuthorizationDelegate> 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(true, 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(
std::map<uint32_t, std::string>({{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_RSASSA, TPM_ALG_SHA256,
std::string(32, 'a'), true /* generate_hash */,
policy_session->GetDelegate(), &signature);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error using key to sign: " << GetErrorString(result);
return false;
}
std::string public_key;
if (!GetRSAPublicKeyFromHandle(scoped_key, &public_key,
policy_session->GetDelegate())) {
LOG(ERROR) << "Error fetching the public key to verify: "
<< GetErrorString(result);
return false;
}
if (!VerifyRSASignature(public_key, std::string(32, 'a'), signature)) {
LOG(ERROR) << "Error using key to verify: " << GetErrorString(result);
return false;
}
std::string ciphertext;
result = utility->AsymmetricEncrypt(key_handle, TPM_ALG_OAEP, TPM_ALG_SHA256,
"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(
std::map<uint32_t, std::string>({{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_OAEP, TPM_ALG_SHA256,
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<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<PolicySession> trial_session = factory_.GetTrialSession();
TPM_RC result;
// Specify a policy that asserts either TPM_CC_Sign or TPM_CC_RSA_Decrypt.
// A key created under this policy can only be used to sign or decrypt.
result = trial_session->StartUnboundSession(true, 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, 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<std::string> 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<HmacSession> hmac_session = factory_.GetHmacSession();
result = hmac_session->StartUnboundSession(true, 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 sign and decrypt operations.
result = utility->CreateRSAKeyPair(
TpmUtility::AsymmetricKeyUsage::kDecryptAndSignKey, 2048, 0x10001,
key_authorization, policy_digest, true, // use_only_policy_authorization
std::vector<uint32_t>(), 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<PolicySession> policy_session = factory_.GetPolicySession();
result = policy_session->StartUnboundSession(true, 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_OAEP, TPM_ALG_SHA256,
"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_OAEP, TPM_ALG_SHA256,
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_RSASSA, TPM_ALG_SHA256,
std::string(32, 'a'), true /* generate_hash */,
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<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> session = factory_.GetHmacSession();
TPM_RC result = session->StartUnboundSession(true /* salted */,
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 = base::Bind(
[](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);
}
},
session.get(), owner_password, utility.get(), index);
base::ScopedClosureRunner scoper(cleanup);
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<std::unique_ptr<ScopedKeyHandle>> key_handles;
std::map<TPM_HANDLE, std::string> public_key_map;
for (size_t i = 0; i < kNumKeys; ++i) {
std::unique_ptr<ScopedKeyHandle> 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<AuthorizationDelegate> 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;
}
}
// TODO(emaxx): This needs to be replaced by base::RandomShuffle() introduced
// by https://crrev.com/c/1023495.
std::mt19937 urng(base::RandUint64());
std::shuffle(key_handles.begin(), key_handles.end(), urng);
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<TpmUtility> utility = factory_.GetTpmUtility();
std::vector<std::unique_ptr<HmacSession>> sessions;
for (size_t i = 0; i < kNumSessions; ++i) {
std::unique_ptr<HmacSession> session(factory_.GetHmacSession().release());
TPM_RC result = session->StartUnboundSession(true /* salted */,
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;
}
}
// TODO(emaxx): This needs to be replaced by base::RandomShuffle() introduced
// by https://crrev.com/c/1023495.
std::mt19937 urng(base::RandUint64());
std::shuffle(sessions.begin(), sessions.end(), urng);
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::EndorsementTest(const std::string& endorsement_password,
const std::string& owner_password) {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> session = factory_.GetHmacSession();
TPM_RC result = session->StartUnboundSession(true /* salted */,
false /* enable encryption */);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting hmac session: " << GetErrorString(result);
return false;
}
session->SetEntityAuthorizationValue(endorsement_password);
std::unique_ptr<HmacSession> session2 = factory_.GetHmacSession();
result = session2->StartUnboundSession(true /* salted */,
false /* enable encryption */);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting hmac session: " << GetErrorString(result);
return false;
}
session2->SetEntityAuthorizationValue(owner_password);
TPM_HANDLE key_handle;
result = utility->GetEndorsementKey(TPM_ALG_RSA, session->GetDelegate(),
session2->GetDelegate(), &key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "GetEndorsementKey(RSA) failed: " << GetErrorString(result);
return false;
}
result = utility->GetEndorsementKey(TPM_ALG_ECC, session->GetDelegate(),
nullptr, &key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "GetEndorsementKey(ECC) failed: " << GetErrorString(result);
return false;
}
return true;
}
bool TrunksClientTest::IdentityKeyTest() {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> session = factory_.GetHmacSession();
TPM_RC result = session->StartUnboundSession(true /* salted */,
false /* enable encryption */);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting hmac session: " << GetErrorString(result);
return false;
}
std::string key_blob;
result = utility->CreateIdentityKey(TPM_ALG_RSA, session->GetDelegate(),
&key_blob);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "CreateIdentityKey(RSA) failed: " << GetErrorString(result);
return false;
}
std::unique_ptr<TpmState> tpm_state(factory_.GetTpmState());
tpm_state->Initialize();
if (tpm_state->IsECCSupported()) {
result = utility->CreateIdentityKey(TPM_ALG_ECC, session->GetDelegate(),
&key_blob);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "CreateIdentityKey(ECC) failed: " << GetErrorString(result);
return false;
}
}
return true;
}
bool TrunksClientTest::PerformRSAEncryptAndDecrypt(
TPM_HANDLE key_handle,
const std::string& key_authorization,
HmacSession* session) {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
std::string ciphertext;
session->SetEntityAuthorizationValue("");
TPM_RC result = utility->AsymmetricEncrypt(
key_handle, TPM_ALG_OAEP, TPM_ALG_SHA256, "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_OAEP, TPM_ALG_SHA256,
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) {
crypto::ScopedRSA rsa(RSA_new());
CHECK(rsa);
crypto::ScopedBIGNUM exponent(BN_new());
CHECK(exponent);
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();
modulus->resize(RSA_size(rsa.get()), 0);
const BIGNUM* n;
RSA_get0_key(rsa.get(), &n, nullptr, nullptr);
CHECK(BN_bn2bin(n, reinterpret_cast<unsigned char*>(base::data(*modulus))));
const BIGNUM* p;
RSA_get0_factors(rsa.get(), &p, nullptr);
prime_factor->resize(BN_num_bytes(p), 0);
CHECK(BN_bn2bin(p,
reinterpret_cast<unsigned char*>(base::data(*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<char*>(buffer), length);
}
}
bool TrunksClientTest::VerifyRSASignature(const std::string& public_key,
const std::string& data,
const std::string& signature) {
auto asn1_ptr = reinterpret_cast<const unsigned char*>(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<const unsigned char*>(digest.data());
std::string mutable_signature(signature);
unsigned char* signature_buffer =
reinterpret_cast<unsigned char*>(base::data(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<TpmUtility> 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<TpmUtility> utility = factory_.GetTpmUtility();
TPM_RC result = utility->Sign(key_handle.get(), TPM_ALG_RSASSA,
TPM_ALG_SHA256, data_to_sign,
true /* generate_hash */, 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;
}
bool TrunksClientTest::GetRSAPublicKeyFromHandle(
const ScopedKeyHandle& key_handle,
std::string* public_key,
AuthorizationDelegate* delegate) {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
TPMT_PUBLIC public_area;
TPM_RC result = utility->GetKeyPublicArea(key_handle.get(), &public_area);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << __func__ << GetErrorString(result);
return false;
}
// Copied from cryptohome::PublicAreaToPublicKeyDER
crypto::ScopedRSA rsa(RSA_new());
CHECK(rsa);
crypto::ScopedBIGNUM e(BN_new()), n(BN_new());
CHECK(e);
CHECK(n);
CHECK(BN_set_word(e.get(), 0x10001)) << "Error setting exponent for RSA.";
CHECK(BN_bin2bn(public_area.unique.rsa.buffer, public_area.unique.rsa.size,
n.get()))
<< "Error setting modulus for RSA.";
CHECK(RSA_set0_key(rsa.get(), n.release(), e.release(), nullptr));
int der_length = i2d_RSAPublicKey(rsa.get(), nullptr);
if (der_length < 0) {
LOG(ERROR) << "Failed to get DER-encoded public key length.";
return false;
}
public_key->resize(der_length);
unsigned char* der_buffer =
reinterpret_cast<unsigned char*>(base::data(*public_key));
der_length = i2d_RSAPublicKey(rsa.get(), &der_buffer);
if (der_length < 0) {
LOG(ERROR) << "Failed to DER-encode public key.";
return false;
}
return true;
}
bool TrunksClientTest::PolicyFidoSignedTest(TPM_ALG_ID signing_algo) {
std::unique_ptr<TpmUtility> utility = factory_.GetTpmUtility();
std::unique_ptr<HmacSession> session = factory_.GetHmacSession();
TPM_RC result;
// 1. Prepare a key to sign.
// 1-a) Create a key pair
if (utility->StartSession(session.get()) != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting hmac session.";
return false;
}
std::string key_blob;
switch (signing_algo) {
case TPM_ALG_RSASSA:
case TPM_ALG_RSAPSS:
result = utility->CreateRSAKeyPair(
TpmUtility::AsymmetricKeyUsage::kSignKey, 2048, 0x10001, "", "",
false, std::vector<uint32_t>(), session->GetDelegate(), &key_blob,
nullptr);
break;
case TPM_ALG_ECDSA:
case TPM_ALG_ECDAA:
case TPM_ALG_SM2:
case TPM_ALG_ECSCHNORR:
result = utility->CreateECCKeyPair(
TpmUtility::AsymmetricKeyUsage::kSignKey, TPM_ECC_NIST_P256, "", "",
false, std::vector<uint32_t>(), session->GetDelegate(), &key_blob,
nullptr);
break;
default:
result = TPM_RC_SCHEME;
LOG(ERROR) << "Unknown hash algorithm: " << GetErrorString(result);
return result;
}
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error creating signing key: " << GetErrorString(result);
return false;
}
// 1-b) Load the key
TPM_HANDLE signing_key_handle;
result =
utility->LoadKey(key_blob, session->GetDelegate(), &signing_key_handle);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error loading signing key: " << GetErrorString(result);
return false;
}
// 2. PolicyFidoSigned in trial session
// 2-a) Start Auth session
std::unique_ptr<PolicySession> trial_session = factory_.GetTrialSession();
result = trial_session->StartUnboundSession(true, true);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting policy session: " << GetErrorString(result);
return false;
}
// 2-b) Sign the authenticatorData and session nonce
const std::string auth_data_fixed =
crypto::SHA256HashString("chromeos:login:nobody");
const std::string auth_data =
std::string("ghijklmn", 8) + auth_data_fixed + std::string("opqrstuv", 8);
const std::vector<trunks::FIDO_DATA_RANGE> auth_data_descr = {
{.offset = 0x0008, .size = 0x0010}, {.offset = 0x0018, .size = 0x0010}};
std::string nonce;
trial_session->GetDelegate()->GetTpmNonce(&nonce);
TPM_ALG_ID hash_algo = TPM_ALG_SHA256;
TPMT_SIGNATURE auth;
result = utility->RawSign(signing_key_handle, signing_algo, hash_algo,
auth_data + nonce,
true, // generate_hash
session->GetDelegate(), &auth);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error using key to sign: " << GetErrorString(result);
return false;
}
// 2-c) Policy Fido Signed (with loaded pub key from 1-b)
std::string signing_key_name;
result = utility->GetKeyName(signing_key_handle, &signing_key_name);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error in getting key name: " << GetErrorString(result);
return false;
}
result = trial_session->PolicyFidoSigned(signing_key_handle, signing_key_name,
auth_data, auth_data_descr, auth,
nullptr);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error with PolicyFidoSigned in trial session: "
<< GetErrorString(result);
return false;
}
// 2-d) Get Policy digest
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();
// 3. Seal an secret object.
const std::string data_to_seal("sealed_data_for_PolicyFidoSigned");
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;
}
// 4. Test failing cases
// 4-1. Start Auth Session ((TPM_SE_POLICY = 0x01))
std::unique_ptr<PolicySession> policy_session = factory_.GetPolicySession();
result = policy_session->StartUnboundSession(true, false);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting policy session: " << GetErrorString(result);
return false;
}
const std::string auth_data2 =
std::string("XXXXXXXX", 8) + auth_data_fixed + std::string("ZZZZZZZZ", 8);
// 4-2. Check PolicyFidoSigned fail with the wrong data auth
result = policy_session->PolicyFidoSigned(
signing_key_handle, signing_key_name, auth_data2, auth_data_descr,
TPMT_SIGNATURE(), session->GetDelegate());
if (result == TPM_RC_SUCCESS) {
LOG(ERROR) << "Unexpected success with PolicyFidoSigned "
<< "with the empty auth: " << GetErrorString(result);
return false;
}
// 4-3. Check PolicyFidoSigned fail with the wrong auth data
// 4-3-1. Sign the command parameters
policy_session->GetDelegate()->GetTpmNonce(&nonce);
result = utility->RawSign(signing_key_handle, signing_algo, hash_algo,
std::string(64, '0') + nonce, // <- wrong authData
true, // generate_hash
session->GetDelegate(), &auth);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error using key to sign: " << GetErrorString(result);
return false;
}
// 4-3-2. Call PolicyFidoSigned with the wrong authData
result = policy_session->PolicyFidoSigned(
signing_key_handle, signing_key_name,
std::string(64, '0'), // <- wrong authData
auth_data_descr, auth, session->GetDelegate());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error with PolicyFidoSigned in policy session: "
<< GetErrorString(result);
return false;
}
// 4-3-3. check UnsealData fail
std::string unsealed_data;
result = utility->UnsealData(sealed_data, policy_session->GetDelegate(),
&unsealed_data);
if (result == TPM_RC_SUCCESS) {
LOG(ERROR) << "Unexpected success in unsealing object: "
<< GetErrorString(result);
return false;
}
// 4-4. Check PolicyFidoSigned fail with the wrong auth_data_descr.
policy_session->GetDelegate()->GetTpmNonce(&nonce);
result = utility->RawSign(signing_key_handle, signing_algo, hash_algo,
auth_data2 + nonce,
true, // generate_hash
session->GetDelegate(), &auth);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error using key to sign: " << GetErrorString(result);
return false;
}
const std::vector<trunks::FIDO_DATA_RANGE> auth_data_descr2 = {
{.offset = 0x0008, .size = 0x0010},
{.offset = 0x0018, .size = 0x00f0}}; // <-- out of range
result = policy_session->PolicyFidoSigned(
signing_key_handle, signing_key_name, auth_data2, auth_data_descr2, auth,
session->GetDelegate());
if (result == TPM_RC_SUCCESS) {
LOG(ERROR) << "Unexpected success with PolicyFidoSigned in policy session: "
<< GetErrorString(result);
return false;
}
// 4-5. Check PolicyFidoSigned fail with a different auth_data_descr
const std::vector<trunks::FIDO_DATA_RANGE> auth_data_descr3 = {
{.offset = 0x0008, .size = 0x0020}};
policy_session->GetDelegate()->GetTpmNonce(&nonce);
result = utility->RawSign(signing_key_handle, signing_algo, hash_algo,
auth_data + nonce,
true, // generate_hash
session->GetDelegate(), &auth);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error using key to sign: " << GetErrorString(result);
return false;
}
result = policy_session->PolicyFidoSigned(
signing_key_handle, signing_key_name, auth_data, auth_data_descr3, auth,
session->GetDelegate());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error with PolicyFidoSigned in policy session: "
<< GetErrorString(result);
return false;
}
result = utility->UnsealData(sealed_data, policy_session->GetDelegate(),
&unsealed_data);
if (result == TPM_RC_SUCCESS) {
LOG(ERROR) << "Unexpected success in unsealing object: "
<< GetErrorString(result);
return false;
}
policy_session.reset();
// 5. Test success cases
// 5-1. Check PolicyFidoSigned success
policy_session = factory_.GetPolicySession();
result = policy_session->StartUnboundSession(true, false);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error starting policy session: " << GetErrorString(result);
return false;
}
// 5-1-1. Sign the command
policy_session->GetDelegate()->GetTpmNonce(&nonce);
result = utility->RawSign(signing_key_handle, signing_algo, hash_algo,
auth_data2 + nonce,
true, // generate_hash
session->GetDelegate(), &auth);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error using key to sign: " << GetErrorString(result);
return false;
}
// 5-1-2. PolicyFidoSigned
result = policy_session->PolicyFidoSigned(
signing_key_handle, signing_key_name, auth_data2, auth_data_descr, auth,
session->GetDelegate());
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error with PolicyFidoSigned in policy session: "
<< GetErrorString(result);
return false;
}
// 5-1-3. Unseal
std::string unsealed_data2;
result = utility->UnsealData(sealed_data, policy_session->GetDelegate(),
&unsealed_data2);
if (result != TPM_RC_SUCCESS) {
LOG(ERROR) << "Error unsealing object: " << GetErrorString(result);
return false;
}
if (data_to_seal != unsealed_data2) {
LOG(ERROR) << "Error unsealed data from TPM does not match original data.";
return false;
}
policy_session.reset();
return true;
}
} // namespace trunks