Inner encryption of configuration file uses the cipher specified by the user

This commit is contained in:
Sebastian Messmer 2015-11-11 01:19:47 -08:00
parent 252a666a37
commit 669e71c056
16 changed files with 220 additions and 116 deletions

View File

@ -2,11 +2,13 @@
#include <messmer/cpp-utils/crypto/symmetric/ciphers.h> #include <messmer/cpp-utils/crypto/symmetric/ciphers.h>
#include <messmer/blockstore/implementations/encrypted/EncryptedBlockStore.h> #include <messmer/blockstore/implementations/encrypted/EncryptedBlockStore.h>
#include "crypto/inner/ConcreteInnerEncryptor.h"
using std::vector; using std::vector;
using std::string; using std::string;
using cpputils::unique_ref; using cpputils::unique_ref;
using cpputils::make_unique_ref; using cpputils::make_unique_ref;
using cpputils::FixedSizeData;
using blockstore::BlockStore; using blockstore::BlockStore;
using std::shared_ptr; using std::shared_ptr;
using std::make_shared; using std::make_shared;
@ -45,6 +47,10 @@ public:
return Cipher::CreateKey(randomGenerator).ToString(); 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: private:
optional<string> _warning; optional<string> _warning;
}; };
@ -80,7 +86,7 @@ const CryCipher& CryCiphers::find(const string &cipherName) {
[cipherName] (const auto& element) { [cipherName] (const auto& element) {
return element->cipherName() == cipherName; return element->cipherName() == cipherName;
}); });
ASSERT(found != CryCiphers::SUPPORTED_CIPHERS.end(), "Unknown Cipher"); ASSERT(found != CryCiphers::SUPPORTED_CIPHERS.end(), "Unknown Cipher: "+cipherName);
return **found; return **found;
} }

View File

