blob: 005d8b8014e8d2bf63e20e8e4aa39070337f8b15 [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.
#include "chaps/opencryptoki_importer.h"
#include <stdlib.h>
#include <map>
#include <string>
#include <vector>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "chaps/chaps_factory_mock.h"
#include "chaps/object_mock.h"
#include "chaps/object_pool_mock.h"
#include "chaps/tpm_utility_mock.h"
using base::FilePath;
using std::map;
using std::string;
using std::vector;
using testing::_;
using testing::AnyNumber;
using testing::DoAll;
using testing::Invoke;
using testing::Return;
using testing::SetArgumentPointee;
using testing::Values;
namespace chaps {
const unsigned char kSampleMasterKeyEncrypted[] = {
80, 118, 191, 150, 143, 171, 162, 61, 89, 32, 95, 219, 44, 244, 51, 84, 117,
228, 36, 225, 240, 122, 234, 92, 182, 224, 133, 238, 100, 18, 116, 130, 166,
177, 7, 103, 223, 122, 112, 136, 126, 30, 191, 253, 137, 85, 70, 187, 220,
137, 248, 155, 89, 152, 113, 153, 113, 48, 59, 148, 246, 114, 146, 13, 86,
254, 227, 3, 229, 70, 247, 165, 101, 76, 3, 58, 134, 230, 84, 113, 94, 226,
134, 130, 34, 100, 56, 157, 5, 255, 127, 180, 147, 56, 43, 233, 32, 254,
209, 52, 41, 48, 15, 127, 110, 187, 183, 254, 123, 20, 182, 153, 107, 192,
136, 229, 72, 243, 38, 238, 155, 59, 216, 15, 17, 72, 39, 209, 196, 66, 53,
140, 236, 132, 19, 69, 58, 107, 103, 22, 19, 70, 175, 35, 126, 16, 56, 132,
150, 89, 182, 12, 3, 166, 206, 160, 194, 12, 250, 211, 141, 73, 109, 83,
144, 253, 166, 71, 109, 219, 143, 202, 237, 89, 185, 136, 249, 104, 78, 68,
11, 169, 144, 194, 57, 140, 147, 104, 175, 229, 20, 223, 98, 109, 187, 120,
200, 126, 81, 147, 31, 13, 239, 36, 233, 221, 78, 117, 59, 248, 156, 231,
189, 232, 48, 128, 150, 128, 84, 244, 30, 117, 183, 150, 70, 30, 234, 2,
233, 161, 120, 96, 185, 155, 34, 75, 173, 200, 78, 183, 66, 8, 144, 72, 20,
92, 246, 229, 255, 55, 148, 160, 153, 9, 150, 16};
const unsigned char kSampleMasterKey[] = {
116, 62, 77, 252, 196, 57, 225, 14, 115, 52, 68, 60, 227, 254, 22, 162, 163,
22, 186, 125, 203, 138, 205, 98, 151, 202, 179, 203, 86, 98, 149, 208};
const unsigned char kSampleAuthDataEncrypted[] = {
37, 239, 160, 111, 19, 123, 167, 118, 161, 223, 61, 242, 63, 146, 22, 223,
100, 79, 178, 52, 206, 121, 155, 88, 23, 68, 144, 66, 167, 187, 83, 13, 101,
221, 218, 185, 99, 23, 149, 3, 239, 142, 78, 62, 239, 155, 114, 83, 106,
108, 168, 225, 241, 58, 49, 59, 235, 234, 51, 92, 241, 75, 120, 26, 8, 36,
238, 241, 33, 192, 170, 136, 138, 57, 87, 210, 181, 143, 111, 181, 251, 30,
50, 64, 48, 96, 195, 223, 172, 221, 19, 127, 253, 182, 102, 219, 36, 245,
246, 106, 157, 177, 230, 129, 130, 253, 51, 91, 214, 35, 221, 43, 174, 7,
185, 169, 92, 126, 52, 160, 212, 233, 158, 142, 120, 255, 212, 32, 10, 176,
112, 73, 71, 51, 72, 143, 218, 157, 186, 106, 146, 71, 24, 94, 216, 98, 114,
127, 56, 47, 38, 35, 63, 141, 193, 82, 107, 240, 39, 154, 28, 134, 32, 96,
16, 32, 54, 233, 74, 242, 136, 178, 236, 0, 243, 5, 78, 98, 219, 0, 104, 70,
235, 248, 169, 38, 88, 129, 219, 84, 197, 53, 232, 186, 157, 6, 24, 161, 86,
118, 85, 227, 72, 215, 30, 64, 236, 224, 234, 168, 16, 118, 4, 154, 170,
157, 85, 80, 158, 87, 14, 17, 76, 15, 11, 151, 157, 15, 42, 92, 34, 255,
244, 162, 195, 158, 162, 207, 167, 119, 9, 218, 218, 148, 33, 54, 131, 66,
125, 12, 141, 245, 162, 229, 134, 227};
const unsigned char kSampleAuthData[] = {
29, 230, 13, 53, 202, 172, 136, 59, 83, 139, 43, 154, 175, 183, 163, 205,
110, 117, 149, 144};
const char kTokenPath[] = ".tpm";
const char kTokenObjectPath[] = "TOK_OBJ";
const char kSampleToken[] = "opencryptoki_sample_token.tgz";
const int kPublicSampleObjects = 3;
const int kPrivateSampleObjects = 2;
string Bytes2String(const unsigned char* bytes, size_t num_bytes) {
return string(reinterpret_cast<const char*>(bytes), num_bytes);
}
void RunCommand(string command) {
int status = system(command.c_str());
ASSERT_EQ(0, WEXITSTATUS(status));
}
// Performs hard-coded transformations as a TPM would do. These match the
// sample token data for this test, they are not useful in general.
bool MockUnbind(int key, const string& input, string* output) {
map<string, string> transforms;
string encrypted = Bytes2String(kSampleMasterKeyEncrypted,
arraysize(kSampleMasterKeyEncrypted));
string decrypted = Bytes2String(kSampleMasterKey,
arraysize(kSampleMasterKey));
transforms[encrypted] = decrypted;
encrypted = Bytes2String(kSampleAuthDataEncrypted,
arraysize(kSampleAuthDataEncrypted));
decrypted = Bytes2String(kSampleAuthData,
arraysize(kSampleAuthData));
transforms[encrypted] = decrypted;
map<string, string>::iterator iter = transforms.find(input);
if (iter != transforms.end()) {
*output = iter->second;
return true;
}
return false;
}
// Creates a very 'nice' object mock.
Object* CreateObjectMock() {
ObjectMock* o = new ObjectMock();
o->SetupFake();
EXPECT_CALL(*o, GetObjectClass()).Times(AnyNumber());
EXPECT_CALL(*o, SetAttributes(_, _)).Times(AnyNumber());
EXPECT_CALL(*o, FinalizeNewObject()).WillRepeatedly(Return(CKR_OK));
EXPECT_CALL(*o, Copy(_)).WillRepeatedly(Return(CKR_OK));
EXPECT_CALL(*o, IsTokenObject()).Times(AnyNumber());
EXPECT_CALL(*o, IsPrivate()).Times(AnyNumber());
EXPECT_CALL(*o, IsAttributePresent(_)).Times(AnyNumber());
EXPECT_CALL(*o, GetAttributeString(_)).Times(AnyNumber());
EXPECT_CALL(*o, GetAttributeInt(_, _)).Times(AnyNumber());
EXPECT_CALL(*o, GetAttributeBool(_, _)).Times(AnyNumber());
EXPECT_CALL(*o, SetAttributeString(_, _)).Times(AnyNumber());
EXPECT_CALL(*o, SetAttributeInt(_, _)).Times(AnyNumber());
EXPECT_CALL(*o, SetAttributeBool(_, _)).Times(AnyNumber());
EXPECT_CALL(*o, GetAttributeMap()).Times(AnyNumber());
EXPECT_CALL(*o, set_handle(_)).Times(AnyNumber());
EXPECT_CALL(*o, set_store_id(_)).Times(AnyNumber());
EXPECT_CALL(*o, handle()).Times(AnyNumber());
EXPECT_CALL(*o, store_id()).Times(AnyNumber());
return o;
}
// A test fixture base class for testing the importer.
class TestImporterBase {
public:
TestImporterBase() {
CHECK(temp_dir_.CreateUniqueTempDir());
importer_.reset(new OpencryptokiImporter(
0, temp_dir_.path().Append(kTokenPath), &tpm_, &factory_));
// Set expectations for the TPM utility mock.
EXPECT_CALL(tpm_, Unbind(_, _, _)).WillRepeatedly(Invoke(MockUnbind));
EXPECT_CALL(tpm_, LoadKey(_, _, _, _))
.WillRepeatedly(DoAll(SetArgumentPointee<3>(1), Return(true)));
EXPECT_CALL(tpm_, LoadKeyWithParent(_, _, _, _, _))
.WillRepeatedly(DoAll(SetArgumentPointee<4>(1), Return(true)));
// Set expectations for the factory mock.
EXPECT_CALL(factory_, CreateObject())
.WillRepeatedly(Invoke(CreateObjectMock));
// Set expectations for the object pool mock.
pool_.SetupFake();
EXPECT_CALL(pool_, Insert(_)).Times(AnyNumber());
EXPECT_CALL(pool_, Import(_)).Times(AnyNumber());
EXPECT_CALL(pool_, Find(_, _)).Times(AnyNumber());
EXPECT_CALL(pool_, SetInternalBlob(3, _)).WillRepeatedly(Return(true));
EXPECT_CALL(pool_, SetInternalBlob(4, _)).WillRepeatedly(Return(true));
}
protected:
void PrepareSampleToken() {
CHECK(temp_dir_.IsValid());
RunCommand(base::StringPrintf("tar -xzf %s -C %s",
kSampleToken,
temp_dir_.path().value().c_str()));
}
ChapsFactoryMock factory_;
ObjectPoolMock pool_;
scoped_ptr<OpencryptokiImporter> importer_;
TPMUtilityMock tpm_;
base::ScopedTempDir temp_dir_;
};
// A function that returns the number of objects expected to be imported.
// Returns -1 if a failure is expected.
struct ModifierResult {
bool import_public_result;
bool import_private_result;
int num_public_objects;
int num_private_objects;
};
typedef ModifierResult (*ModifierCallback)(const char* object_path);
const ModifierResult kModifierSuccess =
{true, true, kPublicSampleObjects, kPrivateSampleObjects};
const ModifierResult kModifierNone = {true, true, 0, 0};
const ModifierResult kModifierPublicOnly =
{true, true, kPublicSampleObjects, 0};
const ModifierResult kModifierOneBadPublic =
{true, true, kPublicSampleObjects - 1, kPrivateSampleObjects};
const ModifierResult kModifierOneBadPrivate =
{true, true, kPublicSampleObjects, kPrivateSampleObjects - 1};
// A parameterized fixture so we can run the same test(s) with multiple modifier
// functions.
class TestImporterWithModifier
: public TestImporterBase,
public testing::TestWithParam<ModifierCallback> {
};
// This test attempts to import a sample token after it has been modified by a
// modifier function.
TEST_P(TestImporterWithModifier, ImportSample) {
PrepareSampleToken();
FilePath object_path =
temp_dir_.path().Append(kTokenPath).Append(kTokenObjectPath);
ModifierCallback modifier = GetParam();
ModifierResult expected_result = modifier(object_path.value().c_str());
EXPECT_EQ(expected_result.import_public_result,
importer_->ImportObjects(&pool_));
vector<const Object*> objects;
pool_.Find(NULL, &objects);
EXPECT_EQ(expected_result.num_public_objects, objects.size());
EXPECT_EQ(expected_result.import_private_result,
importer_->FinishImportAsync(&pool_));
objects.clear();
pool_.Find(NULL, &objects);
int total_objects = expected_result.num_public_objects +
expected_result.num_private_objects;
EXPECT_EQ(total_objects, objects.size());
}
ModifierResult NoModify(const char* object_path) {
// If we don't modify anything, the import should succeed.
return kModifierSuccess;
}
ModifierResult DeleteAll(const char* object_path) {
FilePath token_path = FilePath(object_path).DirName();
RunCommand(base::StringPrintf("rm -rf %s", token_path.value().c_str()));
return kModifierNone;
}
ModifierResult DeleteAllObjectFiles(const char* object_path) {
RunCommand(base::StringPrintf("rm -f %s/*", object_path));
return kModifierNone;
}
ModifierResult DeleteMasterKey(const char* object_path) {
FilePath token_path = FilePath(object_path).DirName();
RunCommand(base::StringPrintf("rm -f %s/MK_PRIVATE",
token_path.value().c_str()));
return kModifierPublicOnly;
}
ModifierResult DeleteObjectIndex(const char* object_path) {
RunCommand(base::StringPrintf("rm -f %s/OBJ.IDX", object_path));
return kModifierNone;
}
ModifierResult DeleteAllButIndex(const char* object_path) {
RunCommand(base::StringPrintf("rm -f %s/*0000", object_path));
return kModifierNone;
}
ModifierResult DeleteHierarchyFile(const char* object_path) {
RunCommand(base::StringPrintf("rm -f %s/50000000", object_path));
return kModifierPublicOnly;
}
ModifierResult TruncateFile0(const char* object_path) {
RunCommand(base::StringPrintf(":> %s/B0000000", object_path));
return kModifierOneBadPublic;
}
ModifierResult TruncateFile5(const char* object_path) {
RunCommand(base::StringPrintf("truncate -s 5 %s/B0000000", object_path));
return kModifierOneBadPublic;
}
ModifierResult TruncateFile21(const char* object_path) {
RunCommand(base::StringPrintf("truncate -s 21 %s/B0000000", object_path));
return kModifierOneBadPublic;
}
ModifierResult TruncateFile80(const char* object_path) {
RunCommand(base::StringPrintf("truncate -s 80 %s/B0000000", object_path));
return kModifierOneBadPublic;
}
ModifierResult TruncateEncrypted(const char* object_path) {
RunCommand(base::StringPrintf("truncate -s 80 %s/C0000000", object_path));
return kModifierOneBadPrivate;
}
ModifierResult AddNotIndexed(const char* object_path) {
RunCommand(base::StringPrintf(":> %s/D0000000", object_path));
return kModifierSuccess;
}
ModifierResult AppendJunk(const char* object_path) {
RunCommand(base::StringPrintf("head -c 100 < /dev/urandom >> %s/B0000000",
object_path));
return kModifierOneBadPublic;
}
ModifierResult AppendJunkEncrypted(const char* object_path) {
RunCommand(base::StringPrintf("head -c 100 < /dev/urandom >> %s/C0000000",
object_path));
return kModifierOneBadPrivate;
}
// List of parameterized test cases.
INSTANTIATE_TEST_CASE_P(ModifierTests,
TestImporterWithModifier,
Values(NoModify,
DeleteAll,
DeleteAllObjectFiles,
DeleteMasterKey,
DeleteObjectIndex,
DeleteAllButIndex,
DeleteHierarchyFile,
TruncateFile0,
TruncateFile5,
TruncateFile21,
TruncateFile80,
TruncateEncrypted,
AddNotIndexed,
AppendJunk,
AppendJunkEncrypted));
ModifierResult RandomizeFile(const char* object_path) {
RunCommand(base::StringPrintf("head -c 1000 < /dev/urandom > %s/C0000000",
object_path));
return kModifierOneBadPrivate;
}
ModifierResult RandomizeObjectAttributes(const char* object_path) {
RunCommand(base::StringPrintf("truncate -s 21 %s/B0000000", object_path));
RunCommand(base::StringPrintf("head -c 1000 < /dev/urandom >> %s/B0000000",
object_path));
return kModifierOneBadPublic;
}
// List of test cases that involve randomization; these are listed separately
// for easy filtering.
INSTANTIATE_TEST_CASE_P(RandomizedTests,
TestImporterWithModifier,
Values(RandomizeFile, RandomizeObjectAttributes));
} // namespace chaps
int main(int argc, char** argv) {
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}