Refactor CryConfigEncryptor
This commit is contained in:
parent
868e12183a
commit
cde1a3b7b9
@ -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 _removePadding(*decrypted);
|
||||
}
|
||||
return std::make_pair(ConfigEncryptionKey(std::move(keyConfig), std::move(key)), std::move(*plaintext));
|
||||
};
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user