diff --git a/ChangeLog.txt b/ChangeLog.txt index b6eeb97f..d9f19e5f 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -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 --------------- * Experimental support for installing CryFS on Mac OS X using homebrew diff --git a/src/cryfs/cli/Cli.cpp b/src/cryfs/cli/Cli.cpp index 7d9a47a8..9dd61d5d 100644 --- a/src/cryfs/cli/Cli.cpp +++ b/src/cryfs/cli/Cli.cpp @@ -70,9 +70,16 @@ using cpputils::dynamic_pointer_move; //TODO Performance difference when setting compiler parameter -maes for scrypt? 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, shared_ptr 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() { cout << "CryFS Version " << version::VERSION_STRING << endl; @@ -110,19 +117,6 @@ namespace cryfs { return true; } - string Cli::_getPassword(function 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 password = _askPasswordFromStdin("Password: "); while (!_checkPassword(password)) { @@ -158,6 +152,15 @@ namespace cryfs { 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) { DontEchoStdinToStdoutRAII _stdin_input_is_hidden_as_long_as_this_is_in_scope; @@ -166,6 +169,11 @@ namespace cryfs { std::getline(cin, result); std::cout << std::endl; + //Remove trailing newline + if (result[result.size()-1] == '\n') { + result.resize(result.size()-1); + } + return result; } @@ -180,10 +188,7 @@ namespace cryfs { CryConfigFile Cli::_loadOrCreateConfig(const ProgramOptions &options) { try { auto configFile = _determineConfigFile(options); - auto config = CryConfigLoader(_console, _keyGenerator, _scryptSettings, - std::bind(&Cli::_getPassword, this, &Cli::_askPasswordForExistingFilesystem), - std::bind(&Cli::_getPassword, this, &Cli::_askPasswordForNewFilesystem), - options.cipher()).loadOrCreate(configFile); + auto config = _loadOrCreateConfigFile(configFile, options.cipher()); if (config == none) { std::cerr << "Could not load config file. Did you enter the correct password?" << std::endl; exit(1); @@ -195,6 +200,20 @@ namespace cryfs { } } + optional Cli::_loadOrCreateConfigFile(const bf::path &configFilePath, const optional &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) { try { auto blockStore = make_unique_ref(options.baseDir()); @@ -269,6 +288,10 @@ namespace cryfs { void Cli::_checkDirAccessible(const bf::path &dir, const std::string &name) { 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?"); if (create) { if (!bf::create_directory(dir)) { diff --git a/src/cryfs/cli/Cli.h b/src/cryfs/cli/Cli.h index 6aed5ec6..5fa00ba9 100644 --- a/src/cryfs/cli/Cli.h +++ b/src/cryfs/cli/Cli.h @@ -21,10 +21,11 @@ namespace cryfs { private: void _runFilesystem(const program_options::ProgramOptions &options); CryConfigFile _loadOrCreateConfig(const program_options::ProgramOptions &options); + boost::optional _loadOrCreateConfigFile(const boost::filesystem::path &configFilePath, const boost::optional &cipher); boost::filesystem::path _determineConfigFile(const program_options::ProgramOptions &options); - std::string _getPassword(std::function askPassword); static std::string _askPasswordForExistingFilesystem(); static std::string _askPasswordForNewFilesystem(); + static std::string _askPasswordNoninteractive(); static std::string _askPasswordFromStdin(const std::string &prompt); static bool _confirmPassword(const std::string &password); static bool _checkPassword(const std::string &password); @@ -39,10 +40,14 @@ namespace cryfs { boost::optional> _createIdleCallback(boost::optional minutes, std::function callback); void _sanityCheckFilesystem(CryDevice *device); + static const std::string CRYFS_FRONTEND_KEY; + static const std::string CRYFS_FRONTEND_NONINTERACTIVE; + cpputils::RandomGenerator &_keyGenerator; cpputils::SCryptSettings _scryptSettings; std::shared_ptr _console; std::shared_ptr _httpClient; + bool _noninteractive; DISALLOW_COPY_AND_ASSIGN(Cli); }; diff --git a/src/cryfs/config/CryConfigConsole.cpp b/src/cryfs/config/CryConfigConsole.cpp index b69b9fdb..0a57c53b 100644 --- a/src/cryfs/config/CryConfigConsole.cpp +++ b/src/cryfs/config/CryConfigConsole.cpp @@ -12,8 +12,8 @@ using std::shared_ptr; namespace cryfs { constexpr const char *CryConfigConsole::DEFAULT_CIPHER; - CryConfigConsole::CryConfigConsole(shared_ptr console) - : _console(std::move(console)), _useDefaultSettings(none) { + CryConfigConsole::CryConfigConsole(shared_ptr console, bool noninteractive) + : _console(std::move(console)), _useDefaultSettings(noninteractive ? optional(true) : none) { } string CryConfigConsole::askCipher() { diff --git a/src/cryfs/config/CryConfigConsole.h b/src/cryfs/config/CryConfigConsole.h index 686516ff..4cee68f6 100644 --- a/src/cryfs/config/CryConfigConsole.h +++ b/src/cryfs/config/CryConfigConsole.h @@ -9,7 +9,7 @@ namespace cryfs { class CryConfigConsole final { public: - CryConfigConsole(std::shared_ptr console); + CryConfigConsole(std::shared_ptr console, bool noninteractive); CryConfigConsole(CryConfigConsole &&rhs) = default; std::string askCipher(); @@ -17,6 +17,7 @@ namespace cryfs { static constexpr const char *DEFAULT_CIPHER = "aes-256-gcm"; private: + bool _checkUseDefaultSettings(); std::string _askCipher() const; diff --git a/src/cryfs/config/CryConfigCreator.cpp b/src/cryfs/config/CryConfigCreator.cpp index 1214f5ac..a85e1a39 100644 --- a/src/cryfs/config/CryConfigCreator.cpp +++ b/src/cryfs/config/CryConfigCreator.cpp @@ -13,8 +13,8 @@ using boost::none; namespace cryfs { - CryConfigCreator::CryConfigCreator(shared_ptr console, RandomGenerator &encryptionKeyGenerator) - :_console(console), _configConsole(console), _encryptionKeyGenerator(encryptionKeyGenerator) { + CryConfigCreator::CryConfigCreator(shared_ptr console, RandomGenerator &encryptionKeyGenerator, bool noninteractive) + :_console(console), _configConsole(console, noninteractive), _encryptionKeyGenerator(encryptionKeyGenerator) { } CryConfig CryConfigCreator::create(const optional &cipherFromCommandLine) { diff --git a/src/cryfs/config/CryConfigCreator.h b/src/cryfs/config/CryConfigCreator.h index 3b714d5e..112f65b0 100644 --- a/src/cryfs/config/CryConfigCreator.h +++ b/src/cryfs/config/CryConfigCreator.h @@ -11,7 +11,7 @@ namespace cryfs { class CryConfigCreator final { public: - CryConfigCreator(std::shared_ptr console, cpputils::RandomGenerator &encryptionKeyGenerator); + CryConfigCreator(std::shared_ptr console, cpputils::RandomGenerator &encryptionKeyGenerator, bool noninteractive); CryConfigCreator(CryConfigCreator &&rhs) = default; CryConfig create(const boost::optional &cipherFromCommandLine); diff --git a/src/cryfs/config/CryConfigLoader.cpp b/src/cryfs/config/CryConfigLoader.cpp index 4f10c972..ae4199f8 100644 --- a/src/cryfs/config/CryConfigLoader.cpp +++ b/src/cryfs/config/CryConfigLoader.cpp @@ -25,8 +25,8 @@ using namespace cpputils::logging; namespace cryfs { -CryConfigLoader::CryConfigLoader(shared_ptr console, RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, function askPasswordForExistingFilesystem, function askPasswordForNewFilesystem, const optional &cipherFromCommandLine) - : _creator(std::move(console), keyGenerator), _scryptSettings(scryptSettings), +CryConfigLoader::CryConfigLoader(shared_ptr console, RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, function askPasswordForExistingFilesystem, function askPasswordForNewFilesystem, const optional &cipherFromCommandLine, bool noninteractive) + : _creator(std::move(console), keyGenerator, noninteractive), _scryptSettings(scryptSettings), _askPasswordForExistingFilesystem(askPasswordForExistingFilesystem), _askPasswordForNewFilesystem(askPasswordForNewFilesystem), _cipherFromCommandLine(cipherFromCommandLine) { } diff --git a/src/cryfs/config/CryConfigLoader.h b/src/cryfs/config/CryConfigLoader.h index d89e3b33..4eb97450 100644 --- a/src/cryfs/config/CryConfigLoader.h +++ b/src/cryfs/config/CryConfigLoader.h @@ -13,7 +13,7 @@ namespace cryfs { class CryConfigLoader final { public: - CryConfigLoader(std::shared_ptr console, cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings &scryptSettings, std::function askPasswordForExistingFilesystem, std::function askPasswordForNewFilesystem, const boost::optional &cipherFromCommandLine); + CryConfigLoader(std::shared_ptr console, cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings &scryptSettings, std::function askPasswordForExistingFilesystem, std::function askPasswordForNewFilesystem, const boost::optional &cipherFromCommandLine, bool noninteractive); CryConfigLoader(CryConfigLoader &&rhs) = default; boost::optional loadOrCreate(const boost::filesystem::path &filename); diff --git a/test/cryfs/cli/CliTest_WrongEnvironment.cpp b/test/cryfs/cli/CliTest_WrongEnvironment.cpp index 63d0e5eb..316dd60d 100644 --- a/test/cryfs/cli/CliTest_WrongEnvironment.cpp +++ b/test/cryfs/cli/CliTest_WrongEnvironment.cpp @@ -4,6 +4,7 @@ namespace bf = boost::filesystem; using ::testing::Values; using ::testing::WithParamInterface; using ::testing::Return; +using ::testing::_; using std::vector; using cpputils::TempFile; @@ -116,10 +117,21 @@ TEST_P(CliTest_WrongEnvironment, MountDirIsBaseDir_BothRelative) { TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist) { _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_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) { if (!GetParam().runningInForeground) {return;} // TODO Make this work also if run in background (see CliTest::EXPECT_RUN_SUCCESS) _basedir.remove(); @@ -163,10 +175,21 @@ TEST_P(CliTest_WrongEnvironment, BaseDir_NoPermission) { TEST_P(CliTest_WrongEnvironment, MountDir_DoesntExist) { _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_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) { if (!GetParam().runningInForeground) {return;} // TODO Make this work also if run in background (see CliTest::EXPECT_RUN_SUCCESS) _mountdir.remove(); diff --git a/test/cryfs/config/CryConfigConsoleTest.cpp b/test/cryfs/config/CryConfigConsoleTest.cpp index b0f693ba..8c471609 100644 --- a/test/cryfs/config/CryConfigConsoleTest.cpp +++ b/test/cryfs/config/CryConfigConsoleTest.cpp @@ -28,10 +28,12 @@ class CryConfigConsoleTest: public ::testing::Test { public: CryConfigConsoleTest() : console(make_shared()), - cryconsole(console) { + cryconsole(console, false), + noninteractiveCryconsole(console, true) { } shared_ptr console; CryConfigConsole cryconsole; + CryConfigConsole noninteractiveCryconsole; }; class CryConfigConsoleTest_Cipher: public CryConfigConsoleTest {}; @@ -52,6 +54,13 @@ TEST_F(CryConfigConsoleTest_Cipher, ChooseDefaultCipher) { 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 { public: string cipherName = GetParam(); diff --git a/test/cryfs/config/CryConfigCreatorTest.cpp b/test/cryfs/config/CryConfigCreatorTest.cpp index d21ecf6c..52aa52c4 100644 --- a/test/cryfs/config/CryConfigCreatorTest.cpp +++ b/test/cryfs/config/CryConfigCreatorTest.cpp @@ -28,10 +28,12 @@ class CryConfigCreatorTest: public ::testing::Test { public: CryConfigCreatorTest() : console(make_shared()), - creator(console, cpputils::Random::PseudoRandom()) { + creator(console, cpputils::Random::PseudoRandom(), false), + noninteractiveCreator(console, cpputils::Random::PseudoRandom(), true) { } shared_ptr console; CryConfigCreator creator; + CryConfigCreator noninteractiveCreator; }; #define EXPECT_ASK_FOR_CIPHER() \ @@ -51,6 +53,11 @@ TEST_F(CryConfigCreatorTest, DoesNotAskForCipherIfSpecified) { 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) { EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseAnyCipher()); CryConfig config = creator.create(none); diff --git a/test/cryfs/config/CryConfigLoaderTest.cpp b/test/cryfs/config/CryConfigLoaderTest.cpp index f3be4bda..def88d5d 100644 --- a/test/cryfs/config/CryConfigLoaderTest.cpp +++ b/test/cryfs/config/CryConfigLoaderTest.cpp @@ -26,41 +26,39 @@ namespace boost { } #include -//TODO Test loading with same/different --cipher argument - class CryConfigLoaderTest: public ::testing::Test, public TestWithMockConsole { public: CryConfigLoaderTest(): file(false) {} - CryConfigLoader loader(const string &password, const optional &cipher = none) { - auto askPassword = [password] {return password;}; - return CryConfigLoader(mockConsole(), cpputils::Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, cipher); + CryConfigLoader loader(const string &password, bool noninteractive, const optional &cipher = none) { + auto askPassword = [password] { return password;}; + return CryConfigLoader(mockConsole(), cpputils::Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, cipher, noninteractive); } - CryConfigFile Create(const string &password = "mypassword", const optional &cipher = none) { + CryConfigFile Create(const string &password = "mypassword", const optional &cipher = none, bool noninteractive = false) { EXPECT_FALSE(file.exists()); - return loader(password, cipher).loadOrCreate(file.path()).value(); + return loader(password, noninteractive, cipher).loadOrCreate(file.path()).value(); } - optional Load(const string &password = "mypassword", const optional &cipher = none) { + optional Load(const string &password = "mypassword", const optional &cipher = none, bool noninteractive = false) { 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") { - auto cfg = loader(password).loadOrCreate(file.path()).value(); + auto cfg = loader(password, false).loadOrCreate(file.path()).value(); cfg.config()->SetRootBlob(rootBlob); cfg.save(); } 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.save(); } 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.save(); } @@ -86,9 +84,19 @@ TEST_F(CryConfigLoaderTest, DoesntLoadIfWrongPassword) { } TEST_F(CryConfigLoaderTest, DoesntLoadIfDifferentCipher) { - Create("mypassword", string("aes-256-gcm")); + Create("mypassword", string("aes-256-gcm"), false); 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 } catch (const std::runtime_error &e) { 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")); } +TEST_F(CryConfigLoaderTest, DoesLoadIfSameCipher_Noninteractive) { + Create("mypassword", string("aes-128-gcm"), true); + Load("mypassword", string("aes-128-gcm"), true); +} + TEST_F(CryConfigLoaderTest, RootBlob_Load) { CreateWithRootBlob("rootblobid"); auto loaded = Load().value(); diff --git a/test/cryfs/filesystem/CryFsTest.cpp b/test/cryfs/filesystem/CryFsTest.cpp index 23903d69..60e633e1 100644 --- a/test/cryfs/filesystem/CryFsTest.cpp +++ b/test/cryfs/filesystem/CryFsTest.cpp @@ -37,7 +37,7 @@ public: CryConfigFile loadOrCreateConfig() { 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 blockStore() { diff --git a/test/cryfs/filesystem/FileSystemTest.cpp b/test/cryfs/filesystem/FileSystemTest.cpp index cf35b22d..9ee176da 100644 --- a/test/cryfs/filesystem/FileSystemTest.cpp +++ b/test/cryfs/filesystem/FileSystemTest.cpp @@ -28,7 +28,7 @@ public: unique_ref createDevice() override { auto blockStore = cpputils::make_unique_ref(); 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(); return make_unique_ref(std::move(config), std::move(blockStore)); }