blob: 784bb5a6a8edafcc38784353718e88d82c4b044f [file] [log] [blame]
// Copyright (c) 2012 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.
//
// Notes:
// - Failed authentication is not tested because it can put the TPM in a state
// where it refuses to perform authenticated operations for a period of time.
// - Poorly formatted key blobs is not tested because they are not handled
// correctly by Trousers and can crash the current process or tcsd.
#include "chaps/tpm_utility.h"
#include <memory>
#include <base/check.h>
#include <brillo/secure_blob.h>
#include <crypto/libcrypto-compat.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include "chaps/chaps_utility.h"
#if USE_TPM2
#include "chaps/tpm2_utility_impl.h"
#else
#include "chaps/tpm_utility_impl.h"
#endif
using std::string;
using std::unique_ptr;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::InvokeWithoutArgs;
using ::testing::Return;
using ::testing::SetArgPointee;
namespace chaps {
class TestTPMUtility : public ::testing::Test {
public:
TestTPMUtility() {
#if USE_TPM2
// Instantiate a TPM2 Utility.
tpm_.reset(new TPM2UtilityImpl());
#else
// Instantiate a TPM1.2 Utility.
tpm_.reset(new TPMUtilityImpl(""));
#endif
}
void SetUp() {
size_ = 2048;
e_ = string("\x1\x0\x1", 3);
unsigned char random[20];
RAND_bytes(random, 20);
auth_ = brillo::SecureBlob(std::begin(random), std::end(random));
EXPECT_TRUE(tpm_->Init());
}
void TestKey() {
string e, n;
EXPECT_TRUE(tpm_->GetRSAPublicKey(key_, &e, &n));
EXPECT_EQ(n.length() * 8, size_);
string input("input"), encrypted;
EXPECT_TRUE(tpm_->Bind(key_, input, &encrypted));
string input2;
EXPECT_TRUE(tpm_->Unbind(key_, encrypted, &input2));
EXPECT_TRUE(input == input2);
string signature;
EXPECT_TRUE(tpm_->Sign(key_, CKM_RSA_PKCS, "", input, &signature));
}
bool InjectKey() {
crypto::ScopedBIGNUM e(BN_new());
CHECK(e);
EXPECT_TRUE(ConvertToBIGNUM(e_, e.get()));
crypto::ScopedRSA key(RSA_new());
CHECK(key);
EXPECT_TRUE(RSA_generate_key_ex(key.get(), size_, e.get(), nullptr));
const BIGNUM* key_n;
const BIGNUM* key_p;
RSA_get0_key(key.get(), &key_n, nullptr, nullptr);
RSA_get0_factors(key.get(), &key_p, nullptr);
string n = ConvertFromBIGNUM(key_n);
string p = ConvertFromBIGNUM(key_p);
bool result = tpm_->WrapRSAKey(0, e_, n, p, auth_, &blob_, &key_);
return result;
}
protected:
unique_ptr<TPMUtility> tpm_;
int size_;
string e_;
brillo::SecureBlob auth_;
int key_ = 0;
string blob_;
};
TEST_F(TestTPMUtility, Authenticate) {
EXPECT_TRUE(InjectKey());
// Setup for authentication.
string master = "master_key";
string encrypted_master;
EXPECT_TRUE(tpm_->Bind(key_, master, &encrypted_master));
// Successful authentication.
brillo::SecureBlob master2;
EXPECT_TRUE(tpm_->Authenticate(0, auth_, blob_, encrypted_master, &master2));
EXPECT_TRUE(master == master2.to_string());
tpm_->UnloadKeysForSlot(0);
// Change password.
unsigned char random[20];
RAND_bytes(random, 20);
brillo::SecureBlob auth2(std::begin(random), std::end(random));
string blob2;
EXPECT_TRUE(tpm_->ChangeAuthData(0, auth_, auth2, blob_, &blob2));
tpm_->UnloadKeysForSlot(0);
// Authenticate with new password.
EXPECT_TRUE(tpm_->Authenticate(0, auth2, blob2, encrypted_master, &master2));
EXPECT_TRUE(master == master2.to_string());
tpm_->UnloadKeysForSlot(0);
}
TEST_F(TestTPMUtility, Random) {
EXPECT_TRUE(tpm_->StirRandom("some_entropy"));
string r;
EXPECT_TRUE(tpm_->GenerateRandom(128, &r));
EXPECT_EQ(128, r.length());
}
TEST_F(TestTPMUtility, GenerateRSAKey) {
EXPECT_TRUE(tpm_->GenerateRSAKey(0, size_, e_, auth_, &blob_, &key_));
TestKey();
tpm_->UnloadKeysForSlot(0);
EXPECT_TRUE(tpm_->LoadKey(0, blob_, auth_, &key_));
TestKey();
tpm_->UnloadKeysForSlot(0);
}
TEST_F(TestTPMUtility, WrappedKey) {
EXPECT_TRUE(InjectKey());
TestKey();
tpm_->UnloadKeysForSlot(0);
EXPECT_TRUE(tpm_->LoadKey(0, blob_, auth_, &key_));
TestKey();
// Test with some unexpected parameters.
EXPECT_FALSE(
tpm_->WrapRSAKey(0, e_, "invalid_n", "invalid_p", auth_, &blob_, &key_));
tpm_->UnloadKeysForSlot(0);
}
TEST_F(TestTPMUtility, BadAuthSize) {
EXPECT_TRUE(InjectKey());
brillo::SecureBlob bad(48);
brillo::SecureBlob tmp;
string master("master"), encrypted;
EXPECT_TRUE(tpm_->Bind(key_, master, &encrypted));
tpm_->UnloadKeysForSlot(0);
EXPECT_FALSE(tpm_->Authenticate(0, bad, blob_, encrypted, &tmp));
EXPECT_FALSE(tpm_->GenerateRSAKey(0, size_, e_, bad, &blob_, &key_));
tpm_->UnloadKeysForSlot(0);
EXPECT_FALSE(tpm_->LoadKey(0, blob_, bad, &key_));
}
TEST_F(TestTPMUtility, BadKeyHandle) {
int key = 17;
string e, n;
EXPECT_FALSE(tpm_->GetRSAPublicKey(key, &e, &n));
string in, out;
EXPECT_FALSE(tpm_->Unbind(key, in, &out));
EXPECT_FALSE(tpm_->Sign(key, CKM_RSA_PKCS, "", in, &out));
}
TEST_F(TestTPMUtility, BadInput) {
const int max_plain = (size_ / 8) - 11;
const int expected_encrypted = (size_ / 8);
EXPECT_TRUE(InjectKey());
string out;
EXPECT_FALSE(tpm_->Bind(key_, string(max_plain + 1, 'a'), &out));
EXPECT_TRUE(tpm_->Bind(key_, string(max_plain, 'a'), &out));
EXPECT_EQ(expected_encrypted, out.length());
EXPECT_FALSE(tpm_->Unbind(key_, out + string(1, 'a'), &out));
tpm_->UnloadKeysForSlot(0);
}
TEST_F(TestTPMUtility, TPMVersionCheck) {
TPMVersion version;
#if USE_TPM2
version = TPMVersion::TPM2_0;
#else
version = TPMVersion::TPM1_2;
#endif
EXPECT_EQ(tpm_->GetTPMVersion(), version);
}
} // namespace chaps