blob: 24719494b5f6d346937c69c432b8f938b6088bdb [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.
// Test methods that run on a real TPM
#include "cryptohome/tpm_live_test.h"
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <base/macros.h>
#include <base/memory/ptr_util.h>
#include <base/optional.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/evp.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include "cryptohome/cryptolib.h"
#include "cryptohome/signature_sealing_backend.h"
#if !USE_TPM2
#include <trousers/scoped_tss_type.h>
#include <trousers/tss.h>
#include <trousers/trousers.h> // NOLINT(build/include_alpha) - needs tss.h
#include "cryptohome/tpm_impl.h"
#endif // !USE_TPM2
using brillo::Blob;
using brillo::BlobFromString;
using brillo::BlobToString;
using brillo::SecureBlob;
namespace cryptohome {
namespace {
// Scoped setter of the owner password of the global Tpm instance.
// Does nothing if the version is different from |TPM_1_2|.
class ScopedTpmOwnerPasswordSetter {
public:
explicit ScopedTpmOwnerPasswordSetter(const SecureBlob& owner_password)
: tpm_(Tpm::GetSingleton()) {
if (ShouldApply()) {
tpm_->GetOwnerPassword(&previous_tpm_owner_password_);
tpm_->SetOwnerPassword(owner_password);
}
}
~ScopedTpmOwnerPasswordSetter() {
if (ShouldApply())
tpm_->SetOwnerPassword(previous_tpm_owner_password_);
}
private:
bool ShouldApply() const { return tpm_->GetVersion() == Tpm::TPM_1_2; }
Tpm* const tpm_;
SecureBlob previous_tpm_owner_password_;
};
} // namespace
TpmLiveTest::TpmLiveTest() : tpm_(Tpm::GetSingleton()) {}
bool TpmLiveTest::RunLiveTests(const SecureBlob& owner_password,
bool tpm2_use_system_owner_password) {
if (!PCRKeyTest()) {
LOG(ERROR) << "Error running PCRKeyTest.";
return false;
}
if (!MultiplePCRKeyTest()) {
LOG(ERROR) << "Error running MultiplePCRKeyTest.";
return false;
}
if (!DecryptionKeyTest()) {
LOG(ERROR) << "Error running Decryption test.";
return false;
}
if (!SealToPcrWithAuthorizationTest()) {
LOG(ERROR) << "Error running SealToPcrWithAuthorizationTest.";
return false;
}
const Tpm::TpmVersion tpm_version = tpm_->GetVersion();
if ((tpm_version == Tpm::TPM_1_2 && !owner_password.empty()) ||
(tpm_version == Tpm::TPM_2_0 && tpm2_use_system_owner_password)) {
if (!NvramTest(owner_password)) {
LOG(ERROR) << "Error running NvramTest.";
return false;
}
}
if (tpm_version != Tpm::TPM_1_2 || !owner_password.empty()) {
if (!SignatureSealedSecretTest(owner_password)) {
LOG(ERROR) << "Error running SignatureSealedSecretTest.";
return false;
}
}
LOG(INFO) << "All tests run successfully.";
return true;
}
bool TpmLiveTest::SignData(const SecureBlob& pcr_bound_key,
const SecureBlob& public_key_der,
int index) {
SecureBlob input_data("input_data");
SecureBlob signature;
if (!tpm_->Sign(pcr_bound_key, input_data, index, &signature)) {
LOG(ERROR) << "Error signing with PCR bound key.";
return false;
}
const unsigned char* public_key_data = public_key_der.data();
crypto::ScopedRSA rsa(
d2i_RSAPublicKey(nullptr, &public_key_data, public_key_der.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public key.";
return false;
}
SecureBlob digest = CryptoLib::Sha256(input_data);
if (!RSA_verify(NID_sha256, digest.data(), digest.size(), signature.data(),
signature.size(), rsa.get())) {
LOG(ERROR) << "Failed to verify signature.";
return false;
}
return true;
}
bool TpmLiveTest::EncryptAndDecryptData(
const SecureBlob& pcr_bound_key,
const std::map<uint32_t, std::string>& pcr_map) {
ScopedKeyHandle handle;
if (tpm_->LoadWrappedKey(pcr_bound_key, &handle) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error loading wrapped key.";
return false;
}
SecureBlob aes_key(32, 'a');
SecureBlob plaintext(32, 'b');
SecureBlob ciphertext;
if (tpm_->EncryptBlob(handle.value(), plaintext, aes_key, &ciphertext) !=
Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error encrypting blob.";
return false;
}
SecureBlob decrypted_plaintext;
if (tpm_->DecryptBlob(handle.value(), ciphertext, aes_key, pcr_map,
&decrypted_plaintext) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error decrypting the data.";
return false;
}
if (plaintext != decrypted_plaintext) {
LOG(ERROR) << "Decrypted plaintext does not match plaintext.";
return false;
}
return true;
}
bool TpmLiveTest::PCRKeyTest() {
LOG(INFO) << "PCRKeyTest started";
uint32_t index = 5;
Blob pcr_data;
if (!tpm_->ReadPCR(index, &pcr_data)) {
LOG(ERROR) << "Error reading pcr value from TPM.";
return false;
}
SecureBlob pcr_bound_key1; // Sign key
SecureBlob pcr_bound_key2; // Decrypt key
SecureBlob pcr_bound_key3; // Sign and decrypt key
SecureBlob public_key_der1;
SecureBlob public_key_der2;
SecureBlob public_key_der3;
SecureBlob creation_blob1;
SecureBlob creation_blob2;
SecureBlob creation_blob3;
std::map<uint32_t, std::string> pcr_map({{index, BlobToString(pcr_data)}});
// Create the keys.
if (!tpm_->CreatePCRBoundKey(pcr_map, AsymmetricKeyUsage::kSignKey,
&pcr_bound_key1, &public_key_der1,
&creation_blob1)) {
LOG(ERROR) << "Error creating PCR bound signing key.";
return false;
}
if (!tpm_->CreatePCRBoundKey(pcr_map, AsymmetricKeyUsage::kDecryptKey,
&pcr_bound_key2, &public_key_der2,
&creation_blob2)) {
LOG(ERROR) << "Error creating PCR bound decryption key.";
return false;
}
if (!tpm_->CreatePCRBoundKey(pcr_map, AsymmetricKeyUsage::kDecryptAndSignKey,
&pcr_bound_key3, &public_key_der3,
&creation_blob3)) {
LOG(ERROR) << "Error creating PCR bound decrypt and sign key.";
return false;
}
if (!tpm_->VerifyPCRBoundKey(pcr_map, pcr_bound_key1, creation_blob1) ||
!tpm_->VerifyPCRBoundKey(pcr_map, pcr_bound_key2, creation_blob2) ||
!tpm_->VerifyPCRBoundKey(pcr_map, pcr_bound_key3, creation_blob3)) {
LOG(ERROR) << "Error verifying PCR bound key.";
return false;
}
// Check that signing key works.
if (!SignData(pcr_bound_key1, public_key_der1, index)) {
LOG(ERROR) << "Error signing the blob.";
return false;
}
// Check that the key cannot be used to decrypt the data.
if (EncryptAndDecryptData(pcr_bound_key1, pcr_map)) {
LOG(ERROR) << "Decrypting the blob succeeded with signing only key.";
return false;
}
// Check that the decryption key works as intended.
if (!EncryptAndDecryptData(pcr_bound_key2, pcr_map)) {
LOG(ERROR) << "Error decrypting the blob.";
return false;
}
// Check that signing data doesn't work (only for TPM2).
if (tpm_->GetVersion() != Tpm::TPM_1_2) {
if (SignData(pcr_bound_key2, public_key_der2, index)) {
LOG(ERROR) << "Signing data succeeded with decryption only key.";
return false;
}
}
// Check that the key created for decryption and signing works for both.
if (!EncryptAndDecryptData(pcr_bound_key3, pcr_map)) {
LOG(ERROR) << "Error decrypting the blob.";
return false;
}
if (!SignData(pcr_bound_key3, public_key_der3, index)) {
LOG(ERROR) << "Error signing the blob.";
return false;
}
// Extend PCR to invalidate the keys.
if (!tpm_->ExtendPCR(index, BlobFromString("01234567890123456789"))) {
LOG(ERROR) << "Error extending PCR.";
return false;
}
if (SignData(pcr_bound_key1, public_key_der1, index)) {
LOG(ERROR) << "Sign succeeded without the correct PCR state.";
return false;
}
if (EncryptAndDecryptData(pcr_bound_key2, pcr_map)) {
LOG(ERROR) << "Decryption succeeded without the correct PCR state.";
return false;
}
if (SignData(pcr_bound_key3, public_key_der3, index)) {
LOG(ERROR) << "Sign succeeded without the correct PCR state.";
return false;
}
if (EncryptAndDecryptData(pcr_bound_key3, pcr_map)) {
LOG(ERROR) << "Decryption succeeded without the correct PCR state.";
return false;
}
LOG(INFO) << "PCRKeyTest ended successfully.";
return true;
}
bool TpmLiveTest::MultiplePCRKeyTest() {
LOG(INFO) << "MultiplePCRKeyTest started";
uint32_t index1 = 7;
uint32_t index2 = 12;
Blob pcr_data1;
Blob pcr_data2;
if (!tpm_->ReadPCR(index1, &pcr_data1) ||
!tpm_->ReadPCR(index2, &pcr_data2)) {
LOG(ERROR) << "Error reading pcr value from TPM.";
return false;
}
SecureBlob pcr_bound_key;
SecureBlob public_key_der;
SecureBlob creation_blob;
std::map<uint32_t, std::string> pcr_map(
{{index1, BlobToString(pcr_data1)}, {index2, BlobToString(pcr_data2)}});
if (!tpm_->CreatePCRBoundKey(pcr_map, AsymmetricKeyUsage::kDecryptKey,
&pcr_bound_key, &public_key_der,
&creation_blob)) {
LOG(ERROR) << "Error creating PCR bound key.";
return false;
}
ScopedKeyHandle handle;
if (tpm_->LoadWrappedKey(pcr_bound_key, &handle) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error loading wrapped key.";
return false;
}
SecureBlob aes_key(32, 'a');
SecureBlob plaintext(32, 'b');
SecureBlob ciphertext;
if (tpm_->EncryptBlob(handle.value(), plaintext, aes_key, &ciphertext) !=
Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error encrypting blob.";
return false;
}
SecureBlob decrypted_plaintext;
if (tpm_->DecryptBlob(handle.value(), ciphertext, aes_key, pcr_map,
&decrypted_plaintext) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error decrypting blob.";
return false;
}
if (plaintext != decrypted_plaintext) {
LOG(ERROR) << "Decrypted plaintext does not match plaintext.";
return false;
}
if (!tpm_->VerifyPCRBoundKey(pcr_map, pcr_bound_key, creation_blob)) {
LOG(ERROR) << "Error verifying PCR bound key.";
return false;
}
// Extend a PCR that is bound to the key, to invalidate it.
if (!tpm_->ExtendPCR(index2, BlobFromString("01234567890123456789"))) {
LOG(ERROR) << "Error extending PCR.";
return false;
}
// Check that the text cannot be decrypted anymore, after the PCR change.
if (tpm_->DecryptBlob(handle.value(), ciphertext, aes_key, pcr_map,
&decrypted_plaintext) == Tpm::kTpmRetryNone) {
LOG(ERROR) << "Decrypt succeeded without the correct PCR state.";
return false;
}
if (!tpm_->ReadPCR(index2, &pcr_data2)) {
LOG(ERROR) << "Error reading pcr value from TPM.";
return false;
}
// Check that the text cannot be decrypted even with the right PCR values.
pcr_map[index2] = BlobToString(pcr_data2);
if (tpm_->DecryptBlob(handle.value(), ciphertext, aes_key, pcr_map,
&decrypted_plaintext) == Tpm::kTpmRetryNone) {
LOG(ERROR) << "Decrypt succeeded without the correct PCR state.";
return false;
}
// Check that VerifyPCRBoundKey also fails.
if (tpm_->VerifyPCRBoundKey(pcr_map, pcr_bound_key, creation_blob)) {
LOG(ERROR) << "VerifyPCRBoundKey succeeded without the correct PCR state.";
return false;
}
// Check that even a newly encrypted text cannot be decrypted.
if (tpm_->EncryptBlob(handle.value(), plaintext, aes_key, &ciphertext) !=
Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error encrypting blob.";
return false;
}
if (tpm_->DecryptBlob(handle.value(), ciphertext, aes_key, pcr_map,
&decrypted_plaintext) == Tpm::kTpmRetryNone) {
LOG(ERROR) << "Decrypt succeeded without the correct PCR state.";
return false;
}
LOG(INFO) << "MultiplePCRKeyTest ended successfully.";
return true;
}
bool TpmLiveTest::DecryptionKeyTest() {
LOG(INFO) << "DecryptionKeyTest started";
SecureBlob n;
SecureBlob p;
uint32_t tpm_key_bits = 2048;
if (!CryptoLib::CreateRsaKey(tpm_key_bits, &n, &p)) {
LOG(ERROR) << "Error creating RSA key.";
return false;
}
SecureBlob wrapped_key;
if (!tpm_->WrapRsaKey(n, p, &wrapped_key)) {
LOG(ERROR) << "Error wrapping RSA key.";
return false;
}
ScopedKeyHandle handle;
if (tpm_->LoadWrappedKey(wrapped_key, &handle) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error loading key.";
return false;
}
SecureBlob aes_key(32, 'a');
SecureBlob plaintext(32, 'b');
SecureBlob ciphertext;
if (tpm_->EncryptBlob(handle.value(), plaintext, aes_key, &ciphertext) !=
Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error encrypting blob.";
return false;
}
SecureBlob decrypted_plaintext;
if (tpm_->DecryptBlob(handle.value(), ciphertext, aes_key,
std::map<uint32_t, std::string>(),
&decrypted_plaintext) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error decrypting blob.";
return false;
}
if (plaintext != decrypted_plaintext) {
LOG(ERROR) << "Decrypted plaintext does not match plaintext.";
return false;
}
LOG(INFO) << "DecryptionKeyTest ended successfully.";
return true;
}
bool TpmLiveTest::SealToPcrWithAuthorizationTest() {
LOG(INFO) << "SealToPcrWithAuthorizationTest started";
SecureBlob n;
SecureBlob p;
uint32_t tpm_key_bits = 2048;
if (!CryptoLib::CreateRsaKey(tpm_key_bits, &n, &p)) {
LOG(ERROR) << "Error creating RSA key.";
return false;
}
SecureBlob wrapped_key;
if (!tpm_->WrapRsaKey(n, p, &wrapped_key)) {
LOG(ERROR) << "Error wrapping RSA key.";
return false;
}
ScopedKeyHandle handle;
if (tpm_->LoadWrappedKey(wrapped_key, &handle) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error loading key.";
return false;
}
uint32_t index1 = 4;
uint32_t index2 = 11;
std::map<uint32_t, std::string> pcr_map({{index1, ""}, {index2, ""}});
SecureBlob plaintext(32, 'a');
SecureBlob auth_blob(256, 'b');
SecureBlob ciphertext;
if (tpm_->SealToPcrWithAuthorization(handle.value(), plaintext, auth_blob,
pcr_map,
&ciphertext) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error sealing the blob.";
return false;
}
SecureBlob unsealed_text;
if (tpm_->UnsealWithAuthorization(handle.value(), ciphertext, auth_blob,
pcr_map,
&unsealed_text) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Error unsealing blob.";
return false;
}
if (plaintext != unsealed_text) {
LOG(ERROR) << "Unsealed plaintext does not match plaintext.";
return false;
}
// Check that unsealing doesn't work with wrong auth_blob.
auth_blob.char_data()[255] = 'a';
if (tpm_->UnsealWithAuthorization(handle.value(), ciphertext, auth_blob,
pcr_map,
&unsealed_text) == Tpm::kTpmRetryNone &&
plaintext == unsealed_text) {
LOG(ERROR) << "UnsealWithAuthorization failed to fail.";
return false;
}
LOG(INFO) << "SealToPcrWithAuthorizationTest ended successfully.";
return true;
}
bool TpmLiveTest::NvramTest(const SecureBlob& owner_password) {
LOG(INFO) << "NvramTest started";
const ScopedTpmOwnerPasswordSetter scoped_tpm_owner_password_setter(
owner_password);
uint32_t index = 12;
SecureBlob nvram_data("nvram_data");
if (tpm_->IsNvramDefined(index)) {
if (!tpm_->DestroyNvram(index)) {
LOG(ERROR) << "Error destroying old Nvram.";
return false;
}
if (tpm_->IsNvramDefined(index)) {
LOG(ERROR) << "Nvram still defined after it was destroyed.";
return false;
}
}
if (!tpm_->DefineNvram(
index, nvram_data.size(),
Tpm::kTpmNvramWriteDefine | Tpm::kTpmNvramBindToPCR0)) {
LOG(ERROR) << "Defining Nvram index.";
return false;
}
if (!tpm_->IsNvramDefined(index)) {
LOG(ERROR) << "Nvram index is not defined after creating.";
return false;
}
if (tpm_->GetNvramSize(index) != nvram_data.size()) {
LOG(ERROR) << "Nvram space is of incorrect size.";
return false;
}
if (tpm_->IsNvramLocked(index)) {
LOG(ERROR) << "Nvram should not be locked before writing.";
return false;
}
if (!tpm_->WriteNvram(index, nvram_data)) {
LOG(ERROR) << "Error writing to Nvram.";
return false;
}
if (!tpm_->WriteLockNvram(index)) {
LOG(ERROR) << "Error locking Nvram space.";
return false;
}
if (!tpm_->IsNvramLocked(index)) {
LOG(ERROR) << "Nvram should be locked after locking.";
return false;
}
SecureBlob data;
if (!tpm_->ReadNvram(index, &data)) {
LOG(ERROR) << "Error reading from Nvram.";
return false;
}
if (data != nvram_data) {
LOG(ERROR) << "Data read from Nvram did not match data written.";
return false;
}
if (tpm_->WriteNvram(index, nvram_data)) {
LOG(ERROR) << "We should not be able to write to a locked Nvram space.";
return false;
}
if (!tpm_->DestroyNvram(index)) {
LOG(ERROR) << "Error destroying Nvram space.";
return false;
}
if (tpm_->IsNvramDefined(index)) {
LOG(ERROR) << "Nvram still defined after it was destroyed.";
return false;
}
LOG(INFO) << "NvramTest ended successfully.";
return true;
}
namespace {
struct SignatureSealedSecretTestCaseParam {
SignatureSealedSecretTestCaseParam(
const std::string& test_case_description,
Tpm* tpm,
int key_size_bits,
const std::vector<ChallengeSignatureAlgorithm>& supported_algorithms,
base::Optional<ChallengeSignatureAlgorithm> expected_algorithm,
int openssl_algorithm_nid)
: test_case_description(test_case_description),
tpm(tpm),
key_size_bits(key_size_bits),
supported_algorithms(supported_algorithms),
expected_algorithm(expected_algorithm),
openssl_algorithm_nid(openssl_algorithm_nid) {}
SignatureSealedSecretTestCaseParam(SignatureSealedSecretTestCaseParam&&) =
default;
static SignatureSealedSecretTestCaseParam MakeSuccessful(
const std::string& test_case_description,
Tpm* tpm,
int key_size_bits,
const std::vector<ChallengeSignatureAlgorithm>& supported_algorithms,
ChallengeSignatureAlgorithm expected_algorithm,
int openssl_algorithm_nid) {
return SignatureSealedSecretTestCaseParam(
test_case_description, tpm, key_size_bits, supported_algorithms,
expected_algorithm, openssl_algorithm_nid);
}
static SignatureSealedSecretTestCaseParam MakeFailing(
const std::string& test_case_description,
Tpm* tpm,
int key_size_bits,
const std::vector<ChallengeSignatureAlgorithm>& supported_algorithms) {
return SignatureSealedSecretTestCaseParam(
test_case_description, tpm, key_size_bits, supported_algorithms, {}, 0);
}
bool expect_success() const { return expected_algorithm.has_value(); }
std::string test_case_description;
Tpm* tpm;
int key_size_bits;
std::vector<ChallengeSignatureAlgorithm> supported_algorithms;
base::Optional<ChallengeSignatureAlgorithm> expected_algorithm;
int openssl_algorithm_nid;
};
class SignatureSealedSecretTestCase final {
public:
using UnsealingSession = SignatureSealingBackend::UnsealingSession;
SignatureSealedSecretTestCase(SignatureSealedSecretTestCaseParam param,
const SecureBlob& owner_password)
: param_(std::move(param)), owner_password_(owner_password) {
LOG(INFO) << "SignatureSealedSecretTestCase: " << param_.key_size_bits
<< "-bit key, " << param_.test_case_description;
}
SignatureSealedSecretTestCase(const SignatureSealedSecretTestCase&) = delete;
SignatureSealedSecretTestCase& operator=(
const SignatureSealedSecretTestCase&) = delete;
~SignatureSealedSecretTestCase() { CleanUpDelegate(); }
bool SetUp() {
if (!GenerateRsaKey(param_.key_size_bits, &pkey_, &key_spki_der_)) {
LOG(ERROR) << "Error generating the RSA key";
return false;
}
if (!InitDelegate()) {
LOG(ERROR) << "Error creating the delegate";
return false;
}
return true;
}
bool Run() {
if (!param_.expect_success()) {
if (!CheckSecretCreationFails()) {
LOG(ERROR) << "Error: successfully created secret unexpectedly";
return false;
}
return true;
}
// Create a secret.
SecureBlob secret_value;
SignatureSealedData sealed_secret_data;
if (!CreateSecret(&secret_value, &sealed_secret_data)) {
LOG(ERROR) << "Error creating a secret";
return false;
}
// Unseal the secret.
Blob first_challenge_value;
Blob first_challenge_signature;
SecureBlob first_unsealed_value;
if (!Unseal(sealed_secret_data, &first_challenge_value,
&first_challenge_signature, &first_unsealed_value)) {
LOG(ERROR) << "Error unsealing a secret";
return false;
}
if (first_unsealed_value != secret_value) {
LOG(ERROR)
<< "Error: unsealing returned different value than at creation time";
return false;
}
// Unseal the secret again - the challenge is different, but the result is
// the same.
Blob second_challenge_value;
Blob second_challenge_signature;
SecureBlob second_unsealed_value;
if (!Unseal(sealed_secret_data, &second_challenge_value,
&second_challenge_signature, &second_unsealed_value)) {
LOG(ERROR) << "Error unsealing secret for the second time";
return false;
}
if (first_challenge_value == second_challenge_value) {
LOG(ERROR) << "Error: challenge value collision";
return false;
}
if (second_unsealed_value != secret_value) {
LOG(ERROR)
<< "Error: unsealing returned different value than at creation time";
return false;
}
// Unsealing with a bad challenge response fails.
if (!CheckUnsealingFailsWithOldSignature(sealed_secret_data,
first_challenge_signature) ||
!CheckUnsealingFailsWithBadAlgorithmSignature(sealed_secret_data) ||
!CheckUnsealingFailsWithBadSignature(sealed_secret_data)) {
LOG(ERROR) << "Failed testing against bad challenge responses";
return false;
}
// Unsealing with a bad key fails.
if (!CheckUnsealingFailsWithWrongAlgorithm(sealed_secret_data) ||
!CheckUnsealingFailsWithWrongKey(sealed_secret_data)) {
LOG(ERROR) << "Failed testing against bad keys";
return false;
}
// Create and unseal another secret - it has a different value.
SecureBlob another_secret_value;
SignatureSealedData another_sealed_secret_data;
if (!CreateSecret(&another_secret_value, &another_sealed_secret_data)) {
LOG(ERROR) << "Error creating another secret";
return false;
}
if (another_secret_value == secret_value) {
LOG(ERROR) << "Error: secret value collision";
return false;
}
Blob third_challenge_value;
Blob third_challenge_signature;
SecureBlob third_unsealed_value;
if (!Unseal(another_sealed_secret_data, &third_challenge_value,
&third_challenge_signature, &third_unsealed_value)) {
LOG(ERROR) << "Error unsealing another secret";
return false;
}
if (third_unsealed_value != another_secret_value) {
LOG(ERROR)
<< "Error: unsealing returned different value than at creation time";
return false;
}
// Unsealing after PCRs change fails.
if (!CheckUnsealingFailsWithChangedPcrs(another_sealed_secret_data)) {
LOG(ERROR) << "Failed testing against changed PCRs";
return false;
}
return true;
}
private:
static constexpr uint32_t kPcrIndexToExtend = 15;
const std::set<uint32_t> kPcrIndexes{0, kPcrIndexToExtend};
static constexpr uint8_t kDelegateFamilyLabel = 100;
static constexpr uint8_t kDelegateLabel = 101;
Tpm* tpm() { return param_.tpm; }
SignatureSealingBackend* backend() {
return tpm()->GetSignatureSealingBackend();
}
static bool GenerateRsaKey(int key_size_bits,
crypto::ScopedEVP_PKEY* pkey,
Blob* key_spki_der) {
crypto::ScopedEVP_PKEY_CTX pkey_context(
EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr));
if (!pkey_context)
return false;
if (EVP_PKEY_keygen_init(pkey_context.get()) <= 0)
return false;
if (EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_context.get(), key_size_bits) <=
0) {
return false;
}
EVP_PKEY* pkey_raw = nullptr;
if (EVP_PKEY_keygen(pkey_context.get(), &pkey_raw) <= 0)
return false;
pkey->reset(pkey_raw);
// Obtain the DER-encoded Subject Public Key Info.
const int key_spki_der_length = i2d_PUBKEY(pkey->get(), nullptr);
if (key_spki_der_length < 0)
return false;
key_spki_der->resize(key_spki_der_length);
unsigned char* key_spki_der_buffer = key_spki_der->data();
return i2d_PUBKEY(pkey->get(), &key_spki_der_buffer) ==
key_spki_der->size();
}
// Creates the TPM 1.2 delegate.
bool InitDelegate() {
if (tpm()->GetVersion() != Tpm::TPM_1_2)
return true;
const ScopedTpmOwnerPasswordSetter scoped_tpm_owner_password_setter(
owner_password_);
return tpm()->CreateDelegate(kPcrIndexes, kDelegateFamilyLabel,
kDelegateLabel, &delegate_blob_,
&delegate_secret_);
}
// Deletes the TPM 1.2 delegate and family from the TPM's NVRAM. Not doing
// that will result in the NVRAM space exhaustion after several launches of
// the test.
void CleanUpDelegate() {
#if !USE_TPM2
CHECK_EQ(Tpm::TPM_1_2, tpm()->GetVersion());
using trousers::ScopedTssContext;
using trousers::ScopedTssMemory;
using trousers::ScopedTssObject;
if (delegate_blob_.empty() || delegate_secret_.empty())
return;
// Obtain the TPM context and handle with the owner authorization.
const ScopedTpmOwnerPasswordSetter scoped_tpm_owner_password_setter(
owner_password_);
ScopedTssContext tpm_context;
TSS_HTPM tpm_handle = 0;
if (!static_cast<TpmImpl*>(tpm())->ConnectContextAsOwner(tpm_context.ptr(),
&tpm_handle)) {
LOG(ERROR)
<< "Failed to clean up the delegate: error connecting to the TPM";
return;
}
// Obtain all TPM delegates and delegate families.
UINT32 family_table_size = 0;
TSS_FAMILY_TABLE_ENTRY* family_table_ptr = nullptr;
UINT32 delegate_table_size = 0;
TSS_DELEGATION_TABLE_ENTRY* delegate_table_ptr = nullptr;
TSS_RESULT tss_result = Tspi_TPM_Delegate_ReadTables(
tpm_context, &family_table_size, &family_table_ptr,
&delegate_table_size, &delegate_table_ptr);
if (TPM_ERROR(tss_result)) {
LOG(ERROR)
<< "Failed to clean up the delegate: error reading delegate table: "
<< Trspi_Error_String(tss_result);
return;
}
ScopedTssMemory scoped_family_table(
tpm_context, reinterpret_cast<BYTE*>(family_table_ptr));
ScopedTssMemory scoped_delegate_table(
tpm_context, reinterpret_cast<BYTE*>(delegate_table_ptr));
// Invalidate the delegate families which have the test label. Note that
// this removes from the NVRAM both the delegate families and the delegates
// themselves.
int invalidated_family_count = 0;
UINT64 family_table_offset = 0;
for (int family_index = 0; family_index < family_table_size;
++family_index) {
TSS_FAMILY_TABLE_ENTRY family_entry;
Trspi_UnloadBlob_TSS_FAMILY_TABLE_ENTRY(
&family_table_offset, scoped_family_table.value(), &family_entry);
if (family_entry.label == kDelegateFamilyLabel) {
ScopedTssObject<TSS_HDELFAMILY> family_handle(tpm_context);
tss_result = Tspi_TPM_Delegate_GetFamily(
tpm_handle, family_entry.familyID, family_handle.ptr());
if (TPM_ERROR(tss_result)) {
LOG(ERROR) << "Failed to clean up the delegate: error getting "
"delegate family handle: "
<< Trspi_Error_String(tss_result);
continue;
}
tss_result =
Tspi_TPM_Delegate_InvalidateFamily(tpm_handle, family_handle);
if (TPM_ERROR(tss_result)) {
LOG(ERROR) << "Failed to clean up the delegate: error invalidating "
"delegate family: "
<< Trspi_Error_String(tss_result);
continue;
}
++invalidated_family_count;
}
}
if (!invalidated_family_count) {
LOG(ERROR) << "Failed to clean up the delegate: no entry was "
"successfully invalidated";
return;
}
VLOG(1) << "Delegate families cleaned up: " << invalidated_family_count;
#endif // !USE_TPM2
}
bool CreateSecret(SecureBlob* secret_value,
SignatureSealedData* sealed_secret_data) {
std::map<uint32_t, Blob> pcr_values;
if (!GetCurrentPcrValues(&pcr_values)) {
LOG(ERROR) << "Error reading PCR values";
return false;
}
if (!backend()->CreateSealedSecret(
key_spki_der_, param_.supported_algorithms,
{pcr_values, pcr_values}, delegate_blob_, delegate_secret_,
secret_value, sealed_secret_data)) {
LOG(ERROR) << "Error creating signature-sealed secret";
return false;
}
return true;
}
bool CheckSecretCreationFails() {
std::map<uint32_t, Blob> pcr_values;
if (!GetCurrentPcrValues(&pcr_values)) {
LOG(ERROR) << "Error reading PCR values";
return false;
}
SecureBlob secret_value;
SignatureSealedData sealed_secret_data;
if (backend()->CreateSealedSecret(key_spki_der_,
param_.supported_algorithms, {pcr_values},
delegate_blob_, delegate_secret_,
&secret_value, &sealed_secret_data)) {
LOG(ERROR) << "Error: secret creation completed unexpectedly";
return false;
}
return true;
}
bool GetCurrentPcrValues(std::map<uint32_t, Blob>* pcr_values) {
for (auto pcr_index : kPcrIndexes) {
if (!tpm()->ReadPCR(pcr_index, &(*pcr_values)[pcr_index])) {
LOG(ERROR) << "Error reading PCR value " << pcr_index;
return false;
}
}
return true;
}
bool Unseal(const SignatureSealedData& sealed_secret_data,
Blob* challenge_value,
Blob* challenge_signature,
SecureBlob* unsealed_value) {
std::unique_ptr<UnsealingSession> unsealing_session(
backend()->CreateUnsealingSession(sealed_secret_data, key_spki_der_,
param_.supported_algorithms,
delegate_blob_, delegate_secret_));
if (!unsealing_session) {
LOG(ERROR) << "Error starting the unsealing session";
return false;
}
if (unsealing_session->GetChallengeAlgorithm() !=
*param_.expected_algorithm) {
LOG(ERROR) << "Wrong challenge signature algorithm";
return false;
}
*challenge_value = unsealing_session->GetChallengeValue();
if (challenge_value->empty()) {
LOG(ERROR) << "The challenge is empty";
return false;
}
if (!SignWithKey(*challenge_value, param_.openssl_algorithm_nid,
challenge_signature)) {
LOG(ERROR) << "Error generating signature of challenge";
return false;
}
if (!unsealing_session->Unseal(*challenge_signature, unsealed_value)) {
LOG(ERROR) << "Error unsealing the secret";
return false;
}
if (unsealed_value->empty()) {
LOG(ERROR) << "Error: empty unsealing result";
return false;
}
return true;
}
bool CheckUnsealingFailsWithOldSignature(
const SignatureSealedData& sealed_secret_data,
const Blob& challenge_signature) {
std::unique_ptr<UnsealingSession> unsealing_session(
backend()->CreateUnsealingSession(sealed_secret_data, key_spki_der_,
param_.supported_algorithms,
delegate_blob_, delegate_secret_));
if (!unsealing_session) {
LOG(ERROR) << "Error starting the unsealing session";
return false;
}
SecureBlob unsealed_value;
if (unsealing_session->Unseal(challenge_signature, &unsealed_value)) {
LOG(ERROR)
<< "Error: unsealing completed with an old challenge signature";
return false;
}
return true;
}
bool CheckUnsealingFailsWithBadAlgorithmSignature(
const SignatureSealedData& sealed_secret_data) {
std::unique_ptr<UnsealingSession> unsealing_session(
backend()->CreateUnsealingSession(sealed_secret_data, key_spki_der_,
param_.supported_algorithms,
delegate_blob_, delegate_secret_));
if (!unsealing_session) {
LOG(ERROR) << "Error starting the unsealing session";
return false;
}
const int wrong_openssl_algorithm_nid =
param_.openssl_algorithm_nid == NID_sha1 ? NID_sha256 : NID_sha1;
Blob challenge_signature;
if (!SignWithKey(unsealing_session->GetChallengeValue(),
wrong_openssl_algorithm_nid, &challenge_signature)) {
LOG(ERROR) << "Error generating signature of challenge";
return false;
}
SecureBlob unsealed_value;
if (unsealing_session->Unseal(challenge_signature, &unsealed_value)) {
LOG(ERROR) << "Error: unsealing completed with a wrong signature";
return false;
}
return true;
}
bool CheckUnsealingFailsWithBadSignature(
const SignatureSealedData& sealed_secret_data) {
std::unique_ptr<UnsealingSession> unsealing_session(
backend()->CreateUnsealingSession(sealed_secret_data, key_spki_der_,
param_.supported_algorithms,
delegate_blob_, delegate_secret_));
if (!unsealing_session) {
LOG(ERROR) << "Error starting the unsealing session";
return false;
}
Blob challenge_signature;
if (!SignWithKey(unsealing_session->GetChallengeValue(),
param_.openssl_algorithm_nid, &challenge_signature)) {
LOG(ERROR) << "Error generating signature of challenge";
return false;
}
challenge_signature.front() ^= 1;
SecureBlob unsealed_value;
if (unsealing_session->Unseal(challenge_signature, &unsealed_value)) {
LOG(ERROR) << "Error: unsealing completed with a wrong signature";
return false;
}
return true;
}
bool CheckUnsealingFailsWithWrongAlgorithm(
const SignatureSealedData& sealed_secret_data) {
const ChallengeSignatureAlgorithm wrong_algorithm =
*param_.expected_algorithm == CHALLENGE_RSASSA_PKCS1_V1_5_SHA1
? CHALLENGE_RSASSA_PKCS1_V1_5_SHA256
: CHALLENGE_RSASSA_PKCS1_V1_5_SHA1;
if (backend()->CreateUnsealingSession(sealed_secret_data, key_spki_der_,
{wrong_algorithm}, delegate_blob_,
delegate_secret_)) {
LOG(ERROR) << "Error: unsealing session creation completed with a "
"wrong algorithm";
return false;
}
return true;
}
bool CheckUnsealingFailsWithWrongKey(
const SignatureSealedData& sealed_secret_data) {
crypto::ScopedEVP_PKEY other_pkey;
Blob other_key_spki_der;
if (!GenerateRsaKey(param_.key_size_bits, &other_pkey,
&other_key_spki_der)) {
LOG(ERROR) << "Error generating the other RSA key";
return false;
}
if (backend()->CreateUnsealingSession(
sealed_secret_data, other_key_spki_der, param_.supported_algorithms,
delegate_blob_, delegate_secret_)) {
LOG(ERROR)
<< "Error: unsealing session creation completed with a wrong key";
return false;
}
return true;
}
bool CheckUnsealingFailsWithChangedPcrs(
const SignatureSealedData& sealed_secret_data) {
if (!tpm()->ExtendPCR(kPcrIndexToExtend,
BlobFromString("01234567890123456789"))) {
LOG(ERROR) << "Error extending PCR";
return false;
}
std::unique_ptr<UnsealingSession> unsealing_session(
backend()->CreateUnsealingSession(sealed_secret_data, key_spki_der_,
param_.supported_algorithms,
delegate_blob_, delegate_secret_));
if (!unsealing_session) {
// Unsealing expectedly failed, so the test is passed. (Whether it fails
// here or below after Unseal() depends on the specific
// SignatureSealingBackend implementation.)
return true;
}
Blob challenge_signature;
if (!SignWithKey(unsealing_session->GetChallengeValue(),
param_.openssl_algorithm_nid, &challenge_signature)) {
LOG(ERROR) << "Error generating signature of challenge";
return false;
}
SecureBlob unsealed_value;
if (unsealing_session->Unseal(challenge_signature, &unsealed_value)) {
LOG(ERROR) << "Error: unsealing completed with changed PCRs";
return false;
}
return true;
}
bool SignWithKey(const Blob& unhashed_data,
int algorithm_nid,
Blob* signature) {
signature->resize(EVP_PKEY_size(pkey_.get()));
crypto::ScopedEVP_MD_CTX sign_context(EVP_MD_CTX_new());
unsigned signature_size = 0;
if (!sign_context) {
LOG(ERROR) << "Error creating signing context";
return false;
}
if (!EVP_SignInit(sign_context.get(), EVP_get_digestbynid(algorithm_nid))) {
LOG(ERROR) << "Error initializing signature operation";
return false;
}
if (!EVP_SignUpdate(sign_context.get(), unhashed_data.data(),
unhashed_data.size())) {
LOG(ERROR) << "Error updating signature operation with data";
return false;
}
if (!EVP_SignFinal(sign_context.get(), signature->data(), &signature_size,
pkey_.get())) {
LOG(ERROR) << "Error finalizing signature operation";
return false;
}
CHECK_LE(signature_size, signature->size());
signature->resize(signature_size);
return true;
}
const SignatureSealedSecretTestCaseParam param_;
const SecureBlob owner_password_;
Blob delegate_blob_;
Blob delegate_secret_;
crypto::ScopedEVP_PKEY pkey_;
Blob key_spki_der_;
};
} // namespace
bool TpmLiveTest::SignatureSealedSecretTest(const SecureBlob& owner_password) {
using TestCaseParam = SignatureSealedSecretTestCaseParam;
if (!tpm_->GetSignatureSealingBackend()) {
// Not supported by the Tpm implementation, just skip the test.
return true;
}
LOG(INFO) << "SignatureSealedSecretTest started";
std::vector<TestCaseParam> test_case_params;
for (int key_size_bits : {1024, 2048}) {
test_case_params.push_back(TestCaseParam::MakeSuccessful(
"SHA-1", tpm_, key_size_bits, {CHALLENGE_RSASSA_PKCS1_V1_5_SHA1},
CHALLENGE_RSASSA_PKCS1_V1_5_SHA1, NID_sha1));
if (tpm_->GetVersion() == Tpm::TPM_1_2) {
test_case_params.push_back(
TestCaseParam::MakeFailing("SHA-256", tpm_, key_size_bits,
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA256}));
test_case_params.push_back(
TestCaseParam::MakeFailing("SHA-384", tpm_, key_size_bits,
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA384}));
test_case_params.push_back(
TestCaseParam::MakeFailing("SHA-512", tpm_, key_size_bits,
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA512}));
test_case_params.push_back(TestCaseParam::MakeSuccessful(
"{SHA-1,SHA-256}", tpm_, key_size_bits,
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA256,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA1},
CHALLENGE_RSASSA_PKCS1_V1_5_SHA1, NID_sha1));
} else {
test_case_params.push_back(TestCaseParam::MakeSuccessful(
"SHA-256", tpm_, key_size_bits, {CHALLENGE_RSASSA_PKCS1_V1_5_SHA256},
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256, NID_sha256));
test_case_params.push_back(TestCaseParam::MakeSuccessful(
"SHA-384", tpm_, key_size_bits, {CHALLENGE_RSASSA_PKCS1_V1_5_SHA384},
CHALLENGE_RSASSA_PKCS1_V1_5_SHA384, NID_sha384));
test_case_params.push_back(TestCaseParam::MakeSuccessful(
"SHA-512", tpm_, key_size_bits, {CHALLENGE_RSASSA_PKCS1_V1_5_SHA512},
CHALLENGE_RSASSA_PKCS1_V1_5_SHA512, NID_sha512));
test_case_params.push_back(TestCaseParam::MakeSuccessful(
"{SHA-384,SHA-256,SHA-512}", tpm_, key_size_bits,
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA384,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA512},
CHALLENGE_RSASSA_PKCS1_V1_5_SHA384, NID_sha384));
test_case_params.push_back(TestCaseParam::MakeSuccessful(
"{SHA-1,SHA-256}", tpm_, key_size_bits,
{CHALLENGE_RSASSA_PKCS1_V1_5_SHA1,
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256},
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256, NID_sha256));
}
}
for (auto&& test_case_param : test_case_params) {
SignatureSealedSecretTestCase test_case(std::move(test_case_param),
owner_password);
if (!test_case.SetUp() || !test_case.Run())
return false;
}
LOG(INFO) << "SignatureSealedSecretTest ended successfully.";
return true;
}
} // namespace cryptohome