@ -7,20 +7,13 @@
#include <messmer/cpp-utils/pointer/unique_ref.h> #include <messmer/cpp-utils/pointer/unique_ref.h>
#include <messmer/blockstore/interface/BlockStore.h> #include <messmer/blockstore/interface/BlockStore.h>
#include <messmer/cpp-utils/random/RandomGenerator.h> #include <messmer/cpp-utils/random/RandomGenerator.h>
#include "crypto/inner/InnerEncryptor.h"
namespace cryfs { namespace cryfs {
class CryCipher { class CryCipher;
public:
virtual ~CryCipher() {}
virtual std::string cipherName() const = 0; class CryCiphers final {
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 {
public: public:
static std::vector<std::string> supportedCipherNames(); static std::vector<std::string> supportedCipherNames();
@ -36,6 +29,19 @@ private:
static const std::vector<std::shared_ptr<CryCipher>> SUPPORTED_CIPHERS; 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 #endif

View File

@ -39,16 +39,20 @@ optional<CryConfigFile> CryConfigFile::load(const bf::path &path, const string &
if (decrypted == none) { if (decrypted == none) {
return 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)); return CryConfigFile(path, std::move(config), std::move(*encryptor));
} }
CryConfigFile CryConfigFile::create(const bf::path &path, CryConfig config, const string &password, const SCryptSettings &scryptSettings) { 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)) { if (bf::exists(path)) {
throw std::runtime_error("Config file exists already."); 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(); result.save();
return result; return result;
} }
@ -59,7 +63,7 @@ CryConfigFile::CryConfigFile(const bf::path &path, CryConfig config, unique_ref<
void CryConfigFile::save() const { void CryConfigFile::save() const {
Data configData = _config.save(); Data configData = _config.save();
auto encrypted = _encryptor->encrypt(configData); auto encrypted = _encryptor->encrypt(configData, _config.Cipher());
encrypted.StoreToFile(_path); encrypted.StoreToFile(_path);
} }

View File

@ -34,6 +34,7 @@ optional<CryConfigFile> CryConfigLoader::_loadConfig(const bf::path &filename) {
return none; return none;
} }
if (_cipher != none && config->config()->Cipher() != *_cipher) { 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."); throw std::runtime_error("Filesystem uses "+config->config()->Cipher()+" cipher and not "+*_cipher+" as specified.");
} }
return std::move(*config); return std::move(*config);

View File

@ -1,45 +1,64 @@
#include "CryConfigEncryptor.h" #include "CryConfigEncryptor.h"
#include <messmer/cpp-utils/crypto/RandomPadding.h> #include <messmer/cpp-utils/crypto/RandomPadding.h>
#include "OuterConfig.h"
using std::string; using std::string;
using cpputils::unique_ref; using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::Data; using cpputils::Data;
using cpputils::RandomPadding; using cpputils::RandomPadding;
using cpputils::DerivedKeyConfig; using cpputils::DerivedKeyConfig;
using cpputils::DerivedKey;
using cpputils::FixedSizeData;
using boost::optional; using boost::optional;
using boost::none; using boost::none;
using namespace cpputils::logging; using namespace cpputils::logging;
namespace cryfs { namespace cryfs {
CryConfigEncryptor::CryConfigEncryptor(unique_ref<InnerEncryptor> innerEncryptor, OuterCipher::EncryptionKey outerKey, DerivedKeyConfig keyConfig) constexpr size_t CryConfigEncryptor::OuterKeySize;
: _innerEncryptor(std::move(innerEncryptor)), _outerKey(std::move(outerKey)), _keyConfig(std::move(keyConfig)) { constexpr size_t CryConfigEncryptor::MaxTotalKeySize;
CryConfigEncryptor::CryConfigEncryptor(DerivedKey<MaxTotalKeySize> derivedKey)
: _derivedKey(std::move(derivedKey)) {
} }
Data CryConfigEncryptor::encrypt(const Data &plaintext) { Data CryConfigEncryptor::encrypt(const Data &plaintext, const string &cipherName) const {
auto inner = _innerEncryptor->encrypt(plaintext); InnerConfig innerConfig = _innerEncryptor(cipherName)->encrypt(plaintext);
auto padded = RandomPadding::add(inner, CONFIG_SIZE); Data serializedInnerConfig = innerConfig.serialize();
auto ciphertext = OuterCipher::encrypt(static_cast<const uint8_t*>(padded.data()), padded.size(), _outerKey); OuterConfig outerConfig = _outerEncryptor()->encrypt(serializedInnerConfig);
return OuterConfig{_keyConfig, std::move(ciphertext)}.serialize(); 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); auto outerConfig = OuterConfig::deserialize(data);
if (outerConfig == none) { if (outerConfig == none) {
return 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) { unique_ref<OuterEncryptor> CryConfigEncryptor::_outerEncryptor() const {
auto inner = OuterCipher::decrypt(static_cast<const uint8_t*>(encryptedInnerConfig.data()), encryptedInnerConfig.size(), _outerKey); auto outerKey = _derivedKey.key().take<OuterKeySize>();
if(inner == none) { return make_unique_ref<OuterEncryptor>(outerKey, _derivedKey.config());
return none;
} }
auto depadded = RandomPadding::remove(*inner);
if(depadded == none) { unique_ref<InnerEncryptor> CryConfigEncryptor::_innerEncryptor(const string &cipherName) const {
return none; auto innerKey = _derivedKey.key().drop<OuterKeySize>();
} return CryCiphers::find(cipherName).createInnerConfigEncryptor(innerKey);
return _innerEncryptor->decrypt(*depadded);
} }
} }

View File

@ -5,30 +5,39 @@
#include <messmer/cpp-utils/pointer/unique_ref.h> #include <messmer/cpp-utils/pointer/unique_ref.h>
#include <messmer/cpp-utils/data/Deserializer.h> #include <messmer/cpp-utils/data/Deserializer.h>
#include <messmer/cpp-utils/data/Serializer.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/DerivedKeyConfig.h>
#include <messmer/cpp-utils/crypto/kdf/DerivedKey.h>
#include <messmer/cpp-utils/crypto/symmetric/ciphers.h> #include <messmer/cpp-utils/crypto/symmetric/ciphers.h>
#include "inner/InnerEncryptor.h"
#include "outer/OuterEncryptor.h"
#include "../CryCipher.h"
namespace cryfs { 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 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 //TODO Use own exception for cpputils::Serializer/cpputils::Deserializer errors and only catch them
class CryConfigEncryptor { class CryConfigEncryptor {
public: public:
using OuterCipher = cpputils::AES256_GCM; static constexpr size_t OuterKeySize = OuterEncryptor::Cipher::EncryptionKey::BINARY_LENGTH;
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 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); CryConfigEncryptor(cpputils::DerivedKey<MaxTotalKeySize> derivedKey);
boost::optional <cpputils::Data> decrypt(const cpputils::Data &data);
cpputils::Data encrypt(const cpputils::Data &plaintext, const std::string &cipherName) const;
boost::optional<Decrypted> decrypt(const cpputils::Data &data) const;
private: 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; cpputils::DerivedKey<MaxTotalKeySize> _derivedKey;
OuterCipher::EncryptionKey _outerKey;
cpputils::DerivedKeyConfig _keyConfig;
}; };
} }

View File

@ -1,6 +1,6 @@
#include "CryConfigEncryptorFactory.h" #include "CryConfigEncryptorFactory.h"
#include <messmer/cpp-utils/crypto/symmetric/ciphers.h> #include <messmer/cpp-utils/crypto/symmetric/ciphers.h>
#include "OuterConfig.h" #include "outer/OuterConfig.h"
using namespace cpputils::logging; using namespace cpputils::logging;
using boost::optional; using boost::optional;
@ -11,37 +11,32 @@ using cpputils::Data;
using cpputils::DerivedKey; using cpputils::DerivedKey;
using cpputils::DerivedKeyConfig; using cpputils::DerivedKeyConfig;
using cpputils::SCrypt; using cpputils::SCrypt;
using cpputils::SCryptSettings;
using std::string; using std::string;
namespace cryfs { namespace cryfs {
constexpr size_t CryConfigEncryptorFactory::OuterKeySize;
constexpr size_t CryConfigEncryptorFactory::MaxTotalKeySize;
optional<unique_ref<CryConfigEncryptor>> CryConfigEncryptorFactory::loadKey(const Data &data, optional<unique_ref<CryConfigEncryptor>> CryConfigEncryptorFactory::loadKey(const Data &data,
const string &password) { const string &password) {
using Cipher = cpputils::AES256_GCM; //TODO Allow other ciphers
auto outerConfig = OuterConfig::deserialize(data); auto outerConfig = OuterConfig::deserialize(data);
if (outerConfig == none) { if (outerConfig == none) {
return none; return none;
} }
auto derivedKey = _deriveKey(outerConfig->keyConfig, password); auto derivedKey = _deriveKey(outerConfig->keyConfig, password);
auto outerKey = derivedKey.key().take<OuterKeySize>(); return make_unique_ref<CryConfigEncryptor>(std::move(derivedKey));
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()
);
} }
cpputils::DerivedKey<CryConfigEncryptorFactory::MaxTotalKeySize> DerivedKey<CryConfigEncryptor::MaxTotalKeySize>
CryConfigEncryptorFactory::_deriveKey(const DerivedKeyConfig &keyConfig, const std::string &password) { 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 //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. // (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. // 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); auto key = SCrypt().generateKeyFromConfig<CryConfigEncryptor::MaxTotalKeySize>(password, keyConfig);
return DerivedKey<MaxTotalKeySize>(keyConfig, std::move(key)); 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));
} }
} }

