Refactor interface for key derivation functions. This allows easily adding new (password based) key derivation functions.

This commit is contained in:
Sebastian Messmer 2016-02-22 02:38:06 +01:00
parent 7d9d72d82a
commit d5a0b725fe
28 changed files with 395 additions and 327 deletions

View File

@ -2,9 +2,9 @@ project (cpp-utils)
set(SOURCES
crypto/symmetric/ciphers.cpp
crypto/kdf/DerivedKey.cpp
crypto/kdf/Scrypt.cpp
crypto/kdf/DerivedKeyConfig.cpp
crypto/kdf/SCryptParameters.cpp
crypto/kdf/PasswordBasedKDF.cpp
crypto/RandomPadding.cpp
process/daemonize.cpp
process/subprocess.cpp

View File

@ -1 +0,0 @@
#include "DerivedKey.h"

View File

@ -1,39 +0,0 @@
#pragma once
#ifndef MESSMER_CPPUTILS_CRYPTO_KDF_DERIVEDKEY_H
#define MESSMER_CPPUTILS_CRYPTO_KDF_DERIVEDKEY_H
#include "../../data/FixedSizeData.h"
#include "DerivedKeyConfig.h"
namespace cpputils {
template<size_t KEY_LENGTH>
class DerivedKey final {
public:
DerivedKey(DerivedKeyConfig config, const FixedSizeData<KEY_LENGTH> &key): _config(std::move(config)), _key(key) {}
DerivedKey(DerivedKey &&rhs) = default;
const DerivedKeyConfig &config() const {
return _config;
}
DerivedKeyConfig moveOutConfig() {
return std::move(_config);
}
const FixedSizeData<KEY_LENGTH> &key() const {
return _key;
}
FixedSizeData<KEY_LENGTH> moveOutKey() {
return std::move(_key);
}
private:
DerivedKeyConfig _config;
FixedSizeData<KEY_LENGTH> _key;
DISALLOW_COPY_AND_ASSIGN(DerivedKey);
};
}
#endif

View File

@ -1,27 +0,0 @@
#include "DerivedKeyConfig.h"
using std::istream;
using std::ostream;
using boost::optional;
using boost::none;
namespace cpputils {
void DerivedKeyConfig::serialize(Serializer *target) const {
target->writeUint64(_N);
target->writeUint32(_r);
target->writeUint32(_p);
target->writeData(_salt);
}
size_t DerivedKeyConfig::serializedSize() const {
return Serializer::DataSize(_salt) + sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t);
}
DerivedKeyConfig DerivedKeyConfig::deserialize(Deserializer *source) {
uint64_t N = source->readUint64();
uint32_t r = source->readUint32();
uint32_t p = source->readUint32();
Data salt = source->readData();
return DerivedKeyConfig(std::move(salt), N, r, p);
}
}

View File

@ -0,0 +1 @@
#include "PasswordBasedKDF.h"

View File

@ -0,0 +1,31 @@
#pragma once
#ifndef MESSMER_CPPUTILS_CRYPTO_KDF_PASSWORDBASEDKDF_H
#define MESSMER_CPPUTILS_CRYPTO_KDF_PASSWORDBASEDKDF_H
#include "../../data/FixedSizeData.h"
#include "../../data/Data.h"
namespace cpputils {
class PasswordBasedKDF {
public:
virtual ~PasswordBasedKDF() {}
template<size_t KEYSIZE> FixedSizeData<KEYSIZE> deriveKey(const std::string &password);
virtual const Data &kdfParameters() const = 0;
protected:
virtual void derive(void *destination, size_t size, const std::string &password) = 0;
};
template<size_t KEYSIZE> FixedSizeData<KEYSIZE>
inline PasswordBasedKDF::deriveKey(const std::string &password) {
auto result = FixedSizeData<KEYSIZE>::Null();
derive(result.data(), result.BINARY_LENGTH, password);
return result;
}
}
#endif

View File

@ -0,0 +1,42 @@
#include "SCryptParameters.h"
using std::istream;
using std::ostream;
using boost::optional;
using boost::none;
using cpputils::Data;
namespace cpputils {
Data SCryptParameters::serialize() const {
Serializer serializer(_serializedSize());
serializer.writeUint64(_N);
serializer.writeUint32(_r);
serializer.writeUint32(_p);
serializer.writeTailData(_salt);
return serializer.finished();
}
size_t SCryptParameters::_serializedSize() const {
return _salt.size() + sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t);
}
SCryptParameters SCryptParameters::deserialize(const cpputils::Data &data) {
Deserializer deserializer(&data);
uint64_t N = deserializer.readUint64();
uint32_t r = deserializer.readUint32();
uint32_t p = deserializer.readUint32();
Data salt = deserializer.readTailData();
deserializer.finished();
return SCryptParameters(std::move(salt), N, r, p);
}
#ifndef CRYFS_NO_COMPATIBILITY
SCryptParameters SCryptParameters::deserializeOldFormat(Deserializer *source) {
uint64_t N = source->readUint64();
uint32_t r = source->readUint32();
uint32_t p = source->readUint32();
Data salt = source->readData();
return SCryptParameters(std::move(salt), N, r, p);
}
#endif
}

View File

