Add --cipher and --ciphers command line options

This commit is contained in:
Sebastian Messmer 2015-10-30 19:53:15 +01:00
parent d731ecf6a6
commit bdb6de1c4a
19 changed files with 191 additions and 67 deletions

View File

@ -4,3 +4,6 @@ Version 0.8.1
* Password Encryption: If the configuration file is not specified as command line parameter, it will be put into the base directory. The filesystem can be mounted with the password only, without specifying a config file on command line. * Password Encryption: If the configuration file is not specified as command line parameter, it will be put into the base directory. The filesystem can be mounted with the password only, without specifying a config file on command line.
* Logfiles: Added a --logfile option to specify where logs should be written to. If the option is not specified, CryFs logs to syslog. * Logfiles: Added a --logfile option to specify where logs should be written to. If the option is not specified, CryFs logs to syslog.
* Running in Background: Fixed daemonization. When CryFs is run without "-f" flag, it will run in background. * Running in Background: Fixed daemonization. When CryFs is run without "-f" flag, it will run in background.
* Better error messages when base directory is not existing, not readable or not writeable.
* Allow --cipher=xxx to specify cipher on command line. If cryfs is creating a new filesystem, it will use this cipher. If it is opening an existing filesystem, it will check whether this is the cipher used by it.
* --show-ciphers shows a list of all supported ciphers

View File

