LibCrypto: Fix broken CTR mode, implement RFC 3686 Test Vectors

This commit is contained in:
Ben Wiederhake 2020-07-25 23:40:55 +02:00 committed by Andreas Kling
parent ef4ce54b02
commit a51cbc2978
Notes: sideshowbarker 2024-07-19 04:30:00 +09:00
2 changed files with 241 additions and 12 deletions

View file

@ -82,8 +82,9 @@ namespace Cipher {
*
* Due to this plethora of mutually-incompatible counters,
* the method of counting should be a template parameter.
* This currently implements BIGINT_MIXEDENDIAN_INCR_0, which is not used
* anywhere else.
* This currently implements BIGINT_INCR_0, which means perfect
* interoperability with openssl. The test vectors from RFC 3686 just need to be
* incremented by 1.
* TODO: Implement other counters?
*/
@ -112,6 +113,8 @@ public:
virtual Optional<ByteBuffer> encrypt(const ByteBuffer& in, ByteBuffer& out, Optional<ByteBuffer> ivec = {}) override
{
// Our interpretation of "ivec" is what AES-CTR
// would define as nonce + IV + 4 zero bytes.
return this->encrypt_or_stream(&in, out, ivec);
}
@ -129,20 +132,17 @@ public:
}
private:
static ByteBuffer increment(const ByteBuffer& in)
static void increment_inplace(ByteBuffer& in)
{
ByteBuffer new_buffer(in);
size_t* num_view = (size_t*)new_buffer.data();
for (size_t i = 0; i < in.size() / sizeof(size_t); ++i) {
if (num_view[i] == (size_t)-1) {
num_view[i] = 0;
for (size_t i = in.size(); i > 0;) {
--i;
if (in[i] == (u8)-1) {
in[i] = 0;
} else {
num_view[i]++;
in[i]++;
break;
}
}
return new_buffer;
}
Optional<ByteBuffer> encrypt_or_stream(const ByteBuffer* in, ByteBuffer& out, Optional<ByteBuffer> ivec)
@ -178,7 +178,7 @@ private:
auto write_size = min(block_size, length);
out.overwrite(offset, block.get().data(), write_size);
iv = increment(iv);
increment_inplace(iv);
length -= write_size;
offset += write_size;
}

View file

@ -68,6 +68,7 @@ constexpr const char* DEFAULT_SERVER { "www.google.com" };
// listAllTests
// Cipher
int aes_cbc_tests();
int aes_ctr_tests();
// Hash
int md5_tests();
@ -368,9 +369,11 @@ auto main(int argc, char** argv) -> int
if (mode_sv == "test") {
encrypting = true;
aes_cbc_tests();
aes_ctr_tests();
encrypting = false;
aes_cbc_tests();
aes_ctr_tests();
md5_tests();
sha1_tests();
@ -458,6 +461,9 @@ ByteBuffer operator""_b(const char* string, size_t length)
void aes_cbc_test_name();
void aes_cbc_test_encrypt();
void aes_cbc_test_decrypt();
void aes_ctr_test_name();
void aes_ctr_test_encrypt();
void aes_ctr_test_decrypt();
void md5_test_name();
void md5_test_hash();
@ -644,6 +650,229 @@ void aes_cbc_test_decrypt()
// TODO: Test non-CMS padding options
}
int aes_ctr_tests()
{
aes_ctr_test_name();
if (encrypting) {
aes_ctr_test_encrypt();
} else {
aes_ctr_test_decrypt();
}
return g_some_test_failed ? 1 : 0;
}
void aes_ctr_test_name()
{
I_TEST((AES CTR class name));
Crypto::Cipher::AESCipher::CTRMode cipher("WellHelloFriends"_b, 128, Crypto::Cipher::Intent::Encryption);
if (cipher.class_name() != "AES_CTR")
FAIL(Invalid class name);
else
PASS;
}
#define AS_BB(x) (ByteBuffer::wrap((x), sizeof((x)) / sizeof((x)[0])))
void aes_ctr_test_encrypt()
{
auto test_it = [](auto key, auto ivec, auto in, auto out_expected) {
// nonce is already included in ivec.
Crypto::Cipher::AESCipher::CTRMode cipher(key, 8 * key.size(), Crypto::Cipher::Intent::Encryption);
ByteBuffer out_actual = ByteBuffer::create_zeroed(in.size());
ByteBuffer final_ivec = cipher.encrypt(in, out_actual, ivec).value();
if (out_expected.size() != out_actual.size()) {
FAIL(size mismatch);
printf("Expected %zu bytes but got %zu\n", out_expected.size(), out_actual.size());
print_buffer(out_actual, Crypto::Cipher::AESCipher::block_size());
} else if (memcmp(out_expected.data(), out_actual.data(), out_expected.size()) != 0) {
FAIL(invalid data);
print_buffer(out_actual, Crypto::Cipher::AESCipher::block_size());
} else
PASS;
};
// From RFC 3686, Section 6
{
// Test Vector #1
I_TEST((AES CTR 16 octets with 128 bit key | Encrypt))
u8 key[] {
0xae, 0x68, 0x52, 0xf8, 0x12, 0x10, 0x67, 0xcc, 0x4b, 0xf7, 0xa5, 0x76, 0x55, 0x77, 0xf3, 0x9e
};
u8 ivec[] {
0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h
};
u8 in[] {
0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67
};
u8 out[] {
0xe4, 0x09, 0x5d, 0x4f, 0xb7, 0xa7, 0xb3, 0x79, 0x2d, 0x61, 0x75, 0xa3, 0x26, 0x13, 0x11, 0xb8
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
{
// Test Vector #2
I_TEST((AES CTR 32 octets with 128 bit key | Encrypt))
u8 key[] {
0x7e, 0x24, 0x06, 0x78, 0x17, 0xfa, 0xe0, 0xd7, 0x43, 0xd6, 0xce, 0x1f, 0x32, 0x53, 0x91, 0x63
};
u8 ivec[] {
0x00, 0x6c, 0xb6, 0xdb, 0xc0, 0x54, 0x3b, 0x59, 0xda, 0x48, 0xd9, 0x0b, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h
};
u8 in[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
};
u8 out[] {
0x51, 0x04, 0xa1, 0x06, 0x16, 0x8a, 0x72, 0xd9, 0x79, 0x0d, 0x41, 0xee, 0x8e, 0xda, 0xd3, 0x88,
0xeb, 0x2e, 0x1e, 0xfc, 0x46, 0xda, 0x57, 0xc8, 0xfc, 0xe6, 0x30, 0xdf, 0x91, 0x41, 0xbe, 0x28
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
{
// Test Vector #3
I_TEST((AES CTR 36 octets with 128 bit key | Encrypt))
u8 key[] {
0x76, 0x91, 0xbe, 0x03, 0x5e, 0x50, 0x20, 0xa8, 0xac, 0x6e, 0x61, 0x85, 0x29, 0xf9, 0xa0, 0xdc
};
u8 ivec[] {
0x00, 0xe0, 0x01, 0x7b, 0x27, 0x77, 0x7f, 0x3f, 0x4a, 0x17, 0x86, 0xf0, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h
};
u8 in[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23
};
u8 out[] {
0xc1, 0xcf, 0x48, 0xa8, 0x9f, 0x2f, 0xfd, 0xd9, 0xcf, 0x46, 0x52, 0xe9, 0xef, 0xdb, 0x72, 0xd7, 0x45, 0x40, 0xa4, 0x2b, 0xde, 0x6d, 0x78, 0x36, 0xd5, 0x9a, 0x5c, 0xea, 0xae, 0xf3, 0x10, 0x53, 0x25, 0xb2, 0x07, 0x2f
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
{
// Test Vector #4
I_TEST((AES CTR 16 octets with 192 bit key | Encrypt))
u8 key[] {
0x16, 0xaf, 0x5b, 0x14, 0x5f, 0xc9, 0xf5, 0x79, 0xc1, 0x75, 0xf9, 0x3e, 0x3b, 0xfb, 0x0e, 0xed, 0x86, 0x3d, 0x06, 0xcc, 0xfd, 0xb7, 0x85, 0x15
};
u8 ivec[] {
0x00, 0x00, 0x00, 0x48, 0x36, 0x73, 0x3c, 0x14, 0x7d, 0x6d, 0x93, 0xcb, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h
};
u8 in[] {
0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67
};
u8 out[] {
0x4b, 0x55, 0x38, 0x4f, 0xe2, 0x59, 0xc9, 0xc8, 0x4e, 0x79, 0x35, 0xa0, 0x03, 0xcb, 0xe9, 0x28
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
{
// Test Vector #5
I_TEST((AES CTR 32 octets with 192 bit key | Encrypt))
u8 key[] {
0x7c, 0x5c, 0xb2, 0x40, 0x1b, 0x3d, 0xc3, 0x3c, 0x19, 0xe7, 0x34, 0x08, 0x19, 0xe0, 0xf6, 0x9c, 0x67, 0x8c, 0x3d, 0xb8, 0xe6, 0xf6, 0xa9, 0x1a
};
u8 ivec[] {
0x00, 0x96, 0xb0, 0x3b, 0x02, 0x0c, 0x6e, 0xad, 0xc2, 0xcb, 0x50, 0x0d, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h
};
u8 in[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
};
u8 out[] {
0x45, 0x32, 0x43, 0xfc, 0x60, 0x9b, 0x23, 0x32, 0x7e, 0xdf, 0xaa, 0xfa, 0x71, 0x31, 0xcd, 0x9f, 0x84, 0x90, 0x70, 0x1c, 0x5a, 0xd4, 0xa7, 0x9c, 0xfc, 0x1f, 0xe0, 0xff, 0x42, 0xf4, 0xfb, 0x00
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
{
// Test Vector #6
I_TEST((AES CTR 36 octets with 192 bit key | Encrypt))
u8 key[] {
0x02, 0xbf, 0x39, 0x1e, 0xe8, 0xec, 0xb1, 0x59, 0xb9, 0x59, 0x61, 0x7b, 0x09, 0x65, 0x27, 0x9b, 0xf5, 0x9b, 0x60, 0xa7, 0x86, 0xd3, 0xe0, 0xfe
};
u8 ivec[] {
0x00, 0x07, 0xbd, 0xfd, 0x5c, 0xbd, 0x60, 0x27, 0x8d, 0xcc, 0x09, 0x12, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h
};
u8 in[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23
};
u8 out[] {
0x96, 0x89, 0x3f, 0xc5, 0x5e, 0x5c, 0x72, 0x2f, 0x54, 0x0b, 0x7d, 0xd1, 0xdd, 0xf7, 0xe7, 0x58, 0xd2, 0x88, 0xbc, 0x95, 0xc6, 0x91, 0x65, 0x88, 0x45, 0x36, 0xc8, 0x11, 0x66, 0x2f, 0x21, 0x88, 0xab, 0xee, 0x09, 0x35
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
{
// Test Vector #7
I_TEST((AES CTR 16 octets with 256 bit key | Encrypt))
u8 key[] {
0x77, 0x6b, 0xef, 0xf2, 0x85, 0x1d, 0xb0, 0x6f, 0x4c, 0x8a, 0x05, 0x42, 0xc8, 0x69, 0x6f, 0x6c, 0x6a, 0x81, 0xaf, 0x1e, 0xec, 0x96, 0xb4, 0xd3, 0x7f, 0xc1, 0xd6, 0x89, 0xe6, 0xc1, 0xc1, 0x04
};
u8 ivec[] {
0x00, 0x00, 0x00, 0x60, 0xdb, 0x56, 0x72, 0xc9, 0x7a, 0xa8, 0xf0, 0xb2, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h
};
u8 in[] {
0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67
};
u8 out[] {
0x14, 0x5a, 0xd0, 0x1d, 0xbf, 0x82, 0x4e, 0xc7, 0x56, 0x08, 0x63, 0xdc, 0x71, 0xe3, 0xe0, 0xc0
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
{
// Test Vector #8
I_TEST((AES CTR 32 octets with 256 bit key | Encrypt))
u8 key[] {
0xf6, 0xd6, 0x6d, 0x6b, 0xd5, 0x2d, 0x59, 0xbb, 0x07, 0x96, 0x36, 0x58, 0x79, 0xef, 0xf8, 0x86, 0xc6, 0x6d, 0xd5, 0x1a, 0x5b, 0x6a, 0x99, 0x74, 0x4b, 0x50, 0x59, 0x0c, 0x87, 0xa2, 0x38, 0x84
};
u8 ivec[] {
0x00, 0xfa, 0xac, 0x24, 0xc1, 0x58, 0x5e, 0xf1, 0x5a, 0x43, 0xd8, 0x75, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h
};
u8 in[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
};
u8 out[] {
0xf0, 0x5e, 0x23, 0x1b, 0x38, 0x94, 0x61, 0x2c, 0x49, 0xee, 0x00, 0x0b, 0x80, 0x4e, 0xb2, 0xa9, 0xb8, 0x30, 0x6b, 0x50, 0x8f, 0x83, 0x9d, 0x6a, 0x55, 0x30, 0x83, 0x1d, 0x93, 0x44, 0xaf, 0x1c
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
{
// Test Vector #9
I_TEST((AES CTR 36 octets with 256 bit key | Encrypt))
u8 key[] {
0xff, 0x7a, 0x61, 0x7c, 0xe6, 0x91, 0x48, 0xe4, 0xf1, 0x72, 0x6e, 0x2f, 0x43, 0x58, 0x1d, 0xe2, 0xaa, 0x62, 0xd9, 0xf8, 0x05, 0x53, 0x2e, 0xdf, 0xf1, 0xee, 0xd6, 0x87, 0xfb, 0x54, 0x15, 0x3d
};
u8 ivec[] {
0x00, 0x1c, 0xc5, 0xb7, 0x51, 0xa5, 0x1d, 0x70, 0xa1, 0xc1, 0x11, 0x48, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h
};
u8 in[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23
};
u8 out[] {
0xeb, 0x6c, 0x52, 0x82, 0x1d, 0x0b, 0xbb, 0xf7, 0xce, 0x75, 0x94, 0x46, 0x2a, 0xca, 0x4f, 0xaa, 0xb4, 0x07, 0xdf, 0x86, 0x65, 0x69, 0xfd, 0x07, 0xf4, 0x8c, 0xc0, 0xb5, 0x83, 0xd6, 0x07, 0x1f, 0x1e, 0xc0, 0xe6, 0xb8
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
// Manual test case
{
// This test checks whether counter overflow crashes.
I_TEST((AES CTR 36 octets with 256 bit key, high counter | Encrypt))
u8 key[] {
0xff, 0x7a, 0x61, 0x7c, 0xe6, 0x91, 0x48, 0xe4, 0xf1, 0x72, 0x6e, 0x2f, 0x43, 0x58, 0x1d, 0xe2, 0xaa, 0x62, 0xd9, 0xf8, 0x05, 0x53, 0x2e, 0xdf, 0xf1, 0xee, 0xd6, 0x87, 0xfb, 0x54, 0x15, 0x3d
};
u8 ivec[] {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
u8 in[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23
};
u8 out[] {
// Pasted from the output. The actual success condition is
// not crashing when incrementing the counter.
0x6e, 0x8c, 0xfc, 0x59, 0x08, 0xa8, 0xc0, 0xf1, 0xe6, 0x85, 0x96, 0xe9, 0xc5, 0x40, 0xb6, 0x8b, 0xfe, 0x28, 0x72, 0xe2, 0x24, 0x11, 0x7e, 0x59, 0xef, 0xac, 0x5c, 0xe1, 0x06, 0x89, 0x09, 0xab, 0xf8, 0x90, 0x1c, 0x66
};
test_it(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out));
}
}
void aes_ctr_test_decrypt()
{
// FIXME Test not implemented yet
// Also, CTR implementation not done yet, even though it's trivial: Just ignore "intent".
}
int md5_tests()
{
md5_test_name();