diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 9b98790da5..325122147f 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -86,10 +86,11 @@ list(REMOVE_ITEM LIBSQL_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../../Userland/Libr file(GLOB LIBSQL_TEST_SOURCES CONFIGURE_DEPENDS "../../Tests/LibSQL/*.cpp") file(GLOB LIBWASM_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibWasm/*/*.cpp") file(GLOB LIBIMAP_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibIMAP/*.cpp") - file(GLOB LIBTEST_SOURCES CONFIGURE_DEPENDS "../../Userland/Libraries/LibTest/*.cpp") list(FILTER LIBTEST_SOURCES EXCLUDE REGEX ".*Main.cpp$") file(GLOB LIBTEST_MAIN CONFIGURE_DEPENDS "../../Userland/Libraries/LibTest/TestMain.cpp") +file(GLOB LIBCRYPTO_TESTS CONFIGURE_DEPENDS "../../Tests/LibCrypto/*.cpp") +file(GLOB LIBTLS_TESTS CONFIGURE_DEPENDS "../../Tests/LibTLS/*.cpp") set(LAGOM_REGEX_SOURCES ${LIBREGEX_LIBC_SOURCES} ${LIBREGEX_SOURCES}) set(LAGOM_CORE_SOURCES ${AK_SOURCES} ${LIBCORE_SOURCES}) @@ -201,6 +202,28 @@ if (BUILD_LAGOM) target_link_libraries(wasm_lagom Lagom) target_link_libraries(wasm_lagom stdc++) + foreach(source ${LIBCRYPTO_TESTS}) + get_filename_component(name ${source} NAME_WE) + add_executable(${name}_lagom ${source} ${LIBCRYPTO_SOURCES} ${LIBTEST_MAIN}) + target_link_libraries(${name}_lagom Lagom LagomTest) + add_test( + NAME ${name}_lagom + COMMAND ${name}_lagom + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + endforeach() + + foreach(source ${LIBTLS_TESTS}) + get_filename_component(name ${source} NAME_WE) + add_executable(${name}_lagom ${source} ${LIBTLS_SOURCES} ${LIBTEST_MAIN}) + target_link_libraries(${name}_lagom Lagom LagomTest) + add_test( + NAME ${name}_lagom + COMMAND ${name}_lagom + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../Tests/LibTLS + ) + endforeach() + foreach(TEST_PATH ${SHELL_TESTS}) get_filename_component(TEST_NAME ${TEST_PATH} NAME_WE) add_test( diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 855bb5d531..bf5502d4d4 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -14,3 +14,5 @@ add_subdirectory(LibSQL) add_subdirectory(LibWasm) add_subdirectory(LibWeb) add_subdirectory(UserspaceEmulator) +add_subdirectory(LibCrypto) +add_subdirectory(LibTLS) diff --git a/Tests/LibCrypto/CMakeLists.txt b/Tests/LibCrypto/CMakeLists.txt new file mode 100644 index 0000000000..e376215ab1 --- /dev/null +++ b/Tests/LibCrypto/CMakeLists.txt @@ -0,0 +1,4 @@ +file(GLOB TEST_SOURCES CONFIGURE_DEPENDS "*.cpp") +foreach(source ${TEST_SOURCES}) + serenity_test(${source} LibCrypto LIBS LibCrypto) +endforeach() diff --git a/Tests/LibCrypto/TestAES.cpp b/Tests/LibCrypto/TestAES.cpp new file mode 100644 index 0000000000..b637595fdb --- /dev/null +++ b/Tests/LibCrypto/TestAES.cpp @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2021, Peter Bocan + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +static ByteBuffer operator""_b(const char* string, size_t length) +{ + return ByteBuffer::copy(string, length); +} + +TEST_CASE(test_AES_CBC_name) +{ + Crypto::Cipher::AESCipher::CBCMode cipher("WellHelloFriends"_b, 128, Crypto::Cipher::Intent::Encryption); + EXPECT_EQ(cipher.class_name(), "AES_CBC"); +} + +static auto test_aes_cbc_encrypt = [](auto& cipher, auto& result) { + auto in = "This is a test! This is another test!"_b; + auto out = cipher.create_aligned_buffer(in.size()); + auto iv = ByteBuffer::create_zeroed(Crypto::Cipher::AESCipher::block_size()); + auto out_span = out.bytes(); + cipher.encrypt(in, out_span, iv); + EXPECT_EQ(out.size(), sizeof(result)); + EXPECT(memcmp(out_span.data(), result, out_span.size()) == 0); +}; + +TEST_CASE(test_AES_CBC_encrypt_with_128bit_key) +{ + u8 result[] { + 0xb8, 0x06, 0x7c, 0xf2, 0xa9, 0x56, 0x63, 0x58, 0x2d, 0x5c, 0xa1, 0x4b, 0xc5, 0xe3, 0x08, + 0xcf, 0xb5, 0x93, 0xfb, 0x67, 0xb6, 0xf7, 0xaf, 0x45, 0x34, 0x64, 0x70, 0x9e, 0xc9, 0x1a, + 0x8b, 0xd3, 0x70, 0x45, 0xf0, 0x79, 0x65, 0xca, 0xb9, 0x03, 0x88, 0x72, 0x1c, 0xdd, 0xab, + 0x45, 0x6b, 0x1c + }; + Crypto::Cipher::AESCipher::CBCMode cipher("WellHelloFriends"_b, 128, Crypto::Cipher::Intent::Encryption); + test_aes_cbc_encrypt(cipher, result); +} + +TEST_CASE(test_AES_CBC_encrypt_with_192bit_key) +{ + u8 result[] { + 0xae, 0xd2, 0x70, 0xc4, 0x9c, 0xaa, 0x83, 0x33, 0xd3, 0xd3, 0xac, 0x11, 0x65, 0x35, 0xf7, + 0x19, 0x48, 0x7c, 0x7a, 0x8a, 0x95, 0x64, 0xe7, 0xc6, 0x0a, 0xdf, 0x10, 0x06, 0xdc, 0x90, + 0x68, 0x51, 0x09, 0xd7, 0x3b, 0x48, 0x1b, 0x8a, 0xd3, 0x50, 0x09, 0xba, 0xfc, 0xde, 0x11, + 0xe0, 0x3f, 0xcb + }; + Crypto::Cipher::AESCipher::CBCMode cipher("Well Hello Friends! whf!"_b, 192, Crypto::Cipher::Intent::Encryption); + test_aes_cbc_encrypt(cipher, result); +} + +TEST_CASE(test_AES_CBC_encrypt_with_256bit_key) +{ + u8 result[] { + 0x0a, 0x44, 0x4d, 0x62, 0x9e, 0x8b, 0xd8, 0x11, 0x80, 0x48, 0x2a, 0x32, 0x53, 0x61, 0xe7, + 0x59, 0x62, 0x55, 0x9e, 0xf4, 0xe6, 0xad, 0xea, 0xc5, 0x0b, 0xf6, 0xbc, 0x6a, 0xcb, 0x9c, + 0x47, 0x9f, 0xc2, 0x21, 0xe6, 0x19, 0x62, 0xc3, 0x75, 0xca, 0xab, 0x2d, 0x18, 0xa1, 0x54, + 0xd1, 0x41, 0xe6 + }; + Crypto::Cipher::AESCipher::CBCMode cipher("WellHelloFriendsWellHelloFriends"_b, 256, Crypto::Cipher::Intent::Encryption); + test_aes_cbc_encrypt(cipher, result); +} + +TEST_CASE(test_AES_CBC_encrypt_with_unsigned_256bit_key) +{ + u8 result[] { + 0x18, 0x71, 0x80, 0x4c, 0x28, 0x07, 0x55, 0x3c, 0x05, 0x33, 0x36, 0x3f, 0x19, 0x38, 0x5c, + 0xbe, 0xf8, 0xb8, 0x0e, 0x0e, 0x66, 0x67, 0x63, 0x9c, 0xbf, 0x73, 0xcd, 0x82, 0xf9, 0xcb, + 0x9d, 0x81, 0x56, 0xc6, 0x75, 0x14, 0x8b, 0x79, 0x60, 0xb0, 0xdf, 0xaa, 0x2c, 0x2b, 0xd4, + 0xd6, 0xa0, 0x46 + }; + u8 key[] { 0x0a, 0x8c, 0x5b, 0x0d, 0x8a, 0x68, 0x43, 0xf7, 0xaf, 0xc0, 0xe3, 0x4e, 0x4b, 0x43, 0xaa, 0x28, 0x69, 0x9b, 0x6f, 0xe7, 0x24, 0x82, 0x1c, 0x71, 0x86, 0xf6, 0x2b, 0x87, 0xd6, 0x8b, 0x8f, 0xf1 }; + Crypto::Cipher::AESCipher::CBCMode cipher(ReadonlyBytes { key, sizeof(key) }, 256, Crypto::Cipher::Intent::Encryption); + test_aes_cbc_encrypt(cipher, result); +} + +// TODO: Test non-CMS padding options for AES CBC encrypt + +static auto test_aes_cbc_decrypt = [](auto& cipher, auto& result, auto result_len) { + auto true_value = "This is a test! This is another test!"; + auto in = ByteBuffer::copy(result, result_len); + auto out = cipher.create_aligned_buffer(in.size()); + auto iv = ByteBuffer::create_zeroed(Crypto::Cipher::AESCipher::block_size()); + auto out_span = out.bytes(); + cipher.decrypt(in, out_span, iv); + EXPECT_EQ(out_span.size(), strlen(true_value)); + EXPECT(memcmp(out_span.data(), true_value, strlen(true_value)) == 0); +}; + +TEST_CASE(test_AES_CBC_128bit_key_decrypt) +{ + u8 result[] { + 0xb8, 0x06, 0x7c, 0xf2, 0xa9, 0x56, 0x63, 0x58, 0x2d, 0x5c, 0xa1, 0x4b, 0xc5, 0xe3, 0x08, + 0xcf, 0xb5, 0x93, 0xfb, 0x67, 0xb6, 0xf7, 0xaf, 0x45, 0x34, 0x64, 0x70, 0x9e, 0xc9, 0x1a, + 0x8b, 0xd3, 0x70, 0x45, 0xf0, 0x79, 0x65, 0xca, 0xb9, 0x03, 0x88, 0x72, 0x1c, 0xdd, 0xab, + 0x45, 0x6b, 0x1c + }; + Crypto::Cipher::AESCipher::CBCMode cipher("WellHelloFriends"_b, 128, Crypto::Cipher::Intent::Decryption); + test_aes_cbc_decrypt(cipher, result, 48); +} + +TEST_CASE(test_AES_CBC_192bit_key_decrypt) +{ + u8 result[] { + 0xae, 0xd2, 0x70, 0xc4, 0x9c, 0xaa, 0x83, 0x33, 0xd3, 0xd3, 0xac, 0x11, 0x65, 0x35, 0xf7, + 0x19, 0x48, 0x7c, 0x7a, 0x8a, 0x95, 0x64, 0xe7, 0xc6, 0x0a, 0xdf, 0x10, 0x06, 0xdc, 0x90, + 0x68, 0x51, 0x09, 0xd7, 0x3b, 0x48, 0x1b, 0x8a, 0xd3, 0x50, 0x09, 0xba, 0xfc, 0xde, 0x11, + 0xe0, 0x3f, 0xcb + }; + Crypto::Cipher::AESCipher::CBCMode cipher("Well Hello Friends! whf!"_b, 192, Crypto::Cipher::Intent::Decryption); + test_aes_cbc_decrypt(cipher, result, 48); +} + +TEST_CASE(test_AES_CBC_256bit_key_decrypt) +{ + u8 result[] { + 0x0a, 0x44, 0x4d, 0x62, 0x9e, 0x8b, 0xd8, 0x11, 0x80, 0x48, 0x2a, 0x32, 0x53, 0x61, 0xe7, + 0x59, 0x62, 0x55, 0x9e, 0xf4, 0xe6, 0xad, 0xea, 0xc5, 0x0b, 0xf6, 0xbc, 0x6a, 0xcb, 0x9c, + 0x47, 0x9f, 0xc2, 0x21, 0xe6, 0x19, 0x62, 0xc3, 0x75, 0xca, 0xab, 0x2d, 0x18, 0xa1, 0x54, + 0xd1, 0x41, 0xe6 + }; + Crypto::Cipher::AESCipher::CBCMode cipher("WellHelloFriendsWellHelloFriends"_b, 256, Crypto::Cipher::Intent::Decryption); + test_aes_cbc_decrypt(cipher, result, 48); +} + +// TODO: Test non-CMS padding options for AES CBC decrypt + +TEST_CASE(test_AES_CTR_name) +{ + Crypto::Cipher::AESCipher::CTRMode cipher("WellHelloFriends"_b, 128, Crypto::Cipher::Intent::Encryption); + EXPECT_EQ(cipher.class_name(), "AES_CTR"); +} + +#define AS_BB(x) (ReadonlyBytes { (x), sizeof((x)) / sizeof((x)[0]) }) + +static auto test_aes_ctr_encrypt = [](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()); + Bytes out_span = out_actual.bytes(); + cipher.encrypt(in, out_span, ivec); + EXPECT_EQ(out_expected.size(), out_actual.size()); + EXPECT(memcmp(out_expected.data(), out_span.data(), out_expected.size()) == 0); +}; + +TEST_CASE(test_AES_CTR_128bit_key_encrypt_16bytes) +{ + 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_aes_ctr_encrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); +} + +TEST_CASE(test_AES_CTR_128bit_key_encrypt_32bytes) +{ + 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_aes_ctr_encrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); +} + +TEST_CASE(test_AES_CTR_128bit_key_encrypt_36bytes) +{ + u8 ivec[] { + 0x00, 0xe0, 0x01, 0x7b, 0x27, 0x77, 0x7f, 0x3f, 0x4a, 0x17, 0x86, 0xf0, 0x00, 0x00, 0x00, 0x00 + 1 // See CTR.h + }; + u8 key[] { + 0x76, 0x91, 0xbe, 0x03, 0x5e, 0x50, 0x20, 0xa8, 0xac, 0x6e, 0x61, 0x85, 0x29, 0xf9, 0xa0, 0xdc + }; + 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_aes_ctr_encrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); +} + +TEST_CASE(test_AES_CTR_192bit_key_encrypt_32bytes) +{ + 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_aes_ctr_encrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); +} + +TEST_CASE(test_AES_CTR_192bit_key_encrypt_36bytes) +{ + 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_aes_ctr_encrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); +} + +TEST_CASE(test_AES_CTR_256bit_encrypt_16bytes) +{ + 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_aes_ctr_encrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); +} + +TEST_CASE(test_AES_CTR_256bit_encrypt_32bytes) +{ + 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_aes_ctr_encrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); +} + +TEST_CASE(test_AES_CTR_256bit_encrypt_36bytes) +{ + 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_aes_ctr_encrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); +} + +TEST_CASE(test_AES_CTR_256bit_encrypt_36bytes_with_high_counter) +{ + // This test checks whether counter overflow crashes. + 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_aes_ctr_encrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); +} + +static auto test_aes_ctr_decrypt = [](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::Decryption); + ByteBuffer out_actual = ByteBuffer::create_zeroed(in.size()); + auto out_span = out_actual.bytes(); + cipher.decrypt(in, out_span, ivec); + EXPECT_EQ(out_expected.size(), out_span.size()); + EXPECT(memcmp(out_expected.data(), out_span.data(), out_expected.size()) == 0); +}; + +// From RFC 3686, Section 6 +TEST_CASE(test_AES_CTR_128bit_decrypt_16bytes) +{ + // This test checks whether counter overflow crashes. + 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 out[] { + 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67 + }; + u8 in[] { + 0xe4, 0x09, 0x5d, 0x4f, 0xb7, 0xa7, 0xb3, 0x79, 0x2d, 0x61, 0x75, 0xa3, 0x26, 0x13, 0x11, 0xb8 + }; + test_aes_ctr_decrypt(AS_BB(key), AS_BB(ivec), AS_BB(in), AS_BB(out)); + // If encryption works, then decryption works, too. +} + +TEST_CASE(test_AES_GCM_name) +{ + Crypto::Cipher::AESCipher::GCMMode cipher("WellHelloFriends"_b, 128, Crypto::Cipher::Intent::Encryption); + EXPECT_EQ(cipher.class_name(), "AES_GCM"); +} + +TEST_CASE(test_AES_GCM_128bit_encrypt_empty) +{ + Crypto::Cipher::AESCipher::GCMMode cipher("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, 128, Crypto::Cipher::Intent::Encryption); + u8 result_tag[] { 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, 0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a }; + Bytes out; + auto tag = ByteBuffer::create_uninitialized(16); + cipher.encrypt({}, out, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b.bytes(), {}, tag); + EXPECT(memcmp(result_tag, tag.data(), tag.size()) == 0); +} + +TEST_CASE(test_AES_GCM_128bit_encrypt_zeros) +{ + Crypto::Cipher::AESCipher::GCMMode cipher("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, 128, Crypto::Cipher::Intent::Encryption); + u8 result_tag[] { 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf }; + u8 result_ct[] { 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78 }; + auto tag = ByteBuffer::create_uninitialized(16); + auto out = ByteBuffer::create_uninitialized(16); + auto out_bytes = out.bytes(); + cipher.encrypt("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b.bytes(), out_bytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b.bytes(), {}, tag); + EXPECT(memcmp(result_ct, out.data(), out.size()) == 0); + EXPECT(memcmp(result_tag, tag.data(), tag.size()) == 0); +} + +TEST_CASE(test_AES_GCM_128bit_encrypt_multiple_blocks_with_iv) +{ + Crypto::Cipher::AESCipher::GCMMode cipher("\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08"_b, 128, Crypto::Cipher::Intent::Encryption); + u8 result_tag[] { 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6, 0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4 }; + u8 result_ct[] { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 }; + auto tag = ByteBuffer::create_uninitialized(16); + auto out = ByteBuffer::create_uninitialized(64); + auto out_bytes = out.bytes(); + cipher.encrypt( + "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39\x1a\xaf\xd2\x55"_b.bytes(), + out_bytes, + "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88\x00\x00\x00\x00"_b.bytes(), + {}, + tag); + EXPECT(memcmp(result_ct, out.data(), out.size()) == 0); + EXPECT(memcmp(result_tag, tag.data(), tag.size()) == 0); +} + +TEST_CASE(test_AES_GCM_128bit_encrypt_with_aad) +{ + Crypto::Cipher::AESCipher::GCMMode cipher("\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08"_b, 128, Crypto::Cipher::Intent::Encryption); + u8 result_tag[] { 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6, 0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4 }; + u8 result_ct[] { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 }; + auto tag = ByteBuffer::create_uninitialized(16); + auto out = ByteBuffer::create_uninitialized(64); + auto out_bytes = out.bytes(); + cipher.encrypt( + "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39\x1a\xaf\xd2\x55"_b.bytes(), + out_bytes, + "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88\x00\x00\x00\x00"_b.bytes(), + {}, + tag); + EXPECT(memcmp(result_ct, out.data(), out.size()) == 0); + EXPECT(memcmp(result_tag, tag.data(), tag.size()) == 0); +} + +TEST_CASE(test_AES_GCM_128bit_decrypt_empty) +{ + Crypto::Cipher::AESCipher::GCMMode cipher("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, 128, Crypto::Cipher::Intent::Encryption); + u8 input_tag[] { 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, 0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a }; + Bytes out; + auto consistency = cipher.decrypt({}, out, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b.bytes(), {}, { input_tag, 16 }); + EXPECT_EQ(consistency, Crypto::VerificationConsistency::Consistent); + EXPECT_EQ(out.size(), 0u); +} + +TEST_CASE(test_AES_GCM_128bit_decrypt_zeros) +{ + Crypto::Cipher::AESCipher::GCMMode cipher("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, 128, Crypto::Cipher::Intent::Encryption); + u8 input_tag[] { 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf }; + u8 input_ct[] { 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78 }; + u8 result_pt[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + auto out = ByteBuffer::create_uninitialized(16); + auto out_bytes = out.bytes(); + auto consistency = cipher.decrypt({ input_ct, 16 }, out_bytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b.bytes(), {}, { input_tag, 16 }); + EXPECT_EQ(consistency, Crypto::VerificationConsistency::Consistent); + EXPECT(memcmp(result_pt, out.data(), out.size()) == 0); +} + +TEST_CASE(test_AES_GCM_128bit_decrypt_multiple_blocks_with_iv) +{ + Crypto::Cipher::AESCipher::GCMMode cipher("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b, 128, Crypto::Cipher::Intent::Encryption); + u8 input_tag[] { 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf }; + u8 input_ct[] { 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78 }; + u8 result_pt[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + auto out = ByteBuffer::create_uninitialized(16); + auto out_bytes = out.bytes(); + auto consistency = cipher.decrypt({ input_ct, 16 }, out_bytes, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_b.bytes(), {}, { input_tag, 16 }); + EXPECT_EQ(consistency, Crypto::VerificationConsistency::Consistent); + EXPECT(memcmp(result_pt, out.data(), out.size()) == 0); +} + +TEST_CASE(test_AES_GCM_128bit_decrypt_multiple_blocks_with_aad) +{ + Crypto::Cipher::AESCipher::GCMMode cipher("\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08"_b, 128, Crypto::Cipher::Intent::Encryption); + u8 input_tag[] { 0x93, 0xae, 0x16, 0x97, 0x49, 0xa3, 0xbf, 0x39, 0x4f, 0x61, 0xb7, 0xc1, 0xb1, 0x2, 0x4f, 0x60 }; + u8 input_ct[] { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 }; + u8 result_pt[] { 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55 }; + auto out = ByteBuffer::create_uninitialized(64); + auto out_bytes = out.bytes(); + auto consistency = cipher.decrypt( + { input_ct, 64 }, + out_bytes, + "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88\x00\x00\x00\x00"_b.bytes(), + "\xde\xad\xbe\xef\xfa\xaf\x11\xcc"_b.bytes(), + { input_tag, 16 }); + EXPECT(memcmp(result_pt, out.data(), out.size()) == 0); + EXPECT_EQ(consistency, Crypto::VerificationConsistency::Consistent); +} diff --git a/Tests/LibCrypto/TestBigInteger.cpp b/Tests/LibCrypto/TestBigInteger.cpp new file mode 100644 index 0000000000..6b5638ee75 --- /dev/null +++ b/Tests/LibCrypto/TestBigInteger.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2021, Peter Bocan + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +static Crypto::UnsignedBigInteger bigint_fibonacci(size_t n) +{ + Crypto::UnsignedBigInteger num1(0); + Crypto::UnsignedBigInteger num2(1); + for (size_t i = 0; i < n; ++i) { + Crypto::UnsignedBigInteger t = num1.plus(num2); + num2 = num1; + num1 = t; + } + return num1; +} + +static Crypto::SignedBigInteger bigint_signed_fibonacci(size_t n) +{ + Crypto::SignedBigInteger num1(0); + Crypto::SignedBigInteger num2(1); + for (size_t i = 0; i < n; ++i) { + Crypto::SignedBigInteger t = num1.plus(num2); + num2 = num1; + num1 = t; + } + return num1; +} + +TEST_CASE(test_bigint_fib500) +{ + Vector result { + 315178285, 505575602, 1883328078, 125027121, 3649625763, + 347570207, 74535262, 3832543808, 2472133297, 1600064941, 65273441 + }; + + EXPECT_EQ(bigint_fibonacci(500).words(), result); +} + +TEST_CASE(test_unsigned_bigint_addition_initialization) +{ + Crypto::UnsignedBigInteger num1; + Crypto::UnsignedBigInteger num2(70); + Crypto::UnsignedBigInteger num3 = num1.plus(num2); + bool pass = (num3 == num2); + pass &= (num1 == Crypto::UnsignedBigInteger(0)); + EXPECT(pass); +} + +TEST_CASE(test_unsigned_bigint_addition_borrow_with_zero) +{ + Crypto::UnsignedBigInteger num1({ UINT32_MAX - 3, UINT32_MAX }); + Crypto::UnsignedBigInteger num2({ UINT32_MAX - 2, 0 }); + Vector expected_result { 4294967289, 0, 1 }; + EXPECT_EQ(num1.plus(num2).words(), expected_result); +} + +TEST_CASE(test_unsigned_bigint_basic_add_to_accumulator) +{ + Crypto::UnsignedBigInteger num1(10); + Crypto::UnsignedBigInteger num2(70); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + EXPECT_EQ(num1.words(), Vector { 80 }); +} + +TEST_CASE(test_unsigned_bigint_basic_add_to_empty_accumulator) +{ + Crypto::UnsignedBigInteger num1({}); + Crypto::UnsignedBigInteger num2(10); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + EXPECT_EQ(num1.words(), Vector { 10 }); +} + +TEST_CASE(test_unsigned_bigint_basic_add_to_smaller_accumulator) +{ + Crypto::UnsignedBigInteger num1(10); + Crypto::UnsignedBigInteger num2({ 10, 10 }); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + Vector expected_result { 20, 10 }; + EXPECT_EQ(num1.words(), expected_result); +} + +TEST_CASE(test_unsigned_bigint_add_to_accumulator_with_multiple_carry_levels) +{ + Crypto::UnsignedBigInteger num1({ UINT32_MAX - 2, UINT32_MAX }); + Crypto::UnsignedBigInteger num2(5); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + Vector expected_result { 2, 0, 1 }; + EXPECT_EQ(num1.words(), expected_result); +} + +TEST_CASE(test_unsigned_bigint_add_to_accumulator_with_leading_zero) +{ + Crypto::UnsignedBigInteger num1(1); + Crypto::UnsignedBigInteger num2({ 1, 0 }); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + EXPECT_EQ(num1.words(), Vector { 2 }); +} + +TEST_CASE(test_unsigned_bigint_add_to_accumulator_with_carry_and_leading_zero) +{ + Crypto::UnsignedBigInteger num1({ UINT32_MAX, 0, 0, 0 }); + Crypto::UnsignedBigInteger num2({ 1, 0 }); + Crypto::UnsignedBigIntegerAlgorithms::add_into_accumulator_without_allocation(num1, num2); + Vector expected_result { 0, 1, 0, 0 }; + EXPECT_EQ(num1.words(), expected_result); +} + +TEST_CASE(test_unsigned_bigint_simple_subtraction) +{ + Crypto::UnsignedBigInteger num1(80); + Crypto::UnsignedBigInteger num2(70); + + EXPECT_EQ(num1.minus(num2), Crypto::UnsignedBigInteger(10)); +} + +TEST_CASE(test_unsigned_bigint_simple_subtraction_invalid) +{ + Crypto::UnsignedBigInteger num1(50); + Crypto::UnsignedBigInteger num2(70); + + EXPECT(num1.minus(num2).is_invalid()); +} + +TEST_CASE(test_unsigned_bigint_simple_subtraction_with_borrow) +{ + Crypto::UnsignedBigInteger num1(UINT32_MAX); + Crypto::UnsignedBigInteger num2(1); + Crypto::UnsignedBigInteger num3 = num1.plus(num2); + Crypto::UnsignedBigInteger result = num3.minus(num2); + EXPECT_EQ(result, num1); +} + +TEST_CASE(test_unsigned_bigint_subtraction_with_large_numbers) +{ + Crypto::UnsignedBigInteger num1 = bigint_fibonacci(343); + Crypto::UnsignedBigInteger num2 = bigint_fibonacci(218); + Crypto::UnsignedBigInteger result = num1.minus(num2); + + Vector expected_result { + 811430588, 2958904896, 1130908877, 2830569969, 3243275482, + 3047460725, 774025231, 7990 + }; + EXPECT_EQ(result.plus(num2), num1); + EXPECT_EQ(result.words(), expected_result); +} + +TEST_CASE(test_unsigned_bigint_subtraction_with_large_numbers2) +{ + Crypto::UnsignedBigInteger num1(Vector { 1483061863, 446680044, 1123294122, 191895498, 3347106536, 16, 0, 0, 0 }); + Crypto::UnsignedBigInteger num2(Vector { 4196414175, 1117247942, 1123294122, 191895498, 3347106536, 16 }); + Crypto::UnsignedBigInteger result = num1.minus(num2); + // this test only verifies that we don't crash on an assertion +} + +TEST_CASE(test_unsigned_bigint_subtraction_regression_1) +{ + auto num = Crypto::UnsignedBigInteger { 1 }.shift_left(256); + Vector expected_result { + 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, + 4294967295, 4294967295, 4294967295, 0 + }; + EXPECT_EQ(num.minus(1).words(), expected_result); +} + +TEST_CASE(test_unsigned_bigint_simple_multiplication) +{ + Crypto::UnsignedBigInteger num1(8); + Crypto::UnsignedBigInteger num2(251); + Crypto::UnsignedBigInteger result = num1.multiplied_by(num2); + EXPECT_EQ(result.words(), Vector { 2008 }); +} + +TEST_CASE(test_unsigned_bigint_multiplication_with_big_numbers1) +{ + Crypto::UnsignedBigInteger num1 = bigint_fibonacci(200); + Crypto::UnsignedBigInteger num2(12345678); + Crypto::UnsignedBigInteger result = num1.multiplied_by(num2); + Vector expected_result { 669961318, 143970113, 4028714974, 3164551305, 1589380278, 2 }; + EXPECT_EQ(result.words(), expected_result); +} + +TEST_CASE(test_unsigned_bigint_multiplication_with_big_numbers2) +{ + Crypto::UnsignedBigInteger num1 = bigint_fibonacci(200); + Crypto::UnsignedBigInteger num2 = bigint_fibonacci(341); + Crypto::UnsignedBigInteger result = num1.multiplied_by(num2); + Vector expected_result { + 3017415433, 2741793511, 1957755698, 3731653885, 3154681877, + 785762127, 3200178098, 4260616581, 529754471, 3632684436, + 1073347813, 2516430 + }; + EXPECT_EQ(result.words(), expected_result); +} + +TEST_CASE(test_unsigned_bigint_simple_division) +{ + Crypto::UnsignedBigInteger num1(27194); + Crypto::UnsignedBigInteger num2(251); + auto result = num1.divided_by(num2); + Crypto::UnsignedDivisionResult expected = { Crypto::UnsignedBigInteger(108), Crypto::UnsignedBigInteger(86) }; + EXPECT_EQ(result.quotient, expected.quotient); + EXPECT_EQ(result.remainder, expected.remainder); +} + +TEST_CASE(test_unsigned_bigint_division_with_big_numbers) +{ + Crypto::UnsignedBigInteger num1 = bigint_fibonacci(386); + Crypto::UnsignedBigInteger num2 = bigint_fibonacci(238); + auto result = num1.divided_by(num2); + Crypto::UnsignedDivisionResult expected = { + Crypto::UnsignedBigInteger(Vector { 2300984486, 2637503534, 2022805584, 107 }), + Crypto::UnsignedBigInteger(Vector { 1483061863, 446680044, 1123294122, 191895498, 3347106536, 16, 0, 0, 0 }) + }; + EXPECT_EQ(result.quotient, expected.quotient); + EXPECT_EQ(result.remainder, expected.remainder); +} + +TEST_CASE(test_unsigned_bigint_division_combined_test) +{ + auto num1 = bigint_fibonacci(497); + auto num2 = bigint_fibonacci(238); + auto div_result = num1.divided_by(num2); + EXPECT_EQ(div_result.quotient.multiplied_by(num2).plus(div_result.remainder), num1); +} + +TEST_CASE(test_unsigned_bigint_base10_from_string) +{ + auto result = Crypto::UnsignedBigInteger::from_base10("57195071295721390579057195715793"); + Vector expected_result { 3806301393, 954919431, 3879607298, 721 }; + EXPECT_EQ(result.words(), expected_result); +} + +TEST_CASE(test_unsigned_bigint_base10_to_string) +{ + auto result = Crypto::UnsignedBigInteger { + Vector { 3806301393, 954919431, 3879607298, 721 } + }.to_base10(); + EXPECT_EQ(result, "57195071295721390579057195715793"); +} + +TEST_CASE(test_bigint_modular_inverse) +{ + auto result = Crypto::NumberTheory::ModularInverse(7, 87); + EXPECT_EQ(result, 25); +} + +TEST_CASE(test_bigint_even_simple_modular_power) +{ + Crypto::UnsignedBigInteger base { 7 }; + Crypto::UnsignedBigInteger exponent { 2 }; + Crypto::UnsignedBigInteger modulo { 10 }; + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + EXPECT_EQ(result.words(), Vector { 9 }); +} + +TEST_CASE(test_bigint_odd_simple_modular_power) +{ + Crypto::UnsignedBigInteger base { 10 }; + Crypto::UnsignedBigInteger exponent { 2 }; + Crypto::UnsignedBigInteger modulo { 9 }; + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + EXPECT_EQ(result.words(), Vector { 1 }); +} + +TEST_CASE(test_bigint_large_even_fibonacci_modular_power) +{ + Crypto::UnsignedBigInteger base = bigint_fibonacci(200); + Crypto::UnsignedBigInteger exponent = bigint_fibonacci(100); + Crypto::UnsignedBigInteger modulo = bigint_fibonacci(150); + // Result according to Wolfram Alpha : 7195284628716783672927396027925 + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + Vector expected_result { 2042093077, 1351416233, 3510104665, 90 }; + EXPECT_EQ(result.words(), expected_result); +} + +TEST_CASE(test_bigint_large_odd_fibonacci_modular_power) +{ + Crypto::UnsignedBigInteger base = bigint_fibonacci(200); + Crypto::UnsignedBigInteger exponent = bigint_fibonacci(100); + Crypto::UnsignedBigInteger modulo = bigint_fibonacci(149); + // Result according to Wolfram Alpha : 1136278609611966596838389694992 + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + Vector expected_result { 2106049040, 2169509253, 1468244710, 14 }; + EXPECT_EQ(result.words(), expected_result); +} + +TEST_CASE(test_bigint_large_odd_fibonacci_with_carry_modular_power) +{ + Crypto::UnsignedBigInteger base = bigint_fibonacci(200); + Crypto::UnsignedBigInteger exponent = bigint_fibonacci(100); + Crypto::UnsignedBigInteger modulo = bigint_fibonacci(185); + // Result according to Wolfram Alpha : 55094573983071006678665780782730672080 + auto result = Crypto::NumberTheory::ModularPower(base, exponent, modulo); + Vector expected_result { 1988720592, 2097784252, 347129583, 695391288 }; + EXPECT_EQ(result.words(), expected_result); +} + +TEST_CASE(test_bigint_modular_power_extra_tests) +{ + struct { + Crypto::UnsignedBigInteger base; + Crypto::UnsignedBigInteger exp; + Crypto::UnsignedBigInteger mod; + Crypto::UnsignedBigInteger expected; + } mod_pow_tests[] = { + { "2988348162058574136915891421498819466320163312926952423791023078876139"_bigint, "2351399303373464486466122544523690094744975233415544072992656881240319"_bigint, "10000"_bigint, "3059"_bigint }, + { "24231"_bigint, "12448"_bigint, "14679"_bigint, "4428"_bigint }, + { "1005404"_bigint, "8352654"_bigint, "8161408"_bigint, "2605696"_bigint }, + { "3665005778"_bigint, "3244425589"_bigint, "565668506"_bigint, "524766494"_bigint }, + { "10662083169959689657"_bigint, "11605678468317533000"_bigint, "1896834583057209739"_bigint, "1292743154593945858"_bigint }, + { "99667739213529524852296932424683448520"_bigint, "123394910770101395416306279070921784207"_bigint, "238026722756504133786938677233768788719"_bigint, "197165477545023317459748215952393063201"_bigint }, + { "49368547511968178788919424448914214709244872098814465088945281575062739912239"_bigint, "25201856190991298572337188495596990852134236115562183449699512394891190792064"_bigint, "45950460777961491021589776911422805972195170308651734432277141467904883064645"_bigint, "39917885806532796066922509794537889114718612292469285403012781055544152450051"_bigint }, + { "48399385336454791246880286907257136254351739111892925951016159217090949616810"_bigint, "5758661760571644379364752528081901787573279669668889744323710906207949658569"_bigint, "32812120644405991429173950312949738783216437173380339653152625840449006970808"_bigint, "7948464125034399875323770213514649646309423451213282653637296324080400293584"_bigint }, + }; + + for (auto test_case : mod_pow_tests) { + auto actual = Crypto::NumberTheory::ModularPower( + test_case.base, test_case.exp, test_case.mod); + + EXPECT_EQ(actual, test_case.expected); + } +} + +TEST_CASE(test_bigint_primality_test) +{ + struct { + Crypto::UnsignedBigInteger candidate; + bool expected_result; + } primality_tests[] = { + { "1180591620717411303424"_bigint, false }, // 2**70 + { "620448401733239439360000"_bigint, false }, // 25! + { "953962166440690129601298432"_bigint, false }, // 12**25 + { "620448401733239439360000"_bigint, false }, // 25! + { "147926426347074375"_bigint, false }, // 35! / 2**32 + { "340282366920938429742726440690708343523"_bigint, false }, // 2 factors near 2^64 + { "73"_bigint, true }, + { "6967"_bigint, true }, + { "787649"_bigint, true }, + { "73513949"_bigint, true }, + { "6691236901"_bigint, true }, + { "741387182759"_bigint, true }, + { "67466615915827"_bigint, true }, + { "9554317039214687"_bigint, true }, + { "533344522150170391"_bigint, true }, + { "18446744073709551557"_bigint, true }, // just below 2**64 + }; + + for (auto test_case : primality_tests) { + bool actual_result = Crypto::NumberTheory::is_probably_prime(test_case.candidate); + EXPECT_EQ(test_case.expected_result, actual_result); + } +} + +TEST_CASE(test_bigint_random_number_generation) +{ + struct { + Crypto::UnsignedBigInteger min; + Crypto::UnsignedBigInteger max; + } random_number_tests[] = { + { "1"_bigint, "1000000"_bigint }, + { "10000000000"_bigint, "20000000000"_bigint }, + { "1000"_bigint, "200000000000000000"_bigint }, + { "200000000000000000"_bigint, "200000000000010000"_bigint }, + }; + + for (auto test_case : random_number_tests) { + auto actual_result = Crypto::NumberTheory::random_number(test_case.min, test_case.max); + EXPECT(!(actual_result < test_case.min)); + EXPECT(actual_result < test_case.max); + } +} + +TEST_CASE(test_bigint_random_distribution) +{ + auto actual_result = Crypto::NumberTheory::random_number( + "1"_bigint, + "100000000000000000000000000000"_bigint); // 10**29 + if (actual_result < "100000000000000000000"_bigint) { // 10**20 + FAIL("Too small"); + outln("The generated number {} is extremely small. This *can* happen by pure chance, but should happen only once in a billion times. So it's probably an error.", actual_result.to_base10()); + } else if ("99999999900000000000000000000"_bigint < actual_result) { // 10**29 - 10**20 + FAIL("Too large"); + outln("The generated number {} is extremely large. This *can* happen by pure chance, but should happen only once in a billion times. So it's probably an error.", actual_result.to_base10()); + } +} + +TEST_CASE(test_bigint_import_big_endian_decode_encode_roundtrip) +{ + u8 random_bytes[128]; + u8 target_buffer[128]; + fill_with_random(random_bytes, 128); + auto encoded = Crypto::UnsignedBigInteger::import_data(random_bytes, 128); + encoded.export_data({ target_buffer, 128 }); + EXPECT(memcmp(target_buffer, random_bytes, 128) == 0); +} + +TEST_CASE(test_bigint_import_big_endian_encode_decode_roundtrip) +{ + u8 target_buffer[128]; + auto encoded = "12345678901234567890"_bigint; + auto size = encoded.export_data({ target_buffer, 128 }); + auto decoded = Crypto::UnsignedBigInteger::import_data(target_buffer, size); + EXPECT_EQ(encoded, decoded); +} + +TEST_CASE(test_bigint_big_endian_import) +{ + auto number = Crypto::UnsignedBigInteger::import_data("hello"); + EXPECT_EQ(number, "448378203247"_bigint); +} + +TEST_CASE(test_bigint_big_endian_export) +{ + auto number = "448378203247"_bigint; + char exported[8] { 0 }; + auto exported_length = number.export_data({ exported, 8 }, true); + EXPECT_EQ(exported_length, 5u); + EXPECT(memcmp(exported + 3, "hello", 5) == 0); +} + +TEST_CASE(test_bigint_bitwise_or) +{ + auto num1 = "1234567"_bigint; + auto num2 = "1234567"_bigint; + EXPECT_EQ(num1.bitwise_or(num2), num1); +} + +TEST_CASE(test_bigint_bitwise_or_different_lengths) +{ + auto num1 = "1234567"_bigint; + auto num2 = "123456789012345678901234567890"_bigint; + auto expected = "123456789012345678901234622167"_bigint; + auto result = num1.bitwise_or(num2); + EXPECT_EQ(result, expected); +} + +TEST_CASE(test_bigint_bitwise_and) +{ + auto num1 = "1234567"_bigint; + auto num2 = "1234561"_bigint; + EXPECT_EQ(num1.bitwise_and(num2), "1234561"_bigint); +} + +TEST_CASE(test_bigint_bitwise_and_different_lengths) +{ + auto num1 = "1234567"_bigint; + auto num2 = "123456789012345678901234567890"_bigint; + EXPECT_EQ(num1.bitwise_and(num2), "1180290"_bigint); +} + +TEST_CASE(test_bigint_bitwise_xor) +{ + auto num1 = "1234567"_bigint; + auto num2 = "1234561"_bigint; + EXPECT_EQ(num1.bitwise_xor(num2), 6); +} + +TEST_CASE(test_bigint_bitwise_xor_different_lengths) +{ + auto num1 = "1234567"_bigint; + auto num2 = "123456789012345678901234567890"_bigint; + EXPECT_EQ(num1.bitwise_xor(num2), "123456789012345678901233441877"_bigint); +} + +TEST_CASE(test_signed_bigint_fibo500) +{ + Vector expected_result { + 315178285, 505575602, 1883328078, 125027121, + 3649625763, 347570207, 74535262, 3832543808, + 2472133297, 1600064941, 65273441 + }; + auto result = bigint_signed_fibonacci(500); + EXPECT_EQ(result.unsigned_value().words(), expected_result); +} + +TEST_CASE(test_signed_addition_edgecase_borrow_with_zero) +{ + Crypto::SignedBigInteger num1 { Crypto::UnsignedBigInteger { { UINT32_MAX - 3, UINT32_MAX } }, false }; + Crypto::SignedBigInteger num2 { Crypto::UnsignedBigInteger { UINT32_MAX - 2 }, false }; + Vector expected_result { 4294967289, 0, 1 }; + EXPECT_EQ(num1.plus(num2).unsigned_value().words(), expected_result); +} + +TEST_CASE(test_signed_addition_edgecase_addition_to_other_sign) +{ + Crypto::SignedBigInteger num1 = INT32_MAX; + Crypto::SignedBigInteger num2 = num1; + num2.negate(); + EXPECT_EQ(num1.plus(num2), Crypto::SignedBigInteger { 0 }); +} + +TEST_CASE(test_signed_subtraction_simple_subtraction_positive_result) +{ + Crypto::SignedBigInteger num1(80); + Crypto::SignedBigInteger num2(70); + EXPECT_EQ(num1.minus(num2), Crypto::SignedBigInteger(10)); +} + +TEST_CASE(test_signed_subtraction_simple_subtraction_negative_result) +{ + Crypto::SignedBigInteger num1(50); + Crypto::SignedBigInteger num2(70); + + EXPECT_EQ(num1.minus(num2), Crypto::SignedBigInteger { -20 }); +} + +TEST_CASE(test_signed_subtraction_simple_subtraction_with_borrow) +{ + Crypto::SignedBigInteger num1(Crypto::UnsignedBigInteger { UINT32_MAX }); + Crypto::SignedBigInteger num2(1); + Crypto::SignedBigInteger num3 = num1.plus(num2); + Crypto::SignedBigInteger result = num2.minus(num3); + num1.negate(); + EXPECT_EQ(result, num1); +} + +TEST_CASE(test_signed_subtraction_with_large_numbers) +{ + Crypto::SignedBigInteger num1 = bigint_signed_fibonacci(343); + Crypto::SignedBigInteger num2 = bigint_signed_fibonacci(218); + Crypto::SignedBigInteger result = num2.minus(num1); + auto expected = Crypto::UnsignedBigInteger { Vector { 811430588, 2958904896, 1130908877, 2830569969, 3243275482, 3047460725, 774025231, 7990 } }; + EXPECT_EQ(result.plus(num1), num2); + EXPECT_EQ(result.unsigned_value(), expected); +} + +TEST_CASE(test_signed_subtraction_with_large_numbers_check_for_assertion) +{ + Crypto::SignedBigInteger num1(Crypto::UnsignedBigInteger { Vector { 1483061863, 446680044, 1123294122, 191895498, 3347106536, 16, 0, 0, 0 } }); + Crypto::SignedBigInteger num2(Crypto::UnsignedBigInteger { Vector { 4196414175, 1117247942, 1123294122, 191895498, 3347106536, 16 } }); + Crypto::SignedBigInteger result = num1.minus(num2); + // this test only verifies that we don't crash on an assertion +} + +TEST_CASE(test_signed_multiplication_with_negative_number) +{ + Crypto::SignedBigInteger num1(8); + Crypto::SignedBigInteger num2(-251); + Crypto::SignedBigInteger result = num1.multiplied_by(num2); + EXPECT_EQ(result, Crypto::SignedBigInteger { -2008 }); +} + +TEST_CASE(test_signed_multiplication_with_big_number) +{ + Crypto::SignedBigInteger num1 = bigint_signed_fibonacci(200); + Crypto::SignedBigInteger num2(-12345678); + Crypto::SignedBigInteger result = num1.multiplied_by(num2); + Vector expected_result { 669961318, 143970113, 4028714974, 3164551305, 1589380278, 2 }; + EXPECT_EQ(result.unsigned_value().words(), expected_result); + EXPECT(result.is_negative()); +} + +TEST_CASE(test_signed_multiplication_with_two_big_numbers) +{ + Crypto::SignedBigInteger num1 = bigint_signed_fibonacci(200); + Crypto::SignedBigInteger num2 = bigint_signed_fibonacci(341); + num1.negate(); + Crypto::SignedBigInteger result = num1.multiplied_by(num2); + Vector expected_results { + 3017415433, 2741793511, 1957755698, 3731653885, + 3154681877, 785762127, 3200178098, 4260616581, + 529754471, 3632684436, 1073347813, 2516430 + }; + EXPECT_EQ(result.unsigned_value().words(), expected_results); + EXPECT(result.is_negative()); +} diff --git a/Tests/LibCrypto/TestChecksum.cpp b/Tests/LibCrypto/TestChecksum.cpp new file mode 100644 index 0000000000..60293f0f8c --- /dev/null +++ b/Tests/LibCrypto/TestChecksum.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Peter Bocan + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +TEST_CASE(test_adler32) +{ + auto do_test = [](ReadonlyBytes input, u32 expected_result) { + auto digest = Crypto::Checksum::Adler32(input).digest(); + EXPECT_EQ(digest, expected_result); + }; + + do_test(String("").bytes(), 0x1); + do_test(String("a").bytes(), 0x00620062); + do_test(String("abc").bytes(), 0x024d0127); + do_test(String("message digest").bytes(), 0x29750586); + do_test(String("abcdefghijklmnopqrstuvwxyz").bytes(), 0x90860b20); +} + +TEST_CASE(test_crc32) +{ + auto do_test = [](ReadonlyBytes input, u32 expected_result) { + auto digest = Crypto::Checksum::CRC32(input).digest(); + EXPECT_EQ(digest, expected_result); + }; + + do_test(String("").bytes(), 0x0); + do_test(String("The quick brown fox jumps over the lazy dog").bytes(), 0x414FA339); + do_test(String("various CRC algorithms input data").bytes(), 0x9BD366AE); +} diff --git a/Tests/LibCrypto/TestHMAC.cpp b/Tests/LibCrypto/TestHMAC.cpp new file mode 100644 index 0000000000..c07d371c8b --- /dev/null +++ b/Tests/LibCrypto/TestHMAC.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2021, [your name here] <[your email here]> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +TEST_CASE(test_hmac_md5_name) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + EXPECT_EQ(hmac.class_name(), "HMAC-MD5"); +} + +TEST_CASE(test_hmac_md5_process) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + u8 result[] { + 0x3b, 0x5b, 0xde, 0x30, 0x3a, 0x54, 0x7b, 0xbb, 0x09, 0xfe, 0x78, 0x89, 0xbc, 0x9f, 0x22, 0xa3 + }; + auto mac = hmac.process("Some bogus data"); + EXPECT(memcmp(result, mac.data, hmac.digest_size()) == 0); +} + +TEST_CASE(test_hmac_md5_process_reuse) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + + auto mac_0 = hmac.process("Some bogus data"); + auto mac_1 = hmac.process("Some bogus data"); + + EXPECT(memcmp(mac_0.data, mac_1.data, hmac.digest_size()) == 0); +} + +TEST_CASE(test_hmac_sha1_name) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + EXPECT_EQ(hmac.class_name(), "HMAC-SHA1"); +} + +TEST_CASE(test_hmac_sha1_process) +{ + u8 key[] { 0xc8, 0x52, 0xe5, 0x4a, 0x2c, 0x03, 0x2b, 0xc9, 0x63, 0xd3, 0xc2, 0x79, 0x0f, 0x76, 0x43, 0xef, 0x36, 0xc3, 0x7a, 0xca }; + Crypto::Authentication::HMAC hmac(ReadonlyBytes { key, sizeof(key) }); + u8 result[] { + 0x2c, 0x57, 0x32, 0x61, 0x3b, 0xa7, 0x84, 0x87, 0x0e, 0x4f, 0x42, 0x07, 0x2f, 0xf0, 0xe7, 0x41, 0xd7, 0x15, 0xf4, 0x56 + }; + u8 value[] { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x03, 0x03, 0x00, 0x10, 0x14, 0x00, 0x00, 0x0c, 0xa1, 0x91, 0x1a, 0x20, 0x59, 0xb5, 0x45, 0xa9, 0xb4, 0xad, 0x75, 0x3e + }; + auto mac = hmac.process(value, 29); + EXPECT(memcmp(result, mac.data, hmac.digest_size()) == 0); +} + +TEST_CASE(test_hmac_sha1_process_reuse) +{ + u8 key[] { 0xc8, 0x52, 0xe5, 0x4a, 0x2c, 0x03, 0x2b, 0xc9, 0x63, 0xd3, 0xc2, 0x79, 0x0f, 0x76, 0x43, 0xef, 0x36, 0xc3, 0x7a, 0xca }; + Crypto::Authentication::HMAC hmac(ReadonlyBytes { key, sizeof(key) }); + u8 result[] { + 0x2c, 0x57, 0x32, 0x61, 0x3b, 0xa7, 0x84, 0x87, 0x0e, 0x4f, 0x42, 0x07, 0x2f, 0xf0, 0xe7, 0x41, 0xd7, 0x15, 0xf4, 0x56 + }; + u8 value[] { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x03, 0x03, 0x00, 0x10, 0x14, 0x00, 0x00, 0x0c, 0xa1, 0x91, 0x1a, 0x20, 0x59, 0xb5, 0x45, 0xa9, 0xb4, 0xad, 0x75, 0x3e + }; + hmac.update(value, 8); + hmac.update(value + 8, 5); + hmac.update(value + 13, 16); + auto mac = hmac.digest(); + EXPECT(memcmp(result, mac.data, hmac.digest_size()) == 0); +} + +TEST_CASE(test_hmac_sha256_name) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + EXPECT_EQ(hmac.class_name(), "HMAC-SHA256"); +} + +TEST_CASE(test_hmac_sha256_process) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + u8 result[] { + 0x1a, 0xf2, 0x20, 0x62, 0xde, 0x3b, 0x84, 0x65, 0xc1, 0x25, 0x23, 0x99, 0x76, 0x15, 0x1b, 0xec, 0x15, 0x21, 0x82, 0x1f, 0x23, 0xca, 0x11, 0x66, 0xdd, 0x8c, 0x6e, 0xf1, 0x81, 0x3b, 0x7f, 0x1b + }; + auto mac = hmac.process("Some bogus data"); + EXPECT(memcmp(result, mac.data, hmac.digest_size()) == 0); +} + +TEST_CASE(test_hmac_sha256_reuse) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + + auto mac_0 = hmac.process("Some bogus data"); + auto mac_1 = hmac.process("Some bogus data"); + + EXPECT(memcmp(mac_0.data, mac_1.data, hmac.digest_size()) == 0); +} + +TEST_CASE(test_hmac_sha256_data_is_same_size_as_block) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + u8 result[] = { + 0x1d, 0x90, 0xce, 0x68, 0x45, 0x0b, 0xba, 0xd6, 0xbe, 0x1c, 0xb2, 0x3a, 0xea, 0x7f, 0xac, 0x4b, 0x68, 0x08, 0xa4, 0x77, 0x81, 0x2a, 0xad, 0x5d, 0x05, 0xe2, 0x15, 0xe8, 0xf4, 0xcb, 0x06, 0xaf + }; + auto mac = hmac.process("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + EXPECT(memcmp(result, mac.data, hmac.digest_size()) == 0); +} + +TEST_CASE(test_hmac_sha256_data_is_bigger_size_as_block) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + u8 result[] = { + 0x9b, 0xa3, 0x9e, 0xf3, 0xb4, 0x30, 0x5f, 0x6f, 0x67, 0xd0, 0xa8, 0xb0, 0xf0, 0xcb, 0x12, 0xf5, 0x85, 0xe2, 0x19, 0xba, 0x0c, 0x8b, 0xe5, 0x43, 0xf0, 0x93, 0x39, 0xa8, 0xa3, 0x07, 0xf1, 0x95 + }; + auto mac = hmac.process("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + EXPECT(memcmp(result, mac.data, hmac.digest_size()) == 0); +} + +TEST_CASE(test_hmac_sha512_name) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + EXPECT_EQ(hmac.class_name(), "HMAC-SHA512"); +} + +TEST_CASE(test_hmac_sha512_process) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + u8 result[] { + 0xeb, 0xa8, 0x34, 0x11, 0xfd, 0x5b, 0x46, 0x5b, 0xef, 0xbb, 0x67, 0x5e, 0x7d, 0xc2, 0x7c, 0x2c, 0x6b, 0xe1, 0xcf, 0xe6, 0xc7, 0xe4, 0x7d, 0xeb, 0xca, 0x97, 0xb7, 0x4c, 0xd3, 0x4d, 0x6f, 0x08, 0x9f, 0x0d, 0x3a, 0xf1, 0xcb, 0x00, 0x79, 0x78, 0x2f, 0x05, 0x8e, 0xeb, 0x94, 0x48, 0x0d, 0x50, 0x64, 0x3b, 0xca, 0x70, 0xe2, 0x69, 0x38, 0x4f, 0xe4, 0xb0, 0x49, 0x0f, 0xc5, 0x4c, 0x7a, 0xa7 + }; + auto mac = hmac.process("Some bogus data"); + EXPECT(memcmp(result, mac.data, hmac.digest_size()) == 0); +} + +TEST_CASE(test_hmac_sha512_reuse) +{ + Crypto::Authentication::HMAC hmac("Well Hello Friends"); + + auto mac_0 = hmac.process("Some bogus data"); + auto mac_1 = hmac.process("Some bogus data"); + + EXPECT(memcmp(mac_0.data, mac_1.data, hmac.digest_size()) == 0); +} diff --git a/Tests/LibCrypto/TestHash.cpp b/Tests/LibCrypto/TestHash.cpp new file mode 100644 index 0000000000..a5f8a4fe83 --- /dev/null +++ b/Tests/LibCrypto/TestHash.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2021, Peter Bocan + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE(test_MD5_name) +{ + Crypto::Hash::MD5 md5; + EXPECT(md5.class_name() == "MD5"); +} + +TEST_CASE(test_MD5_hash_string) +{ + u8 result[] { + 0xaf, 0x04, 0x3a, 0x08, 0x94, 0x38, 0x6e, 0x7f, 0xbf, 0x73, 0xe4, 0xaa, 0xf0, 0x8e, 0xee, 0x4c + }; + auto digest = Crypto::Hash::MD5::hash("Well hello friends"); + EXPECT(memcmp(result, digest.data, Crypto::Hash::MD5::digest_size()) == 0); +} + +TEST_CASE(test_MD5_hash_empty_string) +{ + u8 result[] { + 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e + }; + auto digest = Crypto::Hash::MD5::hash(""); + EXPECT(memcmp(result, digest.data, Crypto::Hash::MD5::digest_size()) == 0); +} + +TEST_CASE(test_MD5_hash_single_character) +{ + u8 result[] { + 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 + }; + auto digest = Crypto::Hash::MD5::hash("a"); + EXPECT(memcmp(result, digest.data, Crypto::Hash::MD5::digest_size()) == 0); +} + +TEST_CASE(test_MD5_hash_alphabet) +{ + u8 result[] { + 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b + }; + auto digest = Crypto::Hash::MD5::hash("abcdefghijklmnopqrstuvwxyz"); + EXPECT(memcmp(result, digest.data, Crypto::Hash::MD5::digest_size()) == 0); +} + +TEST_CASE(test_MD5_hash_long_sequence) +{ + u8 result[] { + 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a + }; + auto digest = Crypto::Hash::MD5::hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"); + EXPECT(memcmp(result, digest.data, Crypto::Hash::MD5::digest_size()) == 0); +} + +TEST_CASE(test_MD5_consecutive_multiple_updates) +{ + u8 result[] { + 0xaf, 0x04, 0x3a, 0x08, 0x94, 0x38, 0x6e, 0x7f, 0xbf, 0x73, 0xe4, 0xaa, 0xf0, 0x8e, 0xee, 0x4c + }; + Crypto::Hash::MD5 md5; + + md5.update("Well"); + md5.update(" hello "); + md5.update("friends"); + auto digest = md5.digest(); + + EXPECT(memcmp(result, digest.data, Crypto::Hash::MD5::digest_size()) == 0); +} + +TEST_CASE(test_MD5_consecutive_updates_reuse) +{ + Crypto::Hash::MD5 md5; + + md5.update("Well"); + md5.update(" hello "); + md5.update("friends"); + auto digest0 = md5.digest(); + + md5.update("Well"); + md5.update(" hello "); + md5.update("friends"); + auto digest1 = md5.digest(); + + EXPECT(memcmp(digest0.data, digest1.data, Crypto::Hash::MD5::digest_size()) == 0); +} + +TEST_CASE(test_SHA1_name) +{ + Crypto::Hash::SHA1 sha; + EXPECT(sha.class_name() == "SHA1"); +} + +TEST_CASE(test_SHA1_hash_empty_string) +{ + u8 result[] { + 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 + }; + auto digest = Crypto::Hash::SHA1::hash(""); + EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA1::digest_size()) == 0); +} + +TEST_CASE(test_SHA1_hash_long_string) +{ + u8 result[] { + 0x12, 0x15, 0x1f, 0xb1, 0x04, 0x44, 0x93, 0xcc, 0xed, 0x54, 0xa6, 0xb8, 0x7e, 0x93, 0x37, 0x7b, 0xb2, 0x13, 0x39, 0xdb + }; + auto digest = Crypto::Hash::SHA1::hash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA1::digest_size()) == 0); +} + +TEST_CASE(test_SHA1_hash_successive_updates) +{ + u8 result[] { + 0xd6, 0x6e, 0xce, 0xd1, 0xf4, 0x08, 0xc6, 0xd8, 0x35, 0xab, 0xf0, 0xc9, 0x05, 0x26, 0xa4, 0xb2, 0xb8, 0xa3, 0x7c, 0xd3 + }; + auto hasher = Crypto::Hash::SHA1 {}; + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaaaaaaaa"); + hasher.update("aaaaaaaaa"); + auto digest = hasher.digest(); + EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA1::digest_size()) == 0); +} + +TEST_CASE(test_SHA256_name) +{ + Crypto::Hash::SHA256 sha; + EXPECT_EQ(sha.class_name(), "SHA256"); +} + +TEST_CASE(test_SHA256_hash_string) +{ + u8 result[] { + 0x9a, 0xcd, 0x50, 0xf9, 0xa2, 0xaf, 0x37, 0xe4, 0x71, 0xf7, 0x61, 0xc3, 0xfe, 0x7b, 0x8d, 0xea, 0x56, 0x17, 0xe5, 0x1d, 0xac, 0x80, 0x2f, 0xe6, 0xc1, 0x77, 0xb7, 0x4a, 0xbf, 0x0a, 0xbb, 0x5a + }; + auto digest = Crypto::Hash::SHA256::hash("Well hello friends"); + EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA256::digest_size()) == 0); +} + +TEST_CASE(test_SHA256_hash_empty_string) +{ + u8 result[] { + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 + }; + auto digest = Crypto::Hash::SHA256::hash(""); + EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA256::digest_size()) == 0); +} + +TEST_CASE(test_SHA384_name) +{ + Crypto::Hash::SHA384 sha; + EXPECT_EQ(sha.class_name(), "SHA384"); +} + +TEST_CASE(test_SHA384_hash_string) +{ + u8 result[] { + 0x2f, 0x01, 0x8e, 0x9a, 0x4f, 0xd1, 0x36, 0xb9, 0x0f, 0xcc, 0x21, 0xde, 0x1a, 0xd4, 0x49, 0x51, 0x57, 0x82, 0x86, 0x84, 0x54, 0x09, 0x82, 0x7b, 0x54, 0x56, 0x93, 0xac, 0x2c, 0x46, 0x0c, 0x1f, 0x5e, 0xec, 0xe0, 0xf7, 0x8b, 0x0b, 0x84, 0x27, 0xc8, 0xb8, 0xbe, 0x49, 0xce, 0x8f, 0x1c, 0xff + }; + auto digest = Crypto::Hash::SHA384::hash("Well hello friends"); + EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA384::digest_size()) == 0); +} + +TEST_CASE(test_SHA512_name) +{ + Crypto::Hash::SHA512 sha; + EXPECT_EQ(sha.class_name(), "SHA512"); +} + +TEST_CASE(test_SHA512_hash_string) +{ + u8 result[] { + 0x00, 0xfe, 0x68, 0x09, 0x71, 0x0e, 0xcb, 0x2b, 0xe9, 0x58, 0x00, 0x13, 0x69, 0x6a, 0x9e, 0x9e, 0xbd, 0x09, 0x1b, 0xfe, 0x14, 0xc9, 0x13, 0x82, 0xc7, 0x40, 0x34, 0xfe, 0xca, 0xe6, 0x87, 0xcb, 0x26, 0x36, 0x92, 0xe6, 0x34, 0x94, 0x3a, 0x11, 0xe5, 0xbb, 0xb5, 0xeb, 0x8e, 0x70, 0xef, 0x64, 0xca, 0xf7, 0x21, 0xb1, 0xde, 0xf2, 0x34, 0x85, 0x6f, 0xa8, 0x56, 0xd8, 0x23, 0xa1, 0x3b, 0x29 + }; + auto digest = Crypto::Hash::SHA512::hash("Well hello friends"); + EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA512::digest_size()) == 0); +} + +TEST_CASE(test_SHA512_hash_empty_string) +{ + u8 result[] { + 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e + }; + auto digest = Crypto::Hash::SHA512::hash(""); + EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA512::digest_size()) == 0); +} + +TEST_CASE(test_ghash_test_name) +{ + Crypto::Authentication::GHash ghash("WellHelloFriends"); + EXPECT_EQ(ghash.class_name(), "GHash"); +} + +TEST_CASE(test_ghash_galois_field_multiply) +{ + u32 x[4] { 0x42831ec2, 0x21777424, 0x4b7221b7, 0x84d0d49c }, + y[4] { 0xb83b5337, 0x08bf535d, 0x0aa6e529, 0x80d53b78 }, z[4] { 0, 0, 0, 0 }; + static constexpr u32 result[4] { 0x59ed3f2b, 0xb1a0aaa0, 0x7c9f56c6, 0xa504647b }; + + Crypto::Authentication::galois_multiply(z, x, y); + EXPECT(memcmp(result, z, 4 * sizeof(u32)) == 0); +} + +TEST_CASE(test_ghash_galois_field_multiply2) +{ + u32 x[4] { 59300558, 1622582162, 4079534777, 1907555960 }, + y[4] { 1726565332, 4018809915, 2286746201, 3392416558 }, z[4]; + constexpr static u32 result[4] { 1580123974, 2440061576, 746958952, 1398005431 }; + + Crypto::Authentication::galois_multiply(z, x, y); + EXPECT(memcmp(result, z, 4 * sizeof(u32)) == 0); +} diff --git a/Tests/LibCrypto/TestRSA.cpp b/Tests/LibCrypto/TestRSA.cpp new file mode 100644 index 0000000000..8f14a7fb19 --- /dev/null +++ b/Tests/LibCrypto/TestRSA.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021, Peter Bocan + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +static ByteBuffer operator""_b(const char* string, size_t length) +{ + return ByteBuffer::copy(string, length); +} + +TEST_CASE(test_RSA_raw_encrypt) +{ + ByteBuffer data { "hellohellohellohellohellohellohellohellohellohellohellohello123-"_b }; + u8 result[] { 0x6f, 0x7b, 0xe2, 0xd3, 0x95, 0xf8, 0x8d, 0x87, 0x6d, 0x10, 0x5e, 0xc3, 0xcd, 0xf7, 0xbb, 0xa6, 0x62, 0x8e, 0x45, 0xa0, 0xf1, 0xe5, 0x0f, 0xdf, 0x69, 0xcb, 0xb6, 0xd5, 0x42, 0x06, 0x7d, 0x72, 0xa9, 0x5e, 0xae, 0xbf, 0xbf, 0x0f, 0xe0, 0xeb, 0x31, 0x31, 0xca, 0x8a, 0x81, 0x1e, 0xb9, 0xec, 0x6d, 0xcc, 0xb8, 0xa4, 0xac, 0xa3, 0x31, 0x05, 0xa9, 0xac, 0xc9, 0xd3, 0xe6, 0x2a, 0x18, 0xfe }; + Crypto::PK::RSA rsa( + "8126832723025844890518845777858816391166654950553329127845898924164623511718747856014227624997335860970996746552094406240834082304784428582653994490504519"_bigint, + "4234603516465654167360850580101327813936403862038934287300450163438938741499875303761385527882335478349599685406941909381269804396099893549838642251053393"_bigint, + "65537"_bigint); + u8 buffer[rsa.output_size()]; + auto buf = Bytes { buffer, sizeof(buffer) }; + rsa.encrypt(data, buf); + EXPECT(memcmp(result, buf.data(), buf.size()) == 0); +} + +// RSA PKCS #1 1.5 +TEST_CASE(test_RSA_PKCS_1_encrypt) +{ + ByteBuffer data { "hellohellohellohellohellohellohellohellohello123-"_b }; + Crypto::PK::RSA_PKCS1_EME rsa( + "8126832723025844890518845777858816391166654950553329127845898924164623511718747856014227624997335860970996746552094406240834082304784428582653994490504519"_bigint, + "4234603516465654167360850580101327813936403862038934287300450163438938741499875303761385527882335478349599685406941909381269804396099893549838642251053393"_bigint, + "65537"_bigint); + u8 buffer[rsa.output_size()]; + auto buf = Bytes { buffer, sizeof(buffer) }; + rsa.encrypt(data, buf); + rsa.decrypt(buf, buf); + + EXPECT(memcmp(buf.data(), "hellohellohellohellohellohellohellohellohello123-", 49) == 0); +} + +// RSA | ASN1 PKCS1 DER / PEM encoded Key import +TEST_CASE(test_RSA_ASN1_PKCS1_DER_PEM_parse) +{ + auto privkey = R"(-----BEGIN RSA PRIVATE KEY----- +MIIBOgIBAAJBAJsrIYHxs1YL9tpfodaWs1lJoMdF4kgFisUFSj6nvBhJUlmBh607AlgTaX0E +DGPYycXYGZ2n6rqmms5lpDXBpUcCAwEAAQJAUNpPkmtEHDENxsoQBUXvXDYeXdePSiIBJhpU +joNOYoR5R9z5oX2cpcyykQ58FC2vKKg+x8N6xczG7qO95tw5UQIhAN354CP/FA+uTeJ6KJ+i +zCBCl58CjNCzO0s5HTc56el5AiEAsvPKXo5/9gS/S4UzDRP6abq7GreixTfjR8LXidk3FL8C +IQCTjYI861Y+hjMnlORkGSdvWlTHUj6gjEOh4TlWeJzQoQIgAxMZOQKtxCZUuxFwzRq4xLRG +nrDlBQpuxz7bwSyQO7UCIHrYMnDohgNbwtA5ZpW3H1cKKQQvueWm6sxW9P5sUrZ3 +-----END RSA PRIVATE KEY-----)"; + + Crypto::PK::RSA rsa(privkey); + if (rsa.public_key().public_exponent() != 65537) { + FAIL("Invalid public exponent"); + } + if (rsa.private_key().private_exponent() != "4234603516465654167360850580101327813936403862038934287300450163438938741499875303761385527882335478349599685406941909381269804396099893549838642251053393"_bigint) { + FAIL("Invalid private exponent"); + } +} + +// RSA | ASN1 PKCS8 DER / PEM encoded Key import +TEST_CASE(test_RSA_ASN1_PKCS8_DER_PEM_parse) +{ + auto privkey = R"(-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7ZBYaG9+CcJP7 +WVFJRI/uw3hljc7WpzeYs8MN82/g9CG1gnEF3P3ZSBdWVr8gnbh05EsSGHKghIce +CB7DNrM5Ab0ru04CuODdPx56xCj+4MmzTc/aq79ntmOt131NGHgq9yVwfJqnSpyl +OoVw7j/Wg4ciwPDQaeLmD1BsE/W9UsF1km7DWasBpW5br82DpudKgJq2Ixf52+rY +TCkMgyWcetx4MfXll4y5ZVtJXCnHJfkCS64EaCqXmClP4ovOuHH4khJ3rW9j4yuL +e5ck3PSXOrtOTR43HZkCXzseCkbW7qKSmk/9ZreImOzOgu8vvw7ewLAQR9qYVS6X +PXY8IilDAgMBAAECggEBAIV3ld5mt90Z/exqA2Fh+fofMyNxyz5Lv2d9sZHAL5FT +kKbND18TtaIKnMSb6Gl8rKJk76slyo7Vlb8oHXEBBsm1mV0KfVenAlHS4QyjpmdT +B5Yz97VR2nQuDfUFpHNC2GQRv5LMzQIWPFfaxKxYpRNOfvOb5Gks4bTmd2tjFAYR +MCbHgPw1liKA9dYKk4NB0301EY05e4Zz8RjqYHkkmOPD7DnjFbHqcFUjVKK5E3vD +WjxNXUbiSudCCN7WLEOyeHZNd+l6kSAVxZuCAp0G3Da5ndXgIStcy4hYi/fL3XQQ +bNpxjfhsjlD3tdHNr3NNYDAqxcxpsyO1NCpCIW3ZVrECgYEA7l6gTZ3e9AiSNlMd +2O2vNnbQ6UZfsEfu2y7HmpCuNJkFkAnM/1h72Krejnn31rRuR6uCFn4YgQUN9Eq0 +E1PJCtTay2ucZw5rqtkewT9QzXvVD9eiGM+MF89UzSCC+dOW0/odkD+xP2evnPvG +PbXztnuERC1pi0YWLj1YcsfsEX0CgYEAyUA2UtYjnvCcteIy+rURT0aoZ9tDMrG+ +Es42EURVv1sduVdUst5R+bXx1aDzpCkcdni3TyxeosvTGAZngI3O8ghh1GV7NPZR +nkiPXjMnhL0Zf+X9gCA6TFANfPuWhMSGijYsCd46diKGDReGYUnmcN9XopeG1h6i +3JiOuVPAIb8CgYBmIcUtfGb6yHFdNV+kgrJ/84ivaqe1MBz3bKO5ZiQ+BRKNFKXx +AkiOHSgeg8PdCpH1w1aJrJ1zKmdANIHThiKtsWXNot3wig03tq+mvSox4Mz5bLrX +RpYP3ZXIDhYQVMhbKt9f3upi8FoeOQJHjp5Nob6aN5rxQaZfSYmMJHzRQQKBgQCO +ALwUGTtLNBYvlKtKEadkG8RKfAFfbOFkXZLy/hfPDRjdJY0DJTIMk+BPT+F6rPOD +eMxHllQ0ZMPPiP1RTT5/s4BsISsdhMy0dhiLbGbvF4s9nugPly3rmPTbgp6DkjQo +o+7RC7iOkO+rnzTXwxBSBpXMiUTAIx/hrdfPVxQT+wKBgCh7N3OLIOH6EWcW1fif +UoENh8rkt/kzm89G1JLwBhuBIBPXUEZt2dS/xSUempqVqFGONpP87gvqxkMTtgCA +73KXn/cxHWM2kmXyHA3kQlOYw6WHjpldQAxLE+TRHXO2JUtZ09Mu4rVXX7lmwbTm +l3vmuDEF3/Bo1C1HTg0xRV/l +-----END PRIVATE KEY-----)"; + + Crypto::PK::RSA rsa(privkey); + if (rsa.public_key().public_exponent() != 65537) { + FAIL("Invalid public exponent"); + } + + if (rsa.private_key().private_exponent() != "16848664331299797559656678180469464902267415922431923391961407795209879741791261105581093539484181644099608161661780611501562625272630894063592208758992911105496755004417051031019663332258403844985328863382168329621318366311519850803972480500782200178279692319955495383119697563295214236936264406600739633470565823022975212999060908747002623721589308539473108154612454595201561671949550531384574873324370774408913092560971930541734744950937900805812300970883306404011323308000168926094053141613790857814489531436452649384151085451448183385611208320292948291211969430321231180227006521681776197974694030147965578466993"_bigint) { + FAIL("Invalid private exponent"); + } +} + +TEST_CASE(test_RSA_encrypt_decrypt) +{ + Crypto::PK::RSA rsa( + "9527497237087650398000977129550904920919162360737979403539302312977329868395261515707123424679295515888026193056908173564681660256268221509339074678416049"_bigint, + "39542231845947188736992321577701849924317746648774438832456325878966594812143638244746284968851807975097653255909707366086606867657273809465195392910913"_bigint, + "65537"_bigint); + + u8 enc_buffer[rsa.output_size()]; + u8 dec_buffer[rsa.output_size()]; + + auto enc = Bytes { enc_buffer, rsa.output_size() }; + auto dec = Bytes { dec_buffer, rsa.output_size() }; + + enc.overwrite(0, "WellHelloFriendsWellHelloFriendsWellHelloFriendsWellHelloFriends", 64); + + rsa.encrypt(enc, dec); + rsa.decrypt(dec, enc); + + EXPECT(memcmp(enc.data(), "WellHelloFriendsWellHelloFriendsWellHelloFriendsWellHelloFriends", 64) == 0); +} + +TEST_CASE(test_RSA_EMSA_PSS_construction) +{ + // This is a template validity test + Crypto::PK::RSA rsa; + Crypto::PK::RSA_EMSA_PSS rsa_esma_pss(rsa); +} diff --git a/Tests/LibTLS/CMakeLists.txt b/Tests/LibTLS/CMakeLists.txt new file mode 100644 index 0000000000..4896f8f016 --- /dev/null +++ b/Tests/LibTLS/CMakeLists.txt @@ -0,0 +1,4 @@ +file(GLOB TEST_SOURCES CONFIGURE_DEPENDS "*.cpp") +foreach(source ${TEST_SOURCES}) + serenity_test(${source} LibTLS LIBS LibTLS LibCrypto) +endforeach() diff --git a/Tests/LibTLS/TestTLSHandshake.cpp b/Tests/LibTLS/TestTLSHandshake.cpp new file mode 100644 index 0000000000..a38f1f7f3c --- /dev/null +++ b/Tests/LibTLS/TestTLSHandshake.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021, Peter Bocan + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +static const char* ca_certs_file = "./ca_certs.ini"; +static int port = 443; + +constexpr const char* DEFAULT_DIGEST_SUITE { "HMAC-SHA256" }; +constexpr const char* DEFAULT_CHECKSUM_SUITE { "CRC32" }; +constexpr const char* DEFAULT_HASH_SUITE { "SHA256" }; +constexpr const char* DEFAULT_CIPHER_SUITE { "AES_CBC" }; +constexpr const char* DEFAULT_SERVER { "www.google.com" }; + +static ByteBuffer operator""_b(const char* string, size_t length) +{ + return ByteBuffer::copy(string, length); +} + +Vector load_certificates(); +String locate_ca_certs_file(); + +String locate_ca_certs_file() +{ + if (Core::File::exists(ca_certs_file)) { + return ca_certs_file; + } + auto on_target_path = String("/etc/ca_certs.ini"); + if (Core::File::exists(on_target_path)) { + return on_target_path; + } + return ""; +} + +Vector load_certificates() +{ + Vector certificates; + auto ca_certs_filepath = locate_ca_certs_file(); + if (ca_certs_filepath == "") { + warnln("Could not locate ca_certs.ini file."); + return certificates; + } + + auto config = Core::ConfigFile::open(ca_certs_filepath); + auto now = Core::DateTime::now(); + auto last_year = Core::DateTime::create(now.year() - 1); + auto next_year = Core::DateTime::create(now.year() + 1); + for (auto& entity : config->groups()) { + Certificate cert; + cert.subject.subject = entity; + cert.issuer.subject = config->read_entry(entity, "issuer_subject", entity); + cert.subject.country = config->read_entry(entity, "country"); + cert.not_before = Crypto::ASN1::parse_generalized_time(config->read_entry(entity, "not_before", "")).value_or(last_year); + cert.not_after = Crypto::ASN1::parse_generalized_time(config->read_entry(entity, "not_after", "")).value_or(next_year); + certificates.append(move(cert)); + } + return certificates; +} + +static Vector s_root_ca_certificates = load_certificates(); + +TEST_CASE(test_TLS_hello_handshake) +{ + Core::EventLoop loop; + RefPtr tls = TLS::TLSv12::construct(nullptr); + tls->set_root_certificates(s_root_ca_certificates); + bool sent_request = false; + ByteBuffer contents = ByteBuffer::create_uninitialized(0); + tls->on_tls_ready_to_write = [&](TLS::TLSv12& tls) { + if (sent_request) + return; + sent_request = true; + if (!tls.write("GET / HTTP/1.1\r\nHost: "_b)) { + FAIL("write(0) failed"); + loop.quit(0); + } + auto* the_server = DEFAULT_SERVER; + if (!tls.write(StringView(the_server).bytes())) { + FAIL("write(1) failed"); + loop.quit(0); + } + if (!tls.write("\r\nConnection : close\r\n\r\n"_b)) { + FAIL("write(2) failed"); + loop.quit(0); + } + }; + tls->on_tls_ready_to_read = [&](TLS::TLSv12& tls) { + auto data = tls.read(); + if (!data.has_value()) { + FAIL("No data received"); + loop.quit(1); + } else { + // print_buffer(data.value(), 16); + contents.append(data.value().data(), data.value().size()); + } + }; + tls->on_tls_finished = [&] { + loop.quit(0); + }; + tls->on_tls_error = [&](TLS::AlertDescription) { + FAIL("Connection failure"); + loop.quit(1); + }; + if (!tls->connect(DEFAULT_SERVER, port)) { + FAIL("connect() failed"); + return; + } + loop.exec(); +} diff --git a/Tests/LibTLS/ca_certs.ini b/Tests/LibTLS/ca_certs.ini new file mode 120000 index 0000000000..2096060259 --- /dev/null +++ b/Tests/LibTLS/ca_certs.ini @@ -0,0 +1 @@ +../../Base/etc/ca_certs.ini \ No newline at end of file