@ -102,7 +102,7 @@ namespace cryfs {
auto console = make_unique_ref<IOStreamConsole>(); auto console = make_unique_ref<IOStreamConsole>();
auto &keyGenerator = Random::OSRandom(); auto &keyGenerator = Random::OSRandom();
std::cout << "Loading config file..." << std::endl; std::cout << "Loading config file..." << std::endl;
auto config = CryConfigLoader(std::move(console), keyGenerator, &Cli::_askPassword).loadOrCreate(configFile); auto config = CryConfigLoader(std::move(console), keyGenerator, &Cli::_askPassword, options.cipher()).loadOrCreate(configFile);
std::cout << "Loading config file...done" << std::endl; std::cout << "Loading config file...done" << std::endl;
if (config == none) { if (config == none) {
std::cerr << "Could not load config file. Did you enter the correct password?" << std::endl; std::cerr << "Could not load config file. Did you enter the correct password?" << std::endl;
@ -218,7 +218,7 @@ namespace cryfs {
cpputils::showBacktraceOnSigSegv(); cpputils::showBacktraceOnSigSegv();
_showVersion(); _showVersion();
ProgramOptions options = program_options::Parser(argc, argv).parse(); ProgramOptions options = program_options::Parser(argc, argv).parse(CryCiphers::supportedCipherNames());
try { try {
_sanityChecks(options); _sanityChecks(options);

View File

@ -6,21 +6,33 @@ using cpputils::unique_ref;
using cpputils::RandomGenerator; using cpputils::RandomGenerator;
using std::string; using std::string;
using std::vector; using std::vector;
using boost::optional;
using boost::none;
namespace cryfs { namespace cryfs {
CryConfigCreator::CryConfigCreator(unique_ref<Console> console, RandomGenerator &encryptionKeyGenerator) CryConfigCreator::CryConfigCreator(unique_ref<Console> console, RandomGenerator &encryptionKeyGenerator)
:_console(std::move(console)), _encryptionKeyGenerator(encryptionKeyGenerator) { :_console(std::move(console)), _encryptionKeyGenerator(encryptionKeyGenerator) {
} }
CryConfig CryConfigCreator::create() { CryConfig CryConfigCreator::create(const optional<string> &cipher) {
CryConfig config; CryConfig config;
config.SetCipher(_generateCipher()); config.SetCipher(_generateCipher(cipher));
config.SetEncryptionKey(_generateEncKey(config.Cipher())); config.SetEncryptionKey(_generateEncKey(config.Cipher()));
config.SetRootBlob(_generateRootBlobKey()); config.SetRootBlob(_generateRootBlobKey());
return config; return config;
} }
string CryConfigCreator::_generateCipher() { string CryConfigCreator::_generateCipher(const optional<string> &cipher) {
if (cipher != none) {
ASSERT(std::find(CryCiphers::supportedCipherNames().begin(), CryCiphers::supportedCipherNames().end(), *cipher) != CryCiphers::supportedCipherNames().end(), "Invalid cipher");
return *cipher;
} else {
return _askCipher();
}
}
string CryConfigCreator::_askCipher() {
vector<string> ciphers = CryCiphers::supportedCipherNames(); vector<string> ciphers = CryCiphers::supportedCipherNames();
string cipherName = ""; string cipherName = "";
bool askAgain = true; bool askAgain = true;

View File

@ -13,9 +13,10 @@ namespace cryfs {
CryConfigCreator(cpputils::unique_ref<cpputils::Console> console, cpputils::RandomGenerator &encryptionKeyGenerator); CryConfigCreator(cpputils::unique_ref<cpputils::Console> console, cpputils::RandomGenerator &encryptionKeyGenerator);
CryConfigCreator(CryConfigCreator &&rhs) = default; CryConfigCreator(CryConfigCreator &&rhs) = default;
CryConfig create(); CryConfig create(const boost::optional<std::string> &cipher);
private: private:
std::string _generateCipher(); std::string _generateCipher(const boost::optional<std::string> &cipher);
std::string _askCipher();
std::string _generateEncKey(const std::string &cipher); std::string _generateEncKey(const std::string &cipher);
std::string _generateRootBlobKey(); std::string _generateRootBlobKey();
bool _showWarningForCipherAndReturnIfOk(const std::string &cipherName); bool _showWarningForCipherAndReturnIfOk(const std::string &cipherName);

View File

@ -20,8 +20,8 @@ using namespace cpputils::logging;
namespace cryfs { namespace cryfs {
CryConfigLoader::CryConfigLoader(unique_ref<Console> console, RandomGenerator &keyGenerator, function<string()> askPassword) CryConfigLoader::CryConfigLoader(unique_ref<Console> console, RandomGenerator &keyGenerator, function<string()> askPassword, const optional<string> &cipher)
: _creator(std::move(console), keyGenerator), _askPassword(askPassword) { : _creator(std::move(console), keyGenerator), _askPassword(askPassword), _cipher(cipher) {
} }
optional<CryConfigFile> CryConfigLoader::_loadConfig(const bf::path &filename) { optional<CryConfigFile> CryConfigLoader::_loadConfig(const bf::path &filename) {
@ -31,6 +31,9 @@ optional<CryConfigFile> CryConfigLoader::_loadConfig(const bf::path &filename) {
LOG(ERROR) << "Could not load config file. Wrong password?"; LOG(ERROR) << "Could not load config file. Wrong password?";
return none; return none;
} }
if (_cipher != none && config->config()->Cipher() != *_cipher) {
throw std::runtime_error("Filesystem uses "+config->config()->Cipher()+" cipher and not "+*_cipher+" as specified.");
}
return std::move(*config); return std::move(*config);
} }

View File

@ -13,7 +13,7 @@ namespace cryfs {
class CryConfigLoader { class CryConfigLoader {
public: public:
CryConfigLoader(cpputils::unique_ref<cpputils::Console> console, cpputils::RandomGenerator &keyGenerator, std::function<std::string()> askPassword); CryConfigLoader(cpputils::unique_ref<cpputils::Console> console, cpputils::RandomGenerator &keyGenerator, std::function<std::string()> askPassword, const boost::optional<std::string> &cipher);
CryConfigLoader(CryConfigLoader &&rhs) = default; CryConfigLoader(CryConfigLoader &&rhs) = default;
template<class SCryptSettings = cpputils::SCryptDefaultSettings> template<class SCryptSettings = cpputils::SCryptDefaultSettings>
@ -26,6 +26,7 @@ private:
CryConfigCreator _creator; CryConfigCreator _creator;
std::function<std::string()> _askPassword; std::function<std::string()> _askPassword;
boost::optional<std::string> _cipher;
DISALLOW_COPY_AND_ASSIGN(CryConfigLoader); DISALLOW_COPY_AND_ASSIGN(CryConfigLoader);
}; };
@ -41,7 +42,7 @@ boost::optional<CryConfigFile> CryConfigLoader::loadOrCreate(const boost::filesy
template<class SCryptSettings> template<class SCryptSettings>
CryConfigFile CryConfigLoader::_createConfig(const boost::filesystem::path &filename) { CryConfigFile CryConfigLoader::_createConfig(const boost::filesystem::path &filename) {
auto config = _creator.create(); auto config = _creator.create(_cipher);
//TODO Ask confirmation if using insecure password (<8 characters) //TODO Ask confirmation if using insecure password (<8 characters)
std::string password = _askPassword(); std::string password = _askPassword();
return CryConfigFile::create<SCryptSettings>(filename, std::move(config), password); return CryConfigFile::create<SCryptSettings>(filename, std::move(config), password);

View File

@ -13,7 +13,9 @@ using std::string;
using boost::optional; using boost::optional;
using boost::none; using boost::none;
Parser::Parser(int argc, char *argv[]) :_options(_argsToVector(argc, argv)) {} Parser::Parser(int argc, char *argv[])
:_options(_argsToVector(argc, argv)) {
}
vector<char*> Parser::_argsToVector(int argc, char *argv[]) { vector<char*> Parser::_argsToVector(int argc, char *argv[]) {
vector<char*> result; vector<char*> result;
@ -23,9 +25,9 @@ vector<char*> Parser::_argsToVector(int argc, char *argv[]) {
return result; return result;
} }
ProgramOptions Parser::parse() const { ProgramOptions Parser::parse(const vector<string> &supportedCiphers) const {
pair<vector<char*>, vector<char*>> options = splitAtDoubleDash(_options); pair<vector<char*>, vector<char*>> options = splitAtDoubleDash(_options);
po::variables_map vm = _parseOptionsOrShowHelp(options.first); po::variables_map vm = _parseOptionsOrShowHelp(options.first, supportedCiphers);
string baseDir = vm["base-dir"].as<string>(); string baseDir = vm["base-dir"].as<string>();
string mountDir = vm["mount-dir"].as<string>(); string mountDir = vm["mount-dir"].as<string>();
@ -38,19 +40,31 @@ ProgramOptions Parser::parse() const {
if (vm.count("logfile")) { if (vm.count("logfile")) {
logfile = vm["logfile"].as<string>(); logfile = vm["logfile"].as<string>();
} }
optional<string> cipher = none;
if (vm.count("cipher")) {
cipher = vm["cipher"].as<string>();
_checkValidCipher(*cipher, supportedCiphers);
}
return ProgramOptions(baseDir, mountDir, configfile, foreground, logfile, options.second); return ProgramOptions(baseDir, mountDir, configfile, foreground, logfile, cipher, options.second);
} }
po::variables_map Parser::_parseOptionsOrShowHelp(const vector<char*> options) { void Parser::_checkValidCipher(const string &cipher, const vector<string> &supportedCiphers) {
if (std::find(supportedCiphers.begin(), supportedCiphers.end(), cipher) == supportedCiphers.end()) {
std::cerr << "Invalid cipher: " << cipher << std::endl;
exit(1);
}
}
po::variables_map Parser::_parseOptionsOrShowHelp(const vector<char*> options, const vector<string> &supportedCiphers) {
try { try {
return _parseOptions(options); return _parseOptions(options, supportedCiphers);
} catch(const std::exception &e) { } catch(const std::exception &e) {
_showHelpAndExit(); _showHelpAndExit();
} }
} }
po::variables_map Parser::_parseOptions(const vector<char*> options) { po::variables_map Parser::_parseOptions(const vector<char*> options, const vector<string> &supportedCiphers) {
po::options_description desc; po::options_description desc;
po::positional_options_description positional_desc; po::positional_options_description positional_desc;
_addAllowedOptions(&desc); _addAllowedOptions(&desc);
@ -62,6 +76,9 @@ po::variables_map Parser::_parseOptions(const vector<char*> options) {
if (vm.count("help")) { if (vm.count("help")) {
_showHelpAndExit(); _showHelpAndExit();
} }
if (vm.count("show-ciphers")) {
_showCiphersAndExit(supportedCiphers);
}
po::notify(vm); po::notify(vm);
return vm; return vm;
@ -73,6 +90,8 @@ void Parser::_addAllowedOptions(po::options_description *desc) {
("help,h", "show help message") ("help,h", "show help message")
("config,c", po::value<string>(), "Configuration file") ("config,c", po::value<string>(), "Configuration file")
("foreground,f", "Run CryFS in foreground.") ("foreground,f", "Run CryFS in foreground.")
("cipher", po::value<string>(), "Cipher to use for encryption. See possible values by calling cryfs with --show-ciphers")
("show-ciphers", "Show list of supported ciphers.")
("logfile", po::value<string>(), "Specify the file to write log messages to. If this is not specified, log messages will go to stdout, or syslog if CryFS is running in the background.") ("logfile", po::value<string>(), "Specify the file to write log messages to. If this is not specified, log messages will go to stdout, or syslog if CryFS is running in the background.")
; ;
desc->add(options); desc->add(options);
@ -89,6 +108,13 @@ void Parser::_addPositionalOptionForBaseDir(po::options_description *desc, po::p
desc->add(hidden); desc->add(hidden);
} }
[[noreturn]] void Parser::_showCiphersAndExit(const vector<string> &supportedCiphers) {
for (const auto &cipher : supportedCiphers) {
std::cerr << cipher << "\n";
}
exit(0);
}
[[noreturn]] void Parser::_showHelpAndExit() { [[noreturn]] void Parser::_showHelpAndExit() {
cerr << "Usage: cryfs [options] rootDir mountPoint [-- [FUSE Mount Options]]\n"; cerr << "Usage: cryfs [options] rootDir mountPoint [-- [FUSE Mount Options]]\n";
po::options_description desc; po::options_description desc;

View File

@ -11,7 +11,7 @@ namespace cryfs {
public: public:
Parser(int argc, char *argv[]); Parser(int argc, char *argv[]);
ProgramOptions parse() const; ProgramOptions parse(const std::vector<std::string> &supportedCiphers) const;
private: private:
static std::vector<char *> _argsToVector(int argc, char *argv[]); static std::vector<char *> _argsToVector(int argc, char *argv[]);
@ -19,8 +19,10 @@ namespace cryfs {
static void _addPositionalOptionForBaseDir(boost::program_options::options_description *desc, static void _addPositionalOptionForBaseDir(boost::program_options::options_description *desc,
boost::program_options::positional_options_description *positional); boost::program_options::positional_options_description *positional);
[[noreturn]] static void _showHelpAndExit(); [[noreturn]] static void _showHelpAndExit();
static boost::program_options::variables_map _parseOptionsOrShowHelp(const std::vector<char*> options); [[noreturn]] static void _showCiphersAndExit(const std::vector<std::string> &supportedCiphers);
static boost::program_options::variables_map _parseOptions(const std::vector<char*> options); static boost::program_options::variables_map _parseOptionsOrShowHelp(const std::vector<char*> options, const std::vector<std::string> &supportedCiphers);
static boost::program_options::variables_map _parseOptions(const std::vector<char*> options, const std::vector<std::string> &supportedCiphers);
static void _checkValidCipher(const std::string &cipher, const std::vector<std::string> &supportedCiphers);
std::vector<char*> _options; std::vector<char*> _options;
}; };

View File

@ -8,9 +8,10 @@ using std::vector;
using boost::optional; using boost::optional;
ProgramOptions::ProgramOptions(const string &baseDir, const string &mountDir, const optional<string> &configFile, ProgramOptions::ProgramOptions(const string &baseDir, const string &mountDir, const optional<string> &configFile,
bool foreground, const optional<string> &logFile, const vector<char*> &fuseOptions) bool foreground, const optional<string> &logFile, const optional<string> &cipher,
const vector<char*> &fuseOptions)
:_baseDir(baseDir), _mountDir(new char[mountDir.size()+1]), _configFile(configFile), _foreground(foreground), :_baseDir(baseDir), _mountDir(new char[mountDir.size()+1]), _configFile(configFile), _foreground(foreground),
_logFile(logFile), _fuseOptions(fuseOptions) { _logFile(logFile), _cipher(cipher), _fuseOptions(fuseOptions) {
std::memcpy(_mountDir, mountDir.c_str(), mountDir.size()+1); std::memcpy(_mountDir, mountDir.c_str(), mountDir.size()+1);
// Fuse needs the mountDir passed as first option (first option = position 1, since 0 is the executable name) // Fuse needs the mountDir passed as first option (first option = position 1, since 0 is the executable name)
ASSERT(_fuseOptions.size() >= 1, "There has to be one parameter at least for the executable name"); ASSERT(_fuseOptions.size() >= 1, "There has to be one parameter at least for the executable name");
@ -50,6 +51,10 @@ const optional<string> &ProgramOptions::logFile() const {
return _logFile; return _logFile;
} }
const optional<string> &ProgramOptions::cipher() const {
return _cipher;
}
const vector<char *> &ProgramOptions::fuseOptions() const { const vector<char *> &ProgramOptions::fuseOptions() const {
return _fuseOptions; return _fuseOptions;
} }

View File

@ -12,7 +12,7 @@ namespace cryfs {
class ProgramOptions final { class ProgramOptions final {
public: public:
ProgramOptions(const std::string &baseDir, const std::string &mountDir, const boost::optional<std::string> &configFile, ProgramOptions(const std::string &baseDir, const std::string &mountDir, const boost::optional<std::string> &configFile,
bool foreground, const boost::optional<std::string> &logFile, bool foreground, const boost::optional<std::string> &logFile, const boost::optional<std::string> &cipher,
const std::vector<char *> &fuseOptions); const std::vector<char *> &fuseOptions);
ProgramOptions(ProgramOptions &&rhs); ProgramOptions(ProgramOptions &&rhs);
~ProgramOptions(); ~ProgramOptions();
@ -22,6 +22,7 @@ namespace cryfs {
const boost::optional<std::string> &configFile() const; const boost::optional<std::string> &configFile() const;
bool foreground() const; bool foreground() const;
const boost::optional<std::string> &logFile() const; const boost::optional<std::string> &logFile() const;
const boost::optional<std::string> &cipher() const;
const std::vector<char *> &fuseOptions() const; const std::vector<char *> &fuseOptions() const;
private: private:
@ -30,6 +31,7 @@ namespace cryfs {
boost::optional<std::string> _configFile; boost::optional<std::string> _configFile;
bool _foreground; bool _foreground;
boost::optional<std::string> _logFile; boost::optional<std::string> _logFile;
boost::optional<std::string> _cipher;
std::vector<char *> _fuseOptions; std::vector<char *> _fuseOptions;
DISALLOW_COPY_AND_ASSIGN(ProgramOptions); DISALLOW_COPY_AND_ASSIGN(ProgramOptions);

View File

@ -6,24 +6,24 @@ using cpputils::TempFile;
using CliTest_Setup = CliTest; using CliTest_Setup = CliTest;
TEST_F(CliTest_Setup, NoSpecialOptions) { TEST_F(CliTest_Setup, NoSpecialOptions) {
EXPECT_RUN_SUCCESS({basedir.c_str(), mountdir.c_str()}); EXPECT_RUN_SUCCESS({basedir.c_str(), mountdir.c_str(), "--cipher", "aes-256-gcm"}, basedir);
} }
TEST_F(CliTest_Setup, NotexistingLogfileGiven) { TEST_F(CliTest_Setup, NotexistingLogfileGiven) {
TempFile notexisting_logfile(false); TempFile notexisting_logfile(false);
EXPECT_RUN_SUCCESS({basedir.c_str(), mountdir.c_str(), "--logfile", notexisting_logfile.path().c_str()}); EXPECT_RUN_SUCCESS({basedir.c_str(), mountdir.c_str(), "--logfile", notexisting_logfile.path().c_str()}, basedir);
//TODO Expect logfile is used (check logfile content) //TODO Expect logfile is used (check logfile content)
} }
TEST_F(CliTest_Setup, ExistingLogfileGiven) { TEST_F(CliTest_Setup, ExistingLogfileGiven) {
EXPECT_RUN_SUCCESS({basedir.c_str(), mountdir.c_str(), "--logfile", logfile.path().c_str()}); EXPECT_RUN_SUCCESS({basedir.c_str(), mountdir.c_str(), "--logfile", logfile.path().c_str()}, basedir);
//TODO Expect logfile is used (check logfile content) //TODO Expect logfile is used (check logfile content)
} }
TEST_F(CliTest_Setup, ConfigfileGiven) { TEST_F(CliTest_Setup, ConfigfileGiven) {
EXPECT_RUN_SUCCESS({basedir.c_str(), "--config", configfile.path().c_str(), mountdir.c_str()}); EXPECT_RUN_SUCCESS({basedir.c_str(), "--config", configfile.path().c_str(), mountdir.c_str()}, basedir);
} }
TEST_F(CliTest_Setup, FuseOptionGiven) { TEST_F(CliTest_Setup, FuseOptionGiven) {
EXPECT_RUN_SUCCESS({basedir.c_str(), mountdir.c_str(), "--", "-f"}); EXPECT_RUN_SUCCESS({basedir.c_str(), mountdir.c_str(), "--", "-f"}, basedir);
} }

View File

@ -43,7 +43,7 @@ public:
} }
void Test_Run_Success() { void Test_Run_Success() {
EXPECT_RUN_SUCCESS(args()); EXPECT_RUN_SUCCESS(args(), basedir);
} }
void Test_Run_Error(const char *expectedError) { void Test_Run_Error(const char *expectedError) {

View File

@ -6,6 +6,7 @@
#include <messmer/cpp-utils/tempfile/TempDir.h> #include <messmer/cpp-utils/tempfile/TempDir.h>
#include <messmer/cpp-utils/tempfile/TempFile.h> #include <messmer/cpp-utils/tempfile/TempFile.h>
#include "../../../src/Cli.h" #include "../../../src/Cli.h"
#include <messmer/cpp-utils/logging/logging.h>
class CliTest : public ::testing::Test { class CliTest : public ::testing::Test {
public: public:
@ -40,14 +41,25 @@ public:
); );
} }
void EXPECT_RUN_SUCCESS(std::vector<const char*> args) { class _UnmountFilesystemInDestructor final {
//TODO public:
/*EXPECT_EXIT( _UnmountFilesystemInDestructor(const boost::filesystem::path &baseDir) :_baseDir(baseDir) {}
~_UnmountFilesystemInDestructor() {
if (0 != system((std::string("fusermount -u ")+_baseDir.c_str()).c_str()), "Could not unmount cryfs") {
cpputils::logging::LOG(cpputils::logging::ERROR) << "Could not unmount cryfs";
}
}
private:
boost::filesystem::path _baseDir;
};
void EXPECT_RUN_SUCCESS(std::vector<const char*> args, const boost::filesystem::path &baseDir) {
/*_UnmountFilesystemInDestructor raii(baseDir);
EXPECT_EXIT(
run(args), run(args),
::testing::ExitedWithCode(0), ::testing::ExitedWithCode(0),
"Filesystem is running" "Filesystem is running"
);*/ );*/
//TODO Then stop running cryfs process again
} }
}; };

View File

@ -36,33 +36,38 @@ public:
#define EXPECT_ASK_FOR_CIPHER() EXPECT_CALL(*console, ask(HasSubstr("block cipher"), UnorderedElementsAreArray(CryCiphers::supportedCipherNames()))) #define EXPECT_ASK_FOR_CIPHER() EXPECT_CALL(*console, ask(HasSubstr("block cipher"), UnorderedElementsAreArray(CryCiphers::supportedCipherNames())))
TEST_F(CryConfigCreatorTest, DoesNotCrash) { TEST_F(CryConfigCreatorTest, DoesAskForCipherIfNotSpecified) {
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseAnyCipher()); EXPECT_ASK_FOR_CIPHER().Times(1).WillOnce(ChooseAnyCipher());
CryConfig config = creator.create(); CryConfig config = creator.create(none);
}
TEST_F(CryConfigCreatorTest, DoesNotAskForCipherIfSpecified) {
EXPECT_ASK_FOR_CIPHER().Times(0);
CryConfig config = creator.create(string("aes-256-gcm"));
} }
TEST_F(CryConfigCreatorTest, ChoosesEmptyRootBlobId) { TEST_F(CryConfigCreatorTest, ChoosesEmptyRootBlobId) {
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseAnyCipher()); EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseAnyCipher());
CryConfig config = creator.create(); CryConfig config = creator.create(none);
EXPECT_EQ("", config.RootBlob()); // This tells CryFS to create a new root blob EXPECT_EQ("", config.RootBlob()); // This tells CryFS to create a new root blob
} }
TEST_F(CryConfigCreatorTest, ChoosesValidEncryptionKey_448) { TEST_F(CryConfigCreatorTest, ChoosesValidEncryptionKey_448) {
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher("mars-448-gcm")); EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher("mars-448-gcm"));
CryConfig config = creator.create(); CryConfig config = creator.create(none);
cpputils::Mars448_GCM::EncryptionKey::FromString(config.EncryptionKey()); // This crashes if invalid cpputils::Mars448_GCM::EncryptionKey::FromString(config.EncryptionKey()); // This crashes if invalid
} }
TEST_F(CryConfigCreatorTest, ChoosesValidEncryptionKey_256) { TEST_F(CryConfigCreatorTest, ChoosesValidEncryptionKey_256) {
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher("aes-256-gcm")); EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher("aes-256-gcm"));
CryConfig config = creator.create(); CryConfig config = creator.create(none);
cpputils::AES256_GCM::EncryptionKey::FromString(config.EncryptionKey()); // This crashes if invalid cpputils::AES256_GCM::EncryptionKey::FromString(config.EncryptionKey()); // This crashes if invalid
} }
TEST_F(CryConfigCreatorTest, ChoosesValidEncryptionKey_128) { TEST_F(CryConfigCreatorTest, ChoosesValidEncryptionKey_128) {
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher("aes-128-gcm")); EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher("aes-128-gcm"));
CryConfig config = creator.create(); CryConfig config = creator.create(none);
cpputils::AES128_GCM::EncryptionKey::FromString(config.EncryptionKey()); // This crashes if invalid cpputils::AES128_GCM::EncryptionKey::FromString(config.EncryptionKey()); // This crashes if invalid
} }
class CryConfigCreatorTest_ChooseCipher: public CryConfigCreatorTest, public ::testing::WithParamInterface<string> { class CryConfigCreatorTest_ChooseCipher: public CryConfigCreatorTest, public ::testing::WithParamInterface<string> {
@ -88,7 +93,7 @@ TEST_P(CryConfigCreatorTest_ChooseCipher, ChoosesCipherCorrectly) {
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher(cipherName)); EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher(cipherName));
CryConfig config = creator.create(); CryConfig config = creator.create(none);
EXPECT_EQ(cipherName, config.Cipher()); EXPECT_EQ(cipherName, config.Cipher());
} }

View File

@ -17,22 +17,24 @@ using ::testing::_;
using namespace cryfs; using namespace cryfs;
//TODO Test loading with same/different --cipher argument
class CryConfigLoaderTest: public ::testing::Test, public TestWithMockConsole { class CryConfigLoaderTest: public ::testing::Test, public TestWithMockConsole {
public: public:
CryConfigLoaderTest(): file(false) {} CryConfigLoaderTest(): file(false) {}
CryConfigLoader loader(const string &password) { CryConfigLoader loader(const string &password, const optional<string> &cipher = none) {
return CryConfigLoader(mockConsole(), cpputils::Random::PseudoRandom(), [password] {return password;}); return CryConfigLoader(mockConsole(), cpputils::Random::PseudoRandom(), [password] {return password;}, cipher);
} }
CryConfigFile Create(const string &password = "mypassword") { CryConfigFile Create(const string &password = "mypassword", const optional<string> &cipher = none) {
EXPECT_FALSE(file.exists()); EXPECT_FALSE(file.exists());
return loader(password).loadOrCreate<SCryptTestSettings>(file.path()).value(); return loader(password, cipher).loadOrCreate<SCryptTestSettings>(file.path()).value();
} }
optional<CryConfigFile> Load(const string &password = "mypassword") { optional<CryConfigFile> Load(const string &password = "mypassword", const optional<string> &cipher = none) {
EXPECT_TRUE(file.exists()); EXPECT_TRUE(file.exists());
return loader(password).loadOrCreate<SCryptTestSettings>(file.path()); return loader(password, cipher).loadOrCreate<SCryptTestSettings>(file.path());
} }
void CreateWithRootBlob(const string &rootBlob, const string &password = "mypassword") { void CreateWithRootBlob(const string &rootBlob, const string &password = "mypassword") {
@ -73,6 +75,21 @@ TEST_F(CryConfigLoaderTest, DoesntLoadIfWrongPassword) {
EXPECT_EQ(none, loaded); EXPECT_EQ(none, loaded);
} }
TEST_F(CryConfigLoaderTest, DoesntLoadIfDifferentCipher) {
Create("mypassword", string("aes-256-gcm"));
try {
Load("mypassword", string("aes-256-cfb"));
EXPECT_TRUE(false); // Should throw exception
} catch (const std::runtime_error &e) {
EXPECT_EQ(string("Filesystem uses aes-256-gcm cipher and not aes-256-cfb as specified."), e.what());
}
}
TEST_F(CryConfigLoaderTest, DoesLoadIfSameCipher) {
Create("mypassword", string("aes-256-gcm"));
Load("mypassword", string("aes-256-gcm"));
}
TEST_F(CryConfigLoaderTest, RootBlob_Load) { TEST_F(CryConfigLoaderTest, RootBlob_Load) {
CreateWithRootBlob("rootblobid"); CreateWithRootBlob("rootblobid");
auto loaded = Load().value(); auto loaded = Load().value();

View File

@ -23,6 +23,7 @@ using cpputils::unique_ref;
using cpputils::Console; using cpputils::Console;
using cpputils::Random; using cpputils::Random;
using blockstore::ondisk::OnDiskBlockStore; using blockstore::ondisk::OnDiskBlockStore;
using boost::none;
namespace bf = boost::filesystem; namespace bf = boost::filesystem;
using namespace cryfs; using namespace cryfs;
@ -33,7 +34,7 @@ public:
} }
CryConfigFile loadOrCreateConfig() { CryConfigFile loadOrCreateConfig() {
return CryConfigLoader(mockConsole(), Random::PseudoRandom(), [] {return "mypassword";}).loadOrCreate(config.path()).value(); return CryConfigLoader(mockConsole(), Random::PseudoRandom(), [] {return "mypassword";}, none).loadOrCreate(config.path()).value();
} }
unique_ref<OnDiskBlockStore> blockStore() { unique_ref<OnDiskBlockStore> blockStore() {

View File

@ -13,6 +13,7 @@ using cpputils::Random;
using fspp::Device; using fspp::Device;
using ::testing::Return; using ::testing::Return;
using ::testing::_; using ::testing::_;
using boost::none;
using blockstore::testfake::FakeBlockStore; using blockstore::testfake::FakeBlockStore;
@ -26,7 +27,7 @@ public:
unique_ref<Device> createDevice() override { unique_ref<Device> createDevice() override {
auto blockStore = cpputils::make_unique_ref<FakeBlockStore>(); auto blockStore = cpputils::make_unique_ref<FakeBlockStore>();
auto config = CryConfigLoader(mockConsole(), Random::PseudoRandom(), [] {return "mypassword";}) auto config = CryConfigLoader(mockConsole(), Random::PseudoRandom(), [] {return "mypassword";}, none)
.loadOrCreate(configFile.path()).value(); .loadOrCreate(configFile.path()).value();
return make_unique_ref<CryDevice>(std::move(config), std::move(blockStore)); return make_unique_ref<CryDevice>(std::move(config), std::move(blockStore));
} }

View File

@ -1,6 +1,8 @@
#include "testutils/ProgramOptionsTestBase.h" #include "testutils/ProgramOptionsTestBase.h"
#include "../../src/program_options/Parser.h" #include "../../src/program_options/Parser.h"
#include "../../src/config/CryCipher.h"
using namespace cryfs;
using namespace cryfs::program_options; using namespace cryfs::program_options;
using std::vector; using std::vector;
using boost::none; using boost::none;
@ -9,7 +11,7 @@ class ProgramOptionsParserTest: public ProgramOptionsTestBase {
public: public:
ProgramOptions parse(std::initializer_list<const char*> options) { ProgramOptions parse(std::initializer_list<const char*> options) {
vector<char*> _options = ProgramOptionsTestBase::options(options); vector<char*> _options = ProgramOptionsTestBase::options(options);
return Parser(_options.size(), _options.data()).parse(); return Parser(_options.size(), _options.data()).parse(CryCiphers::supportedCipherNames());
} }
}; };
@ -29,18 +31,27 @@ TEST_F(ProgramOptionsParserTest, MissingDir) {
TEST_F(ProgramOptionsParserTest, HelpLongOption) { TEST_F(ProgramOptionsParserTest, HelpLongOption) {
EXPECT_DEATH( EXPECT_DEATH(
parse({"./myExecutable", "--help"}), parse({"./myExecutable", "--help"}),
"Usage:" "Usage:"
); );
} }
TEST_F(ProgramOptionsParserTest, HelpShortOption) { TEST_F(ProgramOptionsParserTest, HelpShortOption) {
EXPECT_DEATH( EXPECT_DEATH(
parse({"./myExecutable", "-h"}), parse({"./myExecutable", "-h"}),
"Usage:" "Usage:"
); );
} }
TEST_F(ProgramOptionsParserTest, ShowCiphers) {
EXPECT_EXIT(
parse({"./myExecutable", "--show-ciphers"}),
::testing::ExitedWithCode(0),
"aes-256-gcm"
);
}
TEST_F(ProgramOptionsParserTest, NoSpecialOptions) { TEST_F(ProgramOptionsParserTest, NoSpecialOptions) {
ProgramOptions options = parse({"./myExecutable", "/home/user/baseDir", "/home/user/mountDir"}); ProgramOptions options = parse({"./myExecutable", "/home/user/baseDir", "/home/user/mountDir"});
EXPECT_EQ("/home/user/baseDir", options.baseDir()); EXPECT_EQ("/home/user/baseDir", options.baseDir());
@ -60,6 +71,18 @@ TEST_F(ProgramOptionsParserTest, ConfigfileGiven) {
EXPECT_EQ("/home/user/myconfigfile", options.configFile().value()); EXPECT_EQ("/home/user/myconfigfile", options.configFile().value());
} }
TEST_F(ProgramOptionsParserTest, CipherGiven) {
ProgramOptions options = parse({"./myExecutable", "/home/user/baseDir", "--cipher", "aes-256-gcm", "/home/user/mountDir"});
EXPECT_EQ("aes-256-gcm", options.cipher().value());
}
TEST_F(ProgramOptionsParserTest, InvalidCipher) {
EXPECT_DEATH(
parse({"./myExecutable", "/home/user/baseDir", "--cipher", "invalid-cipher", "/home/user/mountDir"}),
"Invalid cipher: invalid-cipher"
);
}
TEST_F(ProgramOptionsParserTest, FuseOptionGiven) { TEST_F(ProgramOptionsParserTest, FuseOptionGiven) {
ProgramOptions options = parse({"./myExecutable", "/home/user/baseDir", "/home/user/mountDir", "--", "-f"}); ProgramOptions options = parse({"./myExecutable", "/home/user/baseDir", "/home/user/mountDir", "--", "-f"});
EXPECT_EQ("/home/user/baseDir", options.baseDir()); EXPECT_EQ("/home/user/baseDir", options.baseDir());

View File

@ -10,53 +10,63 @@ using std::string;
class ProgramOptionsTest: public ProgramOptionsTestBase {}; class ProgramOptionsTest: public ProgramOptionsTestBase {};
TEST_F(ProgramOptionsTest, BaseDir) { TEST_F(ProgramOptionsTest, BaseDir) {
ProgramOptions testobj("/home/user/mydir", "", none, false, none, options({"./myExecutable"})); ProgramOptions testobj("/home/user/mydir", "", none, false, none, none, options({"./myExecutable"}));
EXPECT_EQ("/home/user/mydir", testobj.baseDir()); EXPECT_EQ("/home/user/mydir", testobj.baseDir());
} }
TEST_F(ProgramOptionsTest, MountDir) { TEST_F(ProgramOptionsTest, MountDir) {
ProgramOptions testobj("", "/home/user/mydir", none, false, none, options({"./myExecutable"})); ProgramOptions testobj("", "/home/user/mydir", none, false, none, none, options({"./myExecutable"}));
EXPECT_EQ("/home/user/mydir", testobj.mountDir()); EXPECT_EQ("/home/user/mydir", testobj.mountDir());
} }
TEST_F(ProgramOptionsTest, ConfigfileNone) { TEST_F(ProgramOptionsTest, ConfigfileNone) {
ProgramOptions testobj("", "", none, true, none, options({"./myExecutable"})); ProgramOptions testobj("", "", none, true, none, none, options({"./myExecutable"}));
EXPECT_EQ(none, testobj.configFile()); EXPECT_EQ(none, testobj.configFile());
} }
TEST_F(ProgramOptionsTest, ConfigfileSome) { TEST_F(ProgramOptionsTest, ConfigfileSome) {
ProgramOptions testobj("", "", string("/home/user/configfile"), true, none, options({"./myExecutable"})); ProgramOptions testobj("", "", string("/home/user/configfile"), true, none, none, options({"./myExecutable"}));
EXPECT_EQ("/home/user/configfile", testobj.configFile().get()); EXPECT_EQ("/home/user/configfile", testobj.configFile().get());
} }
TEST_F(ProgramOptionsTest, ForegroundFalse) { TEST_F(ProgramOptionsTest, ForegroundFalse) {
ProgramOptions testobj("", "", none, false, none, options({"./myExecutable"})); ProgramOptions testobj("", "", none, false, none, none, options({"./myExecutable"}));
EXPECT_FALSE(testobj.foreground()); EXPECT_FALSE(testobj.foreground());
} }
TEST_F(ProgramOptionsTest, ForegroundTrue) { TEST_F(ProgramOptionsTest, ForegroundTrue) {
ProgramOptions testobj("", "", none, true, none, options({"./myExecutable"})); ProgramOptions testobj("", "", none, true, none, none, options({"./myExecutable"}));
EXPECT_TRUE(testobj.foreground()); EXPECT_TRUE(testobj.foreground());
} }
TEST_F(ProgramOptionsTest, LogfileNone) { TEST_F(ProgramOptionsTest, LogfileNone) {
ProgramOptions testobj("", "", none, true, none, options({"./myExecutable"})); ProgramOptions testobj("", "", none, true, none, none, options({"./myExecutable"}));
EXPECT_EQ(none, testobj.logFile()); EXPECT_EQ(none, testobj.logFile());
} }
TEST_F(ProgramOptionsTest, LogfileSome) { TEST_F(ProgramOptionsTest, LogfileSome) {
ProgramOptions testobj("", "", none, true, string("logfile"), options({"./myExecutable"})); ProgramOptions testobj("", "", none, true, string("logfile"), none, options({"./myExecutable"}));
EXPECT_EQ("logfile", testobj.logFile().get()); EXPECT_EQ("logfile", testobj.logFile().get());
} }
TEST_F(ProgramOptionsTest, CipherNone) {
ProgramOptions testobj("", "", none, true, none, none, options({"./myExecutable"}));
EXPECT_EQ(none, testobj.cipher());
}
TEST_F(ProgramOptionsTest, CipherSome) {
ProgramOptions testobj("", "", none, true, none, string("aes-256-gcm"), options({"./myExecutable"}));
EXPECT_EQ("aes-256-gcm", testobj.cipher().get());
}
TEST_F(ProgramOptionsTest, EmptyFuseOptions) { TEST_F(ProgramOptionsTest, EmptyFuseOptions) {
ProgramOptions testobj("/rootDir", "/home/user/mydir", none, false, none, options({"./myExecutable"})); ProgramOptions testobj("/rootDir", "/home/user/mydir", none, false, none, none, options({"./myExecutable"}));
//Fuse should have the mount dir as first parameter //Fuse should have the mount dir as first parameter
EXPECT_VECTOR_EQ({"./myExecutable", "/home/user/mydir"}, testobj.fuseOptions()); EXPECT_VECTOR_EQ({"./myExecutable", "/home/user/mydir"}, testobj.fuseOptions());
} }
TEST_F(ProgramOptionsTest, SomeFuseOptions) { TEST_F(ProgramOptionsTest, SomeFuseOptions) {
ProgramOptions testobj("/rootDir", "/home/user/mydir", none, false, none, options({"./myExecutable", "-f", "--longoption"})); ProgramOptions testobj("/rootDir", "/home/user/mydir", none, false, none, none, options({"./myExecutable", "-f", "--longoption"}));
//Fuse should have the mount dir as first parameter //Fuse should have the mount dir as first parameter
EXPECT_VECTOR_EQ({"./myExecutable", "/home/user/mydir", "-f", "--longoption"}, testobj.fuseOptions()); EXPECT_VECTOR_EQ({"./myExecutable", "/home/user/mydir", "-f", "--longoption"}, testobj.fuseOptions());
} }