- Added test cases for authenticated ciphers

- Fixed corner case for AES256_GCM when decrypt is called on data that can't hold IV and TAG
This commit is contained in:
Sebastian Messmer 2015-04-24 23:55:52 +02:00
parent 0335b243fb
commit e056a65b48
2 changed files with 125 additions and 9 deletions

View File

@ -8,6 +8,7 @@ using CryptoPP::AuthenticatedDecryptionFilter;
using CryptoPP::ArraySource;
using CryptoPP::ArraySink;
using CryptoPP::GCM_64K_Tables;
using CryptoPP::HashVerificationFilter;
namespace blockstore {
namespace encrypted {
@ -31,18 +32,26 @@ Data AES256_GCM::encrypt(const byte *plaintext, unsigned int plaintextSize, cons
}
boost::optional<Data> AES256_GCM::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;
GCM<AES, GCM_64K_Tables>::Decryption decryption;
decryption.SetKeyWithIV((byte*)encKey.data(), encKey.BINARY_LENGTH, ciphertextIV, IV_SIZE);
Data plaintext(plaintextSize(ciphertextSize));
ArraySource((byte*)ciphertextData, ciphertextSize - IV_SIZE, true,
new AuthenticatedDecryptionFilter(decryption,
new ArraySink((byte*)plaintext.data(), plaintext.size())
)
);
return std::move(plaintext);
try {
ArraySource((byte*)ciphertextData, ciphertextSize - IV_SIZE, true,
new AuthenticatedDecryptionFilter(decryption,
new ArraySink((byte*)plaintext.data(), plaintext.size())
)
);
return std::move(plaintext);
} catch (const HashVerificationFilter::HashVerificationFailed &e) {
return boost::none;
}
}
}

View File

@ -6,6 +6,8 @@
#include "../../testutils/DataBlockFixture.h"
#include "../../../utils/Data.h"
#include <boost/optional/optional_io.hpp>
using namespace blockstore::encrypted;
using blockstore::Data;
@ -46,13 +48,13 @@ public:
return Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey).value();
}
Data CreateZeroes(unsigned int size) {
static Data CreateZeroes(unsigned int size) {
Data zeroes(size);
zeroes.FillWithZeroes();
return zeroes;
}
Data CreateData(unsigned int size, unsigned int seed = 0) {
static Data CreateData(unsigned int size, unsigned int seed = 0) {
DataBlockFixture data(size, seed);
Data result(size);
std::memcpy(result.data(), data.data(), size);
@ -115,7 +117,112 @@ REGISTER_TYPED_TEST_CASE_P(CipherTest,
EncryptedSize
);
//TODO For authenticated ciphers, we need test cases checking that authentication fails on manipulations
template<class Cipher>
class AuthenticatedCipherTest: public CipherTest<Cipher> {
public:
void ExpectDoesntDecrypt(const Data &ciphertext) {
auto decrypted = Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey);
EXPECT_FALSE(decrypted);
}
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);
}
TYPED_TEST_P(AuthenticatedCipherTest, TryDecryptDataThatIsTooSmall) {
Data tooSmallCiphertext(TypeParam::ciphertextSize(0) - 1);
this->ExpectDoesntDecrypt(tooSmallCiphertext);
}
TYPED_TEST_P(AuthenticatedCipherTest, 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(AuthenticatedCipherTest, 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(AuthenticatedCipherTest,
ModifyFirstByte_Zeroes_Size1,
ModifyFirstByte_Zeroes,
ModifyFirstByte_Data_Size1,
ModifyFirstByte_Data,
ModifyLastByte_Zeroes,
ModifyLastByte_Data,
ModifyMiddleByte_Zeroes,
ModifyMiddleByte_Data,
TryDecryptZeroesData,
TryDecryptRandomData,
TryDecryptDataThatIsTooSmall,
TryDecryptDataThatIsMuchTooSmall_0,
TryDecryptDataThatIsMuchTooSmall_1
);
INSTANTIATE_TYPED_TEST_CASE_P(AES256_CFB, CipherTest, AES256_CFB);
INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, CipherTest, AES256_GCM);
INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, AuthenticatedCipherTest, AES256_GCM);