Fix test case compatibility with older compilers

This commit is contained in:
Sebastian Messmer 2017-10-08 16:14:51 +01:00
commit a3e26c7480
67 changed files with 832 additions and 255 deletions

View File

@ -7,7 +7,7 @@ references:
run:
name: Initialize Cache
command: |
echo "${APT_COMPILER_PACKAGE}_${BUILD_TOOLSET}_${CXX}_${CC}_${BUILD_TYPE}" > /tmp/_build_env_vars
echo "${APT_COMPILER_PACKAGE}_${BUILD_TOOLSET}_${CXX}_${CC}_${BUILD_TYPE}_${CXXFLAGS}" > /tmp/_build_env_vars
echo Build env vars used for cache keys:
cat /tmp/_build_env_vars
container_setup_pre: &container_setup_pre
@ -49,7 +49,7 @@ references:
sudo chmod o-w /etc/apt/sources.list.d/clang.list
DEBIAN_FRONTEND=noninteractive sudo apt-get update -qq
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y git ccache $APT_COMPILER_PACKAGE cmake make libcurl4-openssl-dev libboost-filesystem-dev libboost-system-dev libboost-chrono-dev libboost-program-options-dev libboost-thread-dev libcrypto++-dev libssl-dev libfuse-dev python
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y git ccache $APT_COMPILER_PACKAGE cmake make libcurl4-openssl-dev libcrypto++-dev libssl-dev libfuse-dev python
# Use /dev/urandom when /dev/random is accessed to use less entropy
sudo cp -a /dev/urandom /dev/random
@ -344,6 +344,17 @@ jobs:
APT_COMPILER_PACKAGE: clang-5.0
CXXFLAGS: "-DCRYFS_NO_COMPATIBILITY"
BUILD_TYPE: "Debug"
address_sanitizer:
<<: *job_definition
environment:
CC: clang-5.0
CXX: clang++-5.0
BUILD_TOOLSET: clang
APT_COMPILER_PACKAGE: clang-5.0
CXXFLAGS: "-O2 -fsanitize=address -fno-omit-frame-pointer -fno-common -fsanitize-address-use-after-scope"
BUILD_TYPE: "Debug"
# Note: Leak detection is disabled because libfuse itself is leaky...
ASAN_OPTIONS: "detect_leaks=0 check_initialization_order=1 detect_stack_use_after_return=1 detect_invalid_pointer_pairs=1 atexit=1"
workflows:
version: 2
@ -384,4 +395,6 @@ workflows:
<<: *enable_for_tags
- no_compatibility:
<<: *enable_for_tags
- address_sanitizer:
<<: *enable_for_tags

View File

