| // 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/object_pool_impl.h" |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "chaps/chaps_factory_mock.h" |
| #include "chaps/handle_generator_mock.h" |
| #include "chaps/object_importer_mock.h" |
| #include "chaps/object_mock.h" |
| #include "chaps/object_store_mock.h" |
| #include "chaps/proto_bindings/attributes.pb.h" |
| |
| using brillo::SecureBlob; |
| 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::SetArgPointee; |
| using Result = chaps::ObjectPool::Result; |
| |
| namespace chaps { |
| |
| namespace { |
| |
| 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; |
| } |
| |
| int CreateHandle() { |
| static int last_handle = 0; |
| return ++last_handle; |
| } |
| |
| } // namespace |
| |
| // A test fixture for object pools. |
| class TestObjectPool : public ::testing::Test { |
| public: |
| TestObjectPool() { |
| // Setup the factory to return functional fake objects. |
| EXPECT_CALL(factory_, CreateObject()) |
| .WillRepeatedly(Invoke(CreateObjectMock)); |
| EXPECT_CALL(handle_generator_, CreateHandle()) |
| .WillRepeatedly(Invoke(CreateHandle)); |
| // Create object pools to test with. |
| store_ = new ObjectStoreMock(); |
| importer_ = new ObjectImporterMock(); |
| pool_.reset( |
| new ObjectPoolImpl(&factory_, &handle_generator_, store_, importer_)); |
| pool2_.reset(new ObjectPoolImpl(&factory_, &handle_generator_, NULL, NULL)); |
| } |
| |
| // Initialize and load private objects. |
| void PreparePools() { |
| EXPECT_CALL(*store_, SetEncryptionKey(_)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*store_, LoadPublicObjectBlobs(_)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*store_, LoadPrivateObjectBlobs(_)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(*store_, GetInternalBlob(_, _)).WillRepeatedly(Return(false)); |
| EXPECT_CALL(*store_, SetInternalBlob(_, _)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*importer_, ImportObjects(pool_.get())) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(*importer_, FinishImportAsync(pool_.get())) |
| .WillRepeatedly(Return(true)); |
| |
| EXPECT_TRUE(pool_->Init()); |
| EXPECT_TRUE(pool_->SetEncryptionKey(SecureBlob())); |
| EXPECT_TRUE(pool_->IsPrivateLoaded()); |
| |
| EXPECT_TRUE(pool2_->Init()); |
| EXPECT_TRUE(pool2_->IsPrivateLoaded()); |
| |
| testing::Mock::VerifyAndClearExpectations(store_); |
| testing::Mock::VerifyAndClearExpectations(importer_); |
| } |
| |
| ChapsFactoryMock factory_; |
| ObjectStoreMock* store_; |
| ObjectImporterMock* importer_; |
| HandleGeneratorMock handle_generator_; |
| std::unique_ptr<ObjectPoolImpl> pool_; |
| std::unique_ptr<ObjectPoolImpl> pool2_; |
| }; |
| |
| // Test object pool initialization when using an object store. |
| TEST_F(TestObjectPool, Init) { |
| // Create some fake persistent objects for the mock store to return. |
| map<int, ObjectBlob> persistent_objects; |
| AttributeList l; |
| Attribute* a = l.add_attribute(); |
| a->set_type(CKA_ID); |
| a->set_value("value"); |
| string s; |
| l.SerializeToString(&s); |
| persistent_objects[1].blob = s; |
| persistent_objects[1].is_private = true; |
| persistent_objects[2].blob = "not_valid_protobuf"; |
| persistent_objects[2].is_private = false; |
| string tmp(32, 'A'); |
| SecureBlob key(tmp.begin(), tmp.end()); |
| EXPECT_CALL(*store_, GetInternalBlob(_, _)).WillRepeatedly(Return(false)); |
| EXPECT_CALL(*store_, SetInternalBlob(_, _)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*store_, SetEncryptionKey(key)) |
| .WillOnce(Return(false)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(*store_, LoadPublicObjectBlobs(_)) |
| .WillOnce(Return(false)) |
| .WillRepeatedly( |
| DoAll(SetArgPointee<0>(persistent_objects), Return(true))); |
| EXPECT_CALL(*store_, LoadPrivateObjectBlobs(_)) |
| .WillOnce(Return(false)) |
| .WillRepeatedly( |
| DoAll(SetArgPointee<0>(persistent_objects), Return(true))); |
| EXPECT_CALL(*importer_, ImportObjects(pool_.get())) |
| .WillOnce(Return(false)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(*importer_, FinishImportAsync(pool_.get())) |
| .WillOnce(Return(false)) |
| .WillRepeatedly(Return(true)); |
| // Loading of public objects happens when the pool is initialized. |
| EXPECT_TRUE(pool2_->Init()); |
| EXPECT_FALSE(pool_->Init()); |
| EXPECT_TRUE(pool_->Init()); |
| EXPECT_TRUE(pool_->Init()); |
| // Loading of private objects happens when the encryption key is set. |
| EXPECT_FALSE(pool_->IsPrivateLoaded()); |
| EXPECT_TRUE(pool2_->SetEncryptionKey(key)); |
| EXPECT_FALSE(pool_->SetEncryptionKey(key)); |
| EXPECT_TRUE(pool_->SetEncryptionKey(key)); |
| EXPECT_TRUE(pool_->SetEncryptionKey(key)); |
| EXPECT_TRUE(pool_->IsPrivateLoaded()); |
| vector<const Object*> v; |
| std::unique_ptr<Object> find_all(CreateObjectMock()); |
| EXPECT_EQ(Result::Success, pool_->Find(find_all.get(), &v)); |
| ASSERT_EQ(3, v.size()); |
| EXPECT_TRUE(v[0]->GetAttributeString(CKA_ID) == string("value")); |
| EXPECT_TRUE(v[1]->GetAttributeString(CKA_ID) == string("value")); |
| EXPECT_TRUE(v[2]->GetAttributeString(CKA_ID) == string("value")); |
| } |
| |
| // Test the methods that should just pass through to the object store. |
| TEST_F(TestObjectPool, StorePassThrough) { |
| string s("test"); |
| SecureBlob blob("test"); |
| EXPECT_CALL(*store_, GetInternalBlob(1, _)) |
| .WillOnce(Return(false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*store_, SetInternalBlob(1, s)) |
| .WillOnce(Return(false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*store_, LoadPublicObjectBlobs(_)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*store_, LoadPrivateObjectBlobs(_)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*store_, SetEncryptionKey(blob)) |
| .WillOnce(Return(false)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_FALSE(pool2_->GetInternalBlob(1, &s)); |
| EXPECT_FALSE(pool2_->SetInternalBlob(1, s)); |
| EXPECT_TRUE(pool2_->SetEncryptionKey(blob)); |
| EXPECT_FALSE(pool_->GetInternalBlob(1, &s)); |
| EXPECT_TRUE(pool_->GetInternalBlob(1, &s)); |
| EXPECT_FALSE(pool_->SetInternalBlob(1, s)); |
| EXPECT_TRUE(pool_->SetInternalBlob(1, s)); |
| EXPECT_FALSE(pool_->SetEncryptionKey(blob)); |
| EXPECT_TRUE(pool_->SetEncryptionKey(blob)); |
| } |
| |
| // Test basic object management operations. |
| TEST_F(TestObjectPool, InsertFindUpdateDelete) { |
| PreparePools(); |
| EXPECT_CALL(*store_, InsertObjectBlob(_, _)) |
| .WillOnce(Return(false)) |
| .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(true))); |
| EXPECT_CALL(*store_, UpdateObjectBlob(3, _)) |
| .WillOnce(Return(false)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(*store_, DeleteObjectBlob(3)) |
| .WillOnce(Return(false)) |
| .WillRepeatedly(Return(true)); |
| vector<const Object*> v; |
| std::unique_ptr<Object> find_all(CreateObjectMock()); |
| EXPECT_EQ(Result::Success, pool2_->Find(find_all.get(), &v)); |
| EXPECT_EQ(0, v.size()); |
| EXPECT_EQ(Result::Success, pool2_->Insert(CreateObjectMock())); |
| EXPECT_EQ(Result::Success, pool2_->Insert(CreateObjectMock())); |
| EXPECT_EQ(Result::Success, pool2_->Find(find_all.get(), &v)); |
| ASSERT_EQ(2, v.size()); |
| Object* o = pool2_->GetModifiableObject(v[0]); |
| EXPECT_EQ(Result::Success, pool2_->Flush(o)); |
| EXPECT_EQ(Result::Success, pool2_->Delete(v[0])); |
| EXPECT_EQ(Result::Success, pool2_->Delete(v[1])); |
| v.clear(); |
| EXPECT_EQ(Result::Success, pool2_->Find(find_all.get(), &v)); |
| EXPECT_EQ(0, v.size()); |
| // Now with the persistent pool. |
| EXPECT_EQ(Result::Success, pool_->Find(find_all.get(), &v)); |
| EXPECT_EQ(0, v.size()); |
| Object* tmp = CreateObjectMock(); |
| EXPECT_NE(Result::Success, pool_->Insert(tmp)); |
| EXPECT_EQ(Result::Success, pool_->Insert(tmp)); |
| EXPECT_EQ(Result::Success, pool_->Find(find_all.get(), &v)); |
| ASSERT_EQ(1, v.size()); |
| o = pool_->GetModifiableObject(v[0]); |
| EXPECT_NE(Result::Success, pool_->Flush(o)); |
| EXPECT_EQ(Result::Success, pool_->Flush(o)); |
| EXPECT_NE(Result::Success, pool_->Delete(v[0])); |
| EXPECT_EQ(Result::Success, pool_->Delete(v[0])); |
| v.clear(); |
| EXPECT_EQ(Result::Success, pool_->Find(find_all.get(), &v)); |
| EXPECT_EQ(0, v.size()); |
| } |
| |
| // Test handling of an invalid object pointer. |
| TEST_F(TestObjectPool, UnknownObject) { |
| PreparePools(); |
| std::unique_ptr<Object> o(CreateObjectMock()); |
| EXPECT_NE(Result::Success, pool_->Flush(o.get())); |
| EXPECT_NE(Result::Success, pool_->Delete(o.get())); |
| EXPECT_NE(Result::Success, pool2_->Flush(o.get())); |
| EXPECT_NE(Result::Success, pool2_->Delete(o.get())); |
| } |
| |
| // Test multiple insertion of the same object pointer. |
| TEST_F(TestObjectPool, DuplicateObject) { |
| PreparePools(); |
| Object* o = CreateObjectMock(); |
| EXPECT_CALL(*store_, InsertObjectBlob(_, _)) |
| .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(true))); |
| EXPECT_EQ(Result::Success, pool_->Insert(o)); |
| EXPECT_NE(Result::Success, pool_->Insert(o)); |
| Object* o2 = CreateObjectMock(); |
| EXPECT_EQ(Result::Success, pool2_->Insert(o2)); |
| EXPECT_NE(Result::Success, pool2_->Insert(o2)); |
| } |
| |
| TEST_F(TestObjectPool, DeleteAll) { |
| PreparePools(); |
| EXPECT_CALL(*store_, InsertObjectBlob(_, _)) |
| .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(true))); |
| EXPECT_CALL(*store_, DeleteAllObjectBlobs()) |
| .WillOnce(Return(false)) |
| .WillRepeatedly(Return(true)); |
| EXPECT_EQ(Result::Success, pool_->Insert(CreateObjectMock())); |
| EXPECT_EQ(Result::Success, pool_->Insert(CreateObjectMock())); |
| EXPECT_EQ(Result::Success, pool_->Insert(CreateObjectMock())); |
| vector<const Object*> v; |
| std::unique_ptr<Object> find_all(CreateObjectMock()); |
| EXPECT_EQ(Result::Success, pool_->Find(find_all.get(), &v)); |
| EXPECT_EQ(3, v.size()); |
| // Test the store failure is passed back but cached objects are still deleted. |
| EXPECT_NE(Result::Success, pool_->DeleteAll()); |
| v.clear(); |
| EXPECT_EQ(Result::Success, pool_->Find(find_all.get(), &v)); |
| EXPECT_EQ(0, v.size()); |
| EXPECT_EQ(Result::Success, pool_->Insert(CreateObjectMock())); |
| EXPECT_EQ(Result::Success, pool_->Insert(CreateObjectMock())); |
| EXPECT_EQ(Result::Success, pool_->Insert(CreateObjectMock())); |
| // Test with store success. |
| EXPECT_EQ(Result::Success, pool_->DeleteAll()); |
| v.clear(); |
| EXPECT_EQ(Result::Success, pool_->Find(find_all.get(), &v)); |
| EXPECT_EQ(0, v.size()); |
| // Test with session pool. |
| EXPECT_CALL(*store_, InsertObjectBlob(_, _)).Times(0); |
| EXPECT_CALL(*store_, DeleteAllObjectBlobs()).Times(0); |
| EXPECT_EQ(Result::Success, pool2_->Insert(CreateObjectMock())); |
| EXPECT_EQ(Result::Success, pool2_->Insert(CreateObjectMock())); |
| EXPECT_EQ(Result::Success, pool2_->Insert(CreateObjectMock())); |
| v.clear(); |
| EXPECT_EQ(Result::Success, pool2_->Find(find_all.get(), &v)); |
| EXPECT_EQ(3, v.size()); |
| EXPECT_EQ(Result::Success, pool2_->DeleteAll()); |
| v.clear(); |
| EXPECT_EQ(Result::Success, pool2_->Find(find_all.get(), &v)); |
| EXPECT_EQ(0, v.size()); |
| } |
| |
| // Test pool with unloaded private objects |
| TEST_F(TestObjectPool, UnloadedPrivateObjects) { |
| EXPECT_FALSE(pool_->IsPrivateLoaded()); |
| |
| // ObjectPool::Find behavior. |
| std::unique_ptr<Object> public_template(CreateObjectMock()); |
| public_template->SetAttributeBool(CKA_PRIVATE, false); |
| std::unique_ptr<Object> private_template(CreateObjectMock()); |
| private_template->SetAttributeBool(CKA_PRIVATE, true); |
| std::unique_ptr<Object> private_keys_template(CreateObjectMock()); |
| private_keys_template->SetAttributeInt(CKA_CLASS, CKO_PRIVATE_KEY); |
| |
| vector<const Object*> v; |
| EXPECT_EQ(Result::WaitForPrivateObjects, |
| pool_->Find(private_template.get(), &v)); |
| EXPECT_EQ(Result::WaitForPrivateObjects, |
| pool_->Find(private_keys_template.get(), &v)); |
| EXPECT_EQ(Result::Success, pool_->Find(public_template.get(), &v)); |
| |
| // ObjectPool::Insert behavior. |
| std::unique_ptr<Object> default_obj(CreateObjectMock()); |
| std::unique_ptr<Object> private_obj(CreateObjectMock()); |
| private_obj->SetAttributeBool(CKA_PRIVATE, true); |
| EXPECT_EQ(Result::WaitForPrivateObjects, pool_->Insert(private_obj.get())); |
| EXPECT_EQ(Result::WaitForPrivateObjects, pool_->Insert(default_obj.get())); |
| |
| Object* public_obj = CreateObjectMock(); |
| public_obj->SetAttributeBool(CKA_PRIVATE, false); |
| EXPECT_CALL(*store_, InsertObjectBlob(_, _)) |
| .WillRepeatedly(DoAll(SetArgPointee<1>(117), Return(true))); |
| EXPECT_EQ(Result::Success, pool_->Insert(public_obj)); |
| } |
| |
| } // namespace chaps |