diff --git a/src/config/CryConfigEncryptor.h b/src/config/CryConfigEncryptor.h index 5a42860d..8fa4ba3e 100644 --- a/src/config/CryConfigEncryptor.h +++ b/src/config/CryConfigEncryptor.h @@ -9,6 +9,7 @@ #include #include #include "crypto/Scrypt.h" +#include namespace cryfs { //TODO Test @@ -28,6 +29,13 @@ namespace cryfs { static boost::optional> decrypt(const cpputils::Data &ciphertext, const std::string &password); static cpputils::Data encrypt(const cpputils::Data &plaintext, const ConfigEncryptionKey &key); private: + + static boost::optional> _decrypt(std::stringstream &stream, const std::string &password); + static bool _checkHeader(std::stringstream &stream); + static boost::optional _loadKey(std::stringstream &stream, const std::string &password); + static std::streampos _getStreamSize(std::istream &stream); + static boost::optional _loadAndDecryptData(std::stringstream &stream, const typename Cipher::EncryptionKey &key); + static const std::string header; }; @@ -47,35 +55,93 @@ namespace cryfs { CryConfigEncryptor::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 + boost::optional::ConfigEncryptionKey, cpputils::Data>> + CryConfigEncryptor::_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 + bool CryConfigEncryptor::_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 + boost::optional::ConfigEncryptionKey> CryConfigEncryptor::_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(&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(password, keyConfig); + auto key = SCrypt().generateKeyFromConfig(password, *keyConfig); std::cout << "done" << std::endl; + return ConfigEncryptionKey(std::move(*keyConfig), std::move(key)); + } + + template + std::streampos CryConfigEncryptor::_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 + boost::optional CryConfigEncryptor::_loadAndDecryptData(std::stringstream &stream, const typename Cipher::EncryptionKey &key) { auto data = cpputils::Data::LoadFromStream(stream); auto decrypted = Cipher::decrypt(static_cast(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 cpputils::Data CryConfigEncryptor::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(&serializedKeyConfigSize), sizeof(serializedKeyConfigSize)); + serializedKeyConfig.StoreToStream(stream); auto paddedPlaintext = _addPadding(plaintext); auto ciphertext = Cipher::encrypt(static_cast(paddedPlaintext.data()), paddedPlaintext.size(), key.key()); ciphertext.StoreToStream(stream); @@ -100,6 +166,7 @@ namespace cryfs { uint32_t size; std::memcpy(&size, reinterpret_cast(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); diff --git a/src/config/crypto/DerivedKeyConfig.cpp b/src/config/crypto/DerivedKeyConfig.cpp index b5acf89c..f57353f9 100644 --- a/src/config/crypto/DerivedKeyConfig.cpp +++ b/src/config/crypto/DerivedKeyConfig.cpp @@ -1,32 +1,42 @@ #include "DerivedKeyConfig.h" #include +#include 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::max(), "We don't support salts bigger than 256 byte"); - stream.write(reinterpret_cast(&saltSize), sizeof(saltSize)); - stream.write(static_cast(_salt.data()), saltSize); - stream.write(reinterpret_cast(&_N), sizeof(_N)); - stream.write(reinterpret_cast(&_r), sizeof(_r)); - stream.write(reinterpret_cast(&_p), sizeof(_p)); + Data result(sizeof(saltSize) + saltSize + sizeof(_N) + sizeof(_r) + sizeof(_p)); + //TODO Write and use a DataSerializer class with DataSerializer::write and so on instead of all these memcpy(reinterpret_cast). + std::memcpy(reinterpret_cast(result.data()), reinterpret_cast(&saltSize), sizeof(saltSize)); + std::memcpy(reinterpret_cast(result.dataOffset(sizeof(saltSize))), reinterpret_cast(_salt.data()), saltSize); + std::memcpy(reinterpret_cast(result.dataOffset(sizeof(saltSize)+saltSize)), reinterpret_cast(&_N), sizeof(_N)); + std::memcpy(reinterpret_cast(result.dataOffset(sizeof(saltSize)+saltSize+sizeof(_N))), reinterpret_cast(&_r), sizeof(_r)); + std::memcpy(reinterpret_cast(result.dataOffset(sizeof(saltSize)+saltSize+sizeof(_N)+sizeof(_r))), reinterpret_cast(&_p), sizeof(_p)); + return result; } - DerivedKeyConfig DerivedKeyConfig::load(istream &stream) { + boost::optional DerivedKeyConfig::load(const Data &data) { uint8_t saltSize; - stream.read(reinterpret_cast(&saltSize), sizeof(saltSize)); + //TODO Write and use a DataDeserializer class instead of all these memcpy(reinterpret_cast). + std::memcpy(reinterpret_cast(&saltSize), reinterpret_cast(data.data()), sizeof(saltSize)); Data salt(saltSize); - stream.read(static_cast(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(salt.data()), reinterpret_cast(data.dataOffset(sizeof(saltSize))), saltSize); decltype(_N) N; - stream.read(reinterpret_cast(&N), sizeof(_N)); + std::memcpy(reinterpret_cast(&N), reinterpret_cast(data.dataOffset(sizeof(saltSize)+saltSize)), sizeof(_N)); decltype(_r) r; - stream.read(reinterpret_cast(&r), sizeof(_r)); + std::memcpy(reinterpret_cast(&r), reinterpret_cast(data.dataOffset(sizeof(saltSize)+saltSize+sizeof(_N))), sizeof(_r)); decltype(_p) p; - stream.read(reinterpret_cast(&p), sizeof(_p)); + std::memcpy(reinterpret_cast(&p), reinterpret_cast(data.dataOffset(sizeof(saltSize)+saltSize+sizeof(_N)+sizeof(_r))), sizeof(_p)); return DerivedKeyConfig(std::move(salt), N, r, p); } } diff --git a/src/config/crypto/DerivedKeyConfig.h b/src/config/crypto/DerivedKeyConfig.h index af62d15c..32f75c7a 100644 --- a/src/config/crypto/DerivedKeyConfig.h +++ b/src/config/crypto/DerivedKeyConfig.h @@ -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 load(const cpputils::Data &data); private: cpputils::Data _salt; diff --git a/test/config/crypto/DerivedKeyConfigTest.cpp b/test/config/crypto/DerivedKeyConfigTest.cpp index de48054f..2c60f412 100644 --- a/test/config/crypto/DerivedKeyConfigTest.cpp +++ b/test/config/crypto/DerivedKeyConfigTest.cpp @@ -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(); } };