View File

@ -12,36 +12,16 @@ namespace cryfs {
//TODO Test //TODO Test
class CryConfigEncryptorFactory { class CryConfigEncryptorFactory {
public: public:
template<class Cipher> static cpputils::unique_ref<CryConfigEncryptor> deriveKey(const std::string &cipherName, const std::string &password, const cpputils::SCryptSettings &scryptSettings);
static cpputils::unique_ref<CryConfigEncryptor> deriveKey(const std::string &password, const cpputils::SCryptSettings &scryptSettings);
static boost::optional<cpputils::unique_ref<CryConfigEncryptor>> loadKey(const cpputils::Data &ciphertext, static boost::optional<cpputils::unique_ref<CryConfigEncryptor>> loadKey(const cpputils::Data &ciphertext,
const std::string &password); const std::string &password);
private: 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 #endif

View File

@ -17,8 +17,9 @@ namespace cryfs {
ConcreteInnerEncryptor(typename Cipher::EncryptionKey key); ConcreteInnerEncryptor(typename Cipher::EncryptionKey key);
cpputils::Data encrypt(const cpputils::Data &plaintext) const override; InnerConfig encrypt(const cpputils::Data &config) const override;
boost::optional<cpputils::Data> decrypt(const cpputils::Data &ciphertext) const override; boost::optional<cpputils::Data> decrypt(const InnerConfig &innerConfig) const override;
private: private:
typename Cipher::EncryptionKey _key; typename Cipher::EncryptionKey _key;
@ -30,16 +31,12 @@ namespace cryfs {
} }
template<class Cipher> template<class Cipher>
boost::optional<cpputils::Data> ConcreteInnerEncryptor<Cipher>::decrypt(const cpputils::Data &ciphertext) const { boost::optional<cpputils::Data> ConcreteInnerEncryptor<Cipher>::decrypt(const InnerConfig &innerConfig) const {
auto innerConfig = InnerConfig::deserialize(ciphertext); if (innerConfig.cipherName != Cipher::NAME) {
if (innerConfig == boost::none) { cpputils::logging::LOG(cpputils::logging::ERROR) << "Initialized ConcreteInnerEncryptor with wrong cipher";
return boost::none; return boost::none;
} }
if (innerConfig->cipherName != Cipher::NAME) { auto decrypted = Cipher::decrypt(static_cast<const uint8_t*>(innerConfig.encryptedConfig.data()), innerConfig.encryptedConfig.size(), _key);
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);
if (decrypted == boost::none) { if (decrypted == boost::none) {
return boost::none; return boost::none;
} }
@ -51,10 +48,10 @@ namespace cryfs {
} }
template<class Cipher> template<class Cipher>
cpputils::Data ConcreteInnerEncryptor<Cipher>::encrypt(const cpputils::Data &plaintext) const { InnerConfig ConcreteInnerEncryptor<Cipher>::encrypt(const cpputils::Data &config) const {
auto paddedPlaintext = cpputils::RandomPadding::add(plaintext, CONFIG_SIZE); auto padded = cpputils::RandomPadding::add(config, CONFIG_SIZE);
auto encrypted = Cipher::encrypt(static_cast<const uint8_t*>(paddedPlaintext.data()), paddedPlaintext.size(), _key); auto encrypted = Cipher::encrypt(static_cast<const uint8_t*>(padded.data()), padded.size(), _key);
return InnerConfig{Cipher::NAME, std::move(encrypted)}.serialize(); return InnerConfig{Cipher::NAME, std::move(encrypted)};
} }
} }

View File

@ -7,12 +7,13 @@
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <messmer/cpp-utils/data/Deserializer.h> #include <messmer/cpp-utils/data/Deserializer.h>
#include <messmer/cpp-utils/data/Serializer.h> #include <messmer/cpp-utils/data/Serializer.h>
#include "InnerConfig.h"
namespace cryfs { namespace cryfs {
class InnerEncryptor { class InnerEncryptor {
public: public:
virtual cpputils::Data encrypt(const cpputils::Data &plaintext) const = 0; virtual InnerConfig encrypt(const cpputils::Data &plaintext) const = 0;
virtual boost::optional<cpputils::Data> decrypt(const cpputils::Data &plaintext) const = 0; virtual boost::optional<cpputils::Data> decrypt(const InnerConfig &innerConfig) const = 0;
}; };
} }

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#ifndef CRYFS_OUTERCONFIG_H #ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERCONFIG_H
#define CRYFS_OUTERCONFIG_H #define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERCONFIG_H
#include <messmer/cpp-utils/crypto/kdf/DerivedKeyConfig.h> #include <messmer/cpp-utils/crypto/kdf/DerivedKeyConfig.h>
#include <messmer/cpp-utils/data/Data.h> #include <messmer/cpp-utils/data/Data.h>

View 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);
}
}

