Refactor CryConfigEncryptor

This commit is contained in:
Sebastian Messmer 2015-10-25 11:33:30 +01:00
parent 868e12183a
commit cde1a3b7b9
4 changed files with 107 additions and 32 deletions

View File

@ -9,6 +9,7 @@
#include <utility>
#include <stdexcept>
#include "crypto/Scrypt.h"
#include <sstream>
namespace cryfs {
//TODO Test
@ -28,6 +29,13 @@ namespace cryfs {
static boost::optional<std::pair<ConfigEncryptionKey, cpputils::Data>> decrypt(const cpputils::Data &ciphertext, const std::string &password);
static cpputils::Data encrypt(const cpputils::Data &plaintext, const ConfigEncryptionKey &key);
private:
static boost::optional<std::pair<ConfigEncryptionKey, cpputils::Data>> _decrypt(std::stringstream &stream, const std::string &password);
static bool _checkHeader(std::stringstream &stream);
static boost::optional<ConfigEncryptionKey> _loadKey(std::stringstream &stream, const std::string &password);
static std::streampos _getStreamSize(std::istream &stream);
static boost::optional<cpputils::Data> _loadAndDecryptData(std::stringstream &stream, const typename Cipher::EncryptionKey &key);
static const std::string header;
};
@ -47,35 +55,93 @@ namespace cryfs {
CryConfigEncryptor<Cipher>::decrypt(const cpputils::Data &ciphertext, const std::string &password) {
std::stringstream stream;
ciphertext.StoreToStream(stream);
char readHeader[header.size()+1];
stream.read(readHeader, header.size()+1);
if (readHeader[header.size()] != '\0' || header != std::string(readHeader)) {
cpputils::logging::LOG(cpputils::logging::ERROR) << "Wrong config file format";
return _decrypt(stream, password);
};
template<class Cipher>
boost::optional<std::pair<typename CryConfigEncryptor<Cipher>::ConfigEncryptionKey, cpputils::Data>>
CryConfigEncryptor<Cipher>::_decrypt(std::stringstream &stream, const std::string &password) {
if(!_checkHeader(stream)) {
return boost::none;
}
auto key = _loadKey(stream, password);
if (key == boost::none) {
return boost::none;
}
auto plaintext = _loadAndDecryptData(stream, key->key());
if (plaintext == boost::none) {
return boost::none;
}
return std::make_pair(std::move(*key), std::move(*plaintext));
};
template<class Cipher>
bool CryConfigEncryptor<Cipher>::_checkHeader(std::stringstream &stream) {
char readHeader[header.size()+1];
stream.read(readHeader, header.size()+1);
bool headerOk = readHeader[header.size()] == '\0' && header == std::string(readHeader);
if (!headerOk) {
cpputils::logging::LOG(cpputils::logging::ERROR) << "Wrong config file format";
}
return headerOk;
}
template<class Cipher>
boost::optional<typename CryConfigEncryptor<Cipher>::ConfigEncryptionKey> CryConfigEncryptor<Cipher>::_loadKey(std::stringstream &stream, const std::string &password) {
uint32_t serializedKeyConfigSize;
uint32_t streamSize = _getStreamSize(stream);
if(streamSize < sizeof(serializedKeyConfigSize)) {
return boost::none;
}
stream.read(reinterpret_cast<char*>(&serializedKeyConfigSize), sizeof(serializedKeyConfigSize));
if((streamSize-sizeof(serializedKeyConfigSize)) < serializedKeyConfigSize) {
return boost::none;
}
cpputils::Data serializedKeyConfig = cpputils::Data::LoadFromStream(stream, serializedKeyConfigSize);
auto keyConfig = DerivedKeyConfig::load(serializedKeyConfig);
if (keyConfig == boost::none) {
return boost::none;
}
auto keyConfig = DerivedKeyConfig::load(stream);
std::cout << "Deriving secure key for config file..." << std::flush;
auto key = SCrypt().generateKeyFromConfig<Cipher::EncryptionKey::BINARY_LENGTH>(password, keyConfig);
auto key = SCrypt().generateKeyFromConfig<Cipher::EncryptionKey::BINARY_LENGTH>(password, *keyConfig);
std::cout << "done" << std::endl;
return ConfigEncryptionKey(std::move(*keyConfig), std::move(key));
}
template<class Cipher>
std::streampos CryConfigEncryptor<Cipher>::_getStreamSize(std::istream &stream) {
auto current_pos = stream.tellg();
//Retrieve length
stream.seekg(0, stream.end);
auto endpos = stream.tellg();
//Restore old position
stream.seekg(current_pos, stream.beg);
return endpos - current_pos;
}
template<class Cipher>
boost::optional<cpputils::Data> CryConfigEncryptor<Cipher>::_loadAndDecryptData(std::stringstream &stream, const typename Cipher::EncryptionKey &key) {
auto data = cpputils::Data::LoadFromStream(stream);
auto decrypted = Cipher::decrypt(static_cast<const uint8_t*>(data.data()), data.size(), key);
if (decrypted == boost::none) {
cpputils::logging::LOG(cpputils::logging::ERROR) << "Couldn't load config file. Wrong password?";
return boost::none;
}
auto plaintext = _removePadding(*decrypted);
if (plaintext == boost::none) {
cpputils::logging::LOG(cpputils::logging::ERROR) << "Config file is invalid: Invalid padding.";
return boost::none;
}
return std::make_pair(ConfigEncryptionKey(std::move(keyConfig), std::move(key)), std::move(*plaintext));
};
return _removePadding(*decrypted);
}
template<class Cipher>
cpputils::Data CryConfigEncryptor<Cipher>::encrypt(const cpputils::Data &plaintext, const ConfigEncryptionKey &key) {
std::stringstream stream;
stream.write(header.c_str(), header.size()+1);
key.config().save(stream);
cpputils::Data serializedKeyConfig = key.config().save();
uint32_t serializedKeyConfigSize = serializedKeyConfig.size();
stream.write(reinterpret_cast<const char*>(&serializedKeyConfigSize), sizeof(serializedKeyConfigSize));
serializedKeyConfig.StoreToStream(stream);
auto paddedPlaintext = _addPadding(plaintext);
auto ciphertext = Cipher::encrypt(static_cast<const uint8_t*>(paddedPlaintext.data()), paddedPlaintext.size(), key.key());
ciphertext.StoreToStream(stream);
@ -100,6 +166,7 @@ namespace cryfs {
uint32_t size;
std::memcpy(&size, reinterpret_cast<const char*>(data.data()), sizeof(size));
if(sizeof(size) + size >= CONFIG_SIZE) {
cpputils::logging::LOG(cpputils::logging::ERROR) << "Config file is invalid: Invalid padding.";
return boost::none;
};
cpputils::Data result(size);

View File

@ -1,32 +1,42 @@
#include "DerivedKeyConfig.h"
#include <messmer/cpp-utils/assert/assert.h>
#include <messmer/cpp-utils/logging/logging.h>
using std::istream;
using std::ostream;
using cpputils::Data;
using namespace cpputils::logging;
namespace cryfs {
void DerivedKeyConfig::save(ostream &stream) const {
Data DerivedKeyConfig::save() const {
uint8_t saltSize = _salt.size();
ASSERT(saltSize < std::numeric_limits<uint8_t>::max(), "We don't support salts bigger than 256 byte");
stream.write(reinterpret_cast<const char *>(&saltSize), sizeof(saltSize));
stream.write(static_cast<const char *>(_salt.data()), saltSize);
stream.write(reinterpret_cast<const char *>(&_N), sizeof(_N));
stream.write(reinterpret_cast<const char *>(&_r), sizeof(_r));
stream.write(reinterpret_cast<const char *>(&_p), sizeof(_p));
Data result(sizeof(saltSize) + saltSize + sizeof(_N) + sizeof(_r) + sizeof(_p));
//TODO Write and use a DataSerializer class with DataSerializer::write<uint8_t> and so on instead of all these memcpy(reinterpret_cast).
std::memcpy(reinterpret_cast<char*>(result.data()), reinterpret_cast<const char*>(&saltSize), sizeof(saltSize));
std::memcpy(reinterpret_cast<char*>(result.dataOffset(sizeof(saltSize))), reinterpret_cast<const char*>(_salt.data()), saltSize);
std::memcpy(reinterpret_cast<char*>(result.dataOffset(sizeof(saltSize)+saltSize)), reinterpret_cast<const char*>(&_N), sizeof(_N));
std::memcpy(reinterpret_cast<char*>(result.dataOffset(sizeof(saltSize)+saltSize+sizeof(_N))), reinterpret_cast<const char*>(&_r), sizeof(_r));
std::memcpy(reinterpret_cast<char*>(result.dataOffset(sizeof(saltSize)+saltSize+sizeof(_N)+sizeof(_r))), reinterpret_cast<const char*>(&_p), sizeof(_p));
return result;
}
DerivedKeyConfig DerivedKeyConfig::load(istream &stream) {
boost::optional<DerivedKeyConfig> DerivedKeyConfig::load(const Data &data) {
uint8_t saltSize;
stream.read(reinterpret_cast<char *>(&saltSize), sizeof(saltSize));
//TODO Write and use a DataDeserializer class instead of all these memcpy(reinterpret_cast).
std::memcpy(reinterpret_cast<char*>(&saltSize), reinterpret_cast<const char*>(data.data()), sizeof(saltSize));
Data salt(saltSize);
stream.read(static_cast<char *>(salt.data()), saltSize);
if (sizeof(saltSize) + saltSize + sizeof(_N) + sizeof(_p) + sizeof(_r) != data.size()) {
LOG(ERROR) << "Could not load DerivedKeyConfig. Wrong size.";
return boost::none;
}
std::memcpy(reinterpret_cast<char*>(salt.data()), reinterpret_cast<const char*>(data.dataOffset(sizeof(saltSize))), saltSize);
decltype(_N) N;
stream.read(reinterpret_cast<char *>(&N), sizeof(_N));
std::memcpy(reinterpret_cast<char*>(&N), reinterpret_cast<const char*>(data.dataOffset(sizeof(saltSize)+saltSize)), sizeof(_N));
decltype(_r) r;
stream.read(reinterpret_cast<char *>(&r), sizeof(_r));
std::memcpy(reinterpret_cast<char*>(&r), reinterpret_cast<const char*>(data.dataOffset(sizeof(saltSize)+saltSize+sizeof(_N))), sizeof(_r));
decltype(_p) p;
stream.read(reinterpret_cast<char *>(&p), sizeof(_p));
std::memcpy(reinterpret_cast<char*>(&p), reinterpret_cast<const char*>(data.dataOffset(sizeof(saltSize)+saltSize+sizeof(_N)+sizeof(_r))), sizeof(_p));
return DerivedKeyConfig(std::move(salt), N, r, p);
}
}

View File

@ -31,9 +31,9 @@ namespace cryfs {
return _p;
}
void save(std::ostream &stream) const;
cpputils::Data save() const;
static DerivedKeyConfig load(std::istream &stream);
static boost::optional<DerivedKeyConfig> load(const cpputils::Data &data);
private:
cpputils::Data _salt;

View File

@ -6,14 +6,12 @@
using namespace cryfs;
using cpputils::DataFixture;
using cpputils::Data;
using std::stringstream;
class DerivedKeyConfigTest : public ::testing::Test {
public:
DerivedKeyConfig SaveAndLoad(const DerivedKeyConfig &source) {
stringstream str;
source.save(str);
return DerivedKeyConfig::load(str);
Data serialized = source.save();
return DerivedKeyConfig::load(serialized).value();
}
};