Inner encryption of configuration file uses the cipher specified by the user
This commit is contained in:
parent
252a666a37
commit
669e71c056
@ -2,11 +2,13 @@
|
||||
|
||||
#include <messmer/cpp-utils/crypto/symmetric/ciphers.h>
|
||||
#include <messmer/blockstore/implementations/encrypted/EncryptedBlockStore.h>
|
||||
#include "crypto/inner/ConcreteInnerEncryptor.h"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using cpputils::unique_ref;
|
||||
using cpputils::make_unique_ref;
|
||||
using cpputils::FixedSizeData;
|
||||
using blockstore::BlockStore;
|
||||
using std::shared_ptr;
|
||||
using std::make_shared;
|
||||
@ -45,6 +47,10 @@ public:
|
||||
return Cipher::CreateKey(randomGenerator).ToString();
|
||||
}
|
||||
|
||||
unique_ref<InnerEncryptor> createInnerConfigEncryptor(const FixedSizeData<CryCiphers::MAX_KEY_SIZE> &key) const override {
|
||||
return make_unique_ref<ConcreteInnerEncryptor<Cipher>>(key.take<Cipher::EncryptionKey::BINARY_LENGTH>());
|
||||
}
|
||||
|
||||
private:
|
||||
optional<string> _warning;
|
||||
};
|
||||
@ -80,7 +86,7 @@ const CryCipher& CryCiphers::find(const string &cipherName) {
|
||||
[cipherName] (const auto& element) {
|
||||
return element->cipherName() == cipherName;
|
||||
});
|
||||
ASSERT(found != CryCiphers::SUPPORTED_CIPHERS.end(), "Unknown Cipher");
|
||||
ASSERT(found != CryCiphers::SUPPORTED_CIPHERS.end(), "Unknown Cipher: "+cipherName);
|
||||
return **found;
|
||||
}
|
||||
|
||||
|
@ -7,20 +7,13 @@
|
||||
#include <messmer/cpp-utils/pointer/unique_ref.h>
|
||||
#include <messmer/blockstore/interface/BlockStore.h>
|
||||
#include <messmer/cpp-utils/random/RandomGenerator.h>
|
||||
#include "crypto/inner/InnerEncryptor.h"
|
||||
|
||||
namespace cryfs {
|
||||
|
||||
class CryCipher {
|
||||
public:
|
||||
virtual ~CryCipher() {}
|
||||
class CryCipher;
|
||||
|
||||
virtual std::string cipherName() const = 0;
|
||||
virtual const boost::optional<std::string> &warning() const = 0;
|
||||
virtual cpputils::unique_ref<blockstore::BlockStore> createEncryptedBlockstore(cpputils::unique_ref<blockstore::BlockStore> baseBlockStore, const std::string &encKey) const = 0;
|
||||
virtual std::string createKey(cpputils::RandomGenerator &randomGenerator) const = 0;
|
||||
};
|
||||
|
||||
class CryCiphers {
|
||||
class CryCiphers final {
|
||||
public:
|
||||
static std::vector<std::string> supportedCipherNames();
|
||||
|
||||
@ -36,6 +29,19 @@ private:
|
||||
static const std::vector<std::shared_ptr<CryCipher>> SUPPORTED_CIPHERS;
|
||||
};
|
||||
|
||||
|
||||
class CryCipher {
|
||||
public:
|
||||
virtual ~CryCipher() {}
|
||||
|
||||
virtual std::string cipherName() const = 0;
|
||||
virtual const boost::optional<std::string> &warning() const = 0;
|
||||
virtual cpputils::unique_ref<blockstore::BlockStore> createEncryptedBlockstore(cpputils::unique_ref<blockstore::BlockStore> baseBlockStore, const std::string &encKey) const = 0;
|
||||
virtual std::string createKey(cpputils::RandomGenerator &randomGenerator) const = 0;
|
||||
virtual cpputils::unique_ref<InnerEncryptor> createInnerConfigEncryptor(const cpputils::FixedSizeData<CryCiphers::MAX_KEY_SIZE> &key) const = 0;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -39,16 +39,20 @@ optional<CryConfigFile> CryConfigFile::load(const bf::path &path, const string &
|
||||
if (decrypted == none) {
|
||||
return none;
|
||||
}
|
||||
CryConfig config = CryConfig::load(*decrypted);
|
||||
CryConfig config = CryConfig::load(decrypted->data);
|
||||
if (config.Cipher() != decrypted->cipherName) {
|
||||
//TODO Test that this fails
|
||||
LOG(ERROR) << "Inner cipher algorithm used to encrypt config file doesn't match config value";
|
||||
return none;
|
||||
}
|
||||
return CryConfigFile(path, std::move(config), std::move(*encryptor));
|
||||
}
|
||||
|
||||
CryConfigFile CryConfigFile::create(const bf::path &path, CryConfig config, const string &password, const SCryptSettings &scryptSettings) {
|
||||
using ConfigCipher = cpputils::AES256_GCM; // TODO Take cipher from config instead
|
||||
if (bf::exists(path)) {
|
||||
throw std::runtime_error("Config file exists already.");
|
||||
}
|
||||
auto result = CryConfigFile(path, std::move(config), CryConfigEncryptorFactory::deriveKey<ConfigCipher>(password, scryptSettings));
|
||||
auto result = CryConfigFile(path, std::move(config), CryConfigEncryptorFactory::deriveKey(config.Cipher(), password, scryptSettings));
|
||||
result.save();
|
||||
return result;
|
||||
}
|
||||
@ -59,7 +63,7 @@ CryConfigFile::CryConfigFile(const bf::path &path, CryConfig config, unique_ref<
|
||||
|
||||
void CryConfigFile::save() const {
|
||||
Data configData = _config.save();
|
||||
auto encrypted = _encryptor->encrypt(configData);
|
||||
auto encrypted = _encryptor->encrypt(configData, _config.Cipher());
|
||||
encrypted.StoreToFile(_path);
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ optional<CryConfigFile> CryConfigLoader::_loadConfig(const bf::path &filename) {
|
||||
return none;
|
||||
}
|
||||
if (_cipher != none && config->config()->Cipher() != *_cipher) {
|
||||
//TODO Test this fails
|
||||
throw std::runtime_error("Filesystem uses "+config->config()->Cipher()+" cipher and not "+*_cipher+" as specified.");
|
||||
}
|
||||
return std::move(*config);
|
||||
|
@ -1,45 +1,64 @@
|
||||
#include "CryConfigEncryptor.h"
|
||||
#include <messmer/cpp-utils/crypto/RandomPadding.h>
|
||||
#include "OuterConfig.h"
|
||||
|
||||
using std::string;
|
||||
using cpputils::unique_ref;
|
||||
using cpputils::make_unique_ref;
|
||||
using cpputils::Data;
|
||||
using cpputils::RandomPadding;
|
||||
using cpputils::DerivedKeyConfig;
|
||||
using cpputils::DerivedKey;
|
||||
using cpputils::FixedSizeData;
|
||||
using boost::optional;
|
||||
using boost::none;
|
||||
using namespace cpputils::logging;
|
||||
|
||||
namespace cryfs {
|
||||
CryConfigEncryptor::CryConfigEncryptor(unique_ref<InnerEncryptor> innerEncryptor, OuterCipher::EncryptionKey outerKey, DerivedKeyConfig keyConfig)
|
||||
: _innerEncryptor(std::move(innerEncryptor)), _outerKey(std::move(outerKey)), _keyConfig(std::move(keyConfig)) {
|
||||
constexpr size_t CryConfigEncryptor::OuterKeySize;
|
||||
constexpr size_t CryConfigEncryptor::MaxTotalKeySize;
|
||||
|
||||
CryConfigEncryptor::CryConfigEncryptor(DerivedKey<MaxTotalKeySize> derivedKey)
|
||||
: _derivedKey(std::move(derivedKey)) {
|
||||
}
|
||||
|
||||
Data CryConfigEncryptor::encrypt(const Data &plaintext) {
|
||||
auto inner = _innerEncryptor->encrypt(plaintext);
|
||||
auto padded = RandomPadding::add(inner, CONFIG_SIZE);
|
||||
auto ciphertext = OuterCipher::encrypt(static_cast<const uint8_t*>(padded.data()), padded.size(), _outerKey);
|
||||
return OuterConfig{_keyConfig, std::move(ciphertext)}.serialize();
|
||||
Data CryConfigEncryptor::encrypt(const Data &plaintext, const string &cipherName) const {
|
||||
InnerConfig innerConfig = _innerEncryptor(cipherName)->encrypt(plaintext);
|
||||
Data serializedInnerConfig = innerConfig.serialize();
|
||||
OuterConfig outerConfig = _outerEncryptor()->encrypt(serializedInnerConfig);
|
||||
return outerConfig.serialize();
|
||||
}
|
||||
|
||||
optional<Data> CryConfigEncryptor::decrypt(const Data &data) {
|
||||
optional<CryConfigEncryptor::Decrypted> CryConfigEncryptor::decrypt(const Data &data) const {
|
||||
auto innerConfig = _loadInnerConfig(data);
|
||||
if (innerConfig == none) {
|
||||
return none;
|
||||
}
|
||||
auto plaintext = _innerEncryptor(innerConfig->cipherName)->decrypt(*innerConfig);
|
||||
if (plaintext == none) {
|
||||
return none;
|
||||
}
|
||||
return Decrypted{std::move(*plaintext), innerConfig->cipherName};
|
||||
}
|
||||
|
||||
optional<InnerConfig> CryConfigEncryptor::_loadInnerConfig(const Data &data) const {
|
||||
auto outerConfig = OuterConfig::deserialize(data);
|
||||
if (outerConfig == none) {
|
||||
return none;
|
||||
}
|
||||
return _decryptInnerConfig(outerConfig->encryptedInnerConfig);
|
||||
auto serializedInnerConfig = _outerEncryptor()->decrypt(*outerConfig);
|
||||
if(serializedInnerConfig == none) {
|
||||
return none;
|
||||
}
|
||||
return InnerConfig::deserialize(*serializedInnerConfig);
|
||||
}
|
||||
|
||||
optional<Data> CryConfigEncryptor::_decryptInnerConfig(const Data &encryptedInnerConfig) {
|
||||
auto inner = OuterCipher::decrypt(static_cast<const uint8_t*>(encryptedInnerConfig.data()), encryptedInnerConfig.size(), _outerKey);
|
||||
if(inner == none) {
|
||||
return none;
|
||||
unique_ref<OuterEncryptor> CryConfigEncryptor::_outerEncryptor() const {
|
||||
auto outerKey = _derivedKey.key().take<OuterKeySize>();
|
||||
return make_unique_ref<OuterEncryptor>(outerKey, _derivedKey.config());
|
||||
}
|
||||
auto depadded = RandomPadding::remove(*inner);
|
||||
if(depadded == none) {
|
||||
return none;
|
||||
}
|
||||
return _innerEncryptor->decrypt(*depadded);
|
||||
|
||||
unique_ref<InnerEncryptor> CryConfigEncryptor::_innerEncryptor(const string &cipherName) const {
|
||||
auto innerKey = _derivedKey.key().drop<OuterKeySize>();
|
||||
return CryCiphers::find(cipherName).createInnerConfigEncryptor(innerKey);
|
||||
}
|
||||
}
|
||||
|
@ -5,30 +5,39 @@
|
||||
#include <messmer/cpp-utils/pointer/unique_ref.h>
|
||||
#include <messmer/cpp-utils/data/Deserializer.h>
|
||||
#include <messmer/cpp-utils/data/Serializer.h>
|
||||
#include "inner/InnerEncryptor.h"
|
||||
#include <messmer/cpp-utils/crypto/kdf/DerivedKeyConfig.h>
|
||||
#include <messmer/cpp-utils/crypto/kdf/DerivedKey.h>
|
||||
#include <messmer/cpp-utils/crypto/symmetric/ciphers.h>
|
||||
#include "inner/InnerEncryptor.h"
|
||||
#include "outer/OuterEncryptor.h"
|
||||
#include "../CryCipher.h"
|
||||
|
||||
namespace cryfs {
|
||||
//TODO Test
|
||||
//TODO Test (whole crypto folder)
|
||||
//TODO Test that encrypted config data always has the same size, no matter how big the plaintext config data
|
||||
//TODO Test that specified inner cipher is used (e.g. can't be decrypted with other cipher)
|
||||
//TODO Use own exception for cpputils::Serializer/cpputils::Deserializer errors and only catch them
|
||||
class CryConfigEncryptor {
|
||||
public:
|
||||
using OuterCipher = cpputils::AES256_GCM;
|
||||
static constexpr size_t CONFIG_SIZE = 1024; // Config data is grown to this size before encryption to hide its actual size
|
||||
static constexpr size_t OuterKeySize = OuterEncryptor::Cipher::EncryptionKey::BINARY_LENGTH;
|
||||
static constexpr size_t MaxTotalKeySize = OuterKeySize + CryCiphers::MAX_KEY_SIZE;
|
||||
|
||||
CryConfigEncryptor(cpputils::unique_ref<InnerEncryptor> innerEncryptor, OuterCipher::EncryptionKey outerKey, cpputils::DerivedKeyConfig keyConfig);
|
||||
struct Decrypted {
|
||||
cpputils::Data data;
|
||||
std::string cipherName;
|
||||
};
|
||||
|
||||
cpputils::Data encrypt(const cpputils::Data &plaintext);
|
||||
boost::optional <cpputils::Data> decrypt(const cpputils::Data &data);
|
||||
CryConfigEncryptor(cpputils::DerivedKey<MaxTotalKeySize> derivedKey);
|
||||
|
||||
cpputils::Data encrypt(const cpputils::Data &plaintext, const std::string &cipherName) const;
|
||||
boost::optional<Decrypted> decrypt(const cpputils::Data &data) const;
|
||||
|
||||
private:
|
||||
boost::optional<cpputils::Data> _decryptInnerConfig(const cpputils::Data &encryptedInnerConfig);
|
||||
boost::optional<InnerConfig> _loadInnerConfig(const cpputils::Data &data) const;
|
||||
cpputils::unique_ref<OuterEncryptor> _outerEncryptor() const;
|
||||
cpputils::unique_ref<InnerEncryptor> _innerEncryptor(const std::string &cipherName) const;
|
||||
|
||||
cpputils::unique_ref<InnerEncryptor> _innerEncryptor;
|
||||
OuterCipher::EncryptionKey _outerKey;
|
||||
cpputils::DerivedKeyConfig _keyConfig;
|
||||
cpputils::DerivedKey<MaxTotalKeySize> _derivedKey;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "CryConfigEncryptorFactory.h"
|
||||
#include <messmer/cpp-utils/crypto/symmetric/ciphers.h>
|
||||
#include "OuterConfig.h"
|
||||
#include "outer/OuterConfig.h"
|
||||
|
||||
using namespace cpputils::logging;
|
||||
using boost::optional;
|
||||
@ -11,37 +11,32 @@ using cpputils::Data;
|
||||
using cpputils::DerivedKey;
|
||||
using cpputils::DerivedKeyConfig;
|
||||
using cpputils::SCrypt;
|
||||
using cpputils::SCryptSettings;
|
||||
using std::string;
|
||||
|
||||
namespace cryfs {
|
||||
|
||||
constexpr size_t CryConfigEncryptorFactory::OuterKeySize;
|
||||
constexpr size_t CryConfigEncryptorFactory::MaxTotalKeySize;
|
||||
|
||||
optional<unique_ref<CryConfigEncryptor>> CryConfigEncryptorFactory::loadKey(const Data &data,
|
||||
const string &password) {
|
||||
using Cipher = cpputils::AES256_GCM; //TODO Allow other ciphers
|
||||
|
||||
auto outerConfig = OuterConfig::deserialize(data);
|
||||
if (outerConfig == none) {
|
||||
return none;
|
||||
}
|
||||
auto derivedKey = _deriveKey(outerConfig->keyConfig, password);
|
||||
auto outerKey = derivedKey.key().take<OuterKeySize>();
|
||||
auto innerKey = derivedKey.key().drop<OuterKeySize>().take<Cipher::EncryptionKey::BINARY_LENGTH>();
|
||||
return make_unique_ref<CryConfigEncryptor>(
|
||||
make_unique_ref<ConcreteInnerEncryptor<Cipher>>(innerKey),
|
||||
outerKey,
|
||||
derivedKey.moveOutConfig()
|
||||
);
|
||||
return make_unique_ref<CryConfigEncryptor>(std::move(derivedKey));
|
||||
}
|
||||
|
||||
cpputils::DerivedKey<CryConfigEncryptorFactory::MaxTotalKeySize>
|
||||
DerivedKey<CryConfigEncryptor::MaxTotalKeySize>
|
||||
CryConfigEncryptorFactory::_deriveKey(const DerivedKeyConfig &keyConfig, const std::string &password) {
|
||||
//TODO It would be better, not to generate a MaxTotalKeySize key here, but to generate the outer key first, and then
|
||||
// (once we know which inner cipher was used) only generate as many key bytes as we need for the inner cipher.
|
||||
// This would need a change in the scrypt interface though, because right now we can't continue past key computations.
|
||||
auto key = SCrypt().generateKeyFromConfig<MaxTotalKeySize>(password, keyConfig);
|
||||
return DerivedKey<MaxTotalKeySize>(keyConfig, std::move(key));
|
||||
auto key = SCrypt().generateKeyFromConfig<CryConfigEncryptor::MaxTotalKeySize>(password, keyConfig);
|
||||
return DerivedKey<CryConfigEncryptor::MaxTotalKeySize>(keyConfig, std::move(key));
|
||||
}
|
||||
|
||||
unique_ref<CryConfigEncryptor> CryConfigEncryptorFactory::deriveKey(const string &cipherName, const string &password, const SCryptSettings &scryptSettings) {
|
||||
auto derivedKey = cpputils::SCrypt().generateKey<CryConfigEncryptor::MaxTotalKeySize>(password, scryptSettings);
|
||||
return make_unique_ref<CryConfigEncryptor>(std::move(derivedKey));
|
||||
}
|
||||
}
|
||||
|
@ -12,36 +12,16 @@ namespace cryfs {
|
||||
//TODO Test
|
||||
class CryConfigEncryptorFactory {
|
||||
public:
|
||||
template<class Cipher>
|
||||
static cpputils::unique_ref<CryConfigEncryptor> deriveKey(const std::string &password, const cpputils::SCryptSettings &scryptSettings);
|
||||
static cpputils::unique_ref<CryConfigEncryptor> deriveKey(const std::string &cipherName, const std::string &password, const cpputils::SCryptSettings &scryptSettings);
|
||||
|
||||
static boost::optional<cpputils::unique_ref<CryConfigEncryptor>> loadKey(const cpputils::Data &ciphertext,
|
||||
const std::string &password);
|
||||
|
||||
private:
|
||||
static constexpr size_t OuterKeySize = CryConfigEncryptor::OuterCipher::EncryptionKey::BINARY_LENGTH;
|
||||
template<class Cipher> static constexpr size_t TotalKeySize();
|
||||
static constexpr size_t MaxTotalKeySize = OuterKeySize + CryCiphers::MAX_KEY_SIZE;
|
||||
|
||||
static cpputils::DerivedKey<MaxTotalKeySize> _deriveKey(const cpputils::DerivedKeyConfig &keyConfig, const std::string &password);
|
||||
static cpputils::DerivedKey<CryConfigEncryptor::MaxTotalKeySize> _deriveKey(const cpputils::DerivedKeyConfig &keyConfig, const std::string &password);
|
||||
static boost::optional<std::string> _loadCipherName(const OuterEncryptor &outerEncryptor, const OuterConfig &outerConfig);
|
||||
};
|
||||
|
||||
template<class Cipher> constexpr size_t CryConfigEncryptorFactory::TotalKeySize() {
|
||||
return OuterKeySize + Cipher::EncryptionKey::BINARY_LENGTH;
|
||||
}
|
||||
|
||||
template<class Cipher>
|
||||
cpputils::unique_ref<CryConfigEncryptor> CryConfigEncryptorFactory::deriveKey(const std::string &password, const cpputils::SCryptSettings &scryptSettings) {
|
||||
//TODO Use _deriveKey(keyConfig, password) instead and get rid of cpputils::SCryptSettings class in favor of cpputils::DerivedKeyConfig
|
||||
auto derivedKey = cpputils::SCrypt().generateKey<TotalKeySize<Cipher>()>(password, scryptSettings);
|
||||
auto outerKey = derivedKey.key().template take<OuterKeySize>();
|
||||
auto innerKey = derivedKey.key().template drop<OuterKeySize>();
|
||||
return cpputils::make_unique_ref<CryConfigEncryptor>(
|
||||
cpputils::make_unique_ref<ConcreteInnerEncryptor<Cipher>>(innerKey),
|
||||
outerKey,
|
||||
derivedKey.moveOutConfig()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -17,8 +17,9 @@ namespace cryfs {
|
||||
|
||||
ConcreteInnerEncryptor(typename Cipher::EncryptionKey key);
|
||||
|
||||
cpputils::Data encrypt(const cpputils::Data &plaintext) const override;
|
||||
boost::optional<cpputils::Data> decrypt(const cpputils::Data &ciphertext) const override;
|
||||
InnerConfig encrypt(const cpputils::Data &config) const override;
|
||||
boost::optional<cpputils::Data> decrypt(const InnerConfig &innerConfig) const override;
|
||||
|
||||
private:
|
||||
|
||||
typename Cipher::EncryptionKey _key;
|
||||
@ -30,16 +31,12 @@ namespace cryfs {
|
||||
}
|
||||
|
||||
template<class Cipher>
|
||||
boost::optional<cpputils::Data> ConcreteInnerEncryptor<Cipher>::decrypt(const cpputils::Data &ciphertext) const {
|
||||
auto innerConfig = InnerConfig::deserialize(ciphertext);
|
||||
if (innerConfig == boost::none) {
|
||||
boost::optional<cpputils::Data> ConcreteInnerEncryptor<Cipher>::decrypt(const InnerConfig &innerConfig) const {
|
||||
if (innerConfig.cipherName != Cipher::NAME) {
|
||||
cpputils::logging::LOG(cpputils::logging::ERROR) << "Initialized ConcreteInnerEncryptor with wrong cipher";
|
||||
return boost::none;
|
||||
}
|
||||
if (innerConfig->cipherName != Cipher::NAME) {
|
||||
cpputils::logging::LOG(cpputils::logging::ERROR) << "Wrong inner cipher used";
|
||||
return boost::none;
|
||||
}
|
||||
auto decrypted = Cipher::decrypt(static_cast<const uint8_t*>(innerConfig->encryptedConfig.data()), innerConfig->encryptedConfig.size(), _key);
|
||||
auto decrypted = Cipher::decrypt(static_cast<const uint8_t*>(innerConfig.encryptedConfig.data()), innerConfig.encryptedConfig.size(), _key);
|
||||
if (decrypted == boost::none) {
|
||||
return boost::none;
|
||||
}
|
||||
@ -51,10 +48,10 @@ namespace cryfs {
|
||||
}
|
||||
|
||||
template<class Cipher>
|
||||
cpputils::Data ConcreteInnerEncryptor<Cipher>::encrypt(const cpputils::Data &plaintext) const {
|
||||
auto paddedPlaintext = cpputils::RandomPadding::add(plaintext, CONFIG_SIZE);
|
||||
auto encrypted = Cipher::encrypt(static_cast<const uint8_t*>(paddedPlaintext.data()), paddedPlaintext.size(), _key);
|
||||
return InnerConfig{Cipher::NAME, std::move(encrypted)}.serialize();
|
||||
InnerConfig ConcreteInnerEncryptor<Cipher>::encrypt(const cpputils::Data &config) const {
|
||||
auto padded = cpputils::RandomPadding::add(config, CONFIG_SIZE);
|
||||
auto encrypted = Cipher::encrypt(static_cast<const uint8_t*>(padded.data()), padded.size(), _key);
|
||||
return InnerConfig{Cipher::NAME, std::move(encrypted)};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,13 @@
|
||||
#include <boost/optional.hpp>
|
||||
#include <messmer/cpp-utils/data/Deserializer.h>
|
||||
#include <messmer/cpp-utils/data/Serializer.h>
|
||||
#include "InnerConfig.h"
|
||||
|
||||
namespace cryfs {
|
||||
class InnerEncryptor {
|
||||
public:
|
||||
virtual cpputils::Data encrypt(const cpputils::Data &plaintext) const = 0;
|
||||
virtual boost::optional<cpputils::Data> decrypt(const cpputils::Data &plaintext) const = 0;
|
||||
virtual InnerConfig encrypt(const cpputils::Data &plaintext) const = 0;
|
||||
virtual boost::optional<cpputils::Data> decrypt(const InnerConfig &innerConfig) const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#ifndef CRYFS_OUTERCONFIG_H
|
||||
#define CRYFS_OUTERCONFIG_H
|
||||
#ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERCONFIG_H
|
||||
#define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERCONFIG_H
|
||||
|
||||
#include <messmer/cpp-utils/crypto/kdf/DerivedKeyConfig.h>
|
||||
#include <messmer/cpp-utils/data/Data.h>
|
33
src/config/crypto/outer/OuterEncryptor.cpp
Normal file
33
src/config/crypto/outer/OuterEncryptor.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "OuterEncryptor.h"
|
||||
#include <messmer/cpp-utils/crypto/RandomPadding.h>
|
||||
#include "OuterConfig.h"
|
||||
|
||||
using std::string;
|
||||
using cpputils::unique_ref;
|
||||
using cpputils::Data;
|
||||
using cpputils::RandomPadding;
|
||||
using cpputils::DerivedKeyConfig;
|
||||
using boost::optional;
|
||||
using boost::none;
|
||||
using namespace cpputils::logging;
|
||||
|
||||
namespace cryfs {
|
||||
OuterEncryptor::OuterEncryptor(Cipher::EncryptionKey key, const cpputils::DerivedKeyConfig &keyConfig)
|
||||
: _key(std::move(key)), _keyConfig(std::move(keyConfig)) {
|
||||
}
|
||||
|
||||
OuterConfig OuterEncryptor::encrypt(const Data &plaintext) const {
|
||||
auto padded = RandomPadding::add(plaintext, CONFIG_SIZE);
|
||||
auto ciphertext = Cipher::encrypt(static_cast<const uint8_t*>(padded.data()), padded.size(), _key);
|
||||
return OuterConfig{_keyConfig, std::move(ciphertext)};
|
||||
}
|
||||
|
||||
optional<Data> OuterEncryptor::decrypt(const OuterConfig &outerConfig) const {
|
||||
ASSERT(outerConfig.keyConfig == _keyConfig, "OuterEncryptor was initialized with wrong key config");
|
||||
auto inner = Cipher::decrypt(static_cast<const uint8_t*>(outerConfig.encryptedInnerConfig.data()), outerConfig.encryptedInnerConfig.size(), _key);
|
||||
if(inner == none) {
|
||||
return none;
|
||||
}
|
||||
return RandomPadding::remove(*inner);
|
||||
}
|
||||
}
|
29
src/config/crypto/outer/OuterEncryptor.h
Normal file
29
src/config/crypto/outer/OuterEncryptor.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERENCRYPTOR_H
|
||||
#define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERENCRYPTOR_H
|
||||
|
||||
#include <messmer/cpp-utils/pointer/unique_ref.h>
|
||||
#include <messmer/cpp-utils/data/Deserializer.h>
|
||||
#include <messmer/cpp-utils/data/Serializer.h>
|
||||
#include <messmer/cpp-utils/crypto/symmetric/ciphers.h>
|
||||
#include "OuterConfig.h"
|
||||
|
||||
namespace cryfs {
|
||||
class OuterEncryptor {
|
||||
public:
|
||||
using Cipher = cpputils::AES256_GCM;
|
||||
static constexpr size_t CONFIG_SIZE = 1024; // Config data is grown to this size before encryption to hide its actual size
|
||||
|
||||
OuterEncryptor(Cipher::EncryptionKey key, const cpputils::DerivedKeyConfig &keyConfig);
|
||||
|
||||
OuterConfig encrypt(const cpputils::Data &encryptedInnerConfig) const;
|
||||
boost::optional<cpputils::Data> decrypt(const OuterConfig &outerConfig) const;
|
||||
|
||||
private:
|
||||
|
||||
Cipher::EncryptionKey _key;
|
||||
cpputils::DerivedKeyConfig _keyConfig;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -4,6 +4,8 @@
|
||||
#include <messmer/cpp-utils/tempfile/TempFile.h>
|
||||
#include <boost/optional/optional_io.hpp>
|
||||
|
||||
//TODO Test that config file is still loadable after changing the cipher and then storing it (i.e. it uses a different inner cipher but is still the same CryConfigFile instance)
|
||||
|
||||
using namespace cryfs;
|
||||
using cpputils::TempFile;
|
||||
using std::string;
|
||||
@ -27,8 +29,14 @@ public:
|
||||
|
||||
TempFile file;
|
||||
|
||||
CryConfig Config() {
|
||||
CryConfig result;
|
||||
result.SetCipher("aes-256-gcm");
|
||||
return result;
|
||||
}
|
||||
|
||||
CryConfigFile CreateAndLoadEmpty(const string &password = "mypassword") {
|
||||
Create(CryConfig(), password);
|
||||
Create(Config(), password);
|
||||
return Load().value();
|
||||
}
|
||||
|
||||
@ -40,6 +48,10 @@ public:
|
||||
return CryConfigFile::load(file.path(), password);
|
||||
}
|
||||
|
||||
void CreateWithCipher(const string &cipher) {
|
||||
return CreateWithCipher(cipher, file);
|
||||
}
|
||||
|
||||
void CreateWithCipher(const string &cipher, const TempFile &tempFile) {
|
||||
CryConfig cfg;
|
||||
cfg.SetCipher(cipher);
|
||||
@ -48,7 +60,7 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(CryConfigFileTest, DoesntLoadIfWrongPassword) {
|
||||
Create(CryConfig(), "mypassword");
|
||||
Create(Config(), "mypassword");
|
||||
auto loaded = Load("mypassword2");
|
||||
EXPECT_EQ(none, loaded);
|
||||
}
|
||||
@ -59,7 +71,7 @@ TEST_F(CryConfigFileTest, RootBlob_Init) {
|
||||
}
|
||||
|
||||
TEST_F(CryConfigFileTest, RootBlob_CreateAndLoad) {
|
||||
CryConfig cfg;
|
||||
CryConfig cfg = Config();
|
||||
cfg.SetRootBlob("rootblobid");
|
||||
Create(std::move(cfg));
|
||||
CryConfigFile loaded = Load().value();
|
||||
@ -80,7 +92,7 @@ TEST_F(CryConfigFileTest, EncryptionKey_Init) {
|
||||
}
|
||||
|
||||
TEST_F(CryConfigFileTest, EncryptionKey_CreateAndLoad) {
|
||||
CryConfig cfg;
|
||||
CryConfig cfg = Config();
|
||||
cfg.SetEncryptionKey("encryptionkey");
|
||||
Create(std::move(cfg));
|
||||
CryConfigFile loaded = Load().value();
|
||||
@ -97,30 +109,42 @@ TEST_F(CryConfigFileTest, EncryptionKey_SaveAndLoad) {
|
||||
|
||||
TEST_F(CryConfigFileTest, Cipher_Init) {
|
||||
CryConfigFile created = CreateAndLoadEmpty();
|
||||
EXPECT_EQ("", created.config()->Cipher());
|
||||
EXPECT_EQ("aes-256-gcm", created.config()->Cipher());
|
||||
}
|
||||
|
||||
TEST_F(CryConfigFileTest, Cipher_CreateAndLoad) {
|
||||
CryConfig cfg;
|
||||
cfg.SetCipher("cipher");
|
||||
CryConfig cfg = Config();
|
||||
cfg.SetCipher("twofish-128-cfb");
|
||||
Create(std::move(cfg));
|
||||
CryConfigFile loaded = Load().value();
|
||||
EXPECT_EQ("cipher", loaded.config()->Cipher());
|
||||
EXPECT_EQ("twofish-128-cfb", loaded.config()->Cipher());
|
||||
}
|
||||
|
||||
TEST_F(CryConfigFileTest, Cipher_SaveAndLoad) {
|
||||
CryConfigFile created = CreateAndLoadEmpty();
|
||||
created.config()->SetCipher("cipher");
|
||||
created.config()->SetCipher("twofish-128-cfb");
|
||||
created.save();
|
||||
CryConfigFile loaded = Load().value();
|
||||
EXPECT_EQ("cipher", loaded.config()->Cipher());
|
||||
EXPECT_EQ("twofish-128-cfb", loaded.config()->Cipher());
|
||||
}
|
||||
|
||||
//Test that the encrypted config file has the same size, no matter how big the plaintext config data.
|
||||
TEST_F(CryConfigFileTest, ConfigFileHasFixedSize) {
|
||||
TempFile file1(false);
|
||||
TempFile file2(false);
|
||||
CreateWithCipher("short", file1);
|
||||
CreateWithCipher("long_cipher_name_that_causes_the_plaintext_config_data_to_be_larger", file2);
|
||||
//It is important to have different cipher name lengths here, because they're on the outer encryption level.
|
||||
//So this ensures that there also is a padding happening on the outer encryption level.
|
||||
CreateWithCipher("aes-128-gcm", file1); // Short cipher name and short key
|
||||
CreateWithCipher("twofish-256-cfb", file2); // Long cipher name and long key
|
||||
EXPECT_EQ(bf::file_size(file1.path()), bf::file_size(file2.path()));
|
||||
}
|
||||
|
||||
TEST_F(CryConfigFileTest, CanSaveAndLoadModififedCipher) {
|
||||
CreateWithCipher("aes-256-gcm");
|
||||
CryConfigFile created = Load().value();
|
||||
EXPECT_EQ("aes-256-gcm", created.config()->Cipher());
|
||||
created.config()->SetCipher("twofish-128-cfb");
|
||||
created.save();
|
||||
CryConfigFile loaded = Load().value();
|
||||
EXPECT_EQ("twofish-128-cfb", loaded.config()->Cipher());
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ TEST_F(CryConfigLoaderTest, EncryptionKey_Create) {
|
||||
}
|
||||
|
||||
TEST_F(CryConfigLoaderTest, Cipher_Load) {
|
||||
CreateWithCipher("ciphername");
|
||||
CreateWithCipher("twofish-128-cfb");
|
||||
auto loaded = Load().value();
|
||||
EXPECT_EQ("ciphername", loaded.config()->Cipher());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user