If CRYFS_FRONTEND=noninteractive is set in the environment, assume we're used by a tool and:

- Don't ask for config. Use default settings for everything that is not specified as command line parameter.
- Don't ask for password confirmation. Password only has to be passed in once to stdin.
This commit is contained in:
Sebastian Messmer 2016-02-21 01:34:21 +01:00
parent 346baf8e9b
commit 9c83d3b2a4
15 changed files with 135 additions and 48 deletions

View File

@ -1,3 +1,9 @@
Version 0.9.3 (unreleased)
---------------
* It's easier for tools and scripts to use CryFS:
If an environment variable CRYFS_FRONTEND=noninteractive is set, we don't ask for options (but take default values for everything that's not specified on command line).
Furthermore, we won't ask for password confirmation when creating a file system but the password only has to be sent once to stdin.
Version 0.9.2 Version 0.9.2
--------------- ---------------
* Experimental support for installing CryFS on Mac OS X using homebrew * Experimental support for installing CryFS on Mac OS X using homebrew

View File

@ -70,9 +70,16 @@ using cpputils::dynamic_pointer_move;
//TODO Performance difference when setting compiler parameter -maes for scrypt? //TODO Performance difference when setting compiler parameter -maes for scrypt?
namespace cryfs { namespace cryfs {
const string Cli::CRYFS_FRONTEND_KEY = "CRYFS_FRONTEND";
const string Cli::CRYFS_FRONTEND_NONINTERACTIVE = "noninteractive";
Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, shared_ptr<Console> console, shared_ptr<HttpClient> httpClient): Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, shared_ptr<Console> console, shared_ptr<HttpClient> httpClient):
_keyGenerator(keyGenerator), _scryptSettings(scryptSettings), _console(console), _httpClient(httpClient) {} _keyGenerator(keyGenerator), _scryptSettings(scryptSettings), _console(console), _httpClient(httpClient), _noninteractive(false) {
char *frontend = std::getenv(CRYFS_FRONTEND_KEY.c_str());
if (frontend != nullptr && frontend == CRYFS_FRONTEND_NONINTERACTIVE) {
_noninteractive = true;
}
}
void Cli::_showVersion() { void Cli::_showVersion() {
cout << "CryFS Version " << version::VERSION_STRING << endl; cout << "CryFS Version " << version::VERSION_STRING << endl;
@ -110,19 +117,6 @@ namespace cryfs {
return true; return true;
} }
string Cli::_getPassword(function<string()> askPassword) {
string password = askPassword();
//Remove trailing newline
if (password[password.size()-1] == '\n') {
password.resize(password.size()-1);
}
//Check that password is valid
if (!_checkPassword(password)) {
throw std::runtime_error("Password invalid.");
}
return password;
}
string Cli::_askPasswordForExistingFilesystem() { string Cli::_askPasswordForExistingFilesystem() {
string password = _askPasswordFromStdin("Password: "); string password = _askPasswordFromStdin("Password: ");
while (!_checkPassword(password)) { while (!_checkPassword(password)) {
@ -158,6 +152,15 @@ namespace cryfs {
return true; return true;
} }
string Cli::_askPasswordNoninteractive() {
//TODO Test
string password = _askPasswordFromStdin("Password: ");
if (!_checkPassword(password)) {
throw std::runtime_error("Invalid password. Password cannot be empty.");
}
return password;
}
string Cli::_askPasswordFromStdin(const string &prompt) { string Cli::_askPasswordFromStdin(const string &prompt) {
DontEchoStdinToStdoutRAII _stdin_input_is_hidden_as_long_as_this_is_in_scope; DontEchoStdinToStdoutRAII _stdin_input_is_hidden_as_long_as_this_is_in_scope;
@ -166,6 +169,11 @@ namespace cryfs {
std::getline(cin, result); std::getline(cin, result);
std::cout << std::endl; std::cout << std::endl;
//Remove trailing newline
if (result[result.size()-1] == '\n') {
result.resize(result.size()-1);
}
return result; return result;
} }
@ -180,10 +188,7 @@ namespace cryfs {
CryConfigFile Cli::_loadOrCreateConfig(const ProgramOptions &options) { CryConfigFile Cli::_loadOrCreateConfig(const ProgramOptions &options) {
try { try {
auto configFile = _determineConfigFile(options); auto configFile = _determineConfigFile(options);
auto config = CryConfigLoader(_console, _keyGenerator, _scryptSettings, auto config = _loadOrCreateConfigFile(configFile, options.cipher());
std::bind(&Cli::_getPassword, this, &Cli::_askPasswordForExistingFilesystem),
std::bind(&Cli::_getPassword, this, &Cli::_askPasswordForNewFilesystem),
options.cipher()).loadOrCreate(configFile);
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;
exit(1); exit(1);
@ -195,6 +200,20 @@ namespace cryfs {
} }
} }
optional<CryConfigFile> Cli::_loadOrCreateConfigFile(const bf::path &configFilePath, const optional<string> &cipher) {
if (_noninteractive) {
return CryConfigLoader(_console, _keyGenerator, _scryptSettings,
&Cli::_askPasswordNoninteractive,
&Cli::_askPasswordNoninteractive,
cipher, _noninteractive).loadOrCreate(configFilePath);
} else {
return CryConfigLoader(_console, _keyGenerator, _scryptSettings,
&Cli::_askPasswordForExistingFilesystem,
&Cli::_askPasswordForNewFilesystem,
cipher, _noninteractive).loadOrCreate(configFilePath);
}
}
void Cli::_runFilesystem(const ProgramOptions &options) { void Cli::_runFilesystem(const ProgramOptions &options) {
try { try {
auto blockStore = make_unique_ref<OnDiskBlockStore>(options.baseDir()); auto blockStore = make_unique_ref<OnDiskBlockStore>(options.baseDir());
@ -269,6 +288,10 @@ namespace cryfs {
void Cli::_checkDirAccessible(const bf::path &dir, const std::string &name) { void Cli::_checkDirAccessible(const bf::path &dir, const std::string &name) {
if (!bf::exists(dir)) { if (!bf::exists(dir)) {
if (_noninteractive) {
//If we use the noninteractive frontend, don't ask whether to create the directory, but just fail.
throw std::runtime_error(name + " not found");
}
bool create = _console->askYesNo("Could not find " + name + ". Do you want to create it?"); bool create = _console->askYesNo("Could not find " + name + ". Do you want to create it?");
if (create) { if (create) {
if (!bf::create_directory(dir)) { if (!bf::create_directory(dir)) {

View File

@ -21,10 +21,11 @@ namespace cryfs {
private: private:
void _runFilesystem(const program_options::ProgramOptions &options); void _runFilesystem(const program_options::ProgramOptions &options);
CryConfigFile _loadOrCreateConfig(const program_options::ProgramOptions &options); CryConfigFile _loadOrCreateConfig(const program_options::ProgramOptions &options);
boost::optional<CryConfigFile> _loadOrCreateConfigFile(const boost::filesystem::path &configFilePath, const boost::optional<std::string> &cipher);
boost::filesystem::path _determineConfigFile(const program_options::ProgramOptions &options); boost::filesystem::path _determineConfigFile(const program_options::ProgramOptions &options);
std::string _getPassword(std::function<std::string()> askPassword);
static std::string _askPasswordForExistingFilesystem(); static std::string _askPasswordForExistingFilesystem();
static std::string _askPasswordForNewFilesystem(); static std::string _askPasswordForNewFilesystem();
static std::string _askPasswordNoninteractive();
static std::string _askPasswordFromStdin(const std::string &prompt); static std::string _askPasswordFromStdin(const std::string &prompt);
static bool _confirmPassword(const std::string &password); static bool _confirmPassword(const std::string &password);
static bool _checkPassword(const std::string &password); static bool _checkPassword(const std::string &password);
@ -39,10 +40,14 @@ namespace cryfs {
boost::optional<cpputils::unique_ref<CallAfterTimeout>> _createIdleCallback(boost::optional<double> minutes, std::function<void()> callback); boost::optional<cpputils::unique_ref<CallAfterTimeout>> _createIdleCallback(boost::optional<double> minutes, std::function<void()> callback);
void _sanityCheckFilesystem(CryDevice *device); void _sanityCheckFilesystem(CryDevice *device);
static const std::string CRYFS_FRONTEND_KEY;
static const std::string CRYFS_FRONTEND_NONINTERACTIVE;
cpputils::RandomGenerator &_keyGenerator; cpputils::RandomGenerator &_keyGenerator;
cpputils::SCryptSettings _scryptSettings; cpputils::SCryptSettings _scryptSettings;
std::shared_ptr<cpputils::Console> _console; std::shared_ptr<cpputils::Console> _console;
std::shared_ptr<cpputils::HttpClient> _httpClient; std::shared_ptr<cpputils::HttpClient> _httpClient;
bool _noninteractive;
DISALLOW_COPY_AND_ASSIGN(Cli); DISALLOW_COPY_AND_ASSIGN(Cli);
}; };

View File

@ -12,8 +12,8 @@ using std::shared_ptr;
namespace cryfs { namespace cryfs {
constexpr const char *CryConfigConsole::DEFAULT_CIPHER; constexpr const char *CryConfigConsole::DEFAULT_CIPHER;
CryConfigConsole::CryConfigConsole(shared_ptr<Console> console) CryConfigConsole::CryConfigConsole(shared_ptr<Console> console, bool noninteractive)
: _console(std::move(console)), _useDefaultSettings(none) { : _console(std::move(console)), _useDefaultSettings(noninteractive ? optional<bool>(true) : none) {
} }
string CryConfigConsole::askCipher() { string CryConfigConsole::askCipher() {

View File

@ -9,7 +9,7 @@
namespace cryfs { namespace cryfs {
class CryConfigConsole final { class CryConfigConsole final {
public: public:
CryConfigConsole(std::shared_ptr<cpputils::Console> console); CryConfigConsole(std::shared_ptr<cpputils::Console> console, bool noninteractive);
CryConfigConsole(CryConfigConsole &&rhs) = default; CryConfigConsole(CryConfigConsole &&rhs) = default;
std::string askCipher(); std::string askCipher();
@ -17,6 +17,7 @@ namespace cryfs {
static constexpr const char *DEFAULT_CIPHER = "aes-256-gcm"; static constexpr const char *DEFAULT_CIPHER = "aes-256-gcm";
private: private:
bool _checkUseDefaultSettings(); bool _checkUseDefaultSettings();
std::string _askCipher() const; std::string _askCipher() const;

View File

@ -13,8 +13,8 @@ using boost::none;
namespace cryfs { namespace cryfs {
CryConfigCreator::CryConfigCreator(shared_ptr<Console> console, RandomGenerator &encryptionKeyGenerator) CryConfigCreator::CryConfigCreator(shared_ptr<Console> console, RandomGenerator &encryptionKeyGenerator, bool noninteractive)
:_console(console), _configConsole(console), _encryptionKeyGenerator(encryptionKeyGenerator) { :_console(console), _configConsole(console, noninteractive), _encryptionKeyGenerator(encryptionKeyGenerator) {
} }
CryConfig CryConfigCreator::create(const optional<string> &cipherFromCommandLine) { CryConfig CryConfigCreator::create(const optional<string> &cipherFromCommandLine) {

View File

@ -11,7 +11,7 @@
namespace cryfs { namespace cryfs {
class CryConfigCreator final { class CryConfigCreator final {
public: public:
CryConfigCreator(std::shared_ptr<cpputils::Console> console, cpputils::RandomGenerator &encryptionKeyGenerator); CryConfigCreator(std::shared_ptr<cpputils::Console> console, cpputils::RandomGenerator &encryptionKeyGenerator, bool noninteractive);
CryConfigCreator(CryConfigCreator &&rhs) = default; CryConfigCreator(CryConfigCreator &&rhs) = default;
CryConfig create(const boost::optional<std::string> &cipherFromCommandLine); CryConfig create(const boost::optional<std::string> &cipherFromCommandLine);

View File

@ -25,8 +25,8 @@ using namespace cpputils::logging;
namespace cryfs { namespace cryfs {
CryConfigLoader::CryConfigLoader(shared_ptr<Console> console, RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, function<string()> askPasswordForExistingFilesystem, function<string()> askPasswordForNewFilesystem, const optional<string> &cipherFromCommandLine) CryConfigLoader::CryConfigLoader(shared_ptr<Console> console, RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, function<string()> askPasswordForExistingFilesystem, function<string()> askPasswordForNewFilesystem, const optional<string> &cipherFromCommandLine, bool noninteractive)
: _creator(std::move(console), keyGenerator), _scryptSettings(scryptSettings), : _creator(std::move(console), keyGenerator, noninteractive), _scryptSettings(scryptSettings),
_askPasswordForExistingFilesystem(askPasswordForExistingFilesystem), _askPasswordForNewFilesystem(askPasswordForNewFilesystem), _askPasswordForExistingFilesystem(askPasswordForExistingFilesystem), _askPasswordForNewFilesystem(askPasswordForNewFilesystem),
_cipherFromCommandLine(cipherFromCommandLine) { _cipherFromCommandLine(cipherFromCommandLine) {
} }

View File

@ -13,7 +13,7 @@ namespace cryfs {
class CryConfigLoader final { class CryConfigLoader final {
public: public:
CryConfigLoader(std::shared_ptr<cpputils::Console> console, cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings &scryptSettings, std::function<std::string()> askPasswordForExistingFilesystem, std::function<std::string()> askPasswordForNewFilesystem, const boost::optional<std::string> &cipherFromCommandLine); CryConfigLoader(std::shared_ptr<cpputils::Console> console, cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings &scryptSettings, std::function<std::string()> askPasswordForExistingFilesystem, std::function<std::string()> askPasswordForNewFilesystem, const boost::optional<std::string> &cipherFromCommandLine, bool noninteractive);
CryConfigLoader(CryConfigLoader &&rhs) = default; CryConfigLoader(CryConfigLoader &&rhs) = default;
boost::optional<CryConfigFile> loadOrCreate(const boost::filesystem::path &filename); boost::optional<CryConfigFile> loadOrCreate(const boost::filesystem::path &filename);

View File

@ -4,6 +4,7 @@ namespace bf = boost::filesystem;
using ::testing::Values; using ::testing::Values;
using ::testing::WithParamInterface; using ::testing::WithParamInterface;
using ::testing::Return; using ::testing::Return;
using ::testing::_;
using std::vector; using std::vector;
using cpputils::TempFile; using cpputils::TempFile;
@ -116,10 +117,21 @@ TEST_P(CliTest_WrongEnvironment, MountDirIsBaseDir_BothRelative) {
TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist) { TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist) {
_basedir.remove(); _basedir.remove();
ON_CALL(*console, askYesNo("Could not find base directory. Do you want to create it?")).WillByDefault(Return(false)); // ON_CALL and not EXPECT_CALL, because this is a death test (i.e. it is forked) and gmock EXPECT_CALL in fork children don't report to parents. // ON_CALL and not EXPECT_CALL, because this is a death test (i.e. it is forked) and gmock EXPECT_CALL in fork children don't report to parents.
ON_CALL(*console, askYesNo("Could not find base directory. Do you want to create it?")).WillByDefault(Return(false));
Test_Run_Error("Error: base directory not found"); Test_Run_Error("Error: base directory not found");
} }
TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist_Noninteractive) {
_basedir.remove();
// We can't set an EXPECT_CALL().Times(0), because this is a death test (i.e. it is forked) and gmock EXPECT_CALL in fork children don't report to parents.
// So we set a default answer that shouldn't crash and check it's not called by checking that it crashes.
ON_CALL(*console, askYesNo("Could not find base directory. Do you want to create it?")).WillByDefault(Return(true));
::setenv("CRYFS_FRONTEND", "noninteractive", 1);
Test_Run_Error("Error: base directory not found");
::unsetenv("CRYFS_FRONTEND");
}
TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist_Create) { TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist_Create) {
if (!GetParam().runningInForeground) {return;} // TODO Make this work also if run in background (see CliTest::EXPECT_RUN_SUCCESS) if (!GetParam().runningInForeground) {return;} // TODO Make this work also if run in background (see CliTest::EXPECT_RUN_SUCCESS)
_basedir.remove(); _basedir.remove();
@ -163,10 +175,21 @@ TEST_P(CliTest_WrongEnvironment, BaseDir_NoPermission) {
TEST_P(CliTest_WrongEnvironment, MountDir_DoesntExist) { TEST_P(CliTest_WrongEnvironment, MountDir_DoesntExist) {
_mountdir.remove(); _mountdir.remove();
ON_CALL(*console, askYesNo("Could not find mount directory. Do you want to create it?")).WillByDefault(Return(false)); // ON_CALL and not EXPECT_CALL, because this is a death test (i.e. it is forked) and gmock EXPECT_CALL in fork children don't report to parents. // ON_CALL and not EXPECT_CALL, because this is a death test (i.e. it is forked) and gmock EXPECT_CALL in fork children don't report to parents.
ON_CALL(*console, askYesNo("Could not find mount directory. Do you want to create it?")).WillByDefault(Return(false));
Test_Run_Error("Error: mount directory not found"); Test_Run_Error("Error: mount directory not found");
} }
TEST_P(CliTest_WrongEnvironment, MountDir_DoesntExist_Noninteractive) {
_mountdir.remove();
// We can't set an EXPECT_CALL().Times(0), because this is a death test (i.e. it is forked) and gmock EXPECT_CALL in fork children don't report to parents.
// So we set a default answer that shouldn't crash and check it's not called by checking that it crashes.
ON_CALL(*console, askYesNo("Could not find base directory. Do you want to create it?")).WillByDefault(Return(true));
::setenv("CRYFS_FRONTEND", "noninteractive", 1);
Test_Run_Error("Error: mount directory not found");
::unsetenv("CRYFS_FRONTEND");
}
TEST_P(CliTest_WrongEnvironment, MountDir_DoesntExist_Create) { TEST_P(CliTest_WrongEnvironment, MountDir_DoesntExist_Create) {
if (!GetParam().runningInForeground) {return;} // TODO Make this work also if run in background (see CliTest::EXPECT_RUN_SUCCESS) if (!GetParam().runningInForeground) {return;} // TODO Make this work also if run in background (see CliTest::EXPECT_RUN_SUCCESS)
_mountdir.remove(); _mountdir.remove();

View File

@ -28,10 +28,12 @@ class CryConfigConsoleTest: public ::testing::Test {
public: public:
CryConfigConsoleTest() CryConfigConsoleTest()
: console(make_shared<MockConsole>()), : console(make_shared<MockConsole>()),
cryconsole(console) { cryconsole(console, false),
noninteractiveCryconsole(console, true) {
} }
shared_ptr<MockConsole> console; shared_ptr<MockConsole> console;
CryConfigConsole cryconsole; CryConfigConsole cryconsole;
CryConfigConsole noninteractiveCryconsole;
}; };
class CryConfigConsoleTest_Cipher: public CryConfigConsoleTest {}; class CryConfigConsoleTest_Cipher: public CryConfigConsoleTest {};
@ -52,6 +54,13 @@ TEST_F(CryConfigConsoleTest_Cipher, ChooseDefaultCipher) {
EXPECT_EQ(CryConfigConsole::DEFAULT_CIPHER, cipher); EXPECT_EQ(CryConfigConsole::DEFAULT_CIPHER, cipher);
} }
TEST_F(CryConfigConsoleTest_Cipher, ChooseDefaultCipherWhenNoninteractiveEnvironment) {
EXPECT_CALL(*console, askYesNo(HasSubstr("default"))).Times(0);
EXPECT_CALL(*console, ask(HasSubstr("block cipher"), _)).Times(0);
string cipher = noninteractiveCryconsole.askCipher();
EXPECT_EQ(CryConfigConsole::DEFAULT_CIPHER, cipher);
}
class CryConfigConsoleTest_Cipher_Choose: public CryConfigConsoleTest_Cipher, public ::testing::WithParamInterface<string> { class CryConfigConsoleTest_Cipher_Choose: public CryConfigConsoleTest_Cipher, public ::testing::WithParamInterface<string> {
public: public:
string cipherName = GetParam(); string cipherName = GetParam();

View File

@ -28,10 +28,12 @@ class CryConfigCreatorTest: public ::testing::Test {
public: public:
CryConfigCreatorTest() CryConfigCreatorTest()
: console(make_shared<MockConsole>()), : console(make_shared<MockConsole>()),
creator(console, cpputils::Random::PseudoRandom()) { creator(console, cpputils::Random::PseudoRandom(), false),
noninteractiveCreator(console, cpputils::Random::PseudoRandom(), true) {
} }
shared_ptr<MockConsole> console; shared_ptr<MockConsole> console;
CryConfigCreator creator; CryConfigCreator creator;
CryConfigCreator noninteractiveCreator;
}; };
#define EXPECT_ASK_FOR_CIPHER() \ #define EXPECT_ASK_FOR_CIPHER() \
@ -51,6 +53,11 @@ TEST_F(CryConfigCreatorTest, DoesNotAskForCipherIfSpecified) {
CryConfig config = creator.create(string("aes-256-gcm")); CryConfig config = creator.create(string("aes-256-gcm"));
} }
TEST_F(CryConfigCreatorTest, DoesNotAskForCipherIfNoninteractive) {
EXPECT_DOES_NOT_ASK_FOR_CIPHER();
CryConfig config = noninteractiveCreator.create(none);
}
TEST_F(CryConfigCreatorTest, ChoosesEmptyRootBlobId) { TEST_F(CryConfigCreatorTest, ChoosesEmptyRootBlobId) {
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseAnyCipher()); EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseAnyCipher());
CryConfig config = creator.create(none); CryConfig config = creator.create(none);

View File

@ -26,41 +26,39 @@ namespace boost {
} }
#include <boost/optional/optional_io.hpp> #include <boost/optional/optional_io.hpp>
//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, const optional<string> &cipher = none) { CryConfigLoader loader(const string &password, bool noninteractive, const optional<string> &cipher = none) {
auto askPassword = [password] {return password;}; auto askPassword = [password] { return password;};
return CryConfigLoader(mockConsole(), cpputils::Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, cipher); return CryConfigLoader(mockConsole(), cpputils::Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, cipher, noninteractive);
} }
CryConfigFile Create(const string &password = "mypassword", const optional<string> &cipher = none) { CryConfigFile Create(const string &password = "mypassword", const optional<string> &cipher = none, bool noninteractive = false) {
EXPECT_FALSE(file.exists()); EXPECT_FALSE(file.exists());
return loader(password, cipher).loadOrCreate(file.path()).value(); return loader(password, noninteractive, cipher).loadOrCreate(file.path()).value();
} }
optional<CryConfigFile> Load(const string &password = "mypassword", const optional<string> &cipher = none) { optional<CryConfigFile> Load(const string &password = "mypassword", const optional<string> &cipher = none, bool noninteractive = false) {
EXPECT_TRUE(file.exists()); EXPECT_TRUE(file.exists());
return loader(password, cipher).loadOrCreate(file.path()); return loader(password, noninteractive, cipher).loadOrCreate(file.path());
} }
void CreateWithRootBlob(const string &rootBlob, const string &password = "mypassword") { void CreateWithRootBlob(const string &rootBlob, const string &password = "mypassword") {
auto cfg = loader(password).loadOrCreate(file.path()).value(); auto cfg = loader(password, false).loadOrCreate(file.path()).value();
cfg.config()->SetRootBlob(rootBlob); cfg.config()->SetRootBlob(rootBlob);
cfg.save(); cfg.save();
} }
void CreateWithCipher(const string &cipher, const string &password = "mypassword") { void CreateWithCipher(const string &cipher, const string &password = "mypassword") {
auto cfg = loader(password).loadOrCreate(file.path()).value(); auto cfg = loader(password, false).loadOrCreate(file.path()).value();
cfg.config()->SetCipher(cipher); cfg.config()->SetCipher(cipher);
cfg.save(); cfg.save();
} }
void CreateWithEncryptionKey(const string &encKey, const string &password = "mypassword") { void CreateWithEncryptionKey(const string &encKey, const string &password = "mypassword") {
auto cfg = loader(password).loadOrCreate(file.path()).value(); auto cfg = loader(password, false).loadOrCreate(file.path()).value();
cfg.config()->SetEncryptionKey(encKey); cfg.config()->SetEncryptionKey(encKey);
cfg.save(); cfg.save();
} }
@ -86,9 +84,19 @@ TEST_F(CryConfigLoaderTest, DoesntLoadIfWrongPassword) {
} }
TEST_F(CryConfigLoaderTest, DoesntLoadIfDifferentCipher) { TEST_F(CryConfigLoaderTest, DoesntLoadIfDifferentCipher) {
Create("mypassword", string("aes-256-gcm")); Create("mypassword", string("aes-256-gcm"), false);
try { try {
Load("mypassword", string("aes-256-cfb")); Load("mypassword", string("aes-256-cfb"), false);
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, DoesntLoadIfDifferentCipher_Noninteractive) {
Create("mypassword", string("aes-256-gcm"), true);
try {
Load("mypassword", string("aes-256-cfb"), true);
EXPECT_TRUE(false); // Should throw exception EXPECT_TRUE(false); // Should throw exception
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
EXPECT_EQ(string("Filesystem uses aes-256-gcm cipher and not aes-256-cfb as specified."), e.what()); EXPECT_EQ(string("Filesystem uses aes-256-gcm cipher and not aes-256-cfb as specified."), e.what());
@ -100,6 +108,11 @@ TEST_F(CryConfigLoaderTest, DoesLoadIfSameCipher) {
Load("mypassword", string("aes-256-gcm")); Load("mypassword", string("aes-256-gcm"));
} }
TEST_F(CryConfigLoaderTest, DoesLoadIfSameCipher_Noninteractive) {
Create("mypassword", string("aes-128-gcm"), true);
Load("mypassword", string("aes-128-gcm"), true);
}
TEST_F(CryConfigLoaderTest, RootBlob_Load) { TEST_F(CryConfigLoaderTest, RootBlob_Load) {
CreateWithRootBlob("rootblobid"); CreateWithRootBlob("rootblobid");
auto loaded = Load().value(); auto loaded = Load().value();

View File

@ -37,7 +37,7 @@ public:
CryConfigFile loadOrCreateConfig() { CryConfigFile loadOrCreateConfig() {
auto askPassword = [] {return "mypassword";}; auto askPassword = [] {return "mypassword";};
return CryConfigLoader(mockConsole(), Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, none).loadOrCreate(config.path()).value(); return CryConfigLoader(mockConsole(), Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, none, true).loadOrCreate(config.path()).value();
} }
unique_ref<OnDiskBlockStore> blockStore() { unique_ref<OnDiskBlockStore> blockStore() {

View File

@ -28,7 +28,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 askPassword = [] {return "mypassword";}; auto askPassword = [] {return "mypassword";};
auto config = CryConfigLoader(mockConsole(), Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, none) auto config = CryConfigLoader(mockConsole(), Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, none, true)
.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));
} }