@ -111,7 +111,7 @@ size_t CompressedBlock<Compressor>::size() const {
template<class Compressor>
void CompressedBlock<Compressor>::resize(size_t newSize) {
_decompressedData = cpputils::DataUtils::resize(std::move(_decompressedData), newSize);
_decompressedData = cpputils::DataUtils::resize(_decompressedData, newSize);
_dataChanged = true;
}

View File

@ -67,7 +67,7 @@ size_t LowToHighLevelBlock::size() const {
}
void LowToHighLevelBlock::resize(size_t newSize) {
_data = DataUtils::resize(std::move(_data), newSize);
_data = DataUtils::resize(_data, newSize);
_dataChanged = true;
}

View File

@ -39,7 +39,7 @@ size_t FakeBlock::size() const {
}
void FakeBlock::resize(size_t newSize) {
*_data = cpputils::DataUtils::resize(std::move(*_data), newSize);
*_data = cpputils::DataUtils::resize(*_data, newSize);
_dataChanged = true;
}

View File

@ -7,6 +7,7 @@ set(SOURCES
crypto/kdf/PasswordBasedKDF.cpp
crypto/RandomPadding.cpp
crypto/symmetric/EncryptionKey.cpp
crypto/hash/Hash.cpp
process/daemonize.cpp
process/subprocess.cpp
tempfile/TempFile.cpp

View File

@ -10,7 +10,7 @@ namespace cpputils {
class AssertFailed final: public std::exception {
public:
AssertFailed(const std::string &message) : _message(message) { }
AssertFailed(std::string message) : _message(std::move(message)) { }
const char *what() const throw() override {
return _message.c_str();

View File

@ -0,0 +1,30 @@
#include "Hash.h"
#include <cpp-utils/random/Random.h>
#include <cryptopp/sha.h>
using cpputils::Random;
using CryptoPP::SHA512;
namespace cpputils {
namespace hash {
Hash hash(const Data& data, Salt salt) {
SHA512 hasher;
hasher.Update((CryptoPP::byte*)salt.data(), Salt::BINARY_LENGTH);
hasher.Update((CryptoPP::byte*)data.data(), data.size());
Digest digest = Digest::Null();
hasher.Final((CryptoPP::byte*)digest.data());
return Hash{
.digest = std::move(digest),
.salt = std::move(salt)
};
}
Salt generateSalt() {
return Random::PseudoRandom().getFixedSize<8>();
}
}
}

View File

@ -0,0 +1,28 @@
#pragma once
#ifndef MESSMER_CPPUTILS_CRYPTO_HASH_HASH_H
#define MESSMER_CPPUTILS_CRYPTO_HASH_HASH_H
#include <cpp-utils/data/FixedSizeData.h>
#include <cpp-utils/data/Data.h>
namespace cpputils {
namespace hash {
using Digest = FixedSizeData<64>;
using Salt = FixedSizeData<8>;
struct Hash final {
Digest digest;
Salt salt;
};
Salt generateSalt();
Hash hash(const cpputils::Data& data, Salt salt);
}
}
#endif

View File

@ -27,7 +27,7 @@ private:
constexpr static size_t BINARY_LENGTH = FixedSizeData<KeySize>::BINARY_LENGTH;
constexpr static size_t STRING_LENGTH = FixedSizeData<KeySize>::STRING_LENGTH;
EncryptionKeyData(const FixedSizeData<KeySize >& keyData);
EncryptionKeyData(const FixedSizeData<KeySize>& keyData);
~EncryptionKeyData();
// Disallow copying and moving

View File

@ -1,5 +1,7 @@
#include "Data.h"
#include <stdexcept>
#include <cryptopp/hex.h>
#include <cpp-utils/crypto/cryptopp_byte.h>
using std::istream;
using std::ofstream;
@ -42,4 +44,26 @@ Data Data::LoadFromStream(istream &stream, size_t size) {
return result;
}
Data Data::FromString(const std::string &data) {
ASSERT(data.size() % 2 == 0, "hex encoded data cannot have odd number of characters");
Data result(data.size() / 2);
CryptoPP::StringSource(data, true,
new CryptoPP::HexDecoder(
new CryptoPP::ArraySink((CryptoPP::byte*)result._data, result.size())
)
);
return result;
}
std::string Data::ToString() const {
std::string result;
CryptoPP::ArraySource((CryptoPP::byte*)_data, _size, true,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink(result)
)
);
ASSERT(result.size() == 2 * _size, "Created wrongly sized string");
return result;
}
}

View File

@ -45,6 +45,10 @@ public:
static Data LoadFromStream(std::istream &stream, size_t size);
void StoreToStream(std::ostream &stream) const;
// TODO Unify ToString/FromString functions from Data/FixedSizeData using free functions
static Data FromString(const std::string &data);
std::string ToString() const;
private:
size_t _size;
void *_data;

View File

@ -2,7 +2,7 @@
namespace cpputils {
namespace DataUtils {
Data resize(Data data, size_t newSize) {
Data resize(const Data& data, size_t newSize) {
Data newData(newSize);
newData.FillWithZeroes(); // TODO Only fill region after copied old data with zeroes
std::memcpy(newData.data(), data.data(), std::min(newData.size(), data.size()));

View File

@ -10,7 +10,7 @@ namespace cpputils {
//Return a new data object with the given size and initialize as much as possible with the given input data.
//If the new data object is larger, then the remaining bytes will be zero filled.
Data resize(Data data, size_t newSize);
Data resize(const Data& data, size_t newSize);
}
}

View File

@ -15,7 +15,7 @@ namespace cpputils {
void get(void *target, size_t bytes);
void add(Data data);
void add(const Data& data);
private:
size_t _usedUntil;
@ -37,7 +37,7 @@ namespace cpputils {
_usedUntil += numBytes;
}
inline void RandomDataBuffer::add(Data newData) {
inline void RandomDataBuffer::add(const Data& newData) {
// Concatenate old and new random data
size_t oldSize = size();
Data combined(oldSize + newData.size());

View File

@ -21,7 +21,7 @@ namespace cpputils {
size_t neededRandomDataSize = _maxSize - _buffer->size();
ASSERT(_maxSize > _buffer->size(), "This could theoretically fail if another thread refilled the buffer. But we should be the only refilling thread.");
Data randomData = _generateRandomData(neededRandomDataSize);
_buffer->add(std::move(randomData));
_buffer->add(randomData);
return true; // Run another iteration (don't terminate thread)
}

View File

@ -17,7 +17,7 @@ namespace cpputils {
void get(void *target, size_t numBytes);
void add(Data data);
void add(const Data& data);
void waitUntilSizeIsLessThan(size_t numBytes);
@ -63,9 +63,9 @@ namespace cpputils {
return gettableBytes;
}
inline void ThreadsafeRandomDataBuffer::add(Data data) {
inline void ThreadsafeRandomDataBuffer::add(const Data& data) {
boost::unique_lock<boost::mutex> lock(_mutex);
_buffer.add(std::move(data));
_buffer.add(data);
_dataAddedCv.notify_all();
}

View File

@ -6,7 +6,7 @@ using boost::none;
namespace cpputils {
LoopThread::LoopThread(function<bool()> loopIteration): _loopIteration(loopIteration), _runningHandle(none) {
LoopThread::LoopThread(function<bool()> loopIteration): _loopIteration(std::move(loopIteration)), _runningHandle(none) {
}
LoopThread::~LoopThread() {

View File

@ -20,7 +20,7 @@ namespace cpputils {
ThreadSystem::Handle ThreadSystem::start(function<bool()> loopIteration) {
boost::unique_lock<boost::mutex> lock(_mutex);
auto thread = _startThread(loopIteration);
_runningThreads.push_back(RunningThread{loopIteration, std::move(thread)});
_runningThreads.push_back(RunningThread{std::move(loopIteration), std::move(thread)});
return std::prev(_runningThreads.end());
}
@ -61,7 +61,9 @@ namespace cpputils {
}
boost::thread ThreadSystem::_startThread(function<bool()> loopIteration) {
return boost::thread(std::bind(&ThreadSystem::_runThread, loopIteration));
return boost::thread([loopIteration = std::move(loopIteration)] {
ThreadSystem::_runThread(std::move(loopIteration));
});
}
void ThreadSystem::_runThread(function<bool()> loopIteration) {

View File

@ -26,7 +26,7 @@ namespace cryfs {
};
inline CallAfterTimeout::CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback)
:_callback(callback), _timeout(timeout), _start(), _checkTimeoutThread(std::bind(&CallAfterTimeout::_checkTimeoutThreadIteration, this)) {
:_callback(std::move(callback)), _timeout(timeout), _start(), _checkTimeoutThread(std::bind(&CallAfterTimeout::_checkTimeoutThreadIteration, this)) {
resetTimer();
_checkTimeoutThread.start();
}

View File

@ -21,6 +21,8 @@
#include "VersionChecker.h"
#include <gitversion/VersionCompare.h>
#include <cpp-utils/io/NoninteractiveConsole.h>
#include <cryfs/localstate/LocalStateDir.h>
#include <cryfs/localstate/BasedirMetadata.h>
#include "Environment.h"
//TODO Many functions accessing the ProgramOptions object. Factor out into class that stores it as a member.
@ -70,8 +72,8 @@ using gitversion::VersionCompare;
namespace cryfs {
Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, shared_ptr<Console> console, shared_ptr<HttpClient> httpClient):
_keyGenerator(keyGenerator), _scryptSettings(scryptSettings), _console(), _httpClient(httpClient), _noninteractive(false) {
Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, shared_ptr<Console> console):
_keyGenerator(keyGenerator), _scryptSettings(scryptSettings), _console(), _noninteractive(false) {
_noninteractive = Environment::isNoninteractive();
if (_noninteractive) {
_console = make_shared<NoninteractiveConsole>(console);
@ -80,7 +82,7 @@ namespace cryfs {
}
}
void Cli::_showVersion() {
void Cli::_showVersion(unique_ref<HttpClient> httpClient) {
cout << "CryFS Version " << gitversion::VersionString() << endl;
if (gitversion::IsDevVersion()) {
cout << "WARNING! This is a development version based on git commit " << gitversion::GitCommitId() <<
@ -97,7 +99,7 @@ namespace cryfs {
} else if (Environment::isNoninteractive()) {
cout << "Automatic checking for security vulnerabilities and updates is disabled in noninteractive mode." << endl;
} else {
_checkForUpdates();
_checkForUpdates(std::move(httpClient));
}
#else
# warning Update checks are disabled. The resulting executable will not go online to check for newer versions or known security vulnerabilities.
@ -105,8 +107,8 @@ namespace cryfs {
cout << endl;
}
void Cli::_checkForUpdates() {
VersionChecker versionChecker(_httpClient);
void Cli::_checkForUpdates(unique_ref<HttpClient> httpClient) {
VersionChecker versionChecker(httpClient.get());
optional<string> newestVersion = versionChecker.newestVersion();
if (newestVersion == none) {
cout << "Could not check for updates." << endl;
@ -195,14 +197,28 @@ namespace cryfs {
return *configFile;
}
void Cli::_checkConfigIntegrity(const bf::path& basedir, const CryConfigFile& config) {
auto basedirMetadata = BasedirMetadata::load();
if (!basedirMetadata.filesystemIdForBasedirIsCorrect(basedir, config.config()->FilesystemId())) {
if (!_console->askYesNo("The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir. This can be genuine if you replaced the filesystem with a different one. If you didn't do that, it is possible that an attacker did. Do you want to continue loading the file system?", false)) {
throw std::runtime_error(
"The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir.");
}
}
// Update local state (or create it if it didn't exist yet)
basedirMetadata.updateFilesystemIdForBasedir(basedir, config.config()->FilesystemId());
basedirMetadata.save();
}
CryConfigLoader::ConfigLoadResult Cli::_loadOrCreateConfig(const ProgramOptions &options) {
try {
auto configFile = _determineConfigFile(options);
auto config = _loadOrCreateConfigFile(configFile, options.cipher(), options.blocksizeBytes(), options.missingBlockIsIntegrityViolation());
auto config = _loadOrCreateConfigFile(std::move(configFile), options.cipher(), options.blocksizeBytes(), options.missingBlockIsIntegrityViolation());
if (config == none) {
std::cerr << "Could not load config file. Did you enter the correct password?" << std::endl;
exit(1);
}
_checkConfigIntegrity(options.baseDir(), config->configFile);
return std::move(*config);
} catch (const std::exception &e) {
std::cerr << "Error: " << e.what() << std::endl;
@ -210,17 +226,17 @@ namespace cryfs {
}
}
optional<CryConfigLoader::ConfigLoadResult> Cli::_loadOrCreateConfigFile(const bf::path &configFilePath, const optional<string> &cipher, const optional<uint32_t> &blocksizeBytes, const optional<bool> &missingBlockIsIntegrityViolation) {
optional<CryConfigLoader::ConfigLoadResult> Cli::_loadOrCreateConfigFile(bf::path configFilePath, const optional<string> &cipher, const optional<uint32_t> &blocksizeBytes, const optional<bool> &missingBlockIsIntegrityViolation) {
if (_noninteractive) {
return CryConfigLoader(_console, _keyGenerator, _scryptSettings,
&Cli::_askPasswordNoninteractive,
&Cli::_askPasswordNoninteractive,
cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(configFilePath);
cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(std::move(configFilePath));
} else {
return CryConfigLoader(_console, _keyGenerator, _scryptSettings,
&Cli::_askPasswordForExistingFilesystem,
&Cli::_askPasswordForNewFilesystem,
cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(configFilePath);
cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(std::move(configFilePath));
}
}
@ -362,9 +378,9 @@ namespace cryfs {
return false;
}
int Cli::main(int argc, const char *argv[]) {
int Cli::main(int argc, const char *argv[], unique_ref<HttpClient> httpClient) {
cpputils::showBacktraceOnSigSegv();
_showVersion();
_showVersion(std::move(httpClient));
ProgramOptions options = program_options::Parser(argc, argv).parse(CryCiphers::supportedCipherNames());

View File

@ -16,14 +16,15 @@
namespace cryfs {
class Cli final {
public:
Cli(cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings &scryptSettings, std::shared_ptr<cpputils::Console> console, std::shared_ptr<cpputils::HttpClient> httpClient);
int main(int argc, const char *argv[]);
Cli(cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings &scryptSettings, std::shared_ptr<cpputils::Console> console);
int main(int argc, const char *argv[], cpputils::unique_ref<cpputils::HttpClient> httpClient);
private:
void _checkForUpdates();
void _checkForUpdates(cpputils::unique_ref<cpputils::HttpClient> httpClient);
void _runFilesystem(const program_options::ProgramOptions &options);
CryConfigLoader::ConfigLoadResult _loadOrCreateConfig(const program_options::ProgramOptions &options);
boost::optional<CryConfigLoader::ConfigLoadResult> _loadOrCreateConfigFile(const boost::filesystem::path &configFilePath, const boost::optional<std::string> &cipher, const boost::optional<uint32_t> &blocksizeBytes, const boost::optional<bool> &missingBlockIsIntegrityViolation);
void _checkConfigIntegrity(const boost::filesystem::path& basedir, const CryConfigFile& config);
boost::optional<CryConfigLoader::ConfigLoadResult> _loadOrCreateConfigFile(boost::filesystem::path configFilePath, const boost::optional<std::string> &cipher, const boost::optional<uint32_t> &blocksizeBytes, const boost::optional<bool> &missingBlockIsIntegrityViolation);
boost::filesystem::path _determineConfigFile(const program_options::ProgramOptions &options);
static std::string _askPasswordForExistingFilesystem();
static std::string _askPasswordForNewFilesystem();
@ -31,7 +32,7 @@ namespace cryfs {
static std::string _askPasswordFromStdin(const std::string &prompt);
static bool _confirmPassword(const std::string &password);
static bool _checkPassword(const std::string &password);
void _showVersion();
void _showVersion(cpputils::unique_ref<cpputils::HttpClient> httpClient);
void _initLogfile(const program_options::ProgramOptions &options);
void _sanityChecks(const program_options::ProgramOptions &options);
void _checkMountdirDoesntContainBasedir(const program_options::ProgramOptions &options);
@ -46,7 +47,6 @@ namespace cryfs {
cpputils::RandomGenerator &_keyGenerator;
cpputils::SCryptSettings _scryptSettings;
std::shared_ptr<cpputils::Console> _console;
std::shared_ptr<cpputils::HttpClient> _httpClient;
bool _noninteractive;
DISALLOW_COPY_AND_ASSIGN(Cli);

View File

@ -8,18 +8,17 @@
using boost::optional;
using boost::none;
using std::string;
using std::shared_ptr;
using std::make_shared;
using cpputils::HttpClient;
using cpputils::CurlHttpClient;
using boost::property_tree::ptree;
using boost::property_tree::json_parser_error;
using cpputils::unique_ref;
using namespace cpputils::logging;
namespace cryfs {
VersionChecker::VersionChecker(shared_ptr<HttpClient> httpClient)
: _versionInfo(_getVersionInfo(std::move(httpClient))) {
VersionChecker::VersionChecker(HttpClient* httpClient)
: _versionInfo(_getVersionInfo(httpClient)) {
}
optional<string> VersionChecker::newestVersion() const {
@ -49,7 +48,7 @@ namespace cryfs {
return none;
}
optional<ptree> VersionChecker::_getVersionInfo(shared_ptr<HttpClient> httpClient) {
optional<ptree> VersionChecker::_getVersionInfo(HttpClient* httpClient) {
long timeoutMsec = 2000;
optional<string> response = httpClient->get("https://www.cryfs.org/version_info.json", timeoutMsec);
if (response == none) {

View File

@ -6,17 +6,18 @@
#include <boost/optional.hpp>
#include <boost/property_tree/ptree.hpp>
#include <cpp-utils/network/HttpClient.h>
#include <cpp-utils/pointer/unique_ref.h>
namespace cryfs {
class VersionChecker final {
public:
//TODO Write a cpputils::shared_ref and use it
VersionChecker(std::shared_ptr<cpputils::HttpClient> httpClient);
VersionChecker(cpputils::HttpClient* httpClient);
boost::optional<std::string> newestVersion() const;
boost::optional<std::string> securityWarningFor(const std::string &version) const;
private:
static boost::optional<boost::property_tree::ptree> _getVersionInfo(std::shared_ptr<cpputils::HttpClient> httpClient);
static boost::optional<boost::property_tree::ptree> _getVersionInfo(cpputils::HttpClient* httpClient);
static boost::optional<boost::property_tree::ptree> _parseJson(const std::string &json);
boost::optional<boost::property_tree::ptree> _versionInfo;

View File

@ -9,14 +9,15 @@ using cpputils::Random;
using cpputils::SCrypt;
using cpputils::CurlHttpClient;
using cpputils::IOStreamConsole;
using cpputils::make_unique_ref;
using std::make_shared;
using std::cerr;
int main(int argc, const char *argv[]) {
try {
auto &keyGenerator = Random::OSRandom();
return Cli(keyGenerator, SCrypt::DefaultSettings, make_shared<IOStreamConsole>(),
make_shared<CurlHttpClient>()).main(argc, argv);
return Cli(keyGenerator, SCrypt::DefaultSettings, make_shared<IOStreamConsole>())
.main(argc, argv, make_unique_ref<CurlHttpClient>());
} catch (const std::exception &e) {
cerr << "Error: " << e.what();
return EXIT_FAILURE;

View File

@ -92,7 +92,7 @@ ProgramOptions Parser::parse(const vector<string> &supportedCiphers) const {
}
}
return ProgramOptions(baseDir, mountDir, configfile, foreground, unmountAfterIdleMinutes, logfile, cipher, blocksizeBytes, noIntegrityChecks, missingBlockIsIntegrityViolation, fuseOptions);
return ProgramOptions(std::move(baseDir), std::move(mountDir), std::move(configfile), foreground, std::move(unmountAfterIdleMinutes), std::move(logfile), std::move(cipher), blocksizeBytes, noIntegrityChecks, std::move(missingBlockIsIntegrityViolation), std::move(fuseOptions));
}
void Parser::_checkValidCipher(const string &cipher, const vector<string> &supportedCiphers) {

View File

@ -8,16 +8,16 @@ using std::vector;
using boost::optional;
namespace bf = boost::filesystem;
ProgramOptions::ProgramOptions(const bf::path &baseDir, const bf::path &mountDir, const optional<bf::path> &configFile,
bool foreground, const optional<double> &unmountAfterIdleMinutes,
const optional<bf::path> &logFile, const optional<string> &cipher,
const optional<uint32_t> &blocksizeBytes,
ProgramOptions::ProgramOptions(bf::path baseDir, bf::path mountDir, optional<bf::path> configFile,
bool foreground, optional<double> unmountAfterIdleMinutes,
optional<bf::path> logFile, optional<string> cipher,
optional<uint32_t> blocksizeBytes,
bool noIntegrityChecks,
const boost::optional<bool> &missingBlockIsIntegrityViolation,
const vector<string> &fuseOptions)
:_baseDir(baseDir), _mountDir(mountDir), _configFile(configFile), _foreground(foreground), _noIntegrityChecks(noIntegrityChecks),
_cipher(cipher), _blocksizeBytes(blocksizeBytes), _unmountAfterIdleMinutes(unmountAfterIdleMinutes),
_missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _logFile(logFile), _fuseOptions(fuseOptions) {
boost::optional<bool> missingBlockIsIntegrityViolation,
vector<string> fuseOptions)
:_baseDir(std::move(baseDir)), _mountDir(std::move(mountDir)), _configFile(std::move(configFile)), _foreground(foreground), _noIntegrityChecks(noIntegrityChecks),
_cipher(std::move(cipher)), _blocksizeBytes(std::move(blocksizeBytes)), _unmountAfterIdleMinutes(std::move(unmountAfterIdleMinutes)),
_missingBlockIsIntegrityViolation(std::move(missingBlockIsIntegrityViolation)), _logFile(std::move(logFile)), _fuseOptions(std::move(fuseOptions)) {
}
const bf::path &ProgramOptions::baseDir() const {

View File

@ -12,15 +12,15 @@ namespace cryfs {
namespace program_options {
class ProgramOptions final {
public:
ProgramOptions(const boost::filesystem::path &baseDir, const boost::filesystem::path &mountDir,
const boost::optional<boost::filesystem::path> &configFile,
bool foreground, const boost::optional<double> &unmountAfterIdleMinutes,
const boost::optional<boost::filesystem::path> &logFile,
const boost::optional<std::string> &cipher,
const boost::optional<uint32_t> &blocksizeBytes,
ProgramOptions(boost::filesystem::path baseDir, boost::filesystem::path mountDir,
boost::optional<boost::filesystem::path> configFile,
bool foreground, boost::optional<double> unmountAfterIdleMinutes,
boost::optional<boost::filesystem::path> logFile,
boost::optional<std::string> cipher,
boost::optional<uint32_t> blocksizeBytes,
bool noIntegrityChecks,
const boost::optional<bool> &missingBlockIsIntegrityViolation,
const std::vector<std::string> &fuseOptions);
boost::optional<bool> missingBlockIsIntegrityViolation,
std::vector<std::string> fuseOptions);
ProgramOptions(ProgramOptions &&rhs) = default;
const boost::filesystem::path &baseDir() const;

View File

@ -40,8 +40,9 @@ set(LIB_SOURCES
filesystem/cachingfsblobstore/SymlinkBlobRef.cpp
filesystem/CryFile.cpp
filesystem/CryDevice.cpp
localstate/MyClientId.cpp
localstate/LocalStateDir.cpp
localstate/LocalStateMetadata.cpp
localstate/BasedirMetadata.cpp
)
add_library(${PROJECT_NAME} STATIC ${LIB_SOURCES})

View File

@ -89,40 +89,40 @@ const std::string &CryConfig::RootBlob() const {
return _rootBlob;
}
void CryConfig::SetRootBlob(const std::string &value) {
_rootBlob = value;
void CryConfig::SetRootBlob(std::string value) {
_rootBlob = std::move(value);
}
const string &CryConfig::EncryptionKey() const {
return _encKey;
}
void CryConfig::SetEncryptionKey(const std::string &value) {
_encKey = value;
void CryConfig::SetEncryptionKey(std::string value) {
_encKey = std::move(value);
}
const std::string &CryConfig::Cipher() const {
return _cipher;
};
void CryConfig::SetCipher(const std::string &value) {
_cipher = value;
void CryConfig::SetCipher(std::string value) {
_cipher = std::move(value);
}
const std::string &CryConfig::Version() const {
return _version;
}
void CryConfig::SetVersion(const std::string &value) {
_version = value;
void CryConfig::SetVersion(std::string value) {
_version = std::move(value);
}
const std::string &CryConfig::CreatedWithVersion() const {
return _createdWithVersion;
}
void CryConfig::SetCreatedWithVersion(const std::string &value) {
_createdWithVersion = value;
void CryConfig::SetCreatedWithVersion(std::string value) {
_createdWithVersion = std::move(value);
}
uint64_t CryConfig::BlocksizeBytes() const {
@ -137,8 +137,8 @@ const CryConfig::FilesystemID &CryConfig::FilesystemId() const {
return _filesystemId;
}
void CryConfig::SetFilesystemId(const FilesystemID &value) {
_filesystemId = value;
void CryConfig::SetFilesystemId(FilesystemID value) {
_filesystemId = std::move(value);
}
optional<uint32_t> CryConfig::ExclusiveClientId() const {

View File

@ -18,26 +18,26 @@ public:
CryConfig(const CryConfig &rhs) = default;
const std::string &RootBlob() const;
void SetRootBlob(const std::string &value);
void SetRootBlob(std::string value);
const std::string &EncryptionKey() const;
void SetEncryptionKey(const std::string &value);
void SetEncryptionKey(std::string value);
const std::string &Cipher() const;
void SetCipher(const std::string &value);
void SetCipher(std::string value);
const std::string &Version() const;
void SetVersion(const std::string &value);
void SetVersion(std::string value);
const std::string &CreatedWithVersion() const;
void SetCreatedWithVersion(const std::string &value);
void SetCreatedWithVersion(std::string value);
uint64_t BlocksizeBytes() const;
void SetBlocksizeBytes(uint64_t value);
using FilesystemID = cpputils::FixedSizeData<16>;
const FilesystemID &FilesystemId() const;
void SetFilesystemId(const FilesystemID &value);
void SetFilesystemId(FilesystemID value);
// If the exclusive client Id is set, then additional integrity measures (i.e. treating missing blocks as integrity violations) are enabled.
// Because this only works in a single-client setting, only this one client Id is allowed to access the file system.

View File

@ -3,7 +3,7 @@
#include <gitversion/gitversion.h>
#include <cpp-utils/random/Random.h>
#include <cryfs/localstate/LocalStateDir.h>
#include <cryfs/localstate/MyClientId.h>
#include <cryfs/localstate/LocalStateMetadata.h>
using cpputils::Console;
using cpputils::unique_ref;
@ -28,9 +28,11 @@ namespace cryfs {
config.SetCreatedWithVersion(gitversion::VersionString());
config.SetBlocksizeBytes(_generateBlocksizeBytes(blocksizeBytesFromCommandLine));
config.SetRootBlob(_generateRootBlobId());
config.SetEncryptionKey(_generateEncKey(config.Cipher()));
config.SetFilesystemId(_generateFilesystemID());
uint32_t myClientId = MyClientId(LocalStateDir::forFilesystemId(config.FilesystemId())).loadOrGenerate();
auto encryptionKey = _generateEncKey(config.Cipher());
auto localState = LocalStateMetadata::loadOrGenerate(LocalStateDir::forFilesystemId(config.FilesystemId()), cpputils::Data::FromString(encryptionKey));
uint32_t myClientId = localState.myClientId();
config.SetEncryptionKey(std::move(encryptionKey));
config.SetExclusiveClientId(_generateExclusiveClientId(missingBlockIsIntegrityViolationFromCommandLine, myClientId));
#ifndef CRYFS_NO_COMPATIBILITY
config.SetHasVersionNumbers(true);

View File

@ -25,7 +25,7 @@ CryConfigFile::~CryConfigFile() {
//We do not call save() here, because we do not want the config file to be re-encrypted on each filesystem run
}
optional<CryConfigFile> CryConfigFile::load(const bf::path &path, const string &password) {
optional<CryConfigFile> CryConfigFile::load(bf::path path, const string &password) {
auto encryptedConfigData = Data::LoadFromFile(path);
if (encryptedConfigData == none) {
LOG(ERROR, "Config file not found");
@ -44,7 +44,7 @@ optional<CryConfigFile> CryConfigFile::load(const bf::path &path, const string &
LOG(ERROR, "Inner cipher algorithm used to encrypt config file doesn't match config value");
return none;
}
auto configFile = CryConfigFile(path, std::move(config), std::move(*encryptor));
auto configFile = CryConfigFile(std::move(path), std::move(config), std::move(*encryptor));
if (decrypted->wasInDeprecatedConfigFormat) {
// Migrate it to new format
configFile.save();
@ -53,17 +53,17 @@ optional<CryConfigFile> CryConfigFile::load(const bf::path &path, const string &
return std::move(configFile);
}
CryConfigFile CryConfigFile::create(const bf::path &path, const CryConfig &config, const string &password, const SCryptSettings &scryptSettings) {
CryConfigFile CryConfigFile::create(bf::path path, CryConfig config, const string &password, const SCryptSettings &scryptSettings) {
if (bf::exists(path)) {
throw std::runtime_error("Config file exists already.");
}
auto result = CryConfigFile(path, config, CryConfigEncryptorFactory::deriveKey(password, scryptSettings));
auto result = CryConfigFile(std::move(path), std::move(config), CryConfigEncryptorFactory::deriveKey(password, scryptSettings));
result.save();
return result;
}
CryConfigFile::CryConfigFile(const bf::path &path, const CryConfig &config, unique_ref<CryConfigEncryptor> encryptor)
: _path (path), _config(config), _encryptor(std::move(encryptor)) {
CryConfigFile::CryConfigFile(bf::path path, CryConfig config, unique_ref<CryConfigEncryptor> encryptor)
: _path(std::move(path)), _config(std::move(config)), _encryptor(std::move(encryptor)) {
}
void CryConfigFile::save() const {

View File

@ -14,15 +14,15 @@ namespace cryfs {
CryConfigFile(CryConfigFile &&rhs) = default;
~CryConfigFile();
static CryConfigFile create(const boost::filesystem::path &path, const CryConfig &config, const std::string &password, const cpputils::SCryptSettings &scryptSettings);
static boost::optional<CryConfigFile> load(const boost::filesystem::path &path, const std::string &password);
static CryConfigFile create(boost::filesystem::path path, CryConfig config, const std::string &password, const cpputils::SCryptSettings &scryptSettings);
static boost::optional<CryConfigFile> load(boost::filesystem::path path, const std::string &password);
void save() const;
CryConfig *config();
const CryConfig *config() const;
private:
CryConfigFile(const boost::filesystem::path &path, const CryConfig &config, cpputils::unique_ref<CryConfigEncryptor> encryptor);
CryConfigFile(boost::filesystem::path path, CryConfig config, cpputils::unique_ref<CryConfigEncryptor> encryptor);
boost::filesystem::path _path;
CryConfig _config;

View File

@ -7,7 +7,7 @@
#include <gitversion/gitversion.h>
#include <gitversion/VersionCompare.h>
#include "../localstate/LocalStateDir.h"
#include "../localstate/MyClientId.h"
#include "../localstate/LocalStateMetadata.h"
namespace bf = boost::filesystem;
using cpputils::unique_ref;
@ -31,16 +31,16 @@ using namespace cpputils::logging;
namespace cryfs {
CryConfigLoader::CryConfigLoader(shared_ptr<Console> console, RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, function<string()> askPasswordForExistingFilesystem, function<string()> askPasswordForNewFilesystem, const optional<string> &cipherFromCommandLine, const boost::optional<uint32_t> &blocksizeBytesFromCommandLine, const boost::optional<bool> &missingBlockIsIntegrityViolationFromCommandLine)
: _console(console), _creator(console, keyGenerator), _scryptSettings(scryptSettings),
: _console(console), _creator(std::move(console), keyGenerator), _scryptSettings(scryptSettings),
_askPasswordForExistingFilesystem(askPasswordForExistingFilesystem), _askPasswordForNewFilesystem(askPasswordForNewFilesystem),
_cipherFromCommandLine(cipherFromCommandLine), _blocksizeBytesFromCommandLine(blocksizeBytesFromCommandLine),
_missingBlockIsIntegrityViolationFromCommandLine(missingBlockIsIntegrityViolationFromCommandLine) {
}
optional<CryConfigLoader::ConfigLoadResult> CryConfigLoader::_loadConfig(const bf::path &filename) {
optional<CryConfigLoader::ConfigLoadResult> CryConfigLoader::_loadConfig(bf::path filename) {
string password = _askPasswordForExistingFilesystem();
std::cout << "Loading config file (this can take some time)..." << std::flush;
auto config = CryConfigFile::load(filename, password);
auto config = CryConfigFile::load(std::move(filename), password);
if (config == none) {
return none;
}
@ -57,7 +57,8 @@ optional<CryConfigLoader::ConfigLoadResult> CryConfigLoader::_loadConfig(const b
config->save();
}
_checkCipher(*config->config());
uint32_t myClientId = MyClientId(LocalStateDir::forFilesystemId(config->config()->FilesystemId())).loadOrGenerate();
auto localState = LocalStateMetadata::loadOrGenerate(LocalStateDir::forFilesystemId(config->config()->FilesystemId()), cpputils::Data::FromString(config->config()->EncryptionKey()));
uint32_t myClientId = localState.myClientId();
_checkMissingBlocksAreIntegrityViolations(&*config, myClientId);
return ConfigLoadResult {std::move(*config), myClientId};
}
@ -100,20 +101,20 @@ void CryConfigLoader::_checkMissingBlocksAreIntegrityViolations(CryConfigFile *c
}
}
optional<CryConfigLoader::ConfigLoadResult> CryConfigLoader::loadOrCreate(const bf::path &filename) {
optional<CryConfigLoader::ConfigLoadResult> CryConfigLoader::loadOrCreate(bf::path filename) {
if (bf::exists(filename)) {
return _loadConfig(filename);
return _loadConfig(std::move(filename));
} else {
return _createConfig(filename);
return _createConfig(std::move(filename));
}
}
CryConfigLoader::ConfigLoadResult CryConfigLoader::_createConfig(const bf::path &filename) {
CryConfigLoader::ConfigLoadResult CryConfigLoader::_createConfig(bf::path filename) {
auto config = _creator.create(_cipherFromCommandLine, _blocksizeBytesFromCommandLine, _missingBlockIsIntegrityViolationFromCommandLine);
//TODO Ask confirmation if using insecure password (<8 characters)
string password = _askPasswordForNewFilesystem();
std::cout << "Creating config file (this can take some time)..." << std::flush;
auto result = CryConfigFile::create(filename, std::move(config.config), password, _scryptSettings);
auto result = CryConfigFile::create(std::move(filename), std::move(config.config), password, _scryptSettings);
std::cout << "done" << std::endl;
return ConfigLoadResult {std::move(result), config.myClientId};
}

View File

@ -21,11 +21,11 @@ public:
uint32_t myClientId;
};
boost::optional<ConfigLoadResult> loadOrCreate(const boost::filesystem::path &filename);
boost::optional<ConfigLoadResult> loadOrCreate(boost::filesystem::path filename);
private:
boost::optional<ConfigLoadResult> _loadConfig(const boost::filesystem::path &filename);
ConfigLoadResult _createConfig(const boost::filesystem::path &filename);
boost::optional<ConfigLoadResult> _loadConfig(boost::filesystem::path filename);
ConfigLoadResult _createConfig(boost::filesystem::path filename);
void _checkVersion(const CryConfig &config);
void _checkCipher(const CryConfig &config) const;
void _checkMissingBlocksAreIntegrityViolations(CryConfigFile *configFile, uint32_t myClientId);

View File

@ -15,8 +15,8 @@ namespace cryfs {
constexpr size_t CryConfigEncryptor::OuterKeySize;
constexpr size_t CryConfigEncryptor::MaxTotalKeySize;
CryConfigEncryptor::CryConfigEncryptor(FixedSizeData<MaxTotalKeySize> derivedKey, cpputils::Data kdfParameters)
: _derivedKey(std::move(derivedKey)), _kdfParameters(std::move(kdfParameters)) {
CryConfigEncryptor::CryConfigEncryptor(const FixedSizeData<MaxTotalKeySize>& derivedKey, cpputils::Data kdfParameters)
: _derivedKey(derivedKey), _kdfParameters(std::move(kdfParameters)) {
}
Data CryConfigEncryptor::encrypt(const Data &plaintext, const string &cipherName) const {

View File

@ -23,7 +23,7 @@ namespace cryfs {
bool wasInDeprecatedConfigFormat;
};
CryConfigEncryptor(cpputils::FixedSizeData<MaxTotalKeySize> derivedKey, cpputils::Data _kdfParameters);
CryConfigEncryptor(const cpputils::FixedSizeData<MaxTotalKeySize>& derivedKey, cpputils::Data _kdfParameters);
cpputils::Data encrypt(const cpputils::Data &plaintext, const std::string &cipherName) const;
boost::optional<Decrypted> decrypt(const cpputils::Data &data) const;

View File

@ -13,7 +13,7 @@ namespace cryfs {
public:
static constexpr size_t CONFIG_SIZE = 900; // Inner config data is grown to this size before encryption to hide its actual size
ConcreteInnerEncryptor(typename Cipher::EncryptionKey key);
ConcreteInnerEncryptor(const typename Cipher::EncryptionKey& key);
InnerConfig encrypt(const cpputils::Data &config) const override;
boost::optional<cpputils::Data> decrypt(const InnerConfig &innerConfig) const override;
@ -26,8 +26,8 @@ namespace cryfs {
};
template<class Cipher>
ConcreteInnerEncryptor<Cipher>::ConcreteInnerEncryptor(typename Cipher::EncryptionKey key)
: _key(std::move(key)) {
ConcreteInnerEncryptor<Cipher>::ConcreteInnerEncryptor(const typename Cipher::EncryptionKey& key)
: _key(key) {
}
template<class Cipher>

View File

@ -11,8 +11,8 @@ using boost::none;
using namespace cpputils::logging;
namespace cryfs {
OuterEncryptor::OuterEncryptor(Cipher::EncryptionKey key, cpputils::Data kdfParameters)
: _key(std::move(key)), _kdfParameters(std::move(kdfParameters)) {
OuterEncryptor::OuterEncryptor(const Cipher::EncryptionKey& key, cpputils::Data kdfParameters)
: _key(key), _kdfParameters(std::move(kdfParameters)) {
}
OuterConfig OuterEncryptor::encrypt(const Data &plaintext) const {

View File

@ -14,7 +14,7 @@ namespace cryfs {
using Cipher = cpputils::AES256_GCM;
static constexpr size_t CONFIG_SIZE = 1024; // Config data is grown to this size before encryption to hide its actual size
OuterEncryptor(Cipher::EncryptionKey key, cpputils::Data kdfParameters);
OuterEncryptor(const Cipher::EncryptionKey& key, cpputils::Data kdfParameters);
OuterConfig encrypt(const cpputils::Data &encryptedInnerConfig) const;
boost::optional<cpputils::Data> decrypt(const OuterConfig &outerConfig) const;

View File

@ -19,7 +19,6 @@
#include <cpp-utils/system/homedir.h>
#include <gitversion/VersionCompare.h>
#include <blockstore/interface/BlockStore2.h>
#include "cryfs/localstate/MyClientId.h"
#include "cryfs/localstate/LocalStateDir.h"
using std::string;

View File

@ -0,0 +1,71 @@
#include "BasedirMetadata.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <cryptopp/sha.h>
#include <boost/filesystem/operations.hpp>
#include "LocalStateDir.h"
namespace bf = boost::filesystem;
using boost::property_tree::ptree;
using boost::property_tree::write_json;
using boost::property_tree::read_json;
using boost::optional;
using boost::none;
using std::ostream;
using std::istream;
using std::ifstream;
using std::ofstream;
using std::string;
namespace cryfs {
namespace {
ptree _load(const bf::path &metadataFilePath) {
ptree result;
ifstream file(metadataFilePath.native());
if (file.good()) {
read_json(file, result);
}
return result;
}
void _save(const bf::path &metadataFilePath, const ptree& data) {
ofstream file(metadataFilePath.native(), std::ios::trunc);
write_json(file, data);
}
string jsonPathForBasedir(const bf::path &basedir) {
return bf::canonical(basedir).native() + ".filesystemId";
}
}
BasedirMetadata::BasedirMetadata(ptree data)
:_data(std::move(data)) {}
BasedirMetadata BasedirMetadata::load() {
return BasedirMetadata(_load(LocalStateDir::forBasedirMetadata()));
}
void BasedirMetadata::save() {
_save(LocalStateDir::forBasedirMetadata(), _data);
}
bool BasedirMetadata::filesystemIdForBasedirIsCorrect(const bf::path &basedir, const CryConfig::FilesystemID &filesystemId) const {
auto entry = _data.get_optional<string>(jsonPathForBasedir(basedir));
if (entry == boost::none) {
return true; // Basedir not known in local state yet.
}
auto filesystemIdFromState = CryConfig::FilesystemID::FromString(*entry);
return filesystemIdFromState == filesystemId;
}
BasedirMetadata& BasedirMetadata::updateFilesystemIdForBasedir(const bf::path &basedir, const CryConfig::FilesystemID &filesystemId) {
_data.put<string>(jsonPathForBasedir(basedir), filesystemId.ToString());
return *this;
}
}

View File

@ -0,0 +1,32 @@
#pragma once
#ifndef MESSMER_CRYFS_LOCALSTATE_BASEDIRMETADATA_H_
#define MESSMER_CRYFS_LOCALSTATE_BASEDIRMETADATA_H_
#include <boost/filesystem/path.hpp>
#include <boost/property_tree/ptree.hpp>
#include "../config/CryConfig.h"
namespace cryfs {
class BasedirMetadata final {
public:
static BasedirMetadata load();
void save();
BasedirMetadata(const BasedirMetadata&) = delete;
BasedirMetadata& operator=(const BasedirMetadata&) = delete;
BasedirMetadata(BasedirMetadata&&) = default;
BasedirMetadata& operator=(BasedirMetadata&&) = default;
bool filesystemIdForBasedirIsCorrect(const boost::filesystem::path &basedir, const CryConfig::FilesystemID &filesystemId) const;
BasedirMetadata& updateFilesystemIdForBasedir(const boost::filesystem::path &basedir, const CryConfig::FilesystemID &filesystemId);
private:
BasedirMetadata(boost::property_tree::ptree data);
boost::property_tree::ptree _data;
};
}
#endif

View File

@ -5,19 +5,29 @@
namespace bf = boost::filesystem;
namespace cryfs {
namespace {
bf::path appDir() {
return cpputils::system::HomeDirectory::get() / ".cryfs";
}
}
bf::path LocalStateDir::forFilesystemId(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;
_createDirIfNotExists(appDir());
bf::path filesystems_dir = appDir() / "filesystems";
_createDirIfNotExists(filesystems_dir);
bf::path this_filesystem_dir = filesystems_dir / filesystemId.ToString();
_createDirIfNotExists(this_filesystem_dir);
return this_filesystem_dir;
}
bf::path LocalStateDir::forBasedirMetadata() {
_createDirIfNotExists(appDir());
return appDir() / "basedirs";
}
void LocalStateDir::_createDirIfNotExists(const bf::path &path) {
if (!bf::exists(path)) {
bf::create_directory(path);
bf::create_directories(path);
}
}
}

View File

@ -11,6 +11,7 @@ namespace cryfs {
class LocalStateDir final {
public:
static boost::filesystem::path forFilesystemId(const CryConfig::FilesystemID &filesystemId);
static boost::filesystem::path forBasedirMetadata();
private:
LocalStateDir(); // static functions only

View File

@ -0,0 +1,121 @@
#include "LocalStateMetadata.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <cpp-utils/random/Random.h>
#include <boost/filesystem.hpp>
#include <blockstore/implementations/integrity/KnownBlockVersions.h>
using boost::optional;
using boost::none;
using boost::property_tree::ptree;
using boost::property_tree::write_json;
using boost::property_tree::read_json;
using std::ifstream;
using std::ofstream;
using std::istream;
using std::ostream;
using std::string;
using blockstore::integrity::KnownBlockVersions;
using cpputils::hash::Hash;
using cpputils::Data;
using cpputils::Random;
namespace bf = boost::filesystem;
namespace cryfs {
LocalStateMetadata::LocalStateMetadata(uint32_t myClientId, Hash encryptionKeyHash)
: _myClientId(myClientId), _encryptionKeyHash(std::move(encryptionKeyHash)) {}
LocalStateMetadata LocalStateMetadata::loadOrGenerate(const bf::path &statePath, const Data& encryptionKey) {
auto metadataFile = statePath / "metadata";
auto loaded = _load(metadataFile);
if (loaded == none) {
// If it couldn't be loaded, generate a new client id.
return _generate(metadataFile, encryptionKey);
}
if (loaded->_encryptionKeyHash.digest != cpputils::hash::hash(encryptionKey, loaded->_encryptionKeyHash.salt).digest) {
throw std::runtime_error("The filesystem encryption key differs from the last time we loaded this filesystem. Did an attacker replace the file system?");
}
return *loaded;
}
optional<LocalStateMetadata> LocalStateMetadata::_load(const bf::path &metadataFilePath) {
ifstream file(metadataFilePath.native());
if (!file.good()) {
// State file doesn't exist
return none;
}
return _deserialize(file);
}
void LocalStateMetadata::_save(const bf::path &metadataFilePath) const {
ofstream file(metadataFilePath.native(), std::ios::trunc);
_serialize(file);
}
namespace {
uint32_t _generateClientId() {
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;
}
#ifndef CRYFS_NO_COMPATIBILITY
optional<uint32_t> _tryLoadClientIdFromLegacyFile(const bf::path &metadataFilePath) {
auto myClientIdFile = metadataFilePath.parent_path() / "myClientId";
ifstream file(myClientIdFile.native());
if (!file.good()) {
return none;
}
uint32_t value;
file >> value;
file.close();
bf::remove(myClientIdFile);
return value;
}
#endif
}
LocalStateMetadata LocalStateMetadata::_generate(const bf::path &metadataFilePath, const Data& encryptionKey) {
uint32_t myClientId = _generateClientId();
#ifndef CRYFS_NO_COMPATIBILITY
// In the old format, this was stored in a "myClientId" file. If that file exists, load it from there.
optional<uint32_t> legacy = _tryLoadClientIdFromLegacyFile(metadataFilePath);
if (legacy != none) {
myClientId = *legacy;
}
#endif
LocalStateMetadata result(myClientId, cpputils::hash::hash(encryptionKey, cpputils::hash::generateSalt()));
result._save(metadataFilePath);
return result;
}
void LocalStateMetadata::_serialize(ostream& stream) const {
ptree pt;
pt.put<uint32_t>("myClientId", myClientId());
pt.put<string>("encryptionKey.salt", _encryptionKeyHash.salt.ToString());
pt.put<string>("encryptionKey.hash", _encryptionKeyHash.digest.ToString());
write_json(stream, pt);
}
LocalStateMetadata LocalStateMetadata::_deserialize(istream& stream) {
ptree pt;
read_json(stream, pt);
uint32_t myClientId = pt.get<uint32_t>("myClientId");
string encryptionKeySalt = pt.get<string>("encryptionKey.salt");
string encryptionKeyDigest = pt.get<string>("encryptionKey.hash");
return LocalStateMetadata(myClientId, Hash{
.digest = cpputils::hash::Digest::FromString(std::move(encryptionKeyDigest)),
.salt = cpputils::hash::Salt::FromString(std::move(encryptionKeySalt))
});
}
}

View File

@ -0,0 +1,38 @@
#pragma once
#ifndef MESSMER_CRYFS_LOCALSTATE_LOCALSTATEMETADATA_H_
#define MESSMER_CRYFS_LOCALSTATE_LOCALSTATEMETADATA_H_
#include <boost/filesystem/path.hpp>
#include <boost/optional.hpp>
#include <iostream>
#include <cpp-utils/crypto/hash/Hash.h>
namespace cryfs {
class LocalStateMetadata final {
public:
static LocalStateMetadata loadOrGenerate(const boost::filesystem::path &statePath, const cpputils::Data& encryptionKey);
uint32_t myClientId() const;
private:
const uint32_t _myClientId;
const cpputils::hash::Hash _encryptionKeyHash;
static boost::optional<LocalStateMetadata> _load(const boost::filesystem::path &metadataFilePath);
static LocalStateMetadata _deserialize(std::istream& stream);
static LocalStateMetadata _generate(const boost::filesystem::path &metadataFilePath, const cpputils::Data& encryptionKey);
void _save(const boost::filesystem::path &metadataFilePath) const;
void _serialize(std::ostream& stream) const;
LocalStateMetadata(uint32_t myClientId, cpputils::hash::Hash encryptionKey);
};
inline uint32_t LocalStateMetadata::myClientId() const {
return _myClientId;
}
}
#endif

View File

@ -1,54 +0,0 @@
#include "MyClientId.h"
#include <fstream>
#include <cpp-utils/random/Random.h>
#include <blockstore/implementations/integrity/KnownBlockVersions.h>
using boost::optional;
using boost::none;
using std::ifstream;
using std::ofstream;
using cpputils::Random;
using blockstore::integrity::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

@ -1,30 +0,0 @@
#pragma once
#ifndef MESSMER_CRYFS_LOCALSTATE_MYCLIENTID_H_
#define MESSMER_CRYFS_LOCALSTATE_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

@ -211,14 +211,14 @@ fuse_operations *operations() {
Fuse::~Fuse() {
for(char *arg : _argv) {
delete arg;
delete[] arg;
arg = nullptr;
}
_argv.clear();
}
Fuse::Fuse(Filesystem *fs, const std::string &fstype, const boost::optional<std::string> &fsname)
:_fs(fs), _mountdir(), _running(false), _fstype(fstype), _fsname(fsname) {
Fuse::Fuse(Filesystem *fs, std::string fstype, boost::optional<std::string> fsname)
:_fs(fs), _mountdir(), _running(false), _fstype(std::move(fstype)), _fsname(std::move(fsname)) {
}
void Fuse::_logException(const std::exception &e) {

View File

@ -19,7 +19,7 @@ class Filesystem;
class Fuse final {
public:
explicit Fuse(Filesystem *fs, const std::string &fstype, const boost::optional<std::string> &fsname);
explicit Fuse(Filesystem *fs, std::string fstype, boost::optional<std::string> fsname);
~Fuse();
void run(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);

View File

@ -16,6 +16,12 @@ using cpputils::DataFixture;
using cpputils::unique_ref;
using boost::optional;
namespace boost {
inline void PrintTo(const optional<cpputils::Data> &, ::std::ostream *os) {
*os << "optional<Data>";
}
}
using namespace blockstore;
class BlockStore2Mock: public BlockStore2 {

View File

@ -5,6 +5,7 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cpp-utils/data/DataFixture.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
#include "blockstore/interface/BlockStore.h"
@ -285,7 +286,7 @@ TYPED_TEST_P(BlockStoreTest, Resize_Smaller_ToZero_BlockIsStillUsable) {
block->resize(0);
this->TestBlockIsUsable(std::move(block), blockStore.get());
}
/*
TYPED_TEST_P(BlockStoreTest, TryCreateTwoBlocksWithSameBlockIdAndSameSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
@ -295,7 +296,7 @@ TYPED_TEST_P(BlockStoreTest, TryCreateTwoBlocksWithSameBlockIdAndSameSize) {
EXPECT_NE(boost::none, block);
EXPECT_EQ(boost::none, block2);
}
/*
TYPED_TEST_P(BlockStoreTest, TryCreateTwoBlocksWithSameBlockIdAndDifferentSize) {
auto blockStore = this->fixture.createBlockStore();
blockstore::BlockId blockId = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
@ -383,6 +384,7 @@ REGISTER_TYPED_TEST_CASE_P(BlockStoreTest,
ForEachBlock_twoblocks,
ForEachBlock_threeblocks,
ForEachBlock_doesntListRemovedBlocks_oneblock,
ForEachBlock_doesntListRemovedBlocks_twoblocks,
Resize_Larger_FromZero,
Resize_Larger_FromZero_BlockIsStillUsable,
Resize_Larger,
@ -390,10 +392,10 @@ REGISTER_TYPED_TEST_CASE_P(BlockStoreTest,
Resize_Smaller,
Resize_Smaller_BlockIsStillUsable,
Resize_Smaller_ToZero,
Resize_Smaller_ToZero_BlockIsStillUsable,
TryCreateTwoBlocksWithSameBlockIdAndSameSize
Resize_Smaller_ToZero_BlockIsStillUsable
//TODO Just disabled because gtest doesn't allow more template parameters. Fix and reenable!
// see https://github.com/google/googletest/issues/1267
//TryCreateTwoBlocksWithSameBlockIdAndSameSize,
//TryCreateTwoBlocksWithSameBlockIdAndDifferentSize,
//TryCreateTwoBlocksWithSameBlockIdAndFirstNullSize,
//TryCreateTwoBlocksWithSameBlockIdAndSecondNullSize,

View File

@ -6,6 +6,7 @@ set(SOURCES
crypto/symmetric/testutils/FakeAuthenticatedCipher.cpp
crypto/kdf/SCryptTest.cpp
crypto/kdf/SCryptParametersTest.cpp
crypto/hash/HashTest.cpp
MacrosIncludeTest.cpp
pointer/unique_ref_test.cpp
pointer/cast_include_test.cpp

View File

@ -0,0 +1,45 @@
#include <gtest/gtest.h>
#include <cpp-utils/crypto/hash/Hash.h>
#include <cpp-utils/data/DataFixture.h>
using namespace cpputils::hash;
using cpputils::DataFixture;
using cpputils::Data;
TEST(HashTest, generateSalt_isIndeterministic) {
EXPECT_NE(generateSalt(), generateSalt());
}
TEST(HashTest, hash_setsSaltCorrectly) {
Salt salt = generateSalt();
Data data = DataFixture::generate(1024);
EXPECT_EQ(salt, hash(data, salt).salt);
}
TEST(HashTest, hash_isDeterministicWithSameDataSameSalt) {
Salt salt = generateSalt();
Data data = DataFixture::generate(1024);
EXPECT_EQ(hash(data, salt).digest, hash(data, salt).digest);
}
TEST(HashTest, hash_isIndeterministicWithSameDataDifferentSalt) {
Salt salt1 = generateSalt();
Salt salt2 = generateSalt();
Data data = DataFixture::generate(1024);
EXPECT_NE(hash(data, salt1).digest, hash(data, salt2).digest);
}
TEST(HashTest, hash_isIndeterministicWithDifferentDataSameSalt) {
Salt salt = generateSalt();
Data data1 = DataFixture::generate(1024, 1);
Data data2 = DataFixture::generate(1024, 2);
EXPECT_NE(hash(data1, salt).digest, hash(data2, salt).digest);
}
TEST(HashTest, hash_isIndeterministicWithDifferentDataDifferentSalt) {
Salt salt1 = generateSalt();
Salt salt2 = generateSalt();
Data data1 = DataFixture::generate(1024, 1);
Data data2 = DataFixture::generate(1024, 2);
EXPECT_NE(hash(data1, salt1).digest, hash(data2, salt2).digest);
}

View File

@ -14,6 +14,7 @@ using cpputils::TempFile;
using std::ifstream;
using std::ofstream;
using std::string;
namespace bf = boost::filesystem;
@ -206,3 +207,17 @@ TEST_F(DataTest, LoadingNonexistingFile) {
TempFile file(false); // Pass false to constructor, so the tempfile is not created
EXPECT_FALSE(Data::LoadFromFile(file.path()));
}
class DataTestWithStringParam: public DataTest, public WithParamInterface<string> {};
INSTANTIATE_TEST_CASE_P(DataTestWithStringParam, DataTestWithStringParam, Values("", "2898B4B8A13C0F0278CCE465DB", "6FFEBAD90C0DAA2B79628F0627CE9841"));
TEST_P(DataTestWithStringParam, FromAndToString) {
Data data = Data::FromString(GetParam());
EXPECT_EQ(GetParam(), data.ToString());
}
TEST_P(DataTestWithStringParam, ToAndFromString) {
Data data = Data::FromString(GetParam());
Data data2 = Data::FromString(data.ToString());
EXPECT_EQ(data, data2);
}

View File

@ -9,6 +9,12 @@ using testing::MatchesRegex;
using namespace cpputils;
// Disable these by default because they depend on network
// and - even if network is available - can fail depending
// on the concrete network setup (e.g. if invalid domains are
// answered with an ISP page instead of HTTP error)
#ifdef CRYFS_ENABLE_NETWORK_TESTS
TEST(CurlHttpClientTest, InvalidProtocol) {
EXPECT_EQ(none, CurlHttpClient().get("invalid://example.com"));
}
@ -30,3 +36,5 @@ TEST(CurlHttpClientTest, ValidHttps) {
string content = CurlHttpClient().get("https://example.com").value();
EXPECT_THAT(content, MatchesRegex(".*Example Domain.*"));
}
#endif

View File

@ -11,6 +11,7 @@ set(SOURCES
CliTest_ShowingHelp.cpp
EnvironmentTest.cpp
VersionCheckerTest.cpp
CliTest_IntegrityCheck.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})

View File

@ -0,0 +1,44 @@
#include "testutils/CliTest.h"
#include <cryfs/config/CryConfigFile.h>
using std::vector;
using std::string;
using cryfs::CryConfig;
using cryfs::CryConfigFile;
class CliTest_IntegrityCheck: public CliTest {
public:
void modifyFilesystemId() {
auto configFile = CryConfigFile::load(basedir / "cryfs.config", "pass").value();
configFile.config()->SetFilesystemId(CryConfig::FilesystemID::FromString("0123456789ABCDEF0123456789ABCDEF"));
configFile.save();
}
void modifyFilesystemKey() {
auto configFile = CryConfigFile::load(basedir / "cryfs.config", "pass").value();
configFile.config()->SetEncryptionKey("0123456789ABCDEF0123456789ABCDEF");
configFile.save();
}
};
TEST_F(CliTest_IntegrityCheck, givenIncorrectFilesystemId_thenFails) {
vector<const char*> args {basedir.c_str(), mountdir.c_str(), "--cipher", "aes-256-gcm", "-f"};
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS can handle that
EXPECT_RUN_SUCCESS(args, mountdir);
modifyFilesystemId();
EXPECT_RUN_ERROR(
args,
"Error: The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir."
);
}
TEST_F(CliTest_IntegrityCheck, givenIncorrectFilesystemKey_thenFails) {
vector<const char*> args {basedir.c_str(), mountdir.c_str(), "--cipher", "aes-256-gcm", "-f"};
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS can handle that
EXPECT_RUN_SUCCESS(args, mountdir);
modifyFilesystemKey();
EXPECT_RUN_ERROR(
args,
"Error: The filesystem encryption key differs from the last time we loaded this filesystem. Did an attacker replace the file system?"
);
}

View File

@ -15,15 +15,15 @@ using namespace cryfs;
class VersionCheckerTest: public ::testing::Test {
public:
unique_ref<VersionChecker> versionChecker() {
return make_unique_ref<VersionChecker>(http);
return make_unique_ref<VersionChecker>(_http.get());
}
void setVersionInfo(const string &versionInfo) {
http->addWebsite("https://www.cryfs.org/version_info.json", versionInfo);
_http->addWebsite("https://www.cryfs.org/version_info.json", versionInfo);
}
private:
shared_ptr<FakeHttpClient> http = make_shared<FakeHttpClient>();
unique_ref<FakeHttpClient> _http = make_unique_ref<FakeHttpClient>();
};
TEST_F(VersionCheckerTest, NewestVersion_NoInternet) {

View File

@ -12,8 +12,9 @@
#include <cpp-utils/process/subprocess.h>
#include <cpp-utils/network/FakeHttpClient.h>
#include "../../cryfs/testutils/MockConsole.h"
#include "../../cryfs/testutils/TestWithFakeHomeDirectory.h"
class CliTest : public ::testing::Test {
class CliTest : public ::testing::Test, TestWithFakeHomeDirectory {
public:
CliTest(): _basedir(), _mountdir(), basedir(_basedir.path()), mountdir(_mountdir.path()), logfile(), configfile(false), console(std::make_shared<MockConsole>()) {}
@ -25,10 +26,10 @@ public:
cpputils::TempFile configfile;
std::shared_ptr<MockConsole> console;
std::shared_ptr<cpputils::HttpClient> _httpClient() {
std::shared_ptr<cpputils::FakeHttpClient> httpClient = std::make_shared<cpputils::FakeHttpClient>();
cpputils::unique_ref<cpputils::HttpClient> _httpClient() {
cpputils::unique_ref<cpputils::FakeHttpClient> httpClient = cpputils::make_unique_ref<cpputils::FakeHttpClient>();
httpClient->addWebsite("https://www.cryfs.org/version_info.json", "{\"version_info\":{\"current\":\"0.8.5\"}}");
return std::shared_ptr<cpputils::HttpClient>(std::move(httpClient));
return std::move(httpClient);
}
void run(std::vector<const char*> args) {
@ -43,7 +44,7 @@ public:
std::cin.putback('\n'); std::cin.putback('s'); std::cin.putback('s'); std::cin.putback('a'); std::cin.putback('p');
std::cin.putback('\n'); std::cin.putback('s'); std::cin.putback('s'); std::cin.putback('a'); std::cin.putback('p');
// Run Cryfs
cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings, console, _httpClient()).main(_args.size(), _args.data());
cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings, console).main(_args.size(), _args.data(), _httpClient());
}
void EXPECT_EXIT_WITH_HELP_MESSAGE(std::vector<const char*> args, const std::string &message = "") {

View File

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

View File

@ -4,6 +4,7 @@
#include <cryfs/config/CryCipher.h>
#include <cpp-utils/crypto/symmetric/ciphers.h>
#include "../testutils/MockConsole.h"
#include "../testutils/TestWithFakeHomeDirectory.h"
#include <cpp-utils/io/NoninteractiveConsole.h>
#include <gitversion/gitversion.h>
@ -46,7 +47,7 @@ using ::testing::WithParamInterface;
#define IGNORE_ASK_FOR_MISSINGBLOCKISINTEGRITYVIOLATION() \
EXPECT_CALL(*console, askYesNo(HasSubstr("missing block"), false))
class CryConfigCreatorTest: public ::testing::Test {
class CryConfigCreatorTest: public ::testing::Test, TestWithFakeHomeDirectory {
public:
CryConfigCreatorTest()
: console(make_shared<MockConsole>()),

View File

@ -1,6 +1,7 @@
#include <gtest/gtest.h>
#include <cryfs/config/CryConfigLoader.h>
#include "../testutils/MockConsole.h"
#include "../testutils/TestWithFakeHomeDirectory.h"
#include <cpp-utils/tempfile/TempFile.h>
#include <cpp-utils/random/Random.h>
#include <cpp-utils/crypto/symmetric/ciphers.h>
@ -14,6 +15,7 @@ using cpputils::make_unique_ref;
using cpputils::TempFile;
using cpputils::SCrypt;
using cpputils::DataFixture;
using cpputils::Data;
using cpputils::NoninteractiveConsole;
using boost::optional;
using boost::none;
@ -32,9 +34,28 @@ namespace boost {
return stream << "CryConfigFile()";
}
}
namespace cryfs {
inline ostream &operator<<(ostream &stream, const CryConfigLoader::ConfigLoadResult &) {
return stream << "ConfigLoadResult()";
}
}
#include <boost/optional/optional_io.hpp>
class CryConfigLoaderTest: public ::testing::Test, public TestWithMockConsole {
class FakeRandomGenerator final : public cpputils::RandomGenerator {
public:
FakeRandomGenerator(Data output)
: _output(std::move(output)) {}
void _get(void *target, size_t bytes) override {
ASSERT_EQ(_output.size(), bytes);
std::memcpy(target, _output.data(), bytes);
}
private:
Data _output;
};
class CryConfigLoaderTest: public ::testing::Test, public TestWithMockConsole, TestWithFakeHomeDirectory {
public:
CryConfigLoaderTest(): file(false) {
console = mockConsole();
@ -78,7 +99,15 @@ public:
}
void CreateWithEncryptionKey(const string &encKey, const string &password = "mypassword") {
auto cfg = loader(password, false).loadOrCreate(file.path()).value().configFile;
auto askPassword = [password] { return password;};
FakeRandomGenerator generator(Data::FromString(encKey));
auto loader = CryConfigLoader(console, generator, SCrypt::TestSettings, askPassword,
askPassword, none, none, none);
ASSERT_NE(boost::none, loader.loadOrCreate(file.path()));
}
void ChangeEncryptionKey(const string &encKey, const string& password = "mypassword") {
auto cfg = CryConfigFile::load(file.path(), password).value();
cfg.config()->SetEncryptionKey(encKey);
cfg.save();
}
@ -96,6 +125,12 @@ public:
cfg.save();
}
void ChangeFilesystemID(const CryConfig::FilesystemID &filesystemId, const string& password = "mypassword") {
auto cfg = CryConfigFile::load(file.path(), password).value();
cfg.config()->SetFilesystemId(filesystemId);
cfg.save();
}
string olderVersion() {
string olderVersion;
if (std::stol(gitversion::MinorVersion()) > 0) {
@ -176,9 +211,18 @@ TEST_F(CryConfigLoaderTest, RootBlob_Create) {
}
TEST_F(CryConfigLoaderTest, EncryptionKey_Load) {
CreateWithEncryptionKey("encryptionkey");
CreateWithEncryptionKey("3B4682CF22F3CA199E385729B9F3CA19D325229E385729B9443CA19D325229E3");
auto loaded = Load().value();
EXPECT_EQ("encryptionkey", loaded.config()->EncryptionKey());
EXPECT_EQ("3B4682CF22F3CA199E385729B9F3CA19D325229E385729B9443CA19D325229E3", loaded.config()->EncryptionKey());
}
TEST_F(CryConfigLoaderTest, EncryptionKey_Load_whenKeyChanged_thenFails) {
CreateWithEncryptionKey("3B4682CF22F3CA199E385729B9F3CA19D325229E385729B9443CA19D325229E3");
ChangeEncryptionKey("3B4682CF22F3CA199E385729B9F3CA19D325229E385729B9443CA19D325229E4");
EXPECT_THROW(
Load(),
std::runtime_error
);
}
TEST_F(CryConfigLoaderTest, EncryptionKey_Create) {

View File

@ -0,0 +1,66 @@
#include <gtest/gtest.h>
#include <cryfs/localstate/BasedirMetadata.h>
#include <cryfs/localstate/LocalStateDir.h>
#include <cryfs/config/CryConfig.h>
#include <cpp-utils/tempfile/TempDir.h>
#include "../testutils/TestWithFakeHomeDirectory.h"
using cpputils::TempDir;
using cryfs::BasedirMetadata;
using std::ofstream;
namespace bf = boost::filesystem;
using FilesystemID = cryfs::CryConfig::FilesystemID ;
class BasedirMetadataTest : public ::testing::Test, TestWithFakeHomeDirectory {
public:
TempDir tempdir;
bf::path basedir1;
bf::path basedir2;
const FilesystemID id1;
const FilesystemID id2;
BasedirMetadataTest()
: tempdir()
, basedir1(tempdir.path() / "my/basedir")
, basedir2(tempdir.path() / "my/other/basedir")
, id1(FilesystemID::FromString("1491BB4932A389EE14BC7090AC772972"))
, id2(FilesystemID::FromString("A1491BB493214BC7090C772972A389EE"))
{
// Create basedirs so bf::canonical() works
bf::create_directories(basedir1);
bf::create_directories(basedir2);
}
};
TEST_F(BasedirMetadataTest, givenEmptyState_whenCalled_thenSucceeds) {
EXPECT_TRUE(BasedirMetadata::load().filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithBasedir_whenCalledForDifferentBasedir_thenSucceeds) {
BasedirMetadata::load().updateFilesystemIdForBasedir(basedir2, id1).save();
EXPECT_TRUE(BasedirMetadata::load().filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithBasedir_whenCalledWithSameId_thenSucceeds) {
BasedirMetadata::load().updateFilesystemIdForBasedir(basedir1, id1).save();
EXPECT_TRUE(BasedirMetadata::load().filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithBasedir_whenCalledWithDifferentId_thenFails) {
BasedirMetadata::load().updateFilesystemIdForBasedir(basedir1, id2).save();
EXPECT_FALSE(BasedirMetadata::load().filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithUpdatedBasedir_whenCalledWithSameId_thenSucceeds) {
BasedirMetadata::load().updateFilesystemIdForBasedir(basedir1, id2).save();
BasedirMetadata::load().updateFilesystemIdForBasedir(basedir1, id1).save();
EXPECT_TRUE(BasedirMetadata::load().filesystemIdForBasedirIsCorrect(basedir1, id1));
}
TEST_F(BasedirMetadataTest, givenStateWithUpdatedBasedir_whenCalledWithDifferentId_thenFails) {
BasedirMetadata::load().updateFilesystemIdForBasedir(basedir1, id2).save();
BasedirMetadata::load().updateFilesystemIdForBasedir(basedir1, id1).save();
EXPECT_FALSE(BasedirMetadata::load().filesystemIdForBasedirIsCorrect(basedir1, id2));
}

View File

@ -0,0 +1,54 @@
#include <gtest/gtest.h>
#include <cryfs/localstate/LocalStateMetadata.h>
#include <cpp-utils/tempfile/TempDir.h>
#include <fstream>
#include <cpp-utils/data/DataFixture.h>
using cpputils::TempDir;
using cpputils::Data;
using cryfs::LocalStateMetadata;
using cpputils::DataFixture;
using std::ofstream;
class LocalStateMetadataTest : public ::testing::Test {
public:
TempDir stateDir;
TempDir stateDir2;
};
TEST_F(LocalStateMetadataTest, myClientId_ValueIsConsistent) {
LocalStateMetadata metadata1 = LocalStateMetadata::loadOrGenerate(stateDir.path(), Data(0));
LocalStateMetadata metadata2 = LocalStateMetadata::loadOrGenerate(stateDir.path(), Data(0));
EXPECT_EQ(metadata1.myClientId(), metadata2.myClientId());
}
TEST_F(LocalStateMetadataTest, myClientId_ValueIsRandomForNewClient) {
LocalStateMetadata metadata1 = LocalStateMetadata::loadOrGenerate(stateDir.path(), Data(0));
LocalStateMetadata metadata2 = LocalStateMetadata::loadOrGenerate(stateDir2.path(), Data(0));
EXPECT_NE(metadata1.myClientId(), metadata2.myClientId());
}
#ifndef CRYFS_NO_COMPATIBILITY
TEST_F(LocalStateMetadataTest, myClientId_TakesLegacyValueIfSpecified) {
ofstream file((stateDir.path() / "myClientId").native());
file << 12345u;
file.close();
LocalStateMetadata metadata = LocalStateMetadata::loadOrGenerate(stateDir.path(), Data(0));
EXPECT_EQ(12345u, metadata.myClientId());
}
#endif
TEST_F(LocalStateMetadataTest, encryptionKeyHash_whenLoadingWithSameKey_thenDoesntCrash) {
LocalStateMetadata::loadOrGenerate(stateDir.path(), DataFixture::generate(1024));
LocalStateMetadata::loadOrGenerate(stateDir.path(), DataFixture::generate(1024));
}
TEST_F(LocalStateMetadataTest, encryptionKeyHash_whenLoadingWithDifferentKey_thenCrashes) {
LocalStateMetadata::loadOrGenerate(stateDir.path(), DataFixture::generate(1024, 1));
EXPECT_THROW(
LocalStateMetadata::loadOrGenerate(stateDir.path(), DataFixture::generate(1024, 2)),
std::runtime_error
);
}

View File

@ -1,23 +0,0 @@
#include <gtest/gtest.h>
#include "cryfs/localstate/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());
}