View 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

View File

@ -4,6 +4,8 @@
#include <messmer/cpp-utils/tempfile/TempFile.h> #include <messmer/cpp-utils/tempfile/TempFile.h>
#include <boost/optional/optional_io.hpp> #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 namespace cryfs;
using cpputils::TempFile; using cpputils::TempFile;
using std::string; using std::string;
@ -27,8 +29,14 @@ public:
TempFile file; TempFile file;
CryConfig Config() {
CryConfig result;
result.SetCipher("aes-256-gcm");
return result;
}
CryConfigFile CreateAndLoadEmpty(const string &password = "mypassword") { CryConfigFile CreateAndLoadEmpty(const string &password = "mypassword") {
Create(CryConfig(), password); Create(Config(), password);
return Load().value(); return Load().value();
} }
@ -40,6 +48,10 @@ public:
return CryConfigFile::load(file.path(), password); return CryConfigFile::load(file.path(), password);
} }
void CreateWithCipher(const string &cipher) {
return CreateWithCipher(cipher, file);
}
void CreateWithCipher(const string &cipher, const TempFile &tempFile) { void CreateWithCipher(const string &cipher, const TempFile &tempFile) {
CryConfig cfg; CryConfig cfg;
cfg.SetCipher(cipher); cfg.SetCipher(cipher);
@ -48,7 +60,7 @@ public:
}; };
TEST_F(CryConfigFileTest, DoesntLoadIfWrongPassword) { TEST_F(CryConfigFileTest, DoesntLoadIfWrongPassword) {
Create(CryConfig(), "mypassword"); Create(Config(), "mypassword");
auto loaded = Load("mypassword2"); auto loaded = Load("mypassword2");
EXPECT_EQ(none, loaded); EXPECT_EQ(none, loaded);
} }
@ -59,7 +71,7 @@ TEST_F(CryConfigFileTest, RootBlob_Init) {
} }
TEST_F(CryConfigFileTest, RootBlob_CreateAndLoad) { TEST_F(CryConfigFileTest, RootBlob_CreateAndLoad) {
CryConfig cfg; CryConfig cfg = Config();
cfg.SetRootBlob("rootblobid"); cfg.SetRootBlob("rootblobid");
Create(std::move(cfg)); Create(std::move(cfg));
CryConfigFile loaded = Load().value(); CryConfigFile loaded = Load().value();
@ -80,7 +92,7 @@ TEST_F(CryConfigFileTest, EncryptionKey_Init) {
} }
TEST_F(CryConfigFileTest, EncryptionKey_CreateAndLoad) { TEST_F(CryConfigFileTest, EncryptionKey_CreateAndLoad) {
CryConfig cfg; CryConfig cfg = Config();
cfg.SetEncryptionKey("encryptionkey"); cfg.SetEncryptionKey("encryptionkey");
Create(std::move(cfg)); Create(std::move(cfg));
CryConfigFile loaded = Load().value(); CryConfigFile loaded = Load().value();
@ -97,30 +109,42 @@ TEST_F(CryConfigFileTest, EncryptionKey_SaveAndLoad) {
TEST_F(CryConfigFileTest, Cipher_Init) { TEST_F(CryConfigFileTest, Cipher_Init) {
CryConfigFile created = CreateAndLoadEmpty(); CryConfigFile created = CreateAndLoadEmpty();
EXPECT_EQ("", created.config()->Cipher()); EXPECT_EQ("aes-256-gcm", created.config()->Cipher());
} }
TEST_F(CryConfigFileTest, Cipher_CreateAndLoad) { TEST_F(CryConfigFileTest, Cipher_CreateAndLoad) {
CryConfig cfg; CryConfig cfg = Config();
cfg.SetCipher("cipher"); cfg.SetCipher("twofish-128-cfb");
Create(std::move(cfg)); Create(std::move(cfg));
CryConfigFile loaded = Load().value(); CryConfigFile loaded = Load().value();
EXPECT_EQ("cipher", loaded.config()->Cipher()); EXPECT_EQ("twofish-128-cfb", loaded.config()->Cipher());
} }
TEST_F(CryConfigFileTest, Cipher_SaveAndLoad) { TEST_F(CryConfigFileTest, Cipher_SaveAndLoad) {
CryConfigFile created = CreateAndLoadEmpty(); CryConfigFile created = CreateAndLoadEmpty();
created.config()->SetCipher("cipher"); created.config()->SetCipher("twofish-128-cfb");
created.save(); created.save();
CryConfigFile loaded = Load().value(); 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 that the encrypted config file has the same size, no matter how big the plaintext config data.
TEST_F(CryConfigFileTest, ConfigFileHasFixedSize) { TEST_F(CryConfigFileTest, ConfigFileHasFixedSize) {
TempFile file1(false); TempFile file1(false);
TempFile file2(false); TempFile file2(false);
CreateWithCipher("short", file1); //It is important to have different cipher name lengths here, because they're on the outer encryption level.
CreateWithCipher("long_cipher_name_that_causes_the_plaintext_config_data_to_be_larger", file2); //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())); 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());
}

View File

@ -114,7 +114,7 @@ TEST_F(CryConfigLoaderTest, EncryptionKey_Create) {
} }
TEST_F(CryConfigLoaderTest, Cipher_Load) { TEST_F(CryConfigLoaderTest, Cipher_Load) {
CreateWithCipher("ciphername"); CreateWithCipher("twofish-128-cfb");
auto loaded = Load().value(); auto loaded = Load().value();
EXPECT_EQ("ciphername", loaded.config()->Cipher()); EXPECT_EQ("ciphername", loaded.config()->Cipher());
} }