diff --git a/implementations/encrypted/Cipher.cpp b/implementations/encrypted/Cipher.cpp new file mode 100644 index 00000000..c866c29b --- /dev/null +++ b/implementations/encrypted/Cipher.cpp @@ -0,0 +1,2 @@ +#include "Cipher.h" + diff --git a/implementations/encrypted/Cipher.h b/implementations/encrypted/Cipher.h new file mode 100644 index 00000000..b5528a34 --- /dev/null +++ b/implementations/encrypted/Cipher.h @@ -0,0 +1,26 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHER_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHER_H_ + +#include + +namespace blockstore { +namespace encrypted { + +class Cipher { +public: + virtual ~Cipher() {} + + virtual unsigned int ciphertextBlockSize(unsigned int ciphertextBlockSize) const = 0; + virtual unsigned int plaintextBlockSize(unsigned int plaintextBlockSize) const = 0; + + virtual void encrypt(const byte *plaintext, unsigned int plaintextSize, byte *ciphertext) const = 0; + virtual void decrypt(const byte *ciphertext, byte *plaintext, unsigned int plaintextSize) const = 0; +}; + +} +} + + + +#endif diff --git a/implementations/encrypted/EncryptedBlock.cpp b/implementations/encrypted/EncryptedBlock.cpp index 59da307d..5f66d9c9 100644 --- a/implementations/encrypted/EncryptedBlock.cpp +++ b/implementations/encrypted/EncryptedBlock.cpp @@ -1,91 +1 @@ #include "EncryptedBlock.h" -#include - -#include "../../utils/BlockStoreUtils.h" - -using CryptoPP::CFB_Mode; -using CryptoPP::AES; - -using std::make_unique; - -//TODO not only encryption, but also hmac - -namespace blockstore { -namespace encrypted { - -constexpr unsigned int EncryptedBlock::IV_SIZE; - -std::unique_ptr EncryptedBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, Data data, const EncryptionKey &encKey) { - Data encrypted = _encrypt(data, encKey); - auto baseBlock = baseBlockStore->tryCreate(key, std::move(encrypted)); - if (baseBlock.get() == nullptr) { - //TODO Test this code branch - return nullptr; - } - - return make_unique(std::move(baseBlock), encKey, std::move(data)); -} - -EncryptedBlock::EncryptedBlock(std::unique_ptr baseBlock, const EncryptionKey &encKey) - :EncryptedBlock(std::move(baseBlock), encKey, Data(USEABLE_BLOCK_SIZE(baseBlock->size()))) { - _decryptFromBaseBlock(); -} - -EncryptedBlock::EncryptedBlock(std::unique_ptr baseBlock, const EncryptionKey &encKey, Data plaintextData) - :Block(baseBlock->key()), - _baseBlock(std::move(baseBlock)), - _plaintextData(std::move(plaintextData)), - _encKey(encKey), - _dataChanged(false) { -} - -EncryptedBlock::~EncryptedBlock() { - _encryptToBaseBlock(); -} - -const void *EncryptedBlock::data() const { - return _plaintextData.data(); -} - -void EncryptedBlock::write(const void *source, uint64_t offset, uint64_t size) { - assert(offset <= _plaintextData.size() && offset + size <= _plaintextData.size()); //Also check offset < _data->size() because of possible overflow in the addition - std::memcpy((uint8_t*)_plaintextData.data()+offset, source, size); - _dataChanged = true; -} - -void EncryptedBlock::flush() { - _encryptToBaseBlock(); - return _baseBlock->flush(); -} - -size_t EncryptedBlock::size() const { - return _plaintextData.size(); -} - -void EncryptedBlock::_decryptFromBaseBlock() { - const byte *iv = (byte*)_baseBlock->data(); - const byte *data = (byte*)_baseBlock->data() + IV_SIZE; - auto decryption = CFB_Mode::Decryption((byte*)_encKey.data(), EncryptionKey::BINARY_LENGTH, iv); - decryption.ProcessData((byte*)_plaintextData.data(), data, _plaintextData.size()); -} - -void EncryptedBlock::_encryptToBaseBlock() { - if (_dataChanged) { - Data encrypted = _encrypt(_plaintextData, _encKey); - _baseBlock->write(encrypted.data(), 0, encrypted.size()); - _dataChanged = false; - } -} - -Data EncryptedBlock::_encrypt(const Data &plaintext, const EncryptionKey &encKey) { - FixedSizeData iv = FixedSizeData::CreateRandom(); - auto encryption = CFB_Mode::Encryption(encKey.data(), EncryptionKey::BINARY_LENGTH, iv.data()); - //TODO More performance when not using "Data encrypted" object, but encrypting directly to a target that was specified via a parameter using a specialized CryptoPP sink - Data encrypted(IV_SIZE + plaintext.size()); - std::memcpy(encrypted.data(), iv.data(), IV_SIZE); - encryption.ProcessData((byte*)encrypted.data() + IV_SIZE, (byte*)plaintext.data(), plaintext.size()); - return encrypted; -} - -} -} diff --git a/implementations/encrypted/EncryptedBlock.h b/implementations/encrypted/EncryptedBlock.h index 84b66019..4be10ef9 100644 --- a/implementations/encrypted/EncryptedBlock.h +++ b/implementations/encrypted/EncryptedBlock.h @@ -3,7 +3,7 @@ #define BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCK_H_ #include "../../interface/Block.h" -#include "EncryptionKey.h" +#include "Cipher.h" #include "../../utils/Data.h" #include "../../interface/BlockStore.h" @@ -12,16 +12,19 @@ namespace blockstore { namespace encrypted { -class EncryptedBlockStore; +template class EncryptedBlockStore; +//TODO not only encryption, but also hmac + +template class EncryptedBlock: public Block { public: - //TODO Storing key twice (in parent class and in object pointed to). Once would be enough. - EncryptedBlock(std::unique_ptr baseBlock, const EncryptionKey &encKey); - EncryptedBlock(std::unique_ptr baseBlock, const EncryptionKey &encKey, Data plaintextData); - virtual ~EncryptedBlock(); + static std::unique_ptr TryCreateNew(BlockStore *baseBlockStore, const Key &key, Data data, const typename Cipher::EncryptionKey &encKey); - static std::unique_ptr TryCreateNew(BlockStore *baseBlockStore, const Key &key, Data data, const EncryptionKey &encKey); + //TODO Storing key twice (in parent class and in object pointed to). Once would be enough. + EncryptedBlock(std::unique_ptr baseBlock, const typename Cipher::EncryptionKey &key); + EncryptedBlock(std::unique_ptr baseBlock, const typename Cipher::EncryptionKey &key, Data plaintextData); + virtual ~EncryptedBlock(); const void *data() const override; void write(const void *source, uint64_t offset, uint64_t size) override; @@ -29,33 +32,99 @@ public: size_t size() const override; - static constexpr unsigned int BASE_BLOCK_SIZE(unsigned int useableBlockSize) { - return useableBlockSize + IV_SIZE; - } - - static constexpr unsigned int USEABLE_BLOCK_SIZE(unsigned int baseBlockSize) { - return baseBlockSize - IV_SIZE; - } + std::unique_ptr releaseBlock(); private: std::unique_ptr _baseBlock; Data _plaintextData; - EncryptionKey _encKey; + typename Cipher::EncryptionKey _encKey; bool _dataChanged; - static constexpr unsigned int IV_SIZE = CryptoPP::AES::BLOCKSIZE; - - byte *baseBlockIV(); - byte *baseBlockData(); - void _encryptToBaseBlock(); void _decryptFromBaseBlock(); - static Data _encrypt(const Data &plaintext, const EncryptionKey &encKey); - DISALLOW_COPY_AND_ASSIGN(EncryptedBlock); }; + + +template +std::unique_ptr> EncryptedBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, Data data, const typename Cipher::EncryptionKey &encKey) { + Data encrypted(Cipher::ciphertextSize(data.size())); + Cipher::encrypt((byte*)data.data(), data.size(), (byte*)encrypted.data(), encKey); + auto baseBlock = baseBlockStore->tryCreate(key, std::move(encrypted)); + if (baseBlock.get() == nullptr) { + //TODO Test this code branch + return nullptr; + } + + return std::make_unique(std::move(baseBlock), encKey, std::move(data)); +} + +template +EncryptedBlock::EncryptedBlock(std::unique_ptr baseBlock, const typename Cipher::EncryptionKey &encKey) + :EncryptedBlock(std::move(baseBlock), encKey, Data(Cipher::plaintextSize(baseBlock->size()))) { + _decryptFromBaseBlock(); +} + +template +EncryptedBlock::EncryptedBlock(std::unique_ptr baseBlock, const typename Cipher::EncryptionKey &encKey, Data plaintextData) + :Block(baseBlock->key()), + _baseBlock(std::move(baseBlock)), + _plaintextData(std::move(plaintextData)), + _encKey(encKey), + _dataChanged(false) { +} + +template +EncryptedBlock::~EncryptedBlock() { + _encryptToBaseBlock(); +} + +template +const void *EncryptedBlock::data() const { + return _plaintextData.data(); +} + +template +void EncryptedBlock::write(const void *source, uint64_t offset, uint64_t size) { + assert(offset <= _plaintextData.size() && offset + size <= _plaintextData.size()); //Also check offset < _data->size() because of possible overflow in the addition + std::memcpy((uint8_t*)_plaintextData.data()+offset, source, size); + _dataChanged = true; +} + +template +void EncryptedBlock::flush() { + _encryptToBaseBlock(); + return _baseBlock->flush(); +} + +template +size_t EncryptedBlock::size() const { + return _plaintextData.size(); +} + +template +void EncryptedBlock::_decryptFromBaseBlock() { + //TODO Change BlockStore so we can read their "class Data" objects instead of "void *data()", and then we can change the Cipher interface to take Data objects instead of "byte *" + size + Cipher::decrypt((byte*)_baseBlock->data(), (byte*)_plaintextData.data(), _plaintextData.size(), _encKey); +} + +template +void EncryptedBlock::_encryptToBaseBlock() { + if (_dataChanged) { + Data encrypted(Cipher::ciphertextSize(_plaintextData.size())); + Cipher::encrypt((byte*)_plaintextData.data(), _plaintextData.size(), (byte*)encrypted.data(), _encKey); + _baseBlock->write(encrypted.data(), 0, encrypted.size()); + _dataChanged = false; + } +} + +template +std::unique_ptr EncryptedBlock::releaseBlock() { + return std::move(_baseBlock); +} + } } diff --git a/implementations/encrypted/EncryptedBlockStore.cpp b/implementations/encrypted/EncryptedBlockStore.cpp index 1eb88f42..5ab7ca32 100644 --- a/implementations/encrypted/EncryptedBlockStore.cpp +++ b/implementations/encrypted/EncryptedBlockStore.cpp @@ -1,42 +1 @@ #include "EncryptedBlockStore.h" -#include "EncryptedBlock.h" -#include -#include "../../utils/BlockStoreUtils.h" - -using std::unique_ptr; -using std::make_unique; - -namespace blockstore { -namespace encrypted { - -EncryptedBlockStore::EncryptedBlockStore(unique_ptr baseBlockStore, const EncryptionKey &encKey) - : _baseBlockStore(std::move(baseBlockStore)), _encKey(encKey) { -} - -Key EncryptedBlockStore::createKey() { - return _baseBlockStore->createKey(); -} - -unique_ptr EncryptedBlockStore::tryCreate(const Key &key, Data data) { - return EncryptedBlock::TryCreateNew(_baseBlockStore.get(), key, std::move(data), _encKey); -} - -unique_ptr EncryptedBlockStore::load(const Key &key) { - auto block = _baseBlockStore->load(key); - if (block.get() == nullptr) { - return nullptr; - } - return make_unique(std::move(block), _encKey); -} - -void EncryptedBlockStore::remove(unique_ptr block) { - return _baseBlockStore->remove(std::move(block)); -} - -uint64_t EncryptedBlockStore::numBlocks() const { - return _baseBlockStore->numBlocks(); -} - -} -} - diff --git a/implementations/encrypted/EncryptedBlockStore.h b/implementations/encrypted/EncryptedBlockStore.h index 2e897d1d..c7933216 100644 --- a/implementations/encrypted/EncryptedBlockStore.h +++ b/implementations/encrypted/EncryptedBlockStore.h @@ -4,14 +4,17 @@ #include "../../interface/BlockStore.h" #include -#include "EncryptionKey.h" +#include +#include "Cipher.h" +#include "EncryptedBlock.h" namespace blockstore { namespace encrypted { +template class EncryptedBlockStore: public BlockStore { public: - EncryptedBlockStore(std::unique_ptr baseBlockStore, const EncryptionKey &encKey); + EncryptedBlockStore(std::unique_ptr baseBlockStore, const typename Cipher::EncryptionKey &encKey); Key createKey() override; std::unique_ptr tryCreate(const Key &key, Data data) override; @@ -21,11 +24,48 @@ public: private: std::unique_ptr _baseBlockStore; - EncryptionKey _encKey; + typename Cipher::EncryptionKey _encKey; DISALLOW_COPY_AND_ASSIGN(EncryptedBlockStore); }; + + +template +EncryptedBlockStore::EncryptedBlockStore(std::unique_ptr baseBlockStore, const typename Cipher::EncryptionKey &encKey) + : _baseBlockStore(std::move(baseBlockStore)), _encKey(encKey) { +} + +template +Key EncryptedBlockStore::createKey() { + return _baseBlockStore->createKey(); +} + +template +std::unique_ptr EncryptedBlockStore::tryCreate(const Key &key, Data data) { + return EncryptedBlock::TryCreateNew(_baseBlockStore.get(), key, std::move(data), _encKey); +} + +template +std::unique_ptr EncryptedBlockStore::load(const Key &key) { + auto block = _baseBlockStore->load(key); + if (block.get() == nullptr) { + return nullptr; + } + return std::make_unique>(std::move(block), _encKey); +} + +template +void EncryptedBlockStore::remove(std::unique_ptr block) { + auto baseBlock = cpputils::dynamic_pointer_move>(block)->releaseBlock(); + return _baseBlockStore->remove(std::move(baseBlock)); +} + +template +uint64_t EncryptedBlockStore::numBlocks() const { + return _baseBlockStore->numBlocks(); +} + } } diff --git a/implementations/encrypted/EncryptionKey.cpp b/implementations/encrypted/EncryptionKey.cpp deleted file mode 100644 index 18a97b45..00000000 --- a/implementations/encrypted/EncryptionKey.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "EncryptionKey.h" diff --git a/implementations/encrypted/EncryptionKey.h b/implementations/encrypted/EncryptionKey.h deleted file mode 100644 index 97cd7a9f..00000000 --- a/implementations/encrypted/EncryptionKey.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTIONKEY_H_ -#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTIONKEY_H_ - -#include "../../utils/FixedSizeData.h" - -namespace blockstore { -namespace encrypted { - -using EncryptionKey = FixedSizeData; - -} -} - -#endif diff --git a/implementations/encrypted/ciphers/AES256_CFB.cpp b/implementations/encrypted/ciphers/AES256_CFB.cpp new file mode 100644 index 00000000..bad5562f --- /dev/null +++ b/implementations/encrypted/ciphers/AES256_CFB.cpp @@ -0,0 +1,27 @@ +#include +#include "AES256_CFB.h" + +using CryptoPP::CFB_Mode; +using CryptoPP::AES; + +namespace blockstore { +namespace encrypted { + +constexpr unsigned int AES256_CFB::IV_SIZE; + +void AES256_CFB::encrypt(const byte *plaintext, unsigned int plaintextSize, byte *ciphertext, const EncryptionKey &encKey) { + FixedSizeData iv = FixedSizeData::CreateRandom(); + auto encryption = CFB_Mode::Encryption(encKey.data(), encKey.BINARY_LENGTH, iv.data()); + std::memcpy(ciphertext, iv.data(), IV_SIZE); + encryption.ProcessData(ciphertext + IV_SIZE, plaintext, plaintextSize); +} + +void AES256_CFB::decrypt(const byte *ciphertext, byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) { + const byte *iv = ciphertext; + const byte *data = ciphertext + IV_SIZE; + auto decryption = CFB_Mode::Decryption((byte*)encKey.data(), encKey.BINARY_LENGTH, iv); + decryption.ProcessData(plaintext, data, plaintextSize); +} + +} +} diff --git a/implementations/encrypted/ciphers/AES256_CFB.h b/implementations/encrypted/ciphers/AES256_CFB.h new file mode 100644 index 00000000..16c8e5f8 --- /dev/null +++ b/implementations/encrypted/ciphers/AES256_CFB.h @@ -0,0 +1,36 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHERS_AES256_CFB_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHERS_AES256_CFB_H_ + +#include "../../../utils/FixedSizeData.h" +#include + +namespace blockstore { +namespace encrypted { + +class AES256_CFB { +public: + using EncryptionKey = FixedSizeData<32>; + static_assert(32 == CryptoPP::AES::MAX_KEYLENGTH, "If AES offered larger keys, we should offer a variant with it"); + + AES256_CFB(const EncryptionKey &key); + + static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) { + return plaintextBlockSize + IV_SIZE; + } + + static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) { + return ciphertextBlockSize - IV_SIZE; + } + + static void encrypt(const byte *plaintext, unsigned int plaintextSize, byte *ciphertext, const EncryptionKey &key); + static void decrypt(const byte *ciphertext, byte *plaintext, unsigned int plaintextSize, const EncryptionKey &key); + +private: + static constexpr unsigned int IV_SIZE = CryptoPP::AES::BLOCKSIZE; +}; + +} +} + +#endif diff --git a/test/implementations/encrypted/EncryptedBlockStoreTest.cpp b/test/implementations/encrypted/EncryptedBlockStoreTest.cpp index b5c6eb29..4a8b35bb 100644 --- a/test/implementations/encrypted/EncryptedBlockStoreTest.cpp +++ b/test/implementations/encrypted/EncryptedBlockStoreTest.cpp @@ -1,3 +1,4 @@ +#include "../../../implementations/encrypted/ciphers/AES256_CFB.h" #include "../../../implementations/encrypted/EncryptedBlockStore.h" #include "../../../implementations/testfake/FakeBlockStore.h" #include "../../testutils/BlockStoreTest.h" @@ -6,8 +7,8 @@ using blockstore::BlockStore; using blockstore::encrypted::EncryptedBlockStore; -using blockstore::encrypted::EncryptionKey; using blockstore::testfake::FakeBlockStore; +using blockstore::encrypted::AES256_CFB; using std::unique_ptr; using std::make_unique; @@ -15,7 +16,7 @@ using std::make_unique; class EncryptedBlockStoreTestFixture: public BlockStoreTestFixture { public: unique_ptr createBlockStore() override { - return make_unique(make_unique(), EncryptionKey::FromString("1491BB4932A389EE14BC7090A272EE5517627CFA147A971A8E6E747E0C772972")); + return make_unique>(make_unique(), AES256_CFB::EncryptionKey::FromString("1491BB4932A389EE14BC7090A272EE5517627CFA147A971A8E6E747E0C772972")); } }; diff --git a/utils/FixedSizeData.h b/utils/FixedSizeData.h index f7b70f5c..1199275f 100644 --- a/utils/FixedSizeData.h +++ b/utils/FixedSizeData.h @@ -9,7 +9,7 @@ namespace blockstore { -template +template class FixedSizeData { public: //Non-virtual destructor because we want objects to be small @@ -35,28 +35,28 @@ private: unsigned char _data[BINARY_LENGTH]; }; -template bool operator==(const FixedSizeData &lhs, const FixedSizeData &rhs); -template bool operator!=(const FixedSizeData &lhs, const FixedSizeData &rhs); +template bool operator==(const FixedSizeData &lhs, const FixedSizeData &rhs); +template bool operator!=(const FixedSizeData &lhs, const FixedSizeData &rhs); // ----- Implementation ----- -template constexpr unsigned int FixedSizeData::BINARY_LENGTH; -template constexpr unsigned int FixedSizeData::STRING_LENGTH; +template constexpr unsigned int FixedSizeData::BINARY_LENGTH; +template constexpr unsigned int FixedSizeData::STRING_LENGTH; -template +template CryptoPP::AutoSeededRandomPool &FixedSizeData::RandomPool() { static CryptoPP::AutoSeededRandomPool singleton; return singleton; } -template +template FixedSizeData FixedSizeData::CreateRandom() { FixedSizeData result; RandomPool().GenerateBlock(result._data, BINARY_LENGTH); return result; } -template +template FixedSizeData FixedSizeData::FromString(const std::string &data) { assert(data.size() == STRING_LENGTH); FixedSizeData result; @@ -68,7 +68,7 @@ FixedSizeData FixedSizeData::FromString(const std::string &data) { return result; } -template +template std::string FixedSizeData::ToString() const { std::string result; CryptoPP::ArraySource(_data, BINARY_LENGTH, true, @@ -80,29 +80,29 @@ std::string FixedSizeData::ToString() const { return result; } -template +template const unsigned char *FixedSizeData::data() const { return _data; } -template +template void FixedSizeData::ToBinary(void *target) const { std::memcpy(target, _data, BINARY_LENGTH); } -template +template FixedSizeData FixedSizeData::FromBinary(const void *source) { FixedSizeData result; std::memcpy(result._data, source, BINARY_LENGTH); return result; } -template +template bool operator==(const FixedSizeData &lhs, const FixedSizeData &rhs) { return 0 == std::memcmp(lhs.data(), rhs.data(), FixedSizeData::BINARY_LENGTH); } -template +template bool operator!=(const FixedSizeData &lhs, const FixedSizeData &rhs) { return !operator==(lhs, rhs); }