Allow opening volumes with password hash

This commit is contained in:
Matéo Duparc 2022-06-29 13:27:33 +02:00
parent 356cf8a160
commit cf822d6a5b
Signed by: hardcoresushi
GPG Key ID: AFE384344A45E13A
10 changed files with 137 additions and 31 deletions

10
src/cpp-utils/SizedData.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#ifndef CPPUTILS_SIZEDDATA_H
#define CPPUTILS_SIZEDDATA_H
struct SizedData {
unsigned char* data;
size_t size;
};
#endif

View File

@ -11,6 +11,7 @@
#include <cpp-utils/io/DontEchoStdinToStdoutRAII.h>
#include <cryfs/impl/filesystem/CryDevice.h>
#include <cryfs/impl/config/CryConfigLoader.h>
#include <cryfs/impl/config/CryDirectKeyProvider.h>
#include <cryfs/impl/config/CryPresetPasswordBasedKeyProvider.h>
#include <boost/filesystem.hpp>
@ -89,9 +90,9 @@ namespace cryfs_cli {
basedirMetadata.save();
}
CryConfigLoader::ConfigLoadResult Cli::_loadOrCreateConfig(const ProgramOptions &options, const LocalStateDir& localStateDir, unique_ptr<string> password) {
CryConfigLoader::ConfigLoadResult Cli::_loadOrCreateConfig(const ProgramOptions &options, const LocalStateDir& localStateDir, Credentials credentials) {
auto configFile = _determineConfigFile(options);
auto config = _loadOrCreateConfigFile(std::move(configFile), localStateDir, std::move(password), options.cipher(), options.blocksizeBytes(), options.allowFilesystemUpgrade(), options.missingBlockIsIntegrityViolation(), options.allowReplacedFilesystem());
auto config = _loadOrCreateConfigFile(std::move(configFile), localStateDir, credentials, options.cipher(), options.blocksizeBytes(), options.allowFilesystemUpgrade(), options.missingBlockIsIntegrityViolation(), options.allowReplacedFilesystem());
if (config.is_left()) {
switch(config.left()) {
case CryConfigFile::LoadError::DecryptionFailed:
@ -104,24 +105,30 @@ namespace cryfs_cli {
return std::move(config.right());
}
either<CryConfigFile::LoadError, CryConfigLoader::ConfigLoadResult> Cli::_loadOrCreateConfigFile(bf::path configFilePath, LocalStateDir localStateDir, unique_ptr<string> password, const optional<string> &cipher, const optional<uint32_t> &blocksizeBytes, bool allowFilesystemUpgrade, const optional<bool> &missingBlockIsIntegrityViolation, bool allowReplacedFilesystem) {
// TODO Instead of passing in _askPasswordXXX functions to KeyProvider, only pass in console and move logic to the key provider,
// for example by having a separate CryPasswordBasedKeyProvider / CryNoninteractivePasswordBasedKeyProvider.
auto keyProvider = make_unique_ref<CryPresetPasswordBasedKeyProvider>(
*password.get(),
make_unique_ref<SCrypt>(_scryptSettings)
);
return CryConfigLoader(_keyGenerator, std::move(keyProvider), std::move(localStateDir), cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(std::move(configFilePath), allowFilesystemUpgrade, allowReplacedFilesystem);
unique_ref<CryKeyProvider> Cli::_createKeyProvider(Credentials credentials) {
if (credentials.password == none) {
return make_unique_ref<CryDirectKeyProvider>(credentials.givenHash);
} else {
return make_unique_ref<CryPresetPasswordBasedKeyProvider>(
*credentials.password,
make_unique_ref<SCrypt>(_scryptSettings),
credentials.returnedHash
);
}
}
fspp::fuse::Fuse* Cli::initFilesystem(const ProgramOptions &options, unique_ptr<string> password) {
either<CryConfigFile::LoadError, CryConfigLoader::ConfigLoadResult> Cli::_loadOrCreateConfigFile(bf::path configFilePath, LocalStateDir localStateDir, Credentials credentials, const optional<string> &cipher, const optional<uint32_t> &blocksizeBytes, bool allowFilesystemUpgrade, const optional<bool> &missingBlockIsIntegrityViolation, bool allowReplacedFilesystem) {
return CryConfigLoader(_keyGenerator, _createKeyProvider(credentials), std::move(localStateDir), cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(std::move(configFilePath), allowFilesystemUpgrade, allowReplacedFilesystem);
}
fspp::fuse::Fuse* Cli::initFilesystem(const ProgramOptions &options, Credentials credentials) {
cpputils::showBacktraceOnCrash();
cpputils::set_thread_name("cryfs");
try {
_sanityChecks(options);
LocalStateDir localStateDir(options.localStateDir());
auto blockStore = make_unique_ref<OnDiskBlockStore2>(options.baseDir());
auto config = _loadOrCreateConfig(options, localStateDir, std::move(password));
auto config = _loadOrCreateConfig(options, localStateDir, credentials);
fspp::fuse::Fuse* fuse = nullptr;
auto onIntegrityViolation = [&fuse] () {

View File

@ -5,11 +5,13 @@
#include <fspp/fuse/Fuse.h>
#include "program_options/ProgramOptions.h"
#include <cryfs/impl/config/CryConfigFile.h>
#include <cryfs/impl/config/CryKeyProvider.h>
#include <boost/filesystem/path.hpp>
#include <cpp-utils/tempfile/TempFile.h>
#include <cpp-utils/io/Console.h>
#include <cpp-utils/random/RandomGenerator.h>
#include <cpp-utils/network/HttpClient.h>
#include <cpp-utils/SizedData.h>
#include <cryfs/impl/filesystem/CryDevice.h>
#include "CallAfterTimeout.h"
#include <cryfs/impl/config/CryConfigLoader.h>
@ -18,16 +20,20 @@
namespace cryfs_cli {
class Cli final {
public:
struct Credentials {
boost::optional<string> password;
SizedData givenHash;
SizedData* returnedHash;
};
Cli(cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings& scryptSettings);
fspp::fuse::Fuse* initFilesystem(const program_options::ProgramOptions &options, std::unique_ptr<string> password);
fspp::fuse::Fuse* initFilesystem(const program_options::ProgramOptions &options, Credentials credentials);
private:
void _checkForUpdates(cpputils::unique_ref<cpputils::HttpClient> httpClient);
cryfs::CryConfigLoader::ConfigLoadResult _loadOrCreateConfig(const program_options::ProgramOptions &options, const cryfs::LocalStateDir& localStateDir, std::unique_ptr<string> password);
cryfs::CryConfigLoader::ConfigLoadResult _loadOrCreateConfig(const program_options::ProgramOptions &options, const cryfs::LocalStateDir& localStateDir, Credentials credentials);
void _checkConfigIntegrity(const boost::filesystem::path& basedir, const cryfs::LocalStateDir& localStateDir, const cryfs::CryConfigFile& config, bool allowReplacedFilesystem);
cpputils::either<cryfs::CryConfigFile::LoadError, cryfs::CryConfigLoader::ConfigLoadResult> _loadOrCreateConfigFile(boost::filesystem::path configFilePath, cryfs::LocalStateDir localStateDir, std::unique_ptr<string> password, const boost::optional<std::string> &cipher, const boost::optional<uint32_t> &blocksizeBytes, bool allowFilesystemUpgrade, const boost::optional<bool> &missingBlockIsIntegrityViolation, bool allowReplacedFilesystem);
cpputils::unique_ref<cryfs::CryKeyProvider> _createKeyProvider(Credentials credentials);
cpputils::either<cryfs::CryConfigFile::LoadError, cryfs::CryConfigLoader::ConfigLoadResult> _loadOrCreateConfigFile(boost::filesystem::path configFilePath, cryfs::LocalStateDir localStateDir, Credentials credentials, const boost::optional<std::string> &cipher, const boost::optional<uint32_t> &blocksizeBytes, bool allowFilesystemUpgrade, const boost::optional<bool> &missingBlockIsIntegrityViolation, bool allowReplacedFilesystem);
boost::filesystem::path _determineConfigFile(const program_options::ProgramOptions &options);
void _initLogfile();
void _sanityChecks(const program_options::ProgramOptions &options);
void _checkDirAccessible(const boost::filesystem::path &dir, const std::string &name, bool createMissingDir, cryfs::ErrorCode errorCode);
void _sanityCheckFilesystem(cryfs::CryDevice *device);

View File

@ -19,6 +19,7 @@ set(LIB_SOURCES
impl/config/CryKeyProvider.cpp
impl/config/CryPasswordBasedKeyProvider.cpp
impl/config/CryPresetPasswordBasedKeyProvider.cpp
impl/config/CryDirectKeyProvider.cpp
impl/filesystem/CryOpenFile.cpp
impl/filesystem/fsblobstore/utils/DirEntry.cpp
impl/filesystem/fsblobstore/utils/DirEntryList.cpp

View File

@ -0,0 +1,18 @@
#include "CryDirectKeyProvider.h"
namespace cryfs {
CryDirectKeyProvider::CryDirectKeyProvider(SizedData encryptionKey) : _encryptionKey(encryptionKey) {}
cpputils::EncryptionKey CryDirectKeyProvider::requestKeyForExistingFilesystem(size_t keySize, const cpputils::Data& kdfParameters) {
ASSERT(_encryptionKey.size == keySize, "CryDirectKeyProvider: Invalid key size requested");
cpputils::EncryptionKey encryptionKey = cpputils::EncryptionKey::Null(_encryptionKey.size);
memcpy(encryptionKey.data(), _encryptionKey.data, _encryptionKey.size);
return encryptionKey;
}
CryKeyProvider::KeyResult CryDirectKeyProvider::requestKeyForNewFilesystem(size_t keySize) {
throw std::logic_error("CryDirectKeyProvider can't be used for new filesystems");
}
}

View File

@ -0,0 +1,25 @@
#pragma once
#ifndef CRYFS_CRYDIRECTKEYPROVIDER_H
#define CRYFS_CRYDIRECTKEYPROVIDER_H
#include "CryKeyProvider.h"
#include <cpp-utils/SizedData.h>
namespace cryfs {
class CryDirectKeyProvider final : public CryKeyProvider {
public:
explicit CryDirectKeyProvider(SizedData encryptionKey);
cpputils::EncryptionKey requestKeyForExistingFilesystem(size_t keySize, const cpputils::Data& kdfParameters) override;
KeyResult requestKeyForNewFilesystem(size_t keySize) override;
private:
SizedData _encryptionKey;
DISALLOW_COPY_AND_ASSIGN(CryDirectKeyProvider);
};
}
#endif

View File

@ -8,15 +8,24 @@ using cpputils::Data;
namespace cryfs {
CryPresetPasswordBasedKeyProvider::CryPresetPasswordBasedKeyProvider(std::string password, unique_ref<PasswordBasedKDF> kdf)
: _password(std::move(password)), _kdf(std::move(kdf)) {}
CryPresetPasswordBasedKeyProvider::CryPresetPasswordBasedKeyProvider(std::string password, unique_ref<PasswordBasedKDF> kdf, SizedData* returnedHash)
: _password(std::move(password)), _kdf(std::move(kdf)), _returnedHash(returnedHash) {}
EncryptionKey CryPresetPasswordBasedKeyProvider::requestKeyForExistingFilesystem(size_t keySize, const Data& kdfParameters) {
return _kdf->deriveExistingKey(keySize, _password, kdfParameters);
EncryptionKey encryptionKey = _kdf->deriveExistingKey(keySize, _password, kdfParameters);
if (_returnedHash != nullptr) {
_returnedHash->data = static_cast<unsigned char*>(encryptionKey.data());
_returnedHash->size = encryptionKey.binaryLength();
}
return encryptionKey;
}
CryPresetPasswordBasedKeyProvider::KeyResult CryPresetPasswordBasedKeyProvider::requestKeyForNewFilesystem(size_t keySize) {
auto keyResult = _kdf->deriveNewKey(keySize, _password);
if (_returnedHash != nullptr) {
_returnedHash->data = static_cast<unsigned char*>(keyResult.key.data());
_returnedHash->size = keyResult.key.binaryLength();
}
return {std::move(keyResult.key), std::move(keyResult.kdfParameters)};
}

View File

@ -5,12 +5,17 @@
#include "CryKeyProvider.h"
#include <functional>
#include <cpp-utils/crypto/kdf/PasswordBasedKDF.h>
#include <cpp-utils/SizedData.h>
namespace cryfs {
class CryPresetPasswordBasedKeyProvider final : public CryKeyProvider {
public:
explicit CryPresetPasswordBasedKeyProvider(std::string password, cpputils::unique_ref<cpputils::PasswordBasedKDF> kdf);
explicit CryPresetPasswordBasedKeyProvider(
std::string password,
cpputils::unique_ref<cpputils::PasswordBasedKDF> kdf,
SizedData* returnedHash
);
cpputils::EncryptionKey requestKeyForExistingFilesystem(size_t keySize, const cpputils::Data& kdfParameters) override;
KeyResult requestKeyForNewFilesystem(size_t keySize) override;
@ -18,6 +23,7 @@ namespace cryfs {
private:
std::string _password;
cpputils::unique_ref<cpputils::PasswordBasedKDF> _kdf;
SizedData* _returnedHash;
DISALLOW_COPY_AND_ASSIGN(CryPresetPasswordBasedKeyProvider);
};

View File

@ -1,6 +1,8 @@
#include <jni.h>
jlong cryfs_init(JNIEnv* env, jstring jbaseDir, jstring jlocalSateDir, jbyteArray jpassword, jboolean createBaseDir, jstring jcipher);
jlong cryfs_init(JNIEnv *env, jstring jbaseDir, jstring jlocalSateDir, jbyteArray jpassword,
jbyteArray jgivenHash, jobject returnedHash, jboolean createBaseDir,
jstring jcipher);
jlong cryfs_create(JNIEnv* env, jlong fusePtr, jstring jpath, mode_t mode);
jlong cryfs_open(JNIEnv* env, jlong fusePtr, jstring jpath, jint flags);
jint cryfs_read(JNIEnv* env, jlong fusePtr, jlong fileHandle, jbyteArray jbuffer, jlong offset);

View File

@ -2,8 +2,6 @@
#include <cryfs-cli/Cli.h>
#include <fspp/fuse/Fuse.h>
using std::unique_ptr;
using std::make_unique;
using boost::none;
using cpputils::Random;
using cpputils::SCrypt;
@ -13,7 +11,10 @@ using fspp::fuse::Fuse;
std::set<jlong> validFusePtrs;
extern "C" jlong cryfs_init(JNIEnv* env, jstring jbaseDir, jstring jlocalStateDir, jbyteArray jpassword, jboolean createBaseDir, jstring jcipher) {
extern "C" jlong
cryfs_init(JNIEnv *env, jstring jbaseDir, jstring jlocalStateDir, jbyteArray jpassword,
jbyteArray jgivenHash, jobject jreturnedHash, jboolean createBaseDir,
jstring jcipher) {
const char* baseDir = env->GetStringUTFChars(jbaseDir, NULL);
const char* localStateDir = env->GetStringUTFChars(jlocalStateDir, NULL);
boost::optional<string> cipher = none;
@ -22,19 +23,40 @@ extern "C" jlong cryfs_init(JNIEnv* env, jstring jbaseDir, jstring jlocalStateDi
cipher = boost::optional<string>(cipherName);
env->ReleaseStringUTFChars(jcipher, cipherName);
}
auto &keyGenerator = Random::OSRandom();
auto &keyGenerator = Random::OSRandom();
ProgramOptions options = ProgramOptions(baseDir, none, localStateDir, false, false, createBaseDir, cipher, none, false, none);
char* password = reinterpret_cast<char*>(env->GetByteArrayElements(jpassword, NULL));
Fuse* fuse = Cli(keyGenerator, SCrypt::DefaultSettings).initFilesystem(options, make_unique<string>(password));
env->ReleaseByteArrayElements(jpassword, reinterpret_cast<jbyte*>(password), 0);
env->ReleaseStringUTFChars(jbaseDir, baseDir);
env->ReleaseStringUTFChars(jlocalStateDir, localStateDir);
struct SizedData returnedHash;
struct Cli::Credentials credentials;
credentials.returnedHash = nullptr;
if (jpassword == NULL) {
credentials.password = none;
credentials.givenHash.data = reinterpret_cast<unsigned char*>(env->GetByteArrayElements(jgivenHash, NULL));
credentials.givenHash.size = env->GetArrayLength(jgivenHash);
} else {
jbyte* password = env->GetByteArrayElements(jpassword, NULL);
credentials.password = string(reinterpret_cast<const char*>(password));
env->ReleaseByteArrayElements(jpassword, password, 0);
if (jreturnedHash != NULL) {
credentials.returnedHash = &returnedHash;
}
}
Fuse* fuse = Cli(keyGenerator, SCrypt::DefaultSettings).initFilesystem(options, credentials);
if (jpassword == NULL) {
env->ReleaseByteArrayElements(jgivenHash, reinterpret_cast<jbyte*>(credentials.givenHash.data), 0);
}
jlong fusePtr = reinterpret_cast<jlong>(fuse);
if (fusePtr != 0) {
validFusePtrs.insert(fusePtr);
if (credentials.returnedHash != nullptr) {
jfieldID value = env->GetFieldID(env->GetObjectClass(jreturnedHash), "value", "Ljava/lang/Object;");
jbyteArray jpasswordHash = env->NewByteArray(returnedHash.size);
env->SetByteArrayRegion(jpasswordHash, 0, returnedHash.size, reinterpret_cast<const jbyte*>(returnedHash.data));
env->SetObjectField(jreturnedHash, value, jpasswordHash);
}
}
return fusePtr;
}