blob: 9538c0cce621c61ad531ac51aa4c4ef9bdcdafee [file] [log] [blame]
// Copyright 2021 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 "cryptohome/cryptohome_ecc_key_loader.h"
#include <map>
#include <string>
#include <base/files/file_path.h>
#include <brillo/secure_blob.h>
#include <libhwsec-foundation/error/testing_helper.h>
#include "cryptohome/mock_platform.h"
#include "cryptohome/mock_tpm.h"
using ::hwsec::TPMError;
using ::hwsec::TPMRetryAction;
using ::hwsec_foundation::error::testing::ReturnError;
using ::testing::_;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::Return;
namespace cryptohome {
const base::FilePath kDefaultCryptohomeKeyFile(
"/home/.shadow/cryptohome.ecc.key");
const TpmKeyHandle kTestKeyHandle = 17; // any non-zero value
// Tests that need to do more setup work before calling Service::Initialize can
// use this instead of ServiceTest.
class CryptohomeEccKeyLoaderTest : public ::testing::Test {
public:
CryptohomeEccKeyLoaderTest() : cryptohome_key_loader_(&tpm_, &platform_) {}
CryptohomeEccKeyLoaderTest(const CryptohomeEccKeyLoaderTest&) = delete;
CryptohomeEccKeyLoaderTest& operator=(const CryptohomeEccKeyLoaderTest&) =
delete;
virtual ~CryptohomeEccKeyLoaderTest() = default;
// Default mock implementations for |tpm_| methods.
// For TPM-related flags: enabled is always true, other flags are settable.
bool IsTpmOwned() const { return is_tpm_owned_; }
void SetIsTpmOwned(bool is_tpm_owned) { is_tpm_owned_ = is_tpm_owned; }
void SetUp() override {
ON_CALL(tpm_, IsEnabled()).WillByDefault(Return(true));
ON_CALL(tpm_, IsOwned())
.WillByDefault(Invoke(this, &CryptohomeEccKeyLoaderTest::IsTpmOwned));
}
void TearDown() override {}
Platform* GetPlatform() { return &platform_; }
protected:
bool HasStoredCryptohomeKey(std::string str) {
std::string stored_key;
if (!platform_.ReadFileToString(kDefaultCryptohomeKeyFile, &stored_key)) {
return false;
}
if (stored_key != str) {
return false;
}
return true;
}
bool HasLoadedCryptohomeKey(TpmKeyHandle handle) {
if (!cryptohome_key_loader_.HasCryptohomeKey()) {
return false;
}
TpmKeyHandle loaded_handle = cryptohome_key_loader_.GetCryptohomeKey();
if (loaded_handle != handle) {
return false;
}
return true;
}
bool is_tpm_owned_ = false;
std::map<base::FilePath, brillo::Blob> files_;
NiceMock<MockTpm> tpm_;
NiceMock<MockPlatform> platform_;
// Declare cryptohome_key_loader_ last, so it gets destroyed before all the
// mocks.
CryptohomeEccKeyLoader cryptohome_key_loader_;
};
ACTION_P(GenerateWrappedKey, wrapped_key) {
*arg0 = brillo::SecureBlob(wrapped_key);
return true;
}
ACTION_P2(LoadWrappedKeyToHandle, tpm, handle) {
arg1->reset(tpm, handle);
return nullptr;
}
TEST_F(CryptohomeEccKeyLoaderTest, LoadCryptohomeKeySuccess) {
SetIsTpmOwned(true);
platform_.WriteFile(kDefaultCryptohomeKeyFile, brillo::Blob());
EXPECT_CALL(tpm_, LoadWrappedKey(_, _))
.WillOnce(LoadWrappedKeyToHandle(&tpm_, kTestKeyHandle));
cryptohome_key_loader_.Init();
EXPECT_TRUE(HasLoadedCryptohomeKey(kTestKeyHandle));
platform_.WriteFile(kDefaultCryptohomeKeyFile, brillo::Blob());
}
TEST_F(CryptohomeEccKeyLoaderTest, LoadCryptohomeKeyNotOwned) {
SetIsTpmOwned(false);
platform_.WriteFile(kDefaultCryptohomeKeyFile, brillo::Blob());
EXPECT_CALL(tpm_, LoadWrappedKey(_, _)).Times(0);
EXPECT_CALL(tpm_, CreateWrappedEccKey(_)).Times(0);
cryptohome_key_loader_.Init();
EXPECT_FALSE(cryptohome_key_loader_.HasCryptohomeKey());
}
TEST_F(CryptohomeEccKeyLoaderTest, LoadCryptohomeKeyTransientFailure) {
// Transient failure on the first attempt leads to key not being loaded.
// But the key is not re-created. Success on the second attempt loads the
// old key.
SetIsTpmOwned(true);
platform_.WriteStringToFile(kDefaultCryptohomeKeyFile, "old-key");
EXPECT_CALL(tpm_, LoadWrappedKey(_, _))
.WillOnce(ReturnError<TPMError>("fake", TPMRetryAction::kCommunication))
.WillOnce(LoadWrappedKeyToHandle(&tpm_, kTestKeyHandle));
EXPECT_CALL(tpm_, CreateWrappedEccKey(_)).Times(0);
cryptohome_key_loader_.Init();
EXPECT_FALSE(cryptohome_key_loader_.HasCryptohomeKey());
cryptohome_key_loader_.Init();
EXPECT_TRUE(HasLoadedCryptohomeKey(kTestKeyHandle));
EXPECT_TRUE(HasStoredCryptohomeKey("old-key"));
}
TEST_F(CryptohomeEccKeyLoaderTest, ReCreateCryptohomeKeyAfterLoadFailure) {
// Permanent failure while loading the key leads to re-creating, storing
// and loading the new key.
SetIsTpmOwned(true);
platform_.WriteStringToFile(kDefaultCryptohomeKeyFile, "old-key");
EXPECT_CALL(tpm_, LoadWrappedKey(_, _))
.WillOnce(ReturnError<TPMError>("fake", TPMRetryAction::kNoRetry))
.WillOnce(LoadWrappedKeyToHandle(&tpm_, kTestKeyHandle));
EXPECT_CALL(tpm_, CreateWrappedEccKey(_))
.WillOnce(GenerateWrappedKey("new-key"));
cryptohome_key_loader_.Init();
EXPECT_TRUE(HasLoadedCryptohomeKey(kTestKeyHandle));
EXPECT_TRUE(HasStoredCryptohomeKey("new-key"));
}
TEST_F(CryptohomeEccKeyLoaderTest,
ReCreateCryptohomeKeyFailureDuringKeyCreation) {
// Permanent failure while loading the key leads to an attempt to re-create
// the key. Which fails. So, nothing new is stored or loaded.
SetIsTpmOwned(true);
platform_.WriteStringToFile(kDefaultCryptohomeKeyFile, "old-key");
EXPECT_CALL(tpm_, LoadWrappedKey(_, _))
.WillOnce(ReturnError<TPMError>("fake", TPMRetryAction::kNoRetry));
EXPECT_CALL(tpm_, CreateWrappedEccKey(_)).WillOnce(Return(false));
cryptohome_key_loader_.Init();
EXPECT_FALSE(cryptohome_key_loader_.HasCryptohomeKey());
EXPECT_TRUE(HasStoredCryptohomeKey("old-key"));
}
TEST_F(CryptohomeEccKeyLoaderTest,
ReCreateCryptohomeKeyFailureDuringKeyLoading) {
// Permanent failure while loading the key leads to re-creating the key.
// It is stored. But then loading fails.
// Still, on the next attempt, the key is loaded, and not re-created again.
SetIsTpmOwned(true);
platform_.WriteStringToFile(kDefaultCryptohomeKeyFile, "old-key");
EXPECT_CALL(tpm_, LoadWrappedKey(_, _))
.WillOnce(ReturnError<TPMError>("fake", TPMRetryAction::kNoRetry))
.WillOnce(ReturnError<TPMError>("fake", TPMRetryAction::kNoRetry))
.WillOnce(LoadWrappedKeyToHandle(&tpm_, kTestKeyHandle));
EXPECT_CALL(tpm_, CreateWrappedEccKey(_))
.WillOnce(GenerateWrappedKey("new-key"));
cryptohome_key_loader_.Init();
EXPECT_FALSE(cryptohome_key_loader_.HasCryptohomeKey());
EXPECT_TRUE(HasStoredCryptohomeKey("new-key"));
cryptohome_key_loader_.Init();
EXPECT_TRUE(HasLoadedCryptohomeKey(kTestKeyHandle));
EXPECT_TRUE(HasStoredCryptohomeKey("new-key"));
}
} // namespace cryptohome