2015-04-24 18:58:42 +02:00
|
|
|
#include <google/gtest/gtest.h>
|
2015-05-06 00:09:11 +02:00
|
|
|
#include "../../../implementations/encrypted/ciphers/Cipher.h"
|
2015-08-31 23:04:56 +02:00
|
|
|
#include "../../../implementations/encrypted/ciphers/ciphers.h"
|
2015-05-06 00:09:11 +02:00
|
|
|
#include "testutils/FakeAuthenticatedCipher.h"
|
2015-04-24 18:58:42 +02:00
|
|
|
|
2015-04-25 16:43:52 +02:00
|
|
|
#include <messmer/cpp-utils/data/DataFixture.h>
|
2015-04-25 02:48:41 +02:00
|
|
|
#include <messmer/cpp-utils/data/Data.h>
|
2015-08-31 23:04:56 +02:00
|
|
|
#include <boost/optional/optional_io.hpp>
|
2015-04-24 23:55:52 +02:00
|
|
|
|
2015-04-24 18:58:42 +02:00
|
|
|
using namespace blockstore::encrypted;
|
2015-04-25 02:48:41 +02:00
|
|
|
using cpputils::Data;
|
2015-04-25 16:43:52 +02:00
|
|
|
using cpputils::DataFixture;
|
2015-04-24 18:58:42 +02:00
|
|
|
|
|
|
|
template<class Cipher>
|
|
|
|
class CipherTest: public ::testing::Test {
|
|
|
|
public:
|
2015-04-24 21:30:54 +02:00
|
|
|
BOOST_CONCEPT_ASSERT((CipherConcept<Cipher>));
|
2015-05-06 00:09:11 +02:00
|
|
|
typename Cipher::EncryptionKey encKey = createKeyFixture();
|
2015-04-24 18:58:42 +02:00
|
|
|
|
2015-05-06 00:09:11 +02:00
|
|
|
static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) {
|
2015-04-25 16:43:52 +02:00
|
|
|
Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed);
|
2015-04-24 18:58:42 +02:00
|
|
|
return Cipher::EncryptionKey::FromBinary(data.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckEncryptThenDecryptIsIdentity(const Data &plaintext) {
|
2015-04-24 19:13:56 +02:00
|
|
|
Data ciphertext = Encrypt(plaintext);
|
|
|
|
Data decrypted = Decrypt(ciphertext);
|
2015-04-25 17:17:06 +02:00
|
|
|
EXPECT_EQ(plaintext, decrypted);
|
2015-04-24 19:13:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckEncryptIsIndeterministic(const Data &plaintext) {
|
|
|
|
Data ciphertext = Encrypt(plaintext);
|
|
|
|
Data ciphertext2 = Encrypt(plaintext);
|
2015-04-25 17:17:06 +02:00
|
|
|
EXPECT_NE(ciphertext, ciphertext2);
|
2015-04-24 19:13:56 +02:00
|
|
|
}
|
|
|
|
|
2015-04-24 21:08:36 +02:00
|
|
|
void CheckEncryptedSize(const Data &plaintext) {
|
|
|
|
Data ciphertext = Encrypt(plaintext);
|
|
|
|
EXPECT_EQ(Cipher::ciphertextSize(plaintext.size()), ciphertext.size());
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:09:11 +02:00
|
|
|
void ExpectDoesntDecrypt(const Data &ciphertext) {
|
|
|
|
auto decrypted = Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey);
|
|
|
|
EXPECT_FALSE(decrypted);
|
|
|
|
}
|
|
|
|
|
2015-04-24 19:13:56 +02:00
|
|
|
Data Encrypt(const Data &plaintext) {
|
2015-04-24 21:08:36 +02:00
|
|
|
return Cipher::encrypt((byte*)plaintext.data(), plaintext.size(), this->encKey);
|
2015-04-24 19:13:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Data Decrypt(const Data &ciphertext) {
|
2015-04-24 21:08:36 +02:00
|
|
|
return Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey).value();
|
2015-04-24 18:58:42 +02:00
|
|
|
}
|
|
|
|
|
2015-04-24 23:55:52 +02:00
|
|
|
static Data CreateZeroes(unsigned int size) {
|
2015-04-25 16:43:52 +02:00
|
|
|
return std::move(Data(size).FillWithZeroes());
|
2015-04-24 18:58:42 +02:00
|
|
|
}
|
|
|
|
|
2015-04-24 23:55:52 +02:00
|
|
|
static Data CreateData(unsigned int size, unsigned int seed = 0) {
|
2015-04-25 16:43:52 +02:00
|
|
|
return DataFixture::generate(size, seed);
|
2015-04-24 18:58:42 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
TYPED_TEST_CASE_P(CipherTest);
|
|
|
|
|
2015-04-24 21:33:25 +02:00
|
|
|
constexpr std::initializer_list<unsigned int> SIZES = {0, 1, 100, 1024, 5000, 1048576, 20971520};
|
2015-04-24 18:58:42 +02:00
|
|
|
|
2015-04-24 21:33:25 +02:00
|
|
|
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)));
|
|
|
|
}
|
2015-04-24 18:58:42 +02:00
|
|
|
}
|
|
|
|
|
2015-04-24 19:13:56 +02:00
|
|
|
TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Zeroes) {
|
|
|
|
for (auto size: SIZES) {
|
|
|
|
Data plaintext = this->CreateZeroes(size);
|
|
|
|
this->CheckEncryptThenDecryptIsIdentity(plaintext);
|
|
|
|
}
|
2015-04-24 18:58:42 +02:00
|
|
|
}
|
|
|
|
|
2015-04-24 19:13:56 +02:00
|
|
|
TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Data) {
|
|
|
|
for (auto size: SIZES) {
|
|
|
|
Data plaintext = this->CreateData(size);
|
|
|
|
this->CheckEncryptThenDecryptIsIdentity(plaintext);
|
|
|
|
}
|
2015-04-24 18:58:42 +02:00
|
|
|
}
|
|
|
|
|
2015-04-24 19:13:56 +02:00
|
|
|
TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Zeroes) {
|
|
|
|
for (auto size: SIZES) {
|
|
|
|
Data plaintext = this->CreateZeroes(size);
|
|
|
|
this->CheckEncryptIsIndeterministic(plaintext);
|
|
|
|
}
|
2015-04-24 18:58:42 +02:00
|
|
|
}
|
|
|
|
|
2015-04-24 19:13:56 +02:00
|
|
|
TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Data) {
|
|
|
|
for (auto size: SIZES) {
|
|
|
|
Data plaintext = this->CreateData(size);
|
|
|
|
this->CheckEncryptIsIndeterministic(plaintext);
|
|
|
|
}
|
2015-04-24 18:58:42 +02:00
|
|
|
}
|
|
|
|
|
2015-04-24 21:08:36 +02:00
|
|
|
TYPED_TEST_P(CipherTest, EncryptedSize) {
|
|
|
|
for (auto size: SIZES) {
|
|
|
|
Data plaintext = this->CreateData(size);
|
|
|
|
this->CheckEncryptedSize(plaintext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:09:11 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-04-24 18:58:42 +02:00
|
|
|
REGISTER_TYPED_TEST_CASE_P(CipherTest,
|
2015-04-24 21:33:25 +02:00
|
|
|
Size,
|
2015-04-24 19:13:56 +02:00
|
|
|
EncryptThenDecrypt_Zeroes,
|
|
|
|
EncryptThenDecrypt_Data,
|
|
|
|
EncryptIsIndeterministic_Zeroes,
|
2015-04-24 21:08:36 +02:00
|
|
|
EncryptIsIndeterministic_Data,
|
2015-05-06 00:09:11 +02:00
|
|
|
EncryptedSize,
|
|
|
|
TryDecryptDataThatIsTooSmall,
|
|
|
|
TryDecryptDataThatIsMuchTooSmall_0,
|
|
|
|
TryDecryptDataThatIsMuchTooSmall_1
|
2015-04-24 18:58:42 +02:00
|
|
|
);
|
|
|
|
|
2015-04-24 23:55:52 +02:00
|
|
|
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,
|
2015-05-06 00:09:11 +02:00
|
|
|
TryDecryptRandomData
|
2015-04-24 23:55:52 +02:00
|
|
|
);
|
|
|
|
|
2015-04-24 18:58:42 +02:00
|
|
|
|
2015-05-06 00:09:11 +02:00
|
|
|
INSTANTIATE_TYPED_TEST_CASE_P(Fake, CipherTest, FakeAuthenticatedCipher);
|
|
|
|
INSTANTIATE_TYPED_TEST_CASE_P(Fake, AuthenticatedCipherTest, FakeAuthenticatedCipher);
|
2015-08-31 23:04:56 +02:00
|
|
|
|
2015-05-06 00:09:11 +02:00
|
|
|
INSTANTIATE_TYPED_TEST_CASE_P(AES256_CFB, CipherTest, AES256_CFB); //CFB mode is not authenticated
|
2015-04-24 23:02:14 +02:00
|
|
|
INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, CipherTest, AES256_GCM);
|
2015-04-24 23:55:52 +02:00
|
|
|
INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, AuthenticatedCipherTest, AES256_GCM);
|
2015-08-31 23:04:56 +02:00
|
|
|
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);
|