ClientId is not loaded/generated inside of KnownBlockVersions, but in CryDevice. This way, CryDevice also has access to the ClientID. This is later needed to recognize whether we're in a single-client or in a multi-client setting.

This commit is contained in:
Sebastian Messmer 2016-06-26 14:35:52 -07:00
parent 1c654305a8
commit 514dbcb6c7
14 changed files with 152 additions and 40 deletions

View File

@ -22,9 +22,10 @@ namespace versioncounting {
const string KnownBlockVersions::HEADER = "cryfs.integritydata.knownblockversions;0";
constexpr uint32_t KnownBlockVersions::CLIENT_ID_FOR_DELETED_BLOCK;
KnownBlockVersions::KnownBlockVersions(const bf::path &stateFilePath)
:_knownVersions(), _lastUpdateClientId(), _stateFilePath(stateFilePath), _myClientId(0), _mutex(), _valid(true) {
KnownBlockVersions::KnownBlockVersions(const bf::path &stateFilePath, uint32_t myClientId)
:_knownVersions(), _lastUpdateClientId(), _stateFilePath(stateFilePath), _myClientId(myClientId), _mutex(), _valid(true) {
unique_lock<mutex> lock(_mutex);
ASSERT(_myClientId != CLIENT_ID_FOR_DELETED_BLOCK, "This is not a valid client id");
_loadStateFile();
}
@ -87,10 +88,7 @@ uint64_t KnownBlockVersions::incrementVersion(const Key &key, uint64_t lastVersi
void KnownBlockVersions::_loadStateFile() {
optional<Data> file = Data::LoadFromFile(_stateFilePath);
if (file == none) {
// File doesn't exist means we loaded empty state. Assign a random client id.
do {
_myClientId = *reinterpret_cast<uint32_t*>(Random::PseudoRandom().getFixedSize<sizeof(uint32_t)>().data());
} while(_myClientId == CLIENT_ID_FOR_DELETED_BLOCK); // Safety check - CLIENT_ID_FOR_DELETED_BLOCK shouldn't be used by any valid client.
// File doesn't exist means we loaded empty state.
return;
}
@ -98,7 +96,6 @@ void KnownBlockVersions::_loadStateFile() {
if (HEADER != deserializer.readString()) {
throw std::runtime_error("Invalid local state: Invalid integrity file header.");
}
_myClientId = deserializer.readUint32();
_deserializeKnownVersions(&deserializer);
_deserializeLastUpdateClientIds(&deserializer);
@ -108,11 +105,10 @@ void KnownBlockVersions::_loadStateFile() {
void KnownBlockVersions::_saveStateFile() const {
Serializer serializer(
Serializer::StringSize(HEADER) + sizeof(uint32_t) +
Serializer::StringSize(HEADER) +
sizeof(uint64_t) + _knownVersions.size() * (sizeof(uint32_t) + Key::BINARY_LENGTH + sizeof(uint64_t)) +
sizeof(uint64_t) + _lastUpdateClientId.size() * (Key::BINARY_LENGTH + sizeof(uint32_t)));
serializer.writeString(HEADER);
serializer.writeUint32(_myClientId);
_serializeKnownVersions(&serializer);
_serializeLastUpdateClientIds(&serializer);

View File

@ -17,7 +17,7 @@ namespace blockstore {
class KnownBlockVersions final {
public:
explicit KnownBlockVersions(const boost::filesystem::path &stateFilePath);
KnownBlockVersions(const boost::filesystem::path &stateFilePath, uint32_t myClientId);
KnownBlockVersions(KnownBlockVersions &&rhs);
~KnownBlockVersions();
@ -36,6 +36,8 @@ namespace blockstore {
uint32_t myClientId() const;
const boost::filesystem::path &path() const;
static constexpr uint32_t CLIENT_ID_FOR_DELETED_BLOCK = 0;
private:
std::unordered_map<ClientIdAndBlockKey, uint64_t> _knownVersions;
std::unordered_map<Key, uint32_t> _lastUpdateClientId; // The client who last updated the block
@ -45,8 +47,6 @@ namespace blockstore {
mutable std::mutex _mutex;
bool _valid;
static constexpr uint32_t CLIENT_ID_FOR_DELETED_BLOCK = 0;
static const std::string HEADER;
void _loadStateFile();

View File

@ -13,8 +13,8 @@ namespace bf = boost::filesystem;
namespace blockstore {
namespace versioncounting {
VersionCountingBlockStore::VersionCountingBlockStore(unique_ref<BlockStore> baseBlockStore, const bf::path &integrityFilePath, bool missingBlockIsIntegrityViolation)
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _integrityViolationDetected(false) {
VersionCountingBlockStore::VersionCountingBlockStore(unique_ref<BlockStore> baseBlockStore, const bf::path &integrityFilePath, uint32_t myClientId, bool missingBlockIsIntegrityViolation)
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath, myClientId), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _integrityViolationDetected(false) {
}
Key VersionCountingBlockStore::createKey() {
@ -99,9 +99,9 @@ namespace blockstore {
}
#ifndef CRYFS_NO_COMPATIBILITY
void VersionCountingBlockStore::migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const bf::path &integrityFilePath) {
void VersionCountingBlockStore::migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const bf::path &integrityFilePath, uint32_t myClientId) {
std::cout << "Migrating file system for integrity features. This can take a while..." << std::flush;
KnownBlockVersions knownBlockVersions(integrityFilePath);
KnownBlockVersions knownBlockVersions(integrityFilePath, myClientId);
baseBlockStore->forEachBlock([&baseBlockStore, &knownBlockVersions] (const Key &key) {
auto block = baseBlockStore->load(key);
ASSERT(block != none, "Couldn't load block for migration");

View File

@ -13,7 +13,7 @@ namespace versioncounting {
class VersionCountingBlockStore final: public BlockStore {
public:
VersionCountingBlockStore(cpputils::unique_ref<BlockStore> baseBlockStore, const boost::filesystem::path &integrityFilePath, bool missingBlockIsIntegrityViolation);
VersionCountingBlockStore(cpputils::unique_ref<BlockStore> baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId, bool missingBlockIsIntegrityViolation);
Key createKey() override;
boost::optional<cpputils::unique_ref<Block>> tryCreate(const Key &key, cpputils::Data data) override;
@ -28,7 +28,7 @@ public:
KnownBlockVersions *knownBlockVersions();
#ifndef CRYFS_NO_COMPATIBILITY
static void migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const boost::filesystem::path &integrityFilePath);
static void migrateFromBlockstoreWithoutVersionNumbers(BlockStore *baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId);
#endif
private:

View File

@ -40,6 +40,7 @@ set(LIB_SOURCES
filesystem/cachingfsblobstore/SymlinkBlobRef.cpp
filesystem/CryFile.cpp
filesystem/CryDevice.cpp
filesystem/MyClientId.cpp
)
add_library(${PROJECT_NAME} STATIC ${LIB_SOURCES})

View File

@ -17,6 +17,7 @@
#include "../config/CryCipher.h"
#include <cpp-utils/system/homedir.h>
#include <gitversion/VersionCompare.h>
#include "MyClientId.h"
using std::string;
@ -78,18 +79,20 @@ unique_ref<blobstore::BlobStore> CryDevice::CreateBlobStore(unique_ref<BlockStor
unique_ref<BlockStore> CryDevice::CreateVersionCountingEncryptedBlockStore(unique_ref<BlockStore> blockStore, CryConfigFile *configFile) {
auto encryptedBlockStore = CreateEncryptedBlockStore(*configFile->config(), std::move(blockStore));
auto integrityFilePath = _integrityFilePath(configFile->config()->FilesystemId());
auto statePath = _statePath(configFile->config()->FilesystemId());
auto integrityFilePath = statePath / "integritydata";
auto myClientId = MyClientId(statePath).loadOrGenerate();
#ifndef CRYFS_NO_COMPATIBILITY
if (!configFile->config()->HasVersionNumbers()) {
VersionCountingBlockStore::migrateFromBlockstoreWithoutVersionNumbers(encryptedBlockStore.get(), integrityFilePath);
VersionCountingBlockStore::migrateFromBlockstoreWithoutVersionNumbers(encryptedBlockStore.get(), integrityFilePath, myClientId);
configFile->config()->SetBlocksizeBytes(configFile->config()->BlocksizeBytes() + VersionCountingBlock::HEADER_LENGTH);
configFile->config()->SetHasVersionNumbers(true);
configFile->save();
}
#endif
return make_unique_ref<VersionCountingBlockStore>(std::move(encryptedBlockStore), integrityFilePath, false);
return make_unique_ref<VersionCountingBlockStore>(std::move(encryptedBlockStore), integrityFilePath, myClientId, false);
}
Key CryDevice::CreateRootBlobAndReturnKey() {
@ -98,14 +101,14 @@ Key CryDevice::CreateRootBlobAndReturnKey() {
return rootBlob->key();
}
bf::path CryDevice::_integrityFilePath(const CryConfig::FilesystemID &filesystemId) {
bf::path CryDevice::_statePath(const CryConfig::FilesystemID &filesystemId) {
bf::path app_dir = cpputils::system::HomeDirectory::get() / ".cryfs";
_createDirIfNotExists(app_dir);
bf::path filesystems_dir = app_dir / "filesystems";
_createDirIfNotExists(filesystems_dir);
bf::path this_filesystem_dir = filesystems_dir / filesystemId.ToString();
_createDirIfNotExists(this_filesystem_dir);
return this_filesystem_dir / "integritydata.knownblockversions";
return this_filesystem_dir;
}
void CryDevice::_createDirIfNotExists(const bf::path &path) {

View File

@ -47,7 +47,7 @@ private:
blockstore::Key _rootKey;
std::vector<std::function<void()>> _onFsAction;
static boost::filesystem::path _integrityFilePath(const CryConfig::FilesystemID &filesystemId);
static boost::filesystem::path _statePath(const CryConfig::FilesystemID &filesystemId);
static void _createDirIfNotExists(const boost::filesystem::path &path);
blockstore::Key GetOrCreateRootKey(CryConfigFile *config);

View File

@ -0,0 +1,54 @@
#include "MyClientId.h"
#include <fstream>
#include <cpp-utils/random/Random.h>
#include <blockstore/implementations/versioncounting/KnownBlockVersions.h>
using boost::optional;
using boost::none;
using std::ifstream;
using std::ofstream;
using cpputils::Random;
using blockstore::versioncounting::KnownBlockVersions;
namespace bf = boost::filesystem;
namespace cryfs {
MyClientId::MyClientId(const bf::path &statePath)
:_stateFilePath(statePath / "myClientId") {
}
uint32_t MyClientId::loadOrGenerate() const {
auto loaded = _load();
if (loaded != none) {
return *loaded;
}
// If it couldn't be loaded, generate a new client id.
auto generated = _generate();
_save(generated);
return generated;
}
uint32_t MyClientId::_generate() {
uint32_t result;
do {
result = *reinterpret_cast<uint32_t*>(Random::PseudoRandom().getFixedSize<sizeof(uint32_t)>().data());
} while(result == KnownBlockVersions::CLIENT_ID_FOR_DELETED_BLOCK); // Safety check - CLIENT_ID_FOR_DELETED_BLOCK shouldn't be used by any valid client.
return result;
}
optional<uint32_t> MyClientId::_load() const {
ifstream file(_stateFilePath.native());
if (!file.good()) {
// State file doesn't exist
return none;
}
uint32_t value;
file >> value;
return value;
}
void MyClientId::_save(uint32_t clientId) const {
ofstream file(_stateFilePath.native());
file << clientId;
}
}

View File

@ -0,0 +1,30 @@
#pragma once
#ifndef MESSMER_CRYFS_FILESYSTEM_MYCLIENTID_H_
#define MESSMER_CRYFS_FILESYSTEM_MYCLIENTID_H_
#include <cpp-utils/macros.h>
#include <boost/filesystem/path.hpp>
#include <boost/optional.hpp>
namespace cryfs {
class MyClientId final {
public:
MyClientId(const boost::filesystem::path &statePath);
uint32_t loadOrGenerate() const;
private:
const boost::filesystem::path _stateFilePath;
static uint32_t _generate();
boost::optional<uint32_t> _load() const;
void _save(uint32_t clientId) const;
DISALLOW_COPY_AND_ASSIGN(MyClientId);
};
}
#endif

View File

@ -11,12 +11,13 @@ using std::unordered_set;
class KnownBlockVersionsTest : public ::testing::Test {
public:
KnownBlockVersionsTest() :stateFile(false), testobj(stateFile.path()) {}
KnownBlockVersionsTest() :stateFile(false), testobj(stateFile.path(), myClientId) {}
blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
blockstore::Key key2 = blockstore::Key::FromString("C772972491BB4932A1389EE14BC7090A");
uint32_t clientId = 0x12345678;
uint32_t clientId2 = 0x23456789;
static constexpr uint32_t myClientId = 0x12345678;
static constexpr uint32_t clientId = 0x23456789;
static constexpr uint32_t clientId2 = 0x34567890;
TempFile stateFile;
KnownBlockVersions testobj;
@ -259,29 +260,29 @@ TEST_F(KnownBlockVersionsTest, checkAndUpdate_doesntAllowRollbackToOldClientWith
TEST_F(KnownBlockVersionsTest, saveAndLoad_empty) {
TempFile stateFile(false);
KnownBlockVersions(stateFile.path());
KnownBlockVersions(stateFile.path(), myClientId);
EXPECT_TRUE(KnownBlockVersions(stateFile.path()).checkAndUpdateVersion(clientId, key, 1));
EXPECT_TRUE(KnownBlockVersions(stateFile.path(), myClientId).checkAndUpdateVersion(clientId, key, 1));
}
TEST_F(KnownBlockVersionsTest, saveAndLoad_oneentry) {
TempFile stateFile(false);
EXPECT_TRUE(KnownBlockVersions(stateFile.path()).checkAndUpdateVersion(clientId, key, 100));
EXPECT_TRUE(KnownBlockVersions(stateFile.path(), myClientId).checkAndUpdateVersion(clientId, key, 100));
KnownBlockVersions obj(stateFile.path());
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_EQ(100u, obj.getBlockVersion(clientId, key));
}
TEST_F(KnownBlockVersionsTest, saveAndLoad_threeentries) {
TempFile stateFile(false);
{
KnownBlockVersions obj(stateFile.path());
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_TRUE(obj.checkAndUpdateVersion(obj.myClientId(), key, 100));
EXPECT_TRUE(obj.checkAndUpdateVersion(obj.myClientId(), key2, 50));
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId, key, 150));
}
KnownBlockVersions obj(stateFile.path());
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_EQ(100u, obj.getBlockVersion(obj.myClientId(), key));
EXPECT_EQ(50u, obj.getBlockVersion(obj.myClientId(), key2));
EXPECT_EQ(150u, obj.getBlockVersion(clientId, key));
@ -289,12 +290,12 @@ TEST_F(KnownBlockVersionsTest, saveAndLoad_threeentries) {
TEST_F(KnownBlockVersionsTest, saveAndLoad_lastUpdateClientIdIsStored) {
{
KnownBlockVersions obj(stateFile.path());
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId, key, 100));
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId2, key, 10));
}
KnownBlockVersions obj(stateFile.path());
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_FALSE(obj.checkAndUpdateVersion(clientId, key, 100));
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId2, key, 10));
EXPECT_TRUE(obj.checkAndUpdateVersion(clientId, key, 101));
@ -339,7 +340,7 @@ TEST_F(KnownBlockVersionsTest, blockShouldExist_deletedBlock) {
}
TEST_F(KnownBlockVersionsTest, path) {
KnownBlockVersions obj(stateFile.path());
KnownBlockVersions obj(stateFile.path(), myClientId);
EXPECT_EQ(stateFile.path(), obj.path());
}

View File

@ -24,7 +24,7 @@ public:
TempFile stateFile;
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<VersionCountingBlockStore>(make_unique_ref<FakeBlockStore>(), stateFile.path(), SINGLECLIENT);
return make_unique_ref<VersionCountingBlockStore>(make_unique_ref<FakeBlockStore>(), stateFile.path(), 0x12345678, SINGLECLIENT);
}
};

View File

@ -27,9 +27,10 @@ public:
VersionCountingBlockStoreTest():
stateFile(false),
baseBlockStore(new FakeBlockStore),
blockStore(make_unique_ref<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path(), false)),
blockStore(make_unique_ref<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path(), myClientId, false)),
data(DataFixture::generate(BLOCKSIZE)) {
}
static constexpr uint32_t myClientId = 0x12345678;
TempFile stateFile;
FakeBlockStore *baseBlockStore;
unique_ref<VersionCountingBlockStore> blockStore;
@ -37,13 +38,13 @@ public:
std::pair<FakeBlockStore *, unique_ptr<VersionCountingBlockStore>> makeBlockStoreWithDeletionPrevention() {
FakeBlockStore *baseBlockStore = new FakeBlockStore;
auto blockStore = make_unique<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path(), true);
auto blockStore = make_unique<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path(), myClientId, true);
return std::make_pair(baseBlockStore, std::move(blockStore));
}
std::pair<FakeBlockStore *, unique_ptr<VersionCountingBlockStore>> makeBlockStoreWithoutDeletionPrevention() {
FakeBlockStore *baseBlockStore = new FakeBlockStore;
auto blockStore = make_unique<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path(), false);
auto blockStore = make_unique<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path(), myClientId, false);
return std::make_pair(baseBlockStore, std::move(blockStore));
}
@ -115,6 +116,8 @@ private:
DISALLOW_COPY_AND_ASSIGN(VersionCountingBlockStoreTest);
};
constexpr uint32_t VersionCountingBlockStoreTest::myClientId;
// Test that a decreasing version number is not allowed
TEST_F(VersionCountingBlockStoreTest, RollbackPrevention_DoesntAllowDecreasingVersionNumberForSameClient_1) {
auto key = CreateBlockReturnKey();

View File

@ -17,6 +17,7 @@ set(SOURCES
filesystem/CryFsTest.cpp
filesystem/CryNodeTest.cpp
filesystem/FileSystemTest.cpp
filesystem/MyClientIdTest.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})

View File

@ -0,0 +1,23 @@
#include <gtest/gtest.h>
#include "../../../src/cryfs/filesystem/MyClientId.h"
#include <cpp-utils/tempfile/TempDir.h>
using cpputils::TempDir;
using cryfs::MyClientId;
class MyClientIdTest : public ::testing::Test {
public:
TempDir stateDir;
TempDir stateDir2;
};
TEST_F(MyClientIdTest, ValueIsConsistent) {
uint32_t myClientId = MyClientId(stateDir.path()).loadOrGenerate();
EXPECT_EQ(myClientId, MyClientId(stateDir.path()).loadOrGenerate());
}
TEST_F(MyClientIdTest, ValueIsRandomForNewClient) {
uint32_t myClientId = MyClientId(stateDir.path()).loadOrGenerate();
EXPECT_NE(myClientId, MyClientId(stateDir2.path()).loadOrGenerate());
}