Add symmetric ciphers to crypto lib
This commit is contained in:
parent
1f86f83ca1
commit
c50371d583
66
crypto/symmetric/CFB_Cipher.h
Normal file
66
crypto/symmetric/CFB_Cipher.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CFBCIPHER_H_
|
||||||
|
#define MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CFBCIPHER_H_
|
||||||
|
|
||||||
|
#include "../../data/FixedSizeData.h"
|
||||||
|
#include "../../data/Data.h"
|
||||||
|
#include "../../random/Random.h"
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <cryptopp/cryptopp/modes.h>
|
||||||
|
#include "Cipher.h"
|
||||||
|
|
||||||
|
namespace cpputils {
|
||||||
|
|
||||||
|
template<typename BlockCipher, unsigned int KeySize>
|
||||||
|
class CFB_Cipher {
|
||||||
|
public:
|
||||||
|
BOOST_CONCEPT_ASSERT((CipherConcept<CFB_Cipher<BlockCipher, KeySize>>));
|
||||||
|
|
||||||
|
using EncryptionKey = FixedSizeData<KeySize>;
|
||||||
|
|
||||||
|
static EncryptionKey CreateKey(RandomGenerator &randomGenerator) {
|
||||||
|
return randomGenerator.getFixedSize<EncryptionKey::BINARY_LENGTH>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Data encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey);
|
||||||
|
static boost::optional<Data> decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr unsigned int IV_SIZE = BlockCipher::BLOCKSIZE;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename BlockCipher, unsigned int KeySize>
|
||||||
|
Data CFB_Cipher<BlockCipher, KeySize>::encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) {
|
||||||
|
FixedSizeData<IV_SIZE> iv = Random::PseudoRandom().getFixedSize<IV_SIZE>();
|
||||||
|
auto encryption = typename CryptoPP::CFB_Mode<BlockCipher>::Encryption(encKey.data(), encKey.BINARY_LENGTH, iv.data());
|
||||||
|
Data ciphertext(ciphertextSize(plaintextSize));
|
||||||
|
std::memcpy(ciphertext.data(), iv.data(), IV_SIZE);
|
||||||
|
encryption.ProcessData((byte*)ciphertext.data() + IV_SIZE, plaintext, plaintextSize);
|
||||||
|
return ciphertext;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BlockCipher, unsigned int KeySize>
|
||||||
|
boost::optional<Data> CFB_Cipher<BlockCipher, KeySize>::decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey) {
|
||||||
|
if (ciphertextSize < IV_SIZE) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte *ciphertextIV = ciphertext;
|
||||||
|
const byte *ciphertextData = ciphertext + IV_SIZE;
|
||||||
|
auto decryption = typename CryptoPP::CFB_Mode<BlockCipher>::Decryption((byte*)encKey.data(), encKey.BINARY_LENGTH, ciphertextIV);
|
||||||
|
Data plaintext(plaintextSize(ciphertextSize));
|
||||||
|
decryption.ProcessData((byte*)plaintext.data(), ciphertextData, plaintext.size());
|
||||||
|
return std::move(plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
30
crypto/symmetric/Cipher.h
Normal file
30
crypto/symmetric/Cipher.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CIPHER_H_
|
||||||
|
#define MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CIPHER_H_
|
||||||
|
|
||||||
|
#include <boost/concept_check.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include "../../data/Data.h"
|
||||||
|
#include "../../random/Random.h"
|
||||||
|
|
||||||
|
namespace cpputils {
|
||||||
|
|
||||||
|
template<class X>
|
||||||
|
struct CipherConcept {
|
||||||
|
public:
|
||||||
|
BOOST_CONCEPT_USAGE(CipherConcept) {
|
||||||
|
same_type(UINT32_C(0), X::ciphertextSize(UINT32_C(5)));
|
||||||
|
same_type(UINT32_C(0), X::plaintextSize(UINT32_C(5)));
|
||||||
|
typename X::EncryptionKey key = X::CreateKey(Random::OSRandom());
|
||||||
|
same_type(Data(0), X::encrypt((uint8_t*)nullptr, UINT32_C(0), key));
|
||||||
|
same_type(boost::optional<Data>(Data(0)), X::decrypt((uint8_t*)nullptr, UINT32_C(0), key));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Type deduction will fail unless the arguments have the same type.
|
||||||
|
template <typename T> void same_type(T const&, T const&);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
84
crypto/symmetric/GCM_Cipher.h
Normal file
84
crypto/symmetric/GCM_Cipher.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_GCMCIPHER_H_
|
||||||
|
#define MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_GCMCIPHER_H_
|
||||||
|
|
||||||
|
#include "../../data/FixedSizeData.h"
|
||||||
|
#include "../../data/Data.h"
|
||||||
|
#include "../../random/Random.h"
|
||||||
|
#include <cryptopp/cryptopp/gcm.h>
|
||||||
|
#include "Cipher.h"
|
||||||
|
|
||||||
|
namespace cpputils {
|
||||||
|
|
||||||
|
template<typename BlockCipher, unsigned int KeySize>
|
||||||
|
class GCM_Cipher {
|
||||||
|
public:
|
||||||
|
BOOST_CONCEPT_ASSERT((CipherConcept<GCM_Cipher<BlockCipher, KeySize>>));
|
||||||
|
|
||||||
|
using EncryptionKey = FixedSizeData<KeySize>;
|
||||||
|
|
||||||
|
static EncryptionKey CreateKey(RandomGenerator &randomGenerator) {
|
||||||
|
return randomGenerator.getFixedSize<EncryptionKey::BINARY_LENGTH>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) {
|
||||||
|
return plaintextBlockSize + IV_SIZE + TAG_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) {
|
||||||
|
return ciphertextBlockSize - IV_SIZE - TAG_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Data encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey);
|
||||||
|
static boost::optional<Data> decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr unsigned int IV_SIZE = BlockCipher::BLOCKSIZE;
|
||||||
|
static constexpr unsigned int TAG_SIZE = 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename BlockCipher, unsigned int KeySize>
|
||||||
|
Data GCM_Cipher<BlockCipher, KeySize>::encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) {
|
||||||
|
FixedSizeData<IV_SIZE> iv = Random::PseudoRandom().getFixedSize<IV_SIZE>();
|
||||||
|
typename CryptoPP::GCM<BlockCipher, CryptoPP::GCM_64K_Tables>::Encryption encryption;
|
||||||
|
encryption.SetKeyWithIV(encKey.data(), encKey.BINARY_LENGTH, iv.data(), IV_SIZE);
|
||||||
|
Data ciphertext(ciphertextSize(plaintextSize));
|
||||||
|
|
||||||
|
std::memcpy(ciphertext.data(), iv.data(), IV_SIZE);
|
||||||
|
CryptoPP::ArraySource(plaintext, plaintextSize, true,
|
||||||
|
new CryptoPP::AuthenticatedEncryptionFilter(encryption,
|
||||||
|
new CryptoPP::ArraySink((byte*)ciphertext.data() + IV_SIZE, ciphertext.size() - IV_SIZE),
|
||||||
|
false, TAG_SIZE
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return ciphertext;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BlockCipher, unsigned int KeySize>
|
||||||
|
boost::optional<Data> GCM_Cipher<BlockCipher, KeySize>::decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey) {
|
||||||
|
if (ciphertextSize < IV_SIZE + TAG_SIZE) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte *ciphertextIV = ciphertext;
|
||||||
|
const byte *ciphertextData = ciphertext + IV_SIZE;
|
||||||
|
typename CryptoPP::GCM<BlockCipher, CryptoPP::GCM_64K_Tables>::Decryption decryption;
|
||||||
|
decryption.SetKeyWithIV((byte*)encKey.data(), encKey.BINARY_LENGTH, ciphertextIV, IV_SIZE);
|
||||||
|
Data plaintext(plaintextSize(ciphertextSize));
|
||||||
|
|
||||||
|
try {
|
||||||
|
CryptoPP::ArraySource((byte*)ciphertextData, ciphertextSize - IV_SIZE, true,
|
||||||
|
new CryptoPP::AuthenticatedDecryptionFilter(decryption,
|
||||||
|
new CryptoPP::ArraySink((byte*)plaintext.data(), plaintext.size()),
|
||||||
|
CryptoPP::AuthenticatedDecryptionFilter::DEFAULT_FLAGS, TAG_SIZE
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return std::move(plaintext);
|
||||||
|
} catch (const CryptoPP::HashVerificationFilter::HashVerificationFailed &e) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
47
crypto/symmetric/ciphers.h
Normal file
47
crypto/symmetric/ciphers.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CIPHERS_H_
|
||||||
|
#define MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_CIPHERS_H_
|
||||||
|
|
||||||
|
#include <cryptopp/cryptopp/aes.h>
|
||||||
|
#include <cryptopp/cryptopp/twofish.h>
|
||||||
|
#include <cryptopp/cryptopp/serpent.h>
|
||||||
|
#include <cryptopp/cryptopp/cast.h>
|
||||||
|
#include <cryptopp/cryptopp/mars.h>
|
||||||
|
#include "GCM_Cipher.h"
|
||||||
|
#include "CFB_Cipher.h"
|
||||||
|
|
||||||
|
namespace cpputils {
|
||||||
|
|
||||||
|
static_assert(32 == CryptoPP::AES::MAX_KEYLENGTH, "If AES offered larger keys, we should offer a variant with it");
|
||||||
|
using AES256_GCM = GCM_Cipher<CryptoPP::AES, 32>;
|
||||||
|
using AES256_CFB = CFB_Cipher<CryptoPP::AES, 32>;
|
||||||
|
using AES128_GCM = GCM_Cipher<CryptoPP::AES, 16>;
|
||||||
|
using AES128_CFB = CFB_Cipher<CryptoPP::AES, 16>;
|
||||||
|
|
||||||
|
static_assert(32 == CryptoPP::Twofish::MAX_KEYLENGTH, "If Twofish offered larger keys, we should offer a variant with it");
|
||||||
|
using Twofish256_GCM = GCM_Cipher<CryptoPP::Twofish, 32>;
|
||||||
|
using Twofish256_CFB = CFB_Cipher<CryptoPP::Twofish, 32>;
|
||||||
|
using Twofish128_GCM = GCM_Cipher<CryptoPP::Twofish, 16>;
|
||||||
|
using Twofish128_CFB = CFB_Cipher<CryptoPP::Twofish, 16>;
|
||||||
|
|
||||||
|
static_assert(32 == CryptoPP::Serpent::MAX_KEYLENGTH, "If Serpent offered larger keys, we should offer a variant with it");
|
||||||
|
using Serpent256_GCM = GCM_Cipher<CryptoPP::Serpent, 32>;
|
||||||
|
using Serpent256_CFB = CFB_Cipher<CryptoPP::Serpent, 32>;
|
||||||
|
using Serpent128_GCM = GCM_Cipher<CryptoPP::Serpent, 16>;
|
||||||
|
using Serpent128_CFB = CFB_Cipher<CryptoPP::Serpent, 16>;
|
||||||
|
|
||||||
|
static_assert(32 == CryptoPP::CAST256::MAX_KEYLENGTH, "If Cast offered larger keys, we should offer a variant with it");
|
||||||
|
using Cast256_GCM = GCM_Cipher<CryptoPP::CAST256, 32>;
|
||||||
|
using Cast256_CFB = CFB_Cipher<CryptoPP::CAST256, 32>;
|
||||||
|
|
||||||
|
static_assert(56 == CryptoPP::MARS::MAX_KEYLENGTH, "If Mars offered larger keys, we should offer a variant with it");
|
||||||
|
using Mars448_GCM = GCM_Cipher<CryptoPP::MARS, 56>;
|
||||||
|
using Mars448_CFB = CFB_Cipher<CryptoPP::MARS, 56>;
|
||||||
|
using Mars256_GCM = GCM_Cipher<CryptoPP::MARS, 32>;
|
||||||
|
using Mars256_CFB = CFB_Cipher<CryptoPP::MARS, 32>;
|
||||||
|
using Mars128_GCM = GCM_Cipher<CryptoPP::MARS, 16>;
|
||||||
|
using Mars128_CFB = CFB_Cipher<CryptoPP::MARS, 16>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
254
test/crypto/symmetric/CipherTest.cpp
Normal file
254
test/crypto/symmetric/CipherTest.cpp
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
#include <google/gtest/gtest.h>
|
||||||
|
#include "../../../crypto/symmetric/Cipher.h"
|
||||||
|
#include "../../../crypto/symmetric/ciphers.h"
|
||||||
|
#include "testutils/FakeAuthenticatedCipher.h"
|
||||||
|
|
||||||
|
#include "../../../data/DataFixture.h"
|
||||||
|
#include "../../../data/Data.h"
|
||||||
|
#include <boost/optional/optional_io.hpp>
|
||||||
|
|
||||||
|
using namespace cpputils;
|
||||||
|
|
||||||
|
template<class Cipher>
|
||||||
|
class CipherTest: public ::testing::Test {
|
||||||
|
public:
|
||||||
|
BOOST_CONCEPT_ASSERT((CipherConcept<Cipher>));
|
||||||
|
typename Cipher::EncryptionKey encKey = createKeyFixture();
|
||||||
|
|
||||||
|
static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) {
|
||||||
|
Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed);
|
||||||
|
return Cipher::EncryptionKey::FromBinary(data.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckEncryptThenDecryptIsIdentity(const Data &plaintext) {
|
||||||
|
Data ciphertext = Encrypt(plaintext);
|
||||||
|
Data decrypted = Decrypt(ciphertext);
|
||||||
|
EXPECT_EQ(plaintext, decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckEncryptIsIndeterministic(const Data &plaintext) {
|
||||||
|
Data ciphertext = Encrypt(plaintext);
|
||||||
|
Data ciphertext2 = Encrypt(plaintext);
|
||||||
|
EXPECT_NE(ciphertext, ciphertext2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckEncryptedSize(const Data &plaintext) {
|
||||||
|
Data ciphertext = Encrypt(plaintext);
|
||||||
|
EXPECT_EQ(Cipher::ciphertextSize(plaintext.size()), ciphertext.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpectDoesntDecrypt(const Data &ciphertext) {
|
||||||
|
auto decrypted = Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey);
|
||||||
|
EXPECT_FALSE(decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
Data Encrypt(const Data &plaintext) {
|
||||||
|
return Cipher::encrypt((byte*)plaintext.data(), plaintext.size(), this->encKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Data Decrypt(const Data &ciphertext) {
|
||||||
|
return Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Data CreateZeroes(unsigned int size) {
|
||||||
|
return std::move(Data(size).FillWithZeroes());
|
||||||
|
}
|
||||||
|
|
||||||
|
static Data CreateData(unsigned int size, unsigned int seed = 0) {
|
||||||
|
return DataFixture::generate(size, seed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TYPED_TEST_CASE_P(CipherTest);
|
||||||
|
|
||||||
|
constexpr std::initializer_list<unsigned int> SIZES = {0, 1, 100, 1024, 5000, 1048576, 20971520};
|
||||||
|
|
||||||
|
TYPED_TEST_P(CipherTest, Size) {
|
||||||
|
for (auto size: SIZES) {
|
||||||
|
EXPECT_EQ(size, TypeParam::ciphertextSize(TypeParam::plaintextSize(size)));
|
||||||
|
EXPECT_EQ(size, TypeParam::plaintextSize(TypeParam::ciphertextSize(size)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Zeroes) {
|
||||||
|
for (auto size: SIZES) {
|
||||||
|
Data plaintext = this->CreateZeroes(size);
|
||||||
|
this->CheckEncryptThenDecryptIsIdentity(plaintext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Data) {
|
||||||
|
for (auto size: SIZES) {
|
||||||
|
Data plaintext = this->CreateData(size);
|
||||||
|
this->CheckEncryptThenDecryptIsIdentity(plaintext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Zeroes) {
|
||||||
|
for (auto size: SIZES) {
|
||||||
|
Data plaintext = this->CreateZeroes(size);
|
||||||
|
this->CheckEncryptIsIndeterministic(plaintext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Data) {
|
||||||
|
for (auto size: SIZES) {
|
||||||
|
Data plaintext = this->CreateData(size);
|
||||||
|
this->CheckEncryptIsIndeterministic(plaintext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(CipherTest, EncryptedSize) {
|
||||||
|
for (auto size: SIZES) {
|
||||||
|
Data plaintext = this->CreateData(size);
|
||||||
|
this->CheckEncryptedSize(plaintext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(CipherTest, TryDecryptDataThatIsTooSmall) {
|
||||||
|
Data tooSmallCiphertext(TypeParam::ciphertextSize(0) - 1);
|
||||||
|
this->ExpectDoesntDecrypt(tooSmallCiphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(CipherTest, TryDecryptDataThatIsMuchTooSmall_0) {
|
||||||
|
static_assert(TypeParam::ciphertextSize(0) > 0, "If this fails, the test case doesn't make sense.");
|
||||||
|
Data tooSmallCiphertext(0);
|
||||||
|
this->ExpectDoesntDecrypt(tooSmallCiphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(CipherTest, TryDecryptDataThatIsMuchTooSmall_1) {
|
||||||
|
static_assert(TypeParam::ciphertextSize(0) > 1, "If this fails, the test case doesn't make sense.");
|
||||||
|
Data tooSmallCiphertext(1);
|
||||||
|
this->ExpectDoesntDecrypt(tooSmallCiphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_TYPED_TEST_CASE_P(CipherTest,
|
||||||
|
Size,
|
||||||
|
EncryptThenDecrypt_Zeroes,
|
||||||
|
EncryptThenDecrypt_Data,
|
||||||
|
EncryptIsIndeterministic_Zeroes,
|
||||||
|
EncryptIsIndeterministic_Data,
|
||||||
|
EncryptedSize,
|
||||||
|
TryDecryptDataThatIsTooSmall,
|
||||||
|
TryDecryptDataThatIsMuchTooSmall_0,
|
||||||
|
TryDecryptDataThatIsMuchTooSmall_1
|
||||||
|
);
|
||||||
|
|
||||||
|
template<class Cipher>
|
||||||
|
class AuthenticatedCipherTest: public CipherTest<Cipher> {
|
||||||
|
public:
|
||||||
|
Data zeroes1 = CipherTest<Cipher>::CreateZeroes(1);
|
||||||
|
Data plaintext1 = CipherTest<Cipher>::CreateData(1);
|
||||||
|
Data zeroes2 = CipherTest<Cipher>::CreateZeroes(100 * 1024);
|
||||||
|
Data plaintext2 = CipherTest<Cipher>::CreateData(100 * 1024);
|
||||||
|
};
|
||||||
|
|
||||||
|
TYPED_TEST_CASE_P(AuthenticatedCipherTest);
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Zeroes_Size1) {
|
||||||
|
Data ciphertext = this->Encrypt(this->zeroes1);
|
||||||
|
*(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1;
|
||||||
|
this->ExpectDoesntDecrypt(ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Data_Size1) {
|
||||||
|
Data ciphertext = this->Encrypt(this->plaintext1);
|
||||||
|
*(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1;
|
||||||
|
this->ExpectDoesntDecrypt(ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Zeroes) {
|
||||||
|
Data ciphertext = this->Encrypt(this->zeroes2);
|
||||||
|
*(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1;
|
||||||
|
this->ExpectDoesntDecrypt(ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Data) {
|
||||||
|
Data ciphertext = this->Encrypt(this->plaintext2);
|
||||||
|
*(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1;
|
||||||
|
this->ExpectDoesntDecrypt(ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, ModifyLastByte_Zeroes) {
|
||||||
|
Data ciphertext = this->Encrypt(this->zeroes2);
|
||||||
|
((byte*)ciphertext.data())[ciphertext.size() - 1] = ((byte*)ciphertext.data())[ciphertext.size() - 1] + 1;
|
||||||
|
this->ExpectDoesntDecrypt(ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, ModifyLastByte_Data) {
|
||||||
|
Data ciphertext = this->Encrypt(this->plaintext2);
|
||||||
|
((byte*)ciphertext.data())[ciphertext.size() - 1] = ((byte*)ciphertext.data())[ciphertext.size() - 1] + 1;
|
||||||
|
this->ExpectDoesntDecrypt(ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, ModifyMiddleByte_Zeroes) {
|
||||||
|
Data ciphertext = this->Encrypt(this->zeroes2);
|
||||||
|
((byte*)ciphertext.data())[ciphertext.size()/2] = ((byte*)ciphertext.data())[ciphertext.size()/2] + 1;
|
||||||
|
this->ExpectDoesntDecrypt(ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, ModifyMiddleByte_Data) {
|
||||||
|
Data ciphertext = this->Encrypt(this->plaintext2);
|
||||||
|
((byte*)ciphertext.data())[ciphertext.size()/2] = ((byte*)ciphertext.data())[ciphertext.size()/2] + 1;
|
||||||
|
this->ExpectDoesntDecrypt(ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, TryDecryptZeroesData) {
|
||||||
|
this->ExpectDoesntDecrypt(this->zeroes2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(AuthenticatedCipherTest, TryDecryptRandomData) {
|
||||||
|
this->ExpectDoesntDecrypt(this->plaintext2);
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_TYPED_TEST_CASE_P(AuthenticatedCipherTest,
|
||||||
|
ModifyFirstByte_Zeroes_Size1,
|
||||||
|
ModifyFirstByte_Zeroes,
|
||||||
|
ModifyFirstByte_Data_Size1,
|
||||||
|
ModifyFirstByte_Data,
|
||||||
|
ModifyLastByte_Zeroes,
|
||||||
|
ModifyLastByte_Data,
|
||||||
|
ModifyMiddleByte_Zeroes,
|
||||||
|
ModifyMiddleByte_Data,
|
||||||
|
TryDecryptZeroesData,
|
||||||
|
TryDecryptRandomData
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Fake, CipherTest, FakeAuthenticatedCipher);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Fake, AuthenticatedCipherTest, FakeAuthenticatedCipher);
|
||||||
|
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(AES256_CFB, CipherTest, AES256_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, CipherTest, AES256_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, AuthenticatedCipherTest, AES256_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(AES128_CFB, CipherTest, AES128_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(AES128_GCM, CipherTest, AES128_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(AES128_GCM, AuthenticatedCipherTest, AES128_GCM);
|
||||||
|
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Twofish256_CFB, CipherTest, Twofish256_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Twofish256_GCM, CipherTest, Twofish256_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Twofish256_GCM, AuthenticatedCipherTest, Twofish256_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Twofish128_CFB, CipherTest, Twofish128_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Twofish128_GCM, CipherTest, Twofish128_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Twofish128_GCM, AuthenticatedCipherTest, Twofish128_GCM);
|
||||||
|
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Serpent256_CFB, CipherTest, Serpent256_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Serpent256_GCM, CipherTest, Serpent256_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Serpent256_GCM, AuthenticatedCipherTest, Serpent256_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Serpent128_CFB, CipherTest, Serpent128_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Serpent128_GCM, CipherTest, Serpent128_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Serpent128_GCM, AuthenticatedCipherTest, Serpent128_GCM);
|
||||||
|
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Cast256_CFB, CipherTest, Cast256_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Cast256_GCM, CipherTest, Cast256_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Cast256_GCM, AuthenticatedCipherTest, Cast256_GCM);
|
||||||
|
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Mars448_CFB, CipherTest, Mars448_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Mars448_GCM, CipherTest, Mars448_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Mars448_GCM, AuthenticatedCipherTest, Mars448_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Mars256_CFB, CipherTest, Mars256_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Mars256_GCM, CipherTest, Mars256_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Mars256_GCM, AuthenticatedCipherTest, Mars256_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Mars128_CFB, CipherTest, Mars128_CFB); //CFB mode is not authenticated
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Mars128_GCM, CipherTest, Mars128_GCM);
|
||||||
|
INSTANTIATE_TYPED_TEST_CASE_P(Mars128_GCM, AuthenticatedCipherTest, Mars128_GCM);
|
@ -0,0 +1,5 @@
|
|||||||
|
#include "FakeAuthenticatedCipher.h"
|
||||||
|
|
||||||
|
namespace cpputils {
|
||||||
|
constexpr unsigned int FakeKey::BINARY_LENGTH;
|
||||||
|
}
|
112
test/crypto/symmetric/testutils/FakeAuthenticatedCipher.h
Normal file
112
test/crypto/symmetric/testutils/FakeAuthenticatedCipher.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef MESSMER_CPPUTILS_TEST_CRYPTO_SYMMETRIC_TESTUTILS_FAKEAUTHENTICATEDCIPHER_H_
|
||||||
|
#define MESSMER_CPPUTILS_TEST_CRYPTO_SYMMETRIC_TESTUTILS_FAKEAUTHENTICATEDCIPHER_H_
|
||||||
|
|
||||||
|
#include "../../../../crypto/symmetric/Cipher.h"
|
||||||
|
#include "../../../../data/FixedSizeData.h"
|
||||||
|
#include "../../../../data/Data.h"
|
||||||
|
#include "../../../../random/RandomGenerator.h"
|
||||||
|
|
||||||
|
namespace cpputils {
|
||||||
|
|
||||||
|
struct FakeKey {
|
||||||
|
static FakeKey FromBinary(const void *data) {
|
||||||
|
return FakeKey{*(uint8_t *) data};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr unsigned int BINARY_LENGTH = 1;
|
||||||
|
|
||||||
|
uint8_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is a fake cipher that uses an indeterministic caesar chiffre and a 4-byte parity for a simple authentication mechanism
|
||||||
|
class FakeAuthenticatedCipher {
|
||||||
|
public:
|
||||||
|
BOOST_CONCEPT_ASSERT((CipherConcept<FakeAuthenticatedCipher>));
|
||||||
|
|
||||||
|
using EncryptionKey = FakeKey;
|
||||||
|
|
||||||
|
static EncryptionKey CreateKey(RandomGenerator &randomGenerator) {
|
||||||
|
auto data = randomGenerator.getFixedSize<1>();
|
||||||
|
return FakeKey{*((uint8_t *) data.data())};
|
||||||
|
}
|
||||||
|
|
||||||
|
static EncryptionKey Key1() {
|
||||||
|
return FakeKey{5};
|
||||||
|
}
|
||||||
|
|
||||||
|
static EncryptionKey Key2() {
|
||||||
|
return FakeKey{63};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) {
|
||||||
|
return plaintextBlockSize + 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) {
|
||||||
|
return ciphertextBlockSize - 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Data encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) {
|
||||||
|
Data result(ciphertextSize(plaintextSize));
|
||||||
|
|
||||||
|
//Add a random IV
|
||||||
|
uint8_t iv = rand();
|
||||||
|
std::memcpy(result.data(), &iv, 1);
|
||||||
|
|
||||||
|
//Use caesar chiffre on plaintext
|
||||||
|
_caesar((byte *) result.data() + 1, plaintext, plaintextSize, encKey.value + iv);
|
||||||
|
|
||||||
|
//Add parity information
|
||||||
|
int32_t parity = _parity((byte *) result.data(), plaintextSize + 1);
|
||||||
|
std::memcpy((byte *) result.data() + plaintextSize + 1, &parity, 4);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boost::optional <Data> decrypt(const byte *ciphertext, unsigned int ciphertextSize,
|
||||||
|
const EncryptionKey &encKey) {
|
||||||
|
//We need at least 5 bytes (iv + parity)
|
||||||
|
if (ciphertextSize < 5) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check parity
|
||||||
|
int32_t expectedParity = _parity(ciphertext, plaintextSize(ciphertextSize) + 1);
|
||||||
|
int32_t actualParity = *(int32_t * )(ciphertext + plaintextSize(ciphertextSize) + 1);
|
||||||
|
if (expectedParity != actualParity) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Decrypt caesar chiffre from ciphertext
|
||||||
|
int32_t iv = *(int32_t *) ciphertext;
|
||||||
|
Data result(plaintextSize(ciphertextSize));
|
||||||
|
_caesar((byte *) result.data(), ciphertext + 1, plaintextSize(ciphertextSize), -(encKey.value + iv));
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int32_t _parity(const byte *data, unsigned int size) {
|
||||||
|
int32_t parity = 34343435; // some init value
|
||||||
|
int32_t *intData = (int32_t *) data;
|
||||||
|
unsigned int intSize = size / sizeof(int32_t);
|
||||||
|
for (unsigned int i = 0; i < intSize; ++i) {
|
||||||
|
parity += intData[i];
|
||||||
|
}
|
||||||
|
unsigned int remainingBytes = size - 4 * intSize;
|
||||||
|
for (unsigned int i = 0; i < remainingBytes; ++i) {
|
||||||
|
parity += (data[4 * intSize + i] << (24 - 8 * i));
|
||||||
|
}
|
||||||
|
return parity;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _caesar(byte *dst, const byte *src, unsigned int size, uint8_t key) {
|
||||||
|
for (unsigned int i = 0; i < size; ++i) {
|
||||||
|
dst[i] = src[i] + key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user