| // 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_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 std::string; |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::Mock; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| using ::testing::SetArgumentPointee; |
| |
| // 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)); |
| EXPECT_CALL(tpm_init_, |
| RemoveTpmOwnerDependency( |
| TpmInit::kInstallAttributes)) |
| .Times(1); |
| } |
| 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(InstallAttributes::kDefaultDataFile, |
| _, _)) |
| .Times(1) |
| .WillOnce(DoAll(SaveArg<1>(serialized_data), Return(true))); |
| brillo::Blob cached_data; |
| EXPECT_CALL(platform_, WriteFile(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_); |
| } |
| |
| 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)); |
| 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)); |
| 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(SetArgumentPointee<1>(serialized_data), Return(true))); |
| EXPECT_CALL(lockbox_, Load(_)) |
| .Times(1) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(lockbox_, Verify(_, _)) |
| .Times(1) |
| .WillOnce(Return(true)); |
| 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(SetArgumentPointee<1>(serialized_data), Return(true))); |
| |
| 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(SetArgumentPointee<0>(error_id), Return(false))); |
| 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(SetArgumentPointee<0>(error_id), Return(false))); |
| EXPECT_CALL(lockbox_, Create(_)) |
| .Times(1) |
| .WillOnce(Return(true)); |
| 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(SetArgumentPointee<0>(error_id), Return(false))); |
| 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)); |
| 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(SetArgumentPointee<1>(error_id), Return(false))); |
| |
| 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(SetArgumentPointee<0>(error_id), Return(false))); |
| Lockbox::ErrorId create_error_id = Lockbox::kErrorIdInsufficientAuthorization; |
| EXPECT_CALL(lockbox_, Create(_)) |
| .Times(1) |
| .WillOnce(DoAll(SetArgumentPointee<0>(create_error_id), Return(false))); |
| 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(SetArgumentPointee<0>(error_id), Return(false))); |
| Lockbox::ErrorId create_error_id = Lockbox::kErrorIdTpmError; |
| EXPECT_CALL(lockbox_, Create(_)) |
| .Times(1) |
| .WillOnce(DoAll(SetArgumentPointee<0>(create_error_id), Return(false))); |
| 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 |