@ -13,19 +13,19 @@ namespace cpputils {
//TODO Test operator==/!=
//TODO Use SCryptSettings as a member here instead of storing _N, _r, _p.
class DerivedKeyConfig final {
class SCryptParameters final {
public:
DerivedKeyConfig(Data salt, uint64_t N, uint32_t r, uint32_t p)
SCryptParameters(Data salt, uint64_t N, uint32_t r, uint32_t p)
: _salt(std::move(salt)),
_N(N), _r(r), _p(p) { }
DerivedKeyConfig(const DerivedKeyConfig &rhs)
SCryptParameters(const SCryptParameters &rhs)
:_salt(rhs._salt.copy()),
_N(rhs._N), _r(rhs._r), _p(rhs._p) { }
DerivedKeyConfig(DerivedKeyConfig &&rhs) = default;
SCryptParameters(SCryptParameters &&rhs) = default;
DerivedKeyConfig &operator=(const DerivedKeyConfig &rhs) {
SCryptParameters &operator=(const SCryptParameters &rhs) {
_salt = rhs._salt.copy();
_N = rhs._N;
_r = rhs._r;
@ -33,7 +33,7 @@ namespace cpputils {
return *this;
}
DerivedKeyConfig &operator=(DerivedKeyConfig &&rhs) = default;
SCryptParameters &operator=(SCryptParameters &&rhs) = default;
const Data &salt() const {
return _salt;
@ -51,24 +51,31 @@ namespace cpputils {
return _p;
}
void serialize(Serializer *destination) const;
cpputils::Data serialize() const;
size_t serializedSize() const;
static SCryptParameters deserialize(const cpputils::Data &data);
static DerivedKeyConfig deserialize(Deserializer *source);
#ifndef CRYFS_NO_COMPATIBILITY
static SCryptParameters deserializeOldFormat(cpputils::Deserializer *deserializer);
size_t serializedSize() const {
return _serializedSize();
}
#endif
private:
size_t _serializedSize() const;
Data _salt;
uint64_t _N;
uint32_t _r;
uint32_t _p;
};
inline bool operator==(const DerivedKeyConfig &lhs, const DerivedKeyConfig &rhs) {
inline bool operator==(const SCryptParameters &lhs, const SCryptParameters &rhs) {
return lhs.salt() == rhs.salt() && lhs.N() == rhs.N() && lhs.r() == rhs.r() && lhs.p() == rhs.p();
}
inline bool operator!=(const DerivedKeyConfig &lhs, const DerivedKeyConfig &rhs) {
inline bool operator!=(const SCryptParameters &lhs, const SCryptParameters &rhs) {
return !operator==(lhs, rhs);
}

View File

@ -1,7 +1,45 @@
#include "Scrypt.h"
using std::string;
namespace cpputils {
constexpr SCryptSettings SCrypt::ParanoidSettings;
constexpr SCryptSettings SCrypt::DefaultSettings;
constexpr SCryptSettings SCrypt::TestSettings;
unique_ref<SCrypt> SCrypt::forNewKey(const SCryptSettings &settings) {
SCryptParameters kdfParameters(Random::PseudoRandom().get(settings.SALT_LEN), settings.N, settings.r, settings.p);
return make_unique_ref<SCrypt>(std::move(kdfParameters));
}
unique_ref<SCrypt> SCrypt::forExistingKey(const Data &parameters) {
return make_unique_ref<SCrypt>(SCryptParameters::deserialize(parameters));
}
SCrypt::SCrypt(SCryptParameters config)
:_config(std::move(config)), _serializedConfig(_config.serialize()), _wasGeneratedBefore(false) {
}
void SCrypt::derive(void *destination, size_t size, const string &password) {
_checkCallOnlyOnce();
int errorcode = crypto_scrypt(reinterpret_cast<const uint8_t*>(password.c_str()), password.size(),
reinterpret_cast<const uint8_t*>(_config.salt().data()), _config.salt().size(),
_config.N(), _config.r(), _config.p(),
static_cast<uint8_t*>(destination), size);
if (errorcode != 0) {
throw std::runtime_error("Error running scrypt key derivation.");
}
}
const Data &SCrypt::kdfParameters() const {
return _serializedConfig;
}
void SCrypt::_checkCallOnlyOnce() {
if (_wasGeneratedBefore) {
throw std::runtime_error("An SCrypt instance can only generate exactly one key. Generating multiple keys would be insecure because we would use the same salt.");
}
_wasGeneratedBefore = true;
}
}

View File

@ -4,11 +4,14 @@
#include "../../macros.h"
#include "../../random/Random.h"
#include "../../pointer/unique_ref.h"
#include "PasswordBasedKDF.h"
extern "C" {
#include <scrypt/lib/crypto/crypto_scrypt.h>
}
#include <stdexcept>
#include "DerivedKey.h"
#include "SCryptParameters.h"
namespace cpputils {
@ -19,36 +22,29 @@ namespace cpputils {
uint32_t p;
};
class SCrypt final {
class SCrypt final : public PasswordBasedKDF {
public:
static constexpr SCryptSettings ParanoidSettings = SCryptSettings {32, 1048576, 8, 16};
static constexpr SCryptSettings DefaultSettings = SCryptSettings {32, 1048576, 4, 1};
static constexpr SCryptSettings TestSettings = SCryptSettings {32, 1024, 1, 1};
SCrypt() {}
static unique_ref<SCrypt> forNewKey(const SCryptSettings &settings);
static unique_ref<SCrypt> forExistingKey(const Data &parameters);
template<size_t KEYSIZE>
DerivedKey<KEYSIZE> generateKey(const std::string &password, const SCryptSettings &settings) {
auto salt = Random::PseudoRandom().get(settings.SALT_LEN);
auto config = DerivedKeyConfig(std::move(salt), settings.N, settings.r, settings.p);
auto key = generateKeyFromConfig<KEYSIZE>(password, config);
return DerivedKey<KEYSIZE>(std::move(config), key);
}
const Data &kdfParameters() const override;
template<size_t KEYSIZE>
FixedSizeData<KEYSIZE> generateKeyFromConfig(const std::string &password, const DerivedKeyConfig &config) {
auto key = FixedSizeData<KEYSIZE>::Null();
int errorcode = crypto_scrypt(reinterpret_cast<const uint8_t*>(password.c_str()), password.size(),
reinterpret_cast<const uint8_t*>(config.salt().data()), config.salt().size(),
config.N(), config.r(), config.p(),
static_cast<uint8_t*>(key.data()), KEYSIZE);
if (errorcode != 0) {
throw std::runtime_error("Error running scrypt key derivation.");
}
return key;
}
SCrypt(SCryptParameters config);
protected:
void derive(void *destination, size_t size, const std::string &password) override;
private:
void _checkCallOnlyOnce();
SCryptParameters _config;
Data _serializedConfig;
bool _wasGeneratedBefore;
DISALLOW_COPY_AND_ASSIGN(SCrypt);
};
}

View File

@ -44,7 +44,12 @@ optional<CryConfigFile> CryConfigFile::load(const bf::path &path, const string &
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));
auto configFile = CryConfigFile(path, std::move(config), std::move(*encryptor));
if (decrypted->wasInDeprecatedConfigFormat) {
// Migrate it to new format
configFile.save();
}
return configFile;
}
CryConfigFile CryConfigFile::create(const bf::path &path, CryConfig config, const string &password, const SCryptSettings &scryptSettings) {

View File

@ -6,8 +6,6 @@ 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;
@ -17,8 +15,8 @@ namespace cryfs {
constexpr size_t CryConfigEncryptor::OuterKeySize;
constexpr size_t CryConfigEncryptor::MaxTotalKeySize;
CryConfigEncryptor::CryConfigEncryptor(DerivedKey<MaxTotalKeySize> derivedKey)
: _derivedKey(std::move(derivedKey)) {
CryConfigEncryptor::CryConfigEncryptor(FixedSizeData<MaxTotalKeySize> derivedKey, cpputils::Data kdfParameters)
: _derivedKey(std::move(derivedKey)), _kdfParameters(std::move(kdfParameters)) {
}
Data CryConfigEncryptor::encrypt(const Data &plaintext, const string &cipherName) const {
@ -29,18 +27,6 @@ namespace cryfs {
}
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;
@ -49,16 +35,24 @@ namespace cryfs {
if(serializedInnerConfig == none) {
return none;
}
return InnerConfig::deserialize(*serializedInnerConfig);
auto innerConfig = InnerConfig::deserialize(*serializedInnerConfig);
if (innerConfig == none) {
return none;
}
auto plaintext = _innerEncryptor(innerConfig->cipherName)->decrypt(*innerConfig);
if (plaintext == none) {
return none;
}
return Decrypted{std::move(*plaintext), innerConfig->cipherName, outerConfig->wasInDeprecatedConfigFormat};
}
unique_ref<OuterEncryptor> CryConfigEncryptor::_outerEncryptor() const {
auto outerKey = _derivedKey.key().take<OuterKeySize>();
return make_unique_ref<OuterEncryptor>(outerKey, _derivedKey.config());
auto outerKey = _derivedKey.take<OuterKeySize>();
return make_unique_ref<OuterEncryptor>(outerKey, _kdfParameters.copy());
}
unique_ref<InnerEncryptor> CryConfigEncryptor::_innerEncryptor(const string &cipherName) const {
auto innerKey = _derivedKey.key().drop<OuterKeySize>();
auto innerKey = _derivedKey.drop<OuterKeySize>();
return CryCiphers::find(cipherName).createInnerConfigEncryptor(innerKey);
}
}

View File

@ -5,8 +5,6 @@
#include <cpp-utils/pointer/unique_ref.h>
#include <cpp-utils/data/Deserializer.h>
#include <cpp-utils/data/Serializer.h>
#include <cpp-utils/crypto/kdf/DerivedKeyConfig.h>
#include <cpp-utils/crypto/kdf/DerivedKey.h>
#include <cpp-utils/crypto/symmetric/ciphers.h>
#include "inner/InnerEncryptor.h"
#include "outer/OuterEncryptor.h"
@ -22,19 +20,20 @@ namespace cryfs {
struct Decrypted {
cpputils::Data data;
std::string cipherName;
bool wasInDeprecatedConfigFormat;
};
CryConfigEncryptor(cpputils::DerivedKey<MaxTotalKeySize> derivedKey);
CryConfigEncryptor(cpputils::FixedSizeData<MaxTotalKeySize> derivedKey, cpputils::Data _kdfParameters);
cpputils::Data encrypt(const cpputils::Data &plaintext, const std::string &cipherName) const;
boost::optional<Decrypted> decrypt(const cpputils::Data &data) const;
private:
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::DerivedKey<MaxTotalKeySize> _derivedKey;
cpputils::FixedSizeData<MaxTotalKeySize> _derivedKey;
cpputils::Data _kdfParameters;
DISALLOW_COPY_AND_ASSIGN(CryConfigEncryptor);
};

View File

@ -8,8 +8,8 @@ using boost::none;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::Data;
using cpputils::DerivedKey;
using cpputils::DerivedKeyConfig;
using cpputils::FixedSizeData;
using cpputils::SCryptParameters;
using cpputils::SCrypt;
using cpputils::SCryptSettings;
using std::string;
@ -22,21 +22,20 @@ namespace cryfs {
if (outerConfig == none) {
return none;
}
auto derivedKey = _deriveKey(outerConfig->keyConfig, password);
return make_unique_ref<CryConfigEncryptor>(std::move(derivedKey));
}
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<CryConfigEncryptor::MaxTotalKeySize>(password, keyConfig);
return DerivedKey<CryConfigEncryptor::MaxTotalKeySize>(keyConfig, std::move(key));
return _deriveKey(SCrypt::forExistingKey(outerConfig->kdfParameters), password);
}
unique_ref<CryConfigEncryptor> CryConfigEncryptorFactory::deriveKey(const string &password, const SCryptSettings &scryptSettings) {
auto derivedKey = cpputils::SCrypt().generateKey<CryConfigEncryptor::MaxTotalKeySize>(password, scryptSettings);
return make_unique_ref<CryConfigEncryptor>(std::move(derivedKey));
return _deriveKey(SCrypt::forNewKey(scryptSettings), password);
}
unique_ref<CryConfigEncryptor>
CryConfigEncryptorFactory::_deriveKey(cpputils::unique_ref<SCrypt> kdf, const 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.
//TODO I might be able to know the actual key size here (at runtime) and switch the SCrypt deriveKey() interface to getting a dynamic size.
auto key = kdf->deriveKey<CryConfigEncryptor::MaxTotalKeySize>(password);
return make_unique_ref<CryConfigEncryptor>(std::move(key), kdf->kdfParameters().copy());
}
}

View File

@ -18,8 +18,7 @@ namespace cryfs {
private:
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);
static cpputils::unique_ref<CryConfigEncryptor> _deriveKey(cpputils::unique_ref<cpputils::SCrypt> kdf, const std::string &password);
};
}

View File

@ -3,7 +3,6 @@
#define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_INNER_CONCRETECRYCONFIGENCRYPTOR_H
#include <cpp-utils/crypto/RandomPadding.h>
#include <cpp-utils/crypto/kdf/DerivedKey.h>
#include "InnerEncryptor.h"
#include "InnerConfig.h"

View File

@ -1,17 +1,21 @@
#include "OuterConfig.h"
#include <cpp-utils/crypto/kdf/SCryptParameters.h>
using std::string;
using std::exception;
using cpputils::Data;
using cpputils::Serializer;
using cpputils::Deserializer;
using cpputils::DerivedKeyConfig;
using cpputils::SCryptParameters;
using boost::optional;
using boost::none;
using namespace cpputils::logging;
namespace cryfs {
const string OuterConfig::HEADER = "cryfs.config;0;scrypt";
#ifndef CRYFS_NO_COMPATIBILITY
const string OuterConfig::OLD_HEADER = "cryfs.config;0;scrypt";
#endif
const string OuterConfig::HEADER = "cryfs.config;1;scrypt";
void OuterConfig::_checkHeader(Deserializer *deserializer) {
string header = deserializer->readString();
@ -27,10 +31,10 @@ namespace cryfs {
Data OuterConfig::serialize() const {
try {
Serializer serializer(Serializer::StringSize(HEADER)
+ keyConfig.serializedSize()
+ Serializer::DataSize(kdfParameters)
+ encryptedInnerConfig.size());
_writeHeader(&serializer);
keyConfig.serialize(&serializer);
serializer.writeData(kdfParameters);
serializer.writeTailData(encryptedInnerConfig);
return serializer.finished();
} catch (const exception &e) {
@ -42,14 +46,37 @@ namespace cryfs {
optional<OuterConfig> OuterConfig::deserialize(const Data &data) {
Deserializer deserializer(&data);
try {
#ifndef CRYFS_NO_COMPATIBILITY
string header = deserializer.readString();
if (header == OLD_HEADER) {
return _deserializeOldFormat(&deserializer);
} else if (header == HEADER) {
return _deserializeNewFormat(&deserializer);
} else {
throw std::runtime_error("Invalid header");
}
#else
_checkHeader(&deserializer);
auto keyConfig = DerivedKeyConfig::deserialize(&deserializer);
auto encryptedInnerConfig = deserializer.readTailData();
deserializer.finished();
return OuterConfig {std::move(keyConfig), std::move(encryptedInnerConfig)};
_deserializeNewFormat(&deserializer);
#endif
} catch (const exception &e) {
LOG(ERROR) << "Error deserializing outer configuration: " << e.what();
return none; // This can be caused by invalid input data and does not have to be a programming error. Don't throw exception.
}
}
OuterConfig OuterConfig::_deserializeOldFormat(Deserializer *deserializer) {
auto kdfParameters = SCryptParameters::deserializeOldFormat(deserializer);
auto kdfParametersSerialized = kdfParameters.serialize();
auto encryptedInnerConfig = deserializer->readTailData();
deserializer->finished();
return OuterConfig {std::move(kdfParametersSerialized), std::move(encryptedInnerConfig), true};
}
OuterConfig OuterConfig::_deserializeNewFormat(Deserializer *deserializer) {
auto kdfParameters = deserializer->readData();
auto encryptedInnerConfig = deserializer->readTailData();
deserializer->finished();
return OuterConfig {std::move(kdfParameters), std::move(encryptedInnerConfig), false};
}
}

View File

@ -2,15 +2,15 @@
#ifndef MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERCONFIG_H
#define MESSMER_CRYFS_SRC_CONFIG_CRYPTO_OUTER_OUTERCONFIG_H
#include <cpp-utils/crypto/kdf/DerivedKeyConfig.h>
#include <cpp-utils/data/Data.h>
#include <cpp-utils/data/Serializer.h>
#include <cpp-utils/data/Deserializer.h>
namespace cryfs {
struct OuterConfig final {
cpputils::DerivedKeyConfig keyConfig;
cpputils::Data kdfParameters;
cpputils::Data encryptedInnerConfig;
bool wasInDeprecatedConfigFormat;
cpputils::Data serialize() const;
static boost::optional<OuterConfig> deserialize(const cpputils::Data &data);
@ -18,8 +18,13 @@ namespace cryfs {
private:
static void _checkHeader(cpputils::Deserializer *deserializer);
static void _writeHeader(cpputils::Serializer *serializer);
static OuterConfig _deserializeNewFormat(cpputils::Deserializer *deserializer);
static const std::string HEADER;
#ifndef CRYFS_NO_COMPATIBILITY
static const std::string OLD_HEADER;
static OuterConfig _deserializeOldFormat(cpputils::Deserializer *deserializer);
#endif
};
}

View File

@ -6,24 +6,23 @@ 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)) {
OuterEncryptor::OuterEncryptor(Cipher::EncryptionKey key, cpputils::Data kdfParameters)
: _key(std::move(key)), _kdfParameters(std::move(kdfParameters)) {
}
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)};
return OuterConfig{_kdfParameters.copy(), std::move(ciphertext), false};
}
optional<Data> OuterEncryptor::decrypt(const OuterConfig &outerConfig) const {
ASSERT(outerConfig.keyConfig == _keyConfig, "OuterEncryptor was initialized with wrong key config");
ASSERT(outerConfig.kdfParameters == _kdfParameters, "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;

View File

@ -14,7 +14,7 @@ namespace cryfs {
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);
OuterEncryptor(Cipher::EncryptionKey key, cpputils::Data kdfParameters);
OuterConfig encrypt(const cpputils::Data &encryptedInnerConfig) const;
boost::optional<cpputils::Data> decrypt(const OuterConfig &outerConfig) const;
@ -22,7 +22,7 @@ namespace cryfs {
private:
Cipher::EncryptionKey _key;
cpputils::DerivedKeyConfig _keyConfig;
cpputils::Data _kdfParameters;
DISALLOW_COPY_AND_ASSIGN(OuterEncryptor);
};

View File

@ -5,8 +5,7 @@ set(SOURCES
crypto/symmetric/CipherTest.cpp
crypto/symmetric/testutils/FakeAuthenticatedCipher.cpp
crypto/kdf/SCryptTest.cpp
crypto/kdf/DerivedKeyTest.cpp
crypto/kdf/DerivedKeyConfigTest.cpp
crypto/kdf/SCryptParametersTest.cpp
MacrosIncludeTest.cpp
pointer/unique_ref_test.cpp
pointer/cast_include_test.cpp

View File

@ -1,86 +0,0 @@
#include <gtest/gtest.h>
#include "cpp-utils/crypto/kdf/DerivedKeyConfig.h"
#include "cpp-utils/data/DataFixture.h"
#include <sstream>
using namespace cpputils;
class DerivedKeyConfigTest : public ::testing::Test {
public:
DerivedKeyConfig SaveAndLoad(const DerivedKeyConfig &source) {
Serializer serializer(source.serializedSize());
source.serialize(&serializer);
Data serialized = serializer.finished();
Deserializer deserializer(&serialized);
return DerivedKeyConfig::deserialize(&deserializer);
}
};
TEST_F(DerivedKeyConfigTest, Salt) {
DerivedKeyConfig cfg(DataFixture::generate(32), 0, 0, 0);
EXPECT_EQ(DataFixture::generate(32), cfg.salt());
}
TEST_F(DerivedKeyConfigTest, Salt_Move) {
DerivedKeyConfig cfg(DataFixture::generate(32), 0, 0, 0);
DerivedKeyConfig moved = std::move(cfg);
EXPECT_EQ(DataFixture::generate(32), moved.salt());
}
TEST_F(DerivedKeyConfigTest, Salt_SaveAndLoad) {
DerivedKeyConfig cfg(DataFixture::generate(32), 0, 0, 0);
DerivedKeyConfig loaded = SaveAndLoad(cfg);
EXPECT_EQ(DataFixture::generate(32), loaded.salt());
}
TEST_F(DerivedKeyConfigTest, N) {
DerivedKeyConfig cfg(Data(0), 1024, 0, 0);
EXPECT_EQ(1024u, cfg.N());
}
TEST_F(DerivedKeyConfigTest, N_Move) {
DerivedKeyConfig cfg(Data(0), 1024, 0, 0);
DerivedKeyConfig moved = std::move(cfg);
EXPECT_EQ(1024u, moved.N());
}
TEST_F(DerivedKeyConfigTest, N_SaveAndLoad) {
DerivedKeyConfig cfg(Data(0), 1024, 0, 0);
DerivedKeyConfig loaded = SaveAndLoad(cfg);
EXPECT_EQ(1024u, loaded.N());
}
TEST_F(DerivedKeyConfigTest, r) {
DerivedKeyConfig cfg(Data(0), 0, 8, 0);
EXPECT_EQ(8u, cfg.r());
}
TEST_F(DerivedKeyConfigTest, r_Move) {
DerivedKeyConfig cfg(Data(0), 0, 8, 0);
DerivedKeyConfig moved = std::move(cfg);
EXPECT_EQ(8u, moved.r());
}
TEST_F(DerivedKeyConfigTest, r_SaveAndLoad) {
DerivedKeyConfig cfg(Data(0), 0, 8, 0);
DerivedKeyConfig loaded = SaveAndLoad(cfg);
EXPECT_EQ(8u, loaded.r());
}
TEST_F(DerivedKeyConfigTest, p) {
DerivedKeyConfig cfg(Data(0), 0, 0, 16);
EXPECT_EQ(16u, cfg.p());
}
TEST_F(DerivedKeyConfigTest, p_Move) {
DerivedKeyConfig cfg(Data(0), 0, 0, 16);
DerivedKeyConfig moved = std::move(cfg);
EXPECT_EQ(16u, moved.p());
}
TEST_F(DerivedKeyConfigTest, p_SaveAndLoad) {
DerivedKeyConfig cfg(Data(0), 0, 0, 16);
DerivedKeyConfig loaded = SaveAndLoad(cfg);
EXPECT_EQ(16u, loaded.p());
}

View File

@ -1,18 +0,0 @@
#include <gtest/gtest.h>
#include "cpp-utils/crypto/kdf/DerivedKey.h"
#include "cpp-utils/data/DataFixture.h"
using namespace cpputils;
TEST(DerivedKeyTest, Config) {
DerivedKey<32> key(DerivedKeyConfig(DataFixture::generate(32, 1), 1024, 8, 16), DataFixture::generateFixedSize<32>(2));
EXPECT_EQ(DataFixture::generate(32, 1), key.config().salt());
EXPECT_EQ(1024u, key.config().N());
EXPECT_EQ(8u, key.config().r());
EXPECT_EQ(16u, key.config().p());
}
TEST(DerivedKeyTest, Key) {
DerivedKey<32> key(DerivedKeyConfig(DataFixture::generate(32, 1), 1024, 8, 16), DataFixture::generateFixedSize<32>(2));
EXPECT_EQ(DataFixture::generateFixedSize<32>(2), key.key());
}

View File

@ -0,0 +1,83 @@
#include <gtest/gtest.h>
#include <cpp-utils/crypto/kdf/SCryptParameters.h>
#include <cpp-utils/data/DataFixture.h>
#include <sstream>
using namespace cpputils;
class SCryptParametersTest : public ::testing::Test {
public:
SCryptParameters SaveAndLoad(const SCryptParameters &source) {
Data serialized = source.serialize();
return SCryptParameters::deserialize(serialized);
}
};
TEST_F(SCryptParametersTest, Salt) {
SCryptParameters cfg(DataFixture::generate(32), 0, 0, 0);
EXPECT_EQ(DataFixture::generate(32), cfg.salt());
}
TEST_F(SCryptParametersTest, Salt_Move) {
SCryptParameters cfg(DataFixture::generate(32), 0, 0, 0);
SCryptParameters moved = std::move(cfg);
EXPECT_EQ(DataFixture::generate(32), moved.salt());
}
TEST_F(SCryptParametersTest, Salt_SaveAndLoad) {
SCryptParameters cfg(DataFixture::generate(32), 0, 0, 0);
SCryptParameters loaded = SaveAndLoad(cfg);
EXPECT_EQ(DataFixture::generate(32), loaded.salt());
}
TEST_F(SCryptParametersTest, N) {
SCryptParameters cfg(Data(0), 1024, 0, 0);
EXPECT_EQ(1024u, cfg.N());
}
TEST_F(SCryptParametersTest, N_Move) {
SCryptParameters cfg(Data(0), 1024, 0, 0);
SCryptParameters moved = std::move(cfg);
EXPECT_EQ(1024u, moved.N());
}
TEST_F(SCryptParametersTest, N_SaveAndLoad) {
SCryptParameters cfg(Data(0), 1024, 0, 0);
SCryptParameters loaded = SaveAndLoad(cfg);
EXPECT_EQ(1024u, loaded.N());
}
TEST_F(SCryptParametersTest, r) {
SCryptParameters cfg(Data(0), 0, 8, 0);
EXPECT_EQ(8u, cfg.r());
}
TEST_F(SCryptParametersTest, r_Move) {
SCryptParameters cfg(Data(0), 0, 8, 0);
SCryptParameters moved = std::move(cfg);
EXPECT_EQ(8u, moved.r());
}
TEST_F(SCryptParametersTest, r_SaveAndLoad) {
SCryptParameters cfg(Data(0), 0, 8, 0);
SCryptParameters loaded = SaveAndLoad(cfg);
EXPECT_EQ(8u, loaded.r());
}
TEST_F(SCryptParametersTest, p) {
SCryptParameters cfg(Data(0), 0, 0, 16);
EXPECT_EQ(16u, cfg.p());
}
TEST_F(SCryptParametersTest, p_Move) {
SCryptParameters cfg(Data(0), 0, 0, 16);
SCryptParameters moved = std::move(cfg);
EXPECT_EQ(16u, moved.p());
}
TEST_F(SCryptParametersTest, p_SaveAndLoad) {
SCryptParameters cfg(Data(0), 0, 0, 16);
SCryptParameters loaded = SaveAndLoad(cfg);
EXPECT_EQ(16u, loaded.p());
}

View File

@ -2,49 +2,65 @@
#include "cpp-utils/crypto/kdf/Scrypt.h"
using namespace cpputils;
using std::string;
TEST(SCryptTest, GeneratedKeyIsReproductible_448) {
auto created = SCrypt().generateKey<56>("mypassword", SCrypt::TestSettings);
auto recreated = SCrypt().generateKeyFromConfig<56>("mypassword", created.config());
EXPECT_EQ(created.key(), recreated);
class SCryptTest : public ::testing::Test {
public:
unique_ref<SCrypt> scryptForNewKey = SCrypt::forNewKey(SCrypt::TestSettings);
unique_ref<SCrypt> scryptForExistingKey = SCrypt::forExistingKey(scryptForNewKey->kdfParameters());
SCryptParameters kdfParameters(const SCrypt &scrypt) {
SCryptParameters result = SCryptParameters::deserialize(scrypt.kdfParameters());
return result;
}
};
TEST_F(SCryptTest, GeneratedKeyIsReproductible_448) {
auto derivedKey = scryptForNewKey->deriveKey<56>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<56>("mypassword");
EXPECT_EQ(derivedKey, rederivedKey);
}
TEST(SCryptTest, GeneratedKeyIsReproductible_256) {
auto created = SCrypt().generateKey<32>("mypassword", SCrypt::TestSettings);
auto recreated = SCrypt().generateKeyFromConfig<32>("mypassword", created.config());
EXPECT_EQ(created.key(), recreated);
TEST_F(SCryptTest, GeneratedKeyIsReproductible_256) {
auto derivedKey = scryptForNewKey->deriveKey<32>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<32>("mypassword");
EXPECT_EQ(derivedKey, rederivedKey);
}
TEST(SCryptTest, GeneratedKeyIsReproductible_128) {
auto created = SCrypt().generateKey<16>("mypassword", SCrypt::TestSettings);
auto recreated = SCrypt().generateKeyFromConfig<16>("mypassword", created.config());
EXPECT_EQ(created.key(), recreated);
TEST_F(SCryptTest, GeneratedKeyIsReproductible_128) {
auto derivedKey = scryptForNewKey->deriveKey<16>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<16>("mypassword");
EXPECT_EQ(derivedKey, rederivedKey);
}
TEST(SCryptTest, GeneratedKeyIsReproductible_DefaultSettings) {
auto created = SCrypt().generateKey<16>("mypassword", SCrypt::DefaultSettings);
auto recreated = SCrypt().generateKeyFromConfig<16>("mypassword", created.config());
EXPECT_EQ(created.key(), recreated);
TEST_F(SCryptTest, GeneratedKeyIsReproductible_DefaultSettings) {
auto derivedKey = scryptForNewKey->deriveKey<16>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<16>("mypassword");
EXPECT_EQ(derivedKey, rederivedKey);
}
TEST(SCryptTest, DifferentPasswordResultsInDifferentKey) {
auto created = SCrypt().generateKey<16>("mypassword", SCrypt::TestSettings);
auto recreated = SCrypt().generateKeyFromConfig<16>("mypassword2", created.config());
EXPECT_NE(created.key(), recreated);
TEST_F(SCryptTest, DifferentPasswordResultsInDifferentKey) {
auto derivedKey = scryptForNewKey->deriveKey<16>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<16>("mypassword2");
EXPECT_NE(derivedKey, rederivedKey);
}
TEST(SCryptTest, UsesCorrectSettings) {
auto created = SCrypt().generateKey<16>("mypassword", SCrypt::TestSettings);
EXPECT_EQ(SCrypt::TestSettings.SALT_LEN, created.config().salt().size());
EXPECT_EQ(SCrypt::TestSettings.N, created.config().N());
EXPECT_EQ(SCrypt::TestSettings.r, created.config().r());
EXPECT_EQ(SCrypt::TestSettings.p, created.config().p());
TEST_F(SCryptTest, UsesCorrectSettings) {
auto scrypt = SCrypt::forNewKey(SCrypt::TestSettings);
auto derivedKey = scrypt->deriveKey<16>("mypassword");
SCryptParameters parameters = kdfParameters(*scrypt);
EXPECT_EQ(SCrypt::TestSettings.SALT_LEN, parameters.salt().size());
EXPECT_EQ(SCrypt::TestSettings.N, parameters.N());
EXPECT_EQ(SCrypt::TestSettings.r, parameters.r());
EXPECT_EQ(SCrypt::TestSettings.p, parameters.p());
}
TEST(SCryptTest, UsesCorrectDefaultSettings) {
auto created = SCrypt().generateKey<16>("mypassword", SCrypt::DefaultSettings);
EXPECT_EQ(SCrypt::DefaultSettings.SALT_LEN, created.config().salt().size());
EXPECT_EQ(SCrypt::DefaultSettings.N, created.config().N());
EXPECT_EQ(SCrypt::DefaultSettings.r, created.config().r());
EXPECT_EQ(SCrypt::DefaultSettings.p, created.config().p());
TEST_F(SCryptTest, UsesCorrectDefaultSettings) {
auto scrypt = SCrypt::forNewKey(SCrypt::DefaultSettings);
auto derivedKey = scrypt->deriveKey<16>("mypassword");
SCryptParameters parameters = kdfParameters(*scrypt);
EXPECT_EQ(SCrypt::DefaultSettings.SALT_LEN, parameters.salt().size());
EXPECT_EQ(SCrypt::DefaultSettings.N, parameters.N());
EXPECT_EQ(SCrypt::DefaultSettings.r, parameters.r());
EXPECT_EQ(SCrypt::DefaultSettings.p, parameters.p());
}

View File

@ -8,8 +8,7 @@ using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::DataFixture;
using cpputils::Data;
using cpputils::DerivedKeyConfig;
using cpputils::DerivedKey;
using cpputils::FixedSizeData;
using cpputils::AES128_CFB;
using cpputils::AES256_GCM;
using cpputils::Twofish256_GCM;
@ -29,7 +28,7 @@ class CryConfigEncryptorTest: public ::testing::Test {
public:
unique_ref<CryConfigEncryptor> makeEncryptor() {
return make_unique_ref<CryConfigEncryptor>(_derivedKey());
return make_unique_ref<CryConfigEncryptor>(_derivedKey(), _kdfParameters());
}
Data changeInnerCipherFieldTo(Data data, const string &newCipherName) {
@ -39,16 +38,17 @@ public:
}
private:
DerivedKey<CryConfigEncryptor::MaxTotalKeySize> _derivedKey() {
auto salt = DataFixture::generate(128, 2);
auto keyConfig = DerivedKeyConfig(std::move(salt), 1024, 1, 2);
auto key = DataFixture::generateFixedSize<CryConfigEncryptor::MaxTotalKeySize>(3);
return DerivedKey<CryConfigEncryptor::MaxTotalKeySize>(std::move(keyConfig), std::move(key));
FixedSizeData<CryConfigEncryptor::MaxTotalKeySize> _derivedKey() {
return DataFixture::generateFixedSize<CryConfigEncryptor::MaxTotalKeySize>(3);
}
Data _kdfParameters() {
return DataFixture::generate(128, 2);
}
unique_ref<OuterEncryptor> _outerEncryptor() {
auto outerKey = _derivedKey().key().take<CryConfigEncryptor::OuterKeySize>();
return make_unique_ref<OuterEncryptor>(outerKey, _derivedKey().config());
auto outerKey = _derivedKey().take<CryConfigEncryptor::OuterKeySize>();
return make_unique_ref<OuterEncryptor>(outerKey, _kdfParameters());
}
InnerConfig _decryptInnerConfig(const Data &data) {

View File

@ -4,7 +4,8 @@
using cpputils::Data;
using cpputils::DataFixture;
using cpputils::DerivedKeyConfig;
using cpputils::Deserializer;
using cpputils::Serializer;
using boost::none;
using std::ostream;
using namespace cryfs;
@ -19,39 +20,36 @@ namespace boost {
class OuterConfigTest: public ::testing::Test {
public:
Data salt() {
Data kdfParameters() {
return DataFixture::generate(128, 2);
}
uint64_t N = 1024;
uint8_t r = 1;
uint8_t p = 2;
};
TEST_F(OuterConfigTest, SomeValues) {
Data serialized = OuterConfig{DerivedKeyConfig(salt(), N, r, p), DataFixture::generate(1024)}.serialize();
Data serialized = OuterConfig{kdfParameters(), DataFixture::generate(1024), false}.serialize();
OuterConfig deserialized = OuterConfig::deserialize(serialized).value();
EXPECT_EQ(DerivedKeyConfig(salt(), N, r, p), deserialized.keyConfig);
EXPECT_EQ(kdfParameters(), deserialized.kdfParameters);
EXPECT_EQ(DataFixture::generate(1024), deserialized.encryptedInnerConfig);
}
TEST_F(OuterConfigTest, DataEmpty) {
Data serialized = OuterConfig{DerivedKeyConfig(salt(), N, r, p), Data(0)}.serialize();
Data serialized = OuterConfig{kdfParameters(), Data(0), false}.serialize();
OuterConfig deserialized = OuterConfig::deserialize(serialized).value();
EXPECT_EQ(DerivedKeyConfig(salt(), N, r, p), deserialized.keyConfig);
EXPECT_EQ(kdfParameters(), deserialized.kdfParameters);
EXPECT_EQ(Data(0), deserialized.encryptedInnerConfig);
}
TEST_F(OuterConfigTest, KeyConfigEmpty) {
Data serialized = OuterConfig{DerivedKeyConfig(Data(0), 0, 0, 0), DataFixture::generate(1024)}.serialize();
Data serialized = OuterConfig{Data(0), DataFixture::generate(1024), false}.serialize();
OuterConfig deserialized = OuterConfig::deserialize(serialized).value();
EXPECT_EQ(DerivedKeyConfig(Data(0), 0, 0, 0), deserialized.keyConfig);
EXPECT_EQ(Data(0), deserialized.kdfParameters);
EXPECT_EQ(DataFixture::generate(1024), deserialized.encryptedInnerConfig);
}
TEST_F(OuterConfigTest, DataAndKeyConfigEmpty) {
Data serialized = OuterConfig{DerivedKeyConfig(Data(0), 0, 0, 0), Data(0)}.serialize();
Data serialized = OuterConfig{Data(0), Data(0), false}.serialize();
OuterConfig deserialized = OuterConfig::deserialize(serialized).value();
EXPECT_EQ(DerivedKeyConfig(Data(0), 0, 0, 0), deserialized.keyConfig);
EXPECT_EQ(Data(0), deserialized.kdfParameters);
EXPECT_EQ(Data(0), deserialized.encryptedInnerConfig);
}

View File

@ -9,7 +9,6 @@ using cpputils::Data;
using cpputils::DataFixture;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::DerivedKeyConfig;
using namespace cryfs;
// This is needed for google test
@ -22,10 +21,13 @@ namespace boost {
class OuterEncryptorTest : public ::testing::Test {
public:
Data kdfParameters() {
return DataFixture::generate(128);
}
unique_ref<OuterEncryptor> makeOuterEncryptor() {
auto key = DataFixture::generateFixedSize<OuterEncryptor::Cipher::EncryptionKey::BINARY_LENGTH>();
auto salt = DataFixture::generate(128);
return make_unique_ref<OuterEncryptor>(key, DerivedKeyConfig(std::move(salt), 1024, 1, 2));
return make_unique_ref<OuterEncryptor>(key, kdfParameters());
}
};