diff --git a/biicode.conf b/biicode.conf index ab437e2d..3f3c4fb1 100644 --- a/biicode.conf +++ b/biicode.conf @@ -5,6 +5,7 @@ google/gmock: 4 google/gtest: 11 messmer/cmake: 3 + messmer/scrypt: 0 messmer/spdlog: 1 [parent] diff --git a/crypto/RandomPadding.cpp b/crypto/RandomPadding.cpp new file mode 100644 index 00000000..79dd6b96 --- /dev/null +++ b/crypto/RandomPadding.cpp @@ -0,0 +1,32 @@ +#include "RandomPadding.h" +#include "../logging/logging.h" +#include "../random/Random.h" + +using boost::optional; +using namespace cpputils::logging; + +namespace cpputils { + Data RandomPadding::add(const Data &data, size_t targetSize) { + uint32_t size = data.size(); + ASSERT(size < targetSize - sizeof(size), "Config data too large. We should increase padding target size."); + Data randomData = Random::PseudoRandom().get(targetSize-sizeof(size)-size); + ASSERT(sizeof(size) + size + randomData.size() == targetSize, "Calculated size of randomData incorrectly"); + Data result(targetSize); + std::memcpy(reinterpret_cast(result.data()), &size, sizeof(size)); + std::memcpy(reinterpret_cast(result.dataOffset(sizeof(size))), reinterpret_cast(data.data()), size); + std::memcpy(reinterpret_cast(result.dataOffset(sizeof(size)+size)), reinterpret_cast(randomData.data()), randomData.size()); + return result; + } + + optional RandomPadding::remove(const Data &data) { + uint32_t size; + std::memcpy(&size, reinterpret_cast(data.data()), sizeof(size)); + if(sizeof(size) + size >= data.size()) { + LOG(ERROR) << "Config file is invalid: Invalid padding."; + return boost::none; + }; + Data result(size); + std::memcpy(reinterpret_cast(result.data()), reinterpret_cast(data.dataOffset(sizeof(size))), size); + return std::move(result); + } +} \ No newline at end of file diff --git a/crypto/RandomPadding.h b/crypto/RandomPadding.h new file mode 100644 index 00000000..147f2b1b --- /dev/null +++ b/crypto/RandomPadding.h @@ -0,0 +1,17 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_RANDOMPADDING_H +#define MESSMER_CPPUTILS_CRYPTO_RANDOMPADDING_H + +#include "../data/Data.h" +#include + +namespace cpputils { + //TODO Test + class RandomPadding { + public: + static Data add(const Data &data, size_t targetSize); + static boost::optional remove(const Data &data); + }; +} + +#endif diff --git a/crypto/kdf/DerivedKey.cpp b/crypto/kdf/DerivedKey.cpp new file mode 100644 index 00000000..7968d456 --- /dev/null +++ b/crypto/kdf/DerivedKey.cpp @@ -0,0 +1 @@ +#include "DerivedKey.h" diff --git a/crypto/kdf/DerivedKey.h b/crypto/kdf/DerivedKey.h new file mode 100644 index 00000000..a985c2df --- /dev/null +++ b/crypto/kdf/DerivedKey.h @@ -0,0 +1,39 @@ +#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 + class DerivedKey { + public: + DerivedKey(DerivedKeyConfig config, const FixedSizeData &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() const { + return _key; + } + + FixedSizeData moveOutKey() { + return std::move(_key); + } + private: + DerivedKeyConfig _config; + FixedSizeData _key; + + DISALLOW_COPY_AND_ASSIGN(DerivedKey); + }; +} + +#endif diff --git a/crypto/kdf/DerivedKeyConfig.cpp b/crypto/kdf/DerivedKeyConfig.cpp new file mode 100644 index 00000000..12bd0440 --- /dev/null +++ b/crypto/kdf/DerivedKeyConfig.cpp @@ -0,0 +1,27 @@ +#include "DerivedKeyConfig.h" + +using std::istream; +using std::ostream; +using boost::optional; +using boost::none; + +namespace cpputils { + void DerivedKeyConfig::serialize(Serializer *target) const { + target->writeData(_salt); + target->writeUint64(_N); + target->writeUint32(_r); + target->writeUint32(_p); + } + + size_t DerivedKeyConfig::serializedSize() const { + return Serializer::DataSize(_salt) + sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t); + } + + DerivedKeyConfig DerivedKeyConfig::load(Deserializer *source) { + Data salt = source->readData(); + uint64_t N = source->readUint64(); + uint32_t r = source->readUint32(); + uint32_t p = source->readUint32(); + return DerivedKeyConfig(std::move(salt), N, r, p); + } +} diff --git a/crypto/kdf/DerivedKeyConfig.h b/crypto/kdf/DerivedKeyConfig.h new file mode 100644 index 00000000..3b4549a0 --- /dev/null +++ b/crypto/kdf/DerivedKeyConfig.h @@ -0,0 +1,51 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_KDF_KEYCONFIG_H +#define MESSMER_CPPUTILS_CRYPTO_KDF_KEYCONFIG_H + +#include "../../data/Data.h" +#include "../../data/Serializer.h" +#include "../../data/Deserializer.h" +#include + +namespace cpputils { + + class DerivedKeyConfig { + public: + DerivedKeyConfig(Data salt, uint64_t N, uint32_t r, uint32_t p) + : _salt(std::move(salt)), + _N(N), _r(r), _p(p) { } + + DerivedKeyConfig(DerivedKeyConfig &&rhs) = default; + + const Data &salt() const { + return _salt; + } + + size_t N() const { + return _N; + } + + size_t r() const { + return _r; + } + + size_t p() const { + return _p; + } + + void serialize(Serializer *destination) const; + + size_t serializedSize() const; + + static DerivedKeyConfig load(Deserializer *source); + + private: + Data _salt; + uint64_t _N; + uint32_t _r; + uint32_t _p; + }; + +} + +#endif diff --git a/crypto/kdf/Scrypt.cpp b/crypto/kdf/Scrypt.cpp new file mode 100644 index 00000000..20521502 --- /dev/null +++ b/crypto/kdf/Scrypt.cpp @@ -0,0 +1,13 @@ +#include "Scrypt.h" + +namespace cpputils { + constexpr size_t SCryptDefaultSettings::SALT_LEN; + constexpr uint64_t SCryptDefaultSettings::N; + constexpr uint32_t SCryptDefaultSettings::r; + constexpr uint32_t SCryptDefaultSettings::p; + + constexpr size_t SCryptParanoidSettings::SALT_LEN; + constexpr uint64_t SCryptParanoidSettings::N; + constexpr uint32_t SCryptParanoidSettings::r; + constexpr uint32_t SCryptParanoidSettings::p; +} \ No newline at end of file diff --git a/crypto/kdf/Scrypt.h b/crypto/kdf/Scrypt.h new file mode 100644 index 00000000..c821aa5a --- /dev/null +++ b/crypto/kdf/Scrypt.h @@ -0,0 +1,57 @@ +#pragma once +#ifndef MESSMER_CPPUTILS_CRYPTO_KDF_SCRYPT_H +#define MESSMER_CPPUTILS_CRYPTO_KDF_SCRYPT_H + +#include "../../macros.h" +#include "../../random/Random.h" +extern "C" { + #include +} +#include +#include "DerivedKey.h" + +namespace cpputils { + + struct SCryptParanoidSettings { + constexpr static size_t SALT_LEN = 32; // Size of the salt + constexpr static uint64_t N = 1048576; // CPU/Memory cost + constexpr static uint32_t r = 8; // Blocksize + constexpr static uint32_t p = 16; // Parallelization + }; + + struct SCryptDefaultSettings { + constexpr static size_t SALT_LEN = 32; // Size of the salt + constexpr static uint64_t N = 524288; // CPU/Memory cost + constexpr static uint32_t r = 1; // Blocksize + constexpr static uint32_t p = 1; // Parallelization + }; + + class SCrypt { + public: + SCrypt() {} + + template DerivedKey generateKey(const std::string &password) { + auto salt = Random::PseudoRandom().get(Settings::SALT_LEN); + auto config = DerivedKeyConfig(std::move(salt), Settings::N, Settings::r, Settings::p); + auto key = generateKeyFromConfig(password, config); + return DerivedKey(std::move(config), key); + } + + template FixedSizeData generateKeyFromConfig(const std::string &password, const DerivedKeyConfig &config) { + auto key = FixedSizeData::Null(); + int errorcode = crypto_scrypt(reinterpret_cast(password.c_str()), password.size(), + reinterpret_cast(config.salt().data()), config.salt().size(), + config.N(), config.r(), config.p(), + static_cast(key.data()), KEYSIZE); + if (errorcode != 0) { + throw std::runtime_error("Error running scrypt key derivation."); + } + return key; + } + + private: + DISALLOW_COPY_AND_ASSIGN(SCrypt); + }; +} + +#endif diff --git a/test/crypto/kdf/DerivedKeyConfigTest.cpp b/test/crypto/kdf/DerivedKeyConfigTest.cpp new file mode 100644 index 00000000..3604ac63 --- /dev/null +++ b/test/crypto/kdf/DerivedKeyConfigTest.cpp @@ -0,0 +1,86 @@ +#include +#include "../../../crypto/kdf/DerivedKeyConfig.h" +#include "../../../data/DataFixture.h" +#include + +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::load(&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(1024, cfg.N()); +} + +TEST_F(DerivedKeyConfigTest, N_Move) { + DerivedKeyConfig cfg(Data(0), 1024, 0, 0); + DerivedKeyConfig moved = std::move(cfg); + EXPECT_EQ(1024, moved.N()); +} + +TEST_F(DerivedKeyConfigTest, N_SaveAndLoad) { + DerivedKeyConfig cfg(Data(0), 1024, 0, 0); + DerivedKeyConfig loaded = SaveAndLoad(cfg); + EXPECT_EQ(1024, loaded.N()); +} + +TEST_F(DerivedKeyConfigTest, r) { + DerivedKeyConfig cfg(Data(0), 0, 8, 0); + EXPECT_EQ(8, cfg.r()); +} + +TEST_F(DerivedKeyConfigTest, r_Move) { + DerivedKeyConfig cfg(Data(0), 0, 8, 0); + DerivedKeyConfig moved = std::move(cfg); + EXPECT_EQ(8, moved.r()); +} + +TEST_F(DerivedKeyConfigTest, r_SaveAndLoad) { + DerivedKeyConfig cfg(Data(0), 0, 8, 0); + DerivedKeyConfig loaded = SaveAndLoad(cfg); + EXPECT_EQ(8, loaded.r()); +} + +TEST_F(DerivedKeyConfigTest, p) { + DerivedKeyConfig cfg(Data(0), 0, 0, 16); + EXPECT_EQ(16, cfg.p()); +} + +TEST_F(DerivedKeyConfigTest, p_Move) { + DerivedKeyConfig cfg(Data(0), 0, 0, 16); + DerivedKeyConfig moved = std::move(cfg); + EXPECT_EQ(16, moved.p()); +} + + +TEST_F(DerivedKeyConfigTest, p_SaveAndLoad) { + DerivedKeyConfig cfg(Data(0), 0, 0, 16); + DerivedKeyConfig loaded = SaveAndLoad(cfg); + EXPECT_EQ(16, loaded.p()); +} \ No newline at end of file diff --git a/test/crypto/kdf/DerivedKeyTest.cpp b/test/crypto/kdf/DerivedKeyTest.cpp new file mode 100644 index 00000000..970e6428 --- /dev/null +++ b/test/crypto/kdf/DerivedKeyTest.cpp @@ -0,0 +1,20 @@ +#include +#include "../../../crypto/kdf/DerivedKey.h" +#include "../../../data/DataFixture.h" + +using namespace cryfs; +using cpputils::DataFixture; +using cpputils::Data; + +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(1024, key.config().N()); + EXPECT_EQ(8, key.config().r()); + EXPECT_EQ(16, 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()); +} diff --git a/test/crypto/kdf/SCryptTest.cpp b/test/crypto/kdf/SCryptTest.cpp new file mode 100644 index 00000000..f2247314 --- /dev/null +++ b/test/crypto/kdf/SCryptTest.cpp @@ -0,0 +1,51 @@ +#include +#include "../../../crypto/kdf/Scrypt.h" +#include "testutils/SCryptTestSettings.h" + +using namespace cryfs; + +TEST(SCryptTest, GeneratedKeyIsReproductible_448) { + auto created = SCrypt().generateKey<56, SCryptTestSettings>("mypassword"); + auto recreated = SCrypt().generateKeyFromConfig<56>("mypassword", created.config()); + EXPECT_EQ(created.key(), recreated); +} + +TEST(SCryptTest, GeneratedKeyIsReproductible_256) { + auto created = SCrypt().generateKey<32, SCryptTestSettings>("mypassword"); + auto recreated = SCrypt().generateKeyFromConfig<32>("mypassword", created.config()); + EXPECT_EQ(created.key(), recreated); +} + +TEST(SCryptTest, GeneratedKeyIsReproductible_128) { + auto created = SCrypt().generateKey<16, SCryptTestSettings>("mypassword"); + auto recreated = SCrypt().generateKeyFromConfig<16>("mypassword", created.config()); + EXPECT_EQ(created.key(), recreated); +} + +TEST(SCryptTest, GeneratedKeyIsReproductible_DefaultSettings) { + auto created = SCrypt().generateKey<16>("mypassword"); + auto recreated = SCrypt().generateKeyFromConfig<16>("mypassword", created.config()); + EXPECT_EQ(created.key(), recreated); +} + +TEST(SCryptTest, DifferentPasswordResultsInDifferentKey) { + auto created = SCrypt().generateKey<16, SCryptTestSettings>("mypassword"); + auto recreated = SCrypt().generateKeyFromConfig<16>("mypassword2", created.config()); + EXPECT_NE(created.key(), recreated); +} + +TEST(SCryptTest, UsesCorrectSettings) { + auto created = SCrypt().generateKey<16, SCryptTestSettings>("mypassword"); + EXPECT_EQ(SCryptTestSettings::SALT_LEN, created.config().salt().size()); + EXPECT_EQ(SCryptTestSettings::N, created.config().N()); + EXPECT_EQ(SCryptTestSettings::r, created.config().r()); + EXPECT_EQ(SCryptTestSettings::p, created.config().p()); +} + +TEST(SCryptTest, UsesCorrectDefaultSettings) { + auto created = SCrypt().generateKey<16>("mypassword"); + EXPECT_EQ(SCryptDefaultSettings::SALT_LEN, created.config().salt().size()); + EXPECT_EQ(SCryptDefaultSettings::N, created.config().N()); + EXPECT_EQ(SCryptDefaultSettings::r, created.config().r()); + EXPECT_EQ(SCryptDefaultSettings::p, created.config().p()); +} diff --git a/test/crypto/kdf/testutils/SCryptTestSettings.cpp b/test/crypto/kdf/testutils/SCryptTestSettings.cpp new file mode 100644 index 00000000..2fda22a2 --- /dev/null +++ b/test/crypto/kdf/testutils/SCryptTestSettings.cpp @@ -0,0 +1,6 @@ +#include "SCryptTestSettings.h" + +constexpr size_t SCryptTestSettings::SALT_LEN; +constexpr uint64_t SCryptTestSettings::N; +constexpr uint32_t SCryptTestSettings::r; +constexpr uint32_t SCryptTestSettings::p; diff --git a/test/crypto/kdf/testutils/SCryptTestSettings.h b/test/crypto/kdf/testutils/SCryptTestSettings.h new file mode 100644 index 00000000..cbbdd16f --- /dev/null +++ b/test/crypto/kdf/testutils/SCryptTestSettings.h @@ -0,0 +1,14 @@ +#ifndef MESSMER_CPPUTILS_TEST_CRYPTO_KDF_TESTUTILS_SCRYPTTESTSETTINGS_H +#define MESSMER_CPPUTILS_TEST_CRYPTO_KDF_TESTUTILS_SCRYPTTESTSETTINGS_H + +#include +#include + +struct SCryptTestSettings { + constexpr static size_t SALT_LEN = 32; // Size of the salt + constexpr static uint64_t N = 1024; // CPU/Memory cost + constexpr static uint32_t r = 1; // Blocksize + constexpr static uint32_t p = 1; // Parallelization +}; + +#endif