blob: b294995acb108f728b54af070f0ec92e82d7d2d9 [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.
//
// Unit tests for InstallAttributes.
#include "cryptohome/install_attributes.h"
#include <string>
#include <vector>
#include <algorithm>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <brillo/secure_blob.h>
#include <gtest/gtest.h>
#include "cryptohome/lockbox.h"
#include "cryptohome/mock_lockbox.h"
#include "cryptohome/mock_platform.h"
#include "cryptohome/mock_tpm.h"
#include "cryptohome/mock_tpm_init.h"
namespace cryptohome {
using base::FilePath;
using ::testing::_;
using ::testing::DoAll;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
// Provides a test fixture for ensuring Lockbox-flows work as expected.
//
// Multiple helpers are included to ensure tests are starting from the same
// baseline for difference scenarios, such as first boot or all-other-normal
// boots.
class InstallAttributesTest : public ::testing::Test {
public:
InstallAttributesTest() : install_attrs_(NULL) { }
virtual ~InstallAttributesTest() { }
virtual void SetUp() {
install_attrs_.set_lockbox(&lockbox_);
install_attrs_.set_platform(&platform_);
// No pre-existing data and no TPM auth.
EXPECT_CALL(lockbox_, set_tpm(&tpm_))
.Times(1);
EXPECT_CALL(tpm_, IsEnabled())
.WillOnce(Return(true));
install_attrs_.SetTpm(&tpm_);
Mock::VerifyAndClearExpectations(&lockbox_);
Mock::VerifyAndClearExpectations(&tpm_);
}
virtual void TearDown() { }
void GetAndCheck(InstallAttributes *install_attrs) {
if (!install_attrs)
install_attrs = &install_attrs_;
brillo::Blob data;
EXPECT_TRUE(install_attrs->Get(kTestName, &data));
std::string data_str(reinterpret_cast<const char*>(data.data()),
data.size());
EXPECT_STREQ(data_str.c_str(), kTestData);
}
// Tests a normal OOBE and stashes the data in the ptr.
void DoOobe(InstallAttributes *install_attrs,
brillo::Blob *serialized_data) {
if (install_attrs->is_secure()) {
EXPECT_CALL(lockbox_, Destroy(_))
.WillOnce(Return(true));
}
EXPECT_TRUE(install_attrs->PrepareSystem());
// Assume authorization and working tpm.
EXPECT_CALL(lockbox_, tpm())
.WillRepeatedly(Return(&tpm_));
if (install_attrs->is_secure()) {
EXPECT_CALL(lockbox_, Create(_))
.WillOnce(Return(true));
ExpectRemovingOwnerDependency();
}
EXPECT_TRUE(install_attrs->Init(&tpm_init_));
brillo::Blob data;
data.assign(kTestData, kTestData + strlen(kTestData));
EXPECT_TRUE(install_attrs->Set(kTestName, data));
if (install_attrs->is_secure()) {
EXPECT_CALL(lockbox_, Store(_, _))
.Times(1)
.WillOnce(Return(true));
}
EXPECT_CALL(platform_,
WriteFileAtomicDurable(
FilePath(InstallAttributes::kDefaultDataFile),
_, _))
.Times(1)
.WillOnce(DoAll(SaveArg<1>(serialized_data), Return(true)));
brillo::Blob cached_data;
EXPECT_CALL(platform_,
WriteFileAtomic(
FilePath(InstallAttributes::kDefaultCacheFile), _, _))
.Times(1)
.WillOnce(DoAll(SaveArg<1>(&cached_data), Return(true)));
EXPECT_TRUE(install_attrs->Finalize());
EXPECT_NE(0, serialized_data->size());
ASSERT_EQ(serialized_data->size(), cached_data.size());
EXPECT_TRUE(std::equal(cached_data.begin(), cached_data.end(),
serialized_data->begin()));
Mock::VerifyAndClearExpectations(&lockbox_);
Mock::VerifyAndClearExpectations(&platform_);
}
// Generate the data we'll need to load from.
void PopulateOobeData(brillo::Blob *data) {
InstallAttributes some_attrs(NULL);
some_attrs.set_lockbox(&lockbox_);
some_attrs.set_platform(&platform_);
DoOobe(&some_attrs, data);
Mock::VerifyAndClearExpectations(&lockbox_);
Mock::VerifyAndClearExpectations(&platform_);
}
void ExpectRemovingOwnerDependency() {
EXPECT_CALL(tpm_init_, RemoveTpmOwnerDependency(
TpmPersistentState::TpmOwnerDependency::kInstallAttributes))
.Times(1);
}
void ExpectNotRemovingOwnerDependency() {
EXPECT_CALL(tpm_init_, RemoveTpmOwnerDependency(_))
.Times(0);
}
static const char* kTestName;
static const char* kTestData;
NiceMock<MockLockbox> lockbox_;
brillo::Blob lockbox_data_;
InstallAttributes install_attrs_;
NiceMock<MockPlatform> platform_;
NiceMock<MockTpm> tpm_;
NiceMock<MockTpmInit> tpm_init_;
private:
DISALLOW_COPY_AND_ASSIGN(InstallAttributesTest);
};
const char* InstallAttributesTest::kTestName = "Shuffle";
const char* InstallAttributesTest::kTestData = "Duffle";
//
// The actual tests!
//
// Normal bootup
TEST_F(InstallAttributesTest, OobeWithTpm) {
brillo::Blob serialized_data;
DoOobe(&install_attrs_, &serialized_data);
}
TEST_F(InstallAttributesTest, OobeWithoutTpm) {
EXPECT_CALL(lockbox_, set_tpm(NULL))
.Times(1);
install_attrs_.SetTpm(NULL);
EXPECT_CALL(platform_, ReadFile(_, _))
.Times(1)
.WillOnce(Return(false));
ExpectNotRemovingOwnerDependency();
EXPECT_TRUE(install_attrs_.Init(&tpm_init_));
EXPECT_TRUE(install_attrs_.is_first_install());
}
TEST_F(InstallAttributesTest, OobeWithTpmBadWrite) {
EXPECT_CALL(lockbox_, Destroy(_))
.WillOnce(Return(true));
EXPECT_TRUE(install_attrs_.PrepareSystem());
// Assume authorization and working tpm.
EXPECT_CALL(lockbox_, tpm())
.WillRepeatedly(Return(&tpm_));
EXPECT_CALL(lockbox_, Create(_))
.WillOnce(Return(true));
ExpectRemovingOwnerDependency();
EXPECT_TRUE(install_attrs_.Init(&tpm_init_));
brillo::Blob data;
data.assign(kTestData, kTestData + strlen(kTestData));
EXPECT_TRUE(install_attrs_.Set(kTestName, data));
brillo::Blob serialized_data;
EXPECT_CALL(lockbox_, Store(_, _))
.Times(1)
.WillOnce(DoAll(SaveArg<0>(&serialized_data), Return(true)));
EXPECT_CALL(platform_, WriteFileAtomicDurable(_, _, _))
.Times(1)
.WillOnce(Return(false));
EXPECT_FALSE(install_attrs_.Finalize());
EXPECT_TRUE(install_attrs_.IsReady());
EXPECT_TRUE(install_attrs_.is_invalid());
EXPECT_TRUE(install_attrs_.is_initialized());
}
TEST_F(InstallAttributesTest, NormalBootWithTpm) {
brillo::Blob serialized_data;
PopulateOobeData(&serialized_data);
// Check the baseline.
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_initialized());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_CALL(platform_, ReadFile(_, _))
.Times(1)
.WillOnce(DoAll(SetArgPointee<1>(serialized_data), Return(true)));
EXPECT_CALL(lockbox_, Load(_))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(lockbox_, Verify(_, _))
.Times(1)
.WillOnce(Return(true));
ExpectRemovingOwnerDependency();
EXPECT_TRUE(install_attrs_.Init(&tpm_init_));
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_TRUE(install_attrs_.is_initialized());
// Make sure the data was parsed correctly.
EXPECT_EQ(1, install_attrs_.Count());
GetAndCheck(NULL);
}
TEST_F(InstallAttributesTest, NormalBootWithoutTpm) {
brillo::Blob serialized_data;
PopulateOobeData(&serialized_data);
EXPECT_CALL(lockbox_, set_tpm(NULL))
.Times(1);
install_attrs_.SetTpm(NULL);
// Check the baseline.
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_initialized());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_CALL(platform_, ReadFile(_, _))
.Times(1)
.WillOnce(DoAll(SetArgPointee<1>(serialized_data), Return(true)));
ExpectRemovingOwnerDependency();
EXPECT_TRUE(install_attrs_.Init(&tpm_init_));
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_TRUE(install_attrs_.is_initialized());
// Make sure the data was parsed correctly.
EXPECT_EQ(1, install_attrs_.Count());
GetAndCheck(NULL);
}
// Represents that the OOBE process was interrupted by
// a reboot or crash prior to Finalize() being called, but
// after the Lockbox was Created.
// Since InstallAttributes Set/Finalize is not atomic, there
// is always the risk of data loss due to failure of the device.
// It will fail-safe however (by failing empty).
TEST_F(InstallAttributesTest, NormalBootUnlocked) {
// Normally, it should be impossible to populate the filesystem
// with any data. We put this here to show anything that may be
// read in is ignored.
brillo::Blob serialized_data;
PopulateOobeData(&serialized_data);
// Check the baseline.
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_initialized());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_TRUE(install_attrs_.is_secure());
Lockbox::ErrorId error_id = Lockbox::kErrorIdNoNvramData;
EXPECT_CALL(lockbox_, Load(_))
.Times(1)
.WillOnce(DoAll(SetArgPointee<0>(error_id), Return(false)));
ExpectRemovingOwnerDependency();
EXPECT_TRUE(install_attrs_.Init(&tpm_init_));
EXPECT_TRUE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_TRUE(install_attrs_.is_initialized());
// Should be empty.
EXPECT_EQ(0, install_attrs_.Count());
}
// Represents that the OOBE process was interrupted by
// a reboot or crash prior to Finalize() being called, and
// before the Lockbox was Created.
TEST_F(InstallAttributesTest, NormalBootNoSpace) {
// Normally, it should be impossible to populate the filesystem
// with any data. We put this here to show anything that may be
// read in is ignored.
brillo::Blob serialized_data;
PopulateOobeData(&serialized_data);
// Check the baseline.
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_initialized());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_TRUE(install_attrs_.is_secure());
Lockbox::ErrorId error_id = Lockbox::kErrorIdNoNvramSpace;
EXPECT_CALL(lockbox_, Load(_))
.Times(1)
.WillOnce(DoAll(SetArgPointee<0>(error_id), Return(false)));
EXPECT_CALL(lockbox_, Create(_))
.Times(1)
.WillOnce(Return(true));
ExpectRemovingOwnerDependency();
EXPECT_TRUE(install_attrs_.Init(&tpm_init_));
EXPECT_TRUE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_TRUE(install_attrs_.is_initialized());
// Should be empty.
EXPECT_EQ(0, install_attrs_.Count());
}
TEST_F(InstallAttributesTest, NormalBootLoadError) {
// Check the baseline.
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_initialized());
EXPECT_FALSE(install_attrs_.is_invalid());
Lockbox::ErrorId error_id = Lockbox::kErrorIdTpmError;
EXPECT_CALL(lockbox_, Load(_))
.Times(1)
.WillOnce(DoAll(SetArgPointee<0>(error_id), Return(false)));
ExpectNotRemovingOwnerDependency();
EXPECT_FALSE(install_attrs_.Init(&tpm_init_));
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_TRUE(install_attrs_.is_invalid());
EXPECT_FALSE(install_attrs_.is_initialized());
// Should be empty.
EXPECT_EQ(0, install_attrs_.Count());
}
TEST_F(InstallAttributesTest, NormalBootReadFileError) {
// Check the baseline.
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_initialized());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_CALL(lockbox_, Load(_))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(platform_, ReadFile(_, _))
.Times(1)
.WillOnce(Return(false));
ExpectNotRemovingOwnerDependency();
EXPECT_FALSE(install_attrs_.Init(&tpm_init_));
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_TRUE(install_attrs_.is_invalid());
EXPECT_FALSE(install_attrs_.is_initialized());
// Should be empty.
EXPECT_EQ(0, install_attrs_.Count());
}
TEST_F(InstallAttributesTest, NormalBootVerifyError) {
// Check the baseline.
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_initialized());
EXPECT_FALSE(install_attrs_.is_invalid());
Lockbox::ErrorId error_id = Lockbox::kErrorIdHashMismatch;
EXPECT_CALL(lockbox_, Load(_))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(platform_, ReadFile(_, _))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(lockbox_, Verify(_, _))
.Times(1)
.WillOnce(DoAll(SetArgPointee<1>(error_id), Return(false)));
ExpectNotRemovingOwnerDependency();
EXPECT_FALSE(install_attrs_.Init(&tpm_init_));
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_TRUE(install_attrs_.is_invalid());
EXPECT_FALSE(install_attrs_.is_initialized());
// Should be empty.
EXPECT_EQ(0, install_attrs_.Count());
}
TEST_F(InstallAttributesTest, LegacyBoot) {
// Check the baseline.
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_initialized());
EXPECT_FALSE(install_attrs_.is_invalid());
Lockbox::ErrorId error_id = Lockbox::kErrorIdNoNvramSpace;
EXPECT_CALL(lockbox_, Load(_))
.Times(1)
.WillOnce(DoAll(SetArgPointee<0>(error_id), Return(false)));
Lockbox::ErrorId create_error_id = Lockbox::kErrorIdInsufficientAuthorization;
EXPECT_CALL(lockbox_, Create(_))
.Times(1)
.WillOnce(DoAll(SetArgPointee<0>(create_error_id), Return(false)));
ExpectRemovingOwnerDependency();
EXPECT_TRUE(install_attrs_.Init(&tpm_init_));
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_TRUE(install_attrs_.is_initialized());
// Should be empty.
EXPECT_EQ(0, install_attrs_.Count());
}
// If the Lockbox Create fails for reasons other than bad password, it
// should still be treated as a LegacyBoot.
TEST_F(InstallAttributesTest, LegacyBootUnexpected) {
// Check the baseline.
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_initialized());
EXPECT_FALSE(install_attrs_.is_invalid());
Lockbox::ErrorId error_id = Lockbox::kErrorIdNoNvramSpace;
EXPECT_CALL(lockbox_, Load(_))
.Times(1)
.WillOnce(DoAll(SetArgPointee<0>(error_id), Return(false)));
Lockbox::ErrorId create_error_id = Lockbox::kErrorIdTpmError;
EXPECT_CALL(lockbox_, Create(_))
.Times(1)
.WillOnce(DoAll(SetArgPointee<0>(create_error_id), Return(false)));
ExpectRemovingOwnerDependency();
EXPECT_TRUE(install_attrs_.Init(&tpm_init_));
EXPECT_FALSE(install_attrs_.is_first_install());
EXPECT_FALSE(install_attrs_.is_invalid());
EXPECT_TRUE(install_attrs_.is_initialized());
// Should be empty.
EXPECT_EQ(0, install_attrs_.Count());
}
} // namespace cryptohome