diff --git a/README.md b/README.md index 5fb7181..7793394 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ The current classes are as described below: | [container](source/container) | [static_variant](source/container/static_variant) | A static\_variant class that can contain any one of its listed template types. | :construction: | | [container](source/container) | [static_view](source/container/static_view) | A non\-owning static\_view into multi\-dimensional memory. | :construction: | | [crypto](source/crypto) | [aes](source/crypto/aes) | An implementation of the aes encryption algorithm for 128, 196, and 256 bits. | :heavy_check_mark: | +| [crypto](source/crypto) | [chacha](source/crypto/chacha) | An implementation of the chacha encryption algorithm. | :heavy_check_mark: | | [crypto](source/crypto) | [rc4](source/crypto/rc4) | An implementation of the rc4 or arc4 encryption algorithm. | :construction: | | [debug](source/debug) | [access](source/debug/access) | Classes and macros for accessing class private members. | :heavy_check_mark: | | [debug](source/debug) | [assert](source/debug/assert) | Macros that define an assert macro that optionally takes a format string and parameters. | :heavy_check_mark: | diff --git a/source/crypto/chacha b/source/crypto/chacha new file mode 100644 index 0000000..4a09e13 --- /dev/null +++ b/source/crypto/chacha @@ -0,0 +1,369 @@ +/* +Copyright (C) 2018-2023 Geoffrey Daniels. https://gpdaniels.com/ + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 3 of the License only. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once +#ifndef GTL_CRYPTO_CHACHA_HPP +#define GTL_CRYPTO_CHACHA_HPP + +// Summary: An implementation of the chacha encryption algorithm. + +#ifndef NDEBUG +# if defined(_MSC_VER) +# define __builtin_trap() __debugbreak() +# endif +/// @brief A simple assert macro to break the program if the chacha is misused. +# define GTL_CHACHA_ASSERT(ASSERTION, MESSAGE) static_cast((ASSERTION) || (__builtin_trap(), 0)) +#else +/// @brief At release time the assert macro is implemented as a nop. +# define GTL_CHACHA_ASSERT(ASSERTION, MESSAGE) static_cast(0) +#endif + +namespace gtl { + /// @brief Different block cipher modes available for the chacha class. + enum class chacha_mode { + ctr_32, // Counter: Using 32 bits for the counter. + ctr_64, // Counter: Using 64 bits for the counter. + }; + + /// @brief The chacha class computes an encrypted output from a sequence of data. + /// @tparam block_cipher_mode The mode used to chain together blocks. + /// @tparam chacha_rounds The number of the rounds used to encrypt or decrypt data. + template + class chacha final { + private: + static_assert (chacha_rounds > 0, "Number of rounds must be greater than zero."); + static_assert (chacha_rounds % 2 == 0, "Number of rounds must be an even number."); + + public: + /// @brief Handy variable for accessing the size in bits. + constexpr static const unsigned long long int size = 256; + + /// @brief Handy variable for accessing the chacha mode type. + constexpr static const chacha_mode mode = block_cipher_mode; + + /// @brief Internal size of each processed block of data in sets of four bytes. + constexpr static const unsigned int block_size = (128 / 4) / 8; + + /// @brief Size of the key in bytes. + constexpr static const unsigned int key_size = size / 8; + + /// @brief Size of the nonce in bytes. + constexpr static const unsigned int nonce_size = size / 2 / 8; + + /// @brief The number of encryption/decryption rounds. + constexpr static const int number_of_rounds = chacha_rounds; + + public: + /// @brief Type used to hold words of data for processing. + struct word_type { + unsigned int data[chacha::block_size] = {}; + }; + + /// @brief Type used to hold blocks of data for processing. + struct block_type { + /// @brief The data stored in the block. + word_type data[chacha::block_size] = {{}}; + }; + + /// @brief Simple type to hold the key data. + struct key_type { + unsigned char data[chacha::key_size] = {}; + }; + + /// @brief Simple type to hold the nonce data. + struct nonce_type { + unsigned char data[chacha::nonce_size] = {}; + }; + + private: + /// @brief Magic initialisation data: "expand 32-byte k". + constexpr static const word_type initialisation_constant = { + ('e' << 0) | ('x' << 8) | ('p' << 16) | ('a' << 24), + ('n' << 0) | ('d' << 8) | (' ' << 16) | ('3' << 24), + ('2' << 0) | ('-' << 8) | ('b' << 16) | ('y' << 24), + ('t' << 0) | ('e' << 8) | (' ' << 16) | ('k' << 24) + }; + + private: + /// @brief Pack a set of four bytes into an integer. + /// @param data The byte array data to store. + /// @return An integer representation of the four bytes. + constexpr static unsigned int pack_bytes_32(const unsigned char* data){ + return + (static_cast(data[0]) << 0) | + (static_cast(data[1]) << 8) | + (static_cast(data[2]) << 16) | + (static_cast(data[3]) << 24); + } + + /// @brief Pack a set of eight bytes into a long integer. + /// @param data The byte array data to store. + /// @return A long integer representation of the eight bytes. + constexpr static unsigned long long int pack_bytes_64(const unsigned char* data){ + return + (static_cast(data[0]) << 0) | + (static_cast(data[1]) << 8) | + (static_cast(data[2]) << 16) | + (static_cast(data[3]) << 24) | + (static_cast(data[4]) << 32) | + (static_cast(data[5]) << 40) | + (static_cast(data[6]) << 48) | + (static_cast(data[7]) << 56); + } + + /// @brief Unpack a byte from an integer given an index. + /// @param data The integer to unpack from. + /// @param index The index of the byte to unpack. + /// @return The byte that was at the index in the integer. + constexpr static unsigned char unpack_byte(unsigned int data, unsigned int index){ + return static_cast((data >> (index * 8)) & 0xFF); + } + + /// @brief Unpack a byte from a long integer given an index. + /// @param data The integer to unpack from. + /// @param index The index of the byte to unpack. + /// @return The byte that was at the index in the integer. + constexpr static unsigned char unpack_byte(unsigned long long int data, unsigned int index){ + return static_cast((data >> (index * 8)) & 0xFF); + } + + /// @brief Rotate a 32 bit value left by a shift amount. + /// @param value The value to rotate. + /// @param shift The number of bits to rotate by. + /// @return The value after it has been rotated left by shift bits. + constexpr static unsigned int rotate_left(unsigned int value, unsigned int shift) { + constexpr const unsigned int mask = (8 * sizeof(unsigned int) - 1); + shift &= mask; + return (value << shift) | (value >> ((static_cast(-static_cast(shift))) & mask)); + } + + private: + constexpr static void quarter_round(int index_a, int index_b, int index_c, int index_d, block_type& state) { + state.data[0].data[index_a] += state.data[1].data[index_b]; + state.data[3].data[index_d] = chacha::rotate_left(state.data[3].data[index_d] ^ state.data[0].data[index_a], 16); + state.data[2].data[index_c] += state.data[3].data[index_d]; + state.data[1].data[index_b] = chacha::rotate_left(state.data[1].data[index_b] ^ state.data[2].data[index_c], 12); + state.data[0].data[index_a] += state.data[1].data[index_b]; + state.data[3].data[index_d] = chacha::rotate_left(state.data[3].data[index_d] ^ state.data[0].data[index_a], 8); + state.data[2].data[index_c] += state.data[3].data[index_d]; + state.data[1].data[index_b] = chacha::rotate_left(state.data[1].data[index_b] ^ state.data[2].data[index_c], 7); + } + + /// @brief Generate a block of cypherstream data. + /// @param key The input key used to encrypt/decrypt the data. + /// @param nonce Additional key data. + /// @return A block of cypherstream data. + constexpr static block_type transform_block(const key_type& key, const nonce_type& nonce) { + block_type state = { + chacha::initialisation_constant, + { chacha::pack_bytes_32(&key.data[0]), chacha::pack_bytes_32(&key.data[4]), chacha::pack_bytes_32(&key.data[8]), chacha::pack_bytes_32(&key.data[12]) }, + { chacha::pack_bytes_32(&key.data[16]), chacha::pack_bytes_32(&key.data[20]), chacha::pack_bytes_32(&key.data[24]), chacha::pack_bytes_32(&key.data[28]) }, + { chacha::pack_bytes_32(&nonce.data[0]), chacha::pack_bytes_32(&nonce.data[4]), chacha::pack_bytes_32(&nonce.data[8]), chacha::pack_bytes_32(&nonce.data[12]) }, + }; + block_type result = state; + for (int i = 0; i < (chacha::number_of_rounds / 2); ++i){ + chacha::quarter_round(0, 0, 0, 0, result); + chacha::quarter_round(1, 1, 1, 1, result); + chacha::quarter_round(2, 2, 2, 2, result); + chacha::quarter_round(3, 3, 3, 3, result); + chacha::quarter_round(0, 1, 2, 3, result); + chacha::quarter_round(1, 2, 3, 0, result); + chacha::quarter_round(2, 3, 0, 1, result); + chacha::quarter_round(3, 0, 1, 2, result); + } + for (unsigned int i = 0; i < chacha::block_size; i++) { + for (unsigned int j = 0; j < chacha::block_size; j++) { + result.data[i].data[j] += state.data[i].data[j]; + } + } + return result; + } + + private: + /// @brief Calculate and return the byte by byte xor of the lhs and rhs words. + /// @param lhs The lhs word. + /// @param rhs The rhs word. + /// @return A word containing the byte by byte xor of the lhs and rhs words. + constexpr static word_type xor_data(const word_type& lhs, const word_type& rhs) { + word_type result; + for (unsigned int i = 0; i < chacha::block_size; ++i) { + result.data[i] = lhs.data[i] ^ rhs.data[i]; + } + return result; + } + + /// @brief Calculate and return the byte by byte xor of the lhs and rhs blocks. + /// @param lhs The lhs block. + /// @param rhs The rhs block. + /// @return A word containing the byte by byte xor of the lhs and rhs blocks. + constexpr static block_type xor_data(const block_type& lhs, const block_type& rhs) { + block_type result; + for (unsigned int i = 0; i < chacha::block_size; ++i) { + result.data[i] = xor_data(lhs.data[i], rhs.data[i]); + } + return result; + } + + + public: + /// @brief Transform a block of data from decrypted to encrypted chaining together blocks using an initialisation vector. + /// @param key The input key used to encrypt the data. + /// @param data Data to encrypt. + /// @param initialisation_vector Data optionally used to seed the encryption process depending on the mode. + /// @return The encrypted data. + constexpr static block_type encrypt_block(const key_type& key, const block_type& data, nonce_type& initialisation_vector = {}) { + block_type result; + if constexpr (chacha::mode == chacha_mode::ctr_32) { + unsigned int counter = chacha::pack_bytes_32(&initialisation_vector.data[0]); + const block_type block = chacha::transform_block(key, initialisation_vector); + result = chacha::xor_data(block, data);; + ++counter; + for (int i = 0; i < 4; ++i) { + initialisation_vector.data[i] = unpack_byte(counter, i); + } + } + else if constexpr (chacha::mode == chacha_mode::ctr_64) { + unsigned long long int counter = chacha::pack_bytes_64(&initialisation_vector.data[0]); + const block_type block = chacha::transform_block(key, initialisation_vector); + result = chacha::xor_data(block, data);; + ++counter; + for (int i = 0; i < 8; ++i) { + initialisation_vector.data[i] = unpack_byte(counter, i); + } + } + else { + static_cast(key); + static_cast(data); + static_cast(initialisation_vector); + static_assert((chacha::mode == chacha_mode::ctr_32) || (chacha::mode == chacha_mode::ctr_64), "No valid mode selected for chacha process."); + } + return result; + } + + /// @brief Transform a block of data from encrypted to decrypted chaining together blocks using an initialisation vector. + /// @param key The input key used to decrypt the data. + /// @param data Data to decrypt. + /// @param initialisation_vector Data optionally used to seed the decryption process depending on the mode. + /// @return The decrypted data. + constexpr static block_type decrypt_block(const key_type& key, const block_type& data, nonce_type& initialisation_vector = {}) { + return encrypt_block(key, data, initialisation_vector); + } + + public: + /// @brief Encrypt a string of data writing the encrypted data into the output string. + /// @param data The string of data to encrypt. + /// @param length The length of the data, must be a multiple of the chacha block size. + /// @param key The encryption key, must be at least the length required by the selected chacha bitsize. + /// @param output Pointer to an output buffer that will receive the encrypted data, must be at least the length of the data buffer. + static void encrypt(const unsigned char* data, const unsigned int length, const unsigned char* key, unsigned char* output) { + const unsigned char iv[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + chacha::encrypt(data, length, key, iv, output); + } + + /// @brief Decrypt a string of data writing the decrypted data into the output string. + /// @param data The string of data to decrypt. + /// @param length The length of the data, must be a multiple of the chacha block size. + /// @param key The decryption key, must be at least the length required by the selected chacha bitsize. + /// @param output Pointer to an output buffer that will receive the decrypted data, must be at least the length of the data buffer. + static void decrypt(const unsigned char* data, const unsigned int length, const unsigned char* key, unsigned char* output) { + const unsigned char iv[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + chacha::decrypt(data, length, key, iv, output); + } + + /// @brief Encrypt a string of data writing the encrypted data into the output string. + /// @param data The string of data to encrypt. + /// @param length The length of the data, must be a multiple of the chacha block size. + /// @param key The encryption key, must be at least the length required by the selected chacha bitsize. + /// @param iv The initialisation vector used to seed the encryption process. + /// @param output Pointer to an output buffer that will receive the encrypted data, must be at least the length of the data buffer. + static void encrypt(const unsigned char* data, const unsigned int length, const unsigned char* key, const unsigned char* iv, unsigned char* output) { + GTL_CHACHA_ASSERT((length % (chacha::block_size * chacha::block_size * 4)) == 0, "Data length must be a multiple of the chacha block size."); + + key_type key_data; + for (unsigned int i = 0; i < chacha::key_size; ++i) { + key_data.data[i] = key[i]; + } + + nonce_type initialisation_vector; + for (unsigned int i = 0; i < chacha::nonce_size; ++i) { + initialisation_vector.data[i] = iv[i]; + } + + for (unsigned int i = 0; i < length / (chacha::block_size * chacha::block_size * 4); ++i) { + block_type block; + for (unsigned int j = 0; j < chacha::block_size; ++j) { + for (unsigned int k = 0; k < chacha::block_size; ++k) { + block.data[j].data[k] = chacha::pack_bytes_32(&data[i * (chacha::block_size * chacha::block_size * 4) + j * (chacha::block_size * 4) + k * 4]); + } + } + + block = chacha::encrypt_block(key_data, block, initialisation_vector); + + for (unsigned int j = 0; j < chacha::block_size; ++j) { + for (unsigned int k = 0; k < chacha::block_size; ++k) { + for (unsigned int l = 0; l < 4; ++l) { + output[i * (chacha::block_size * chacha::block_size * 4) + j * (chacha::block_size * 4) + k * 4 + l] = chacha::unpack_byte(block.data[j].data[k], l); + } + } + } + } + } + + /// @brief Decrypt a string of data writing the decrypted data into the output string. + /// @param data The string of data to decrypt. + /// @param length The length of the data, must be a multiple of the chacha block size. + /// @param key The decryption key, must be at least the length required by the selected chacha bitsize. + /// @param iv The initialisation vector used to seed the decryption process. + /// @param output Pointer to an output buffer that will receive the decrypted data, must be at least the length of the data buffer. + static void decrypt(const unsigned char* data, const unsigned int length, const unsigned char* key, const unsigned char* iv, unsigned char* output) { + GTL_CHACHA_ASSERT((length % (chacha::block_size * chacha::block_size * 4)) == 0, "Data length must be a multiple of the chacha block size."); + + key_type key_data; + for (unsigned int i = 0; i < chacha::key_size; ++i) { + key_data.data[i] = key[i]; + } + + + nonce_type initialisation_vector; + for (unsigned int i = 0; i < chacha::nonce_size; ++i) { + initialisation_vector.data[i] = iv[i]; + } + + for (unsigned int i = 0; i < length / (chacha::block_size * chacha::block_size * 4); ++i) { + block_type block; + for (unsigned int j = 0; j < chacha::block_size; ++j) { + for (unsigned int k = 0; k < chacha::block_size; ++k) { + block.data[j].data[k] = chacha::pack_bytes_32(&data[i * (chacha::block_size * chacha::block_size * 4) + j * (chacha::block_size * 4) + k * 4]); + } + } + + block = chacha::decrypt_block(key_data, block, initialisation_vector); + + for (unsigned int j = 0; j < chacha::block_size; ++j) { + for (unsigned int k = 0; k < chacha::block_size; ++k) { + for (unsigned int l = 0; l < 4; ++l) { + output[i * (chacha::block_size * chacha::block_size * 4) + j * (chacha::block_size * 4) + k * 4 + l] = chacha::unpack_byte(block.data[j].data[k], l); + } + } + } + } + } + }; +} + +#undef GTL_CHACHA_ASSERT + +#endif // GTL_CRYPTO_CHACHA_HPP diff --git a/tests/crypto/chacha.test.cpp b/tests/crypto/chacha.test.cpp new file mode 100644 index 0000000..418abb6 --- /dev/null +++ b/tests/crypto/chacha.test.cpp @@ -0,0 +1,596 @@ +/* +Copyright (C) 2018-2023 Geoffrey Daniels. https://gpdaniels.com/ + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 3 of the License only. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include + +#include + +#if defined(_MSC_VER) +# pragma warning(push, 0) +#endif + +#include +#include + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +static std::string hex_2_string(const unsigned char* hex, const unsigned int length) { + std::string result; + for (unsigned int i = 0; i < length; ++i) { + result.push_back("0123456789ABCDEF"[hex[i] / 16]); + result.push_back("0123456789ABCDEF"[hex[i] % 16]); + } + return result; +} + +using chacha_types = testbench::value_collection<20>; +using chacha_modes = testbench::enum_collection; + +TEST(chacha, traits, standard) { + testbench::test_template( + [](auto test_mode, auto test_value)->void { + constexpr static const gtl::chacha_mode mode = decltype(test_mode)::value; + constexpr static const unsigned long long int value = decltype(test_value)::value; + + REQUIRE((std::is_pod>::value == true)); + + REQUIRE((std::is_trivial>::value == true)); + + REQUIRE((std::is_trivially_copyable>::value == true)); + + REQUIRE((std::is_standard_layout>::value == true)); + } + ); +} + +TEST(chacha, constructor, empty) { + testbench::test_template( + [](auto test_mode, auto test_value)->void { + constexpr static const gtl::chacha_mode mode = decltype(test_mode)::value; + constexpr static const unsigned long long int value = decltype(test_value)::value; + + gtl::chacha chacha; + testbench::do_not_optimise_away(chacha); + } + ); +} + +TEST(chacha, encrypt_decrypt, one_block) { + testbench::test_template( + [](auto test_mode, auto test_value)->void { + constexpr static const gtl::chacha_mode mode = decltype(test_mode)::value; + constexpr static const unsigned long long int value = decltype(test_value)::value; + + gtl::chacha chacha; + constexpr static const unsigned int length = 64; + unsigned char plain[length] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }; + unsigned char key[chacha.key_size] = { + 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 + }; + + unsigned char output[length]; + chacha.encrypt(plain, length, key, output); + unsigned char input[length]; + chacha.decrypt(output, length, key, input); + + REQUIRE(testbench::is_memory_same(plain, input, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(input, length).c_str()); + } + ); +} + +TEST(chacha, encrypt_decrypt, one_block_padded) { + testbench::test_template( + [](auto test_mode, auto test_value)->void { + constexpr static const gtl::chacha_mode mode = decltype(test_mode)::value; + constexpr static const unsigned long long int value = decltype(test_value)::value; + + gtl::chacha chacha; + constexpr static const unsigned int length = 64; + unsigned char plain[length] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x00 + // End byte replaced with a zero. + }; + unsigned char key[chacha.key_size] = { + 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 + }; + + unsigned char output[length]; + chacha.encrypt(plain, length, key, output); + unsigned char input[length]; + chacha.decrypt(output, length, key, input); + + REQUIRE(testbench::is_memory_same(plain, input, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(input, length).c_str()); + } + ); +} + +TEST(chacha, encrypt_decrypt, two_blocks) { + testbench::test_template( + [](auto test_mode, auto test_value)->void { + constexpr static const gtl::chacha_mode mode = decltype(test_mode)::value; + constexpr static const unsigned long long int value = decltype(test_value)::value; + + gtl::chacha chacha; + constexpr static const unsigned int length = 64*2; + unsigned char plain[length] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }; + unsigned char key[chacha.key_size] = { + 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 + }; + + unsigned char output[length]; + chacha.encrypt(plain, length, key, output); + unsigned char input[length]; + chacha.decrypt(output, length, key, input); + + REQUIRE(testbench::is_memory_same(plain, input, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(input, length).c_str()); + } + ); +} + +TEST(chacha, encrypt_decrypt, two_blocks_padded) { + testbench::test_template( + [](auto test_mode, auto test_value)->void { + constexpr static const gtl::chacha_mode mode = decltype(test_mode)::value; + constexpr static const unsigned long long int value = decltype(test_value)::value; + + gtl::chacha chacha; + constexpr static const unsigned int length = 64*2; + unsigned char plain[length] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + + 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned char key[chacha.key_size] = { + 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 + }; + + unsigned char output[length]; + chacha.encrypt(plain, length, key, output); + unsigned char input[length]; + chacha.decrypt(output, length, key, input); + + REQUIRE(testbench::is_memory_same(plain, input, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(input, length).c_str()); + } + ); +} + +TEST(chacha, evaluate, verify_ctr_32_rounds_20) { + gtl::chacha chacha; + constexpr static const unsigned int length = 64*2; + const unsigned char plain[length] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char key[chacha.key_size] = {}; + const unsigned char encrypted[length] = { + 0x76, 0xB8, 0xE0, 0xAD, 0xA0, 0xF1, 0x3D, 0x90, + 0x40, 0x5D, 0x6A, 0xE5, 0x53, 0x86, 0xBD, 0x28, + 0xBD, 0xD2, 0x19, 0xB8, 0xA0, 0x8D, 0xED, 0x1A, + 0xA8, 0x36, 0xEF, 0xCC, 0x8B, 0x77, 0x0D, 0xC7, + 0xDA, 0x41, 0x59, 0x7C, 0x51, 0x57, 0x48, 0x8D, + 0x77, 0x24, 0xE0, 0x3F, 0xB8, 0xD8, 0x4A, 0x37, + 0x6A, 0x43, 0xB8, 0xF4, 0x15, 0x18, 0xA1, 0x1C, + 0xC3, 0x87, 0xB6, 0x69, 0xB2, 0xEE, 0x65, 0x86, + + 0x9F, 0x07, 0xE7, 0xBE, 0x55, 0x51, 0x38, 0x7A, + 0x98, 0xBA, 0x97, 0x7C, 0x73, 0x2D, 0x08, 0x0D, + 0xCB, 0x0F, 0x29, 0xA0, 0x48, 0xE3, 0x65, 0x69, + 0x12, 0xC6, 0x53, 0x3E, 0x32, 0xEE, 0x7A, 0xED, + 0x29, 0xB7, 0x21, 0x76, 0x9C, 0xE6, 0x4E, 0x43, + 0xD5, 0x71, 0x33, 0xB0, 0x74, 0xD8, 0x39, 0xD5, + 0x31, 0xED, 0x1F, 0x28, 0x51, 0x0A, 0xFB, 0x45, + 0xAC, 0xE1, 0x0A, 0x1F, 0x4B, 0x79, 0x4D, 0x6F + }; + + unsigned char output_encrypt[length]; + chacha.encrypt(plain, length, key, output_encrypt); + REQUIRE(testbench::is_memory_same(encrypted, output_encrypt, length), "%s != %s\n", hex_2_string(encrypted, length).c_str(), hex_2_string(output_encrypt, length).c_str()); + + // Decryption is the same process. + unsigned char output_decrypt[length]; + chacha.decrypt(output_encrypt, length, key, output_decrypt); + REQUIRE(testbench::is_memory_same(plain, output_decrypt, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(output_decrypt, length).c_str()); +} + +TEST(chacha, evaluate, verify_ctr_64_rounds_20) { + gtl::chacha chacha; + constexpr static const unsigned int length = 64*2; + const unsigned char plain[length] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char key[chacha.key_size] = {}; + const unsigned char encrypted[length] = { + 0x76, 0xB8, 0xE0, 0xAD, 0xA0, 0xF1, 0x3D, 0x90, + 0x40, 0x5D, 0x6A, 0xE5, 0x53, 0x86, 0xBD, 0x28, + 0xBD, 0xD2, 0x19, 0xB8, 0xA0, 0x8D, 0xED, 0x1A, + 0xA8, 0x36, 0xEF, 0xCC, 0x8B, 0x77, 0x0D, 0xC7, + 0xDA, 0x41, 0x59, 0x7C, 0x51, 0x57, 0x48, 0x8D, + 0x77, 0x24, 0xE0, 0x3F, 0xB8, 0xD8, 0x4A, 0x37, + 0x6A, 0x43, 0xB8, 0xF4, 0x15, 0x18, 0xA1, 0x1C, + 0xC3, 0x87, 0xB6, 0x69, 0xB2, 0xEE, 0x65, 0x86, + + 0x9F, 0x07, 0xE7, 0xBE, 0x55, 0x51, 0x38, 0x7A, + 0x98, 0xBA, 0x97, 0x7C, 0x73, 0x2D, 0x08, 0x0D, + 0xCB, 0x0F, 0x29, 0xA0, 0x48, 0xE3, 0x65, 0x69, + 0x12, 0xC6, 0x53, 0x3E, 0x32, 0xEE, 0x7A, 0xED, + 0x29, 0xB7, 0x21, 0x76, 0x9C, 0xE6, 0x4E, 0x43, + 0xD5, 0x71, 0x33, 0xB0, 0x74, 0xD8, 0x39, 0xD5, + 0x31, 0xED, 0x1F, 0x28, 0x51, 0x0A, 0xFB, 0x45, + 0xAC, 0xE1, 0x0A, 0x1F, 0x4B, 0x79, 0x4D, 0x6F + }; + + unsigned char output_encrypt[length]; + chacha.encrypt(plain, length, key, output_encrypt); + REQUIRE(testbench::is_memory_same(encrypted, output_encrypt, length), "%s != %s\n", hex_2_string(encrypted, length).c_str(), hex_2_string(output_encrypt, length).c_str()); + + // Decryption is the same process. + unsigned char output_decrypt[length]; + chacha.decrypt(output_encrypt, length, key, output_decrypt); + REQUIRE(testbench::is_memory_same(plain, output_decrypt, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(output_decrypt, length).c_str()); +} + +TEST(chacha, evaluate, verify_ctr_32_rounds_20_key) { + gtl::chacha chacha; + constexpr static const unsigned int length = 64*2; + const unsigned char plain[length] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char key[chacha.key_size] = { + 0xC4, 0x6E, 0xC1, 0xB1, 0x8C, 0xE8, 0xA8, 0x78, + 0x72, 0x5A, 0x37, 0xE7, 0x80, 0xDF, 0xB7, 0x35, + 0x1F, 0x68, 0xED, 0x2E, 0x19, 0x4C, 0x79, 0xFB, + 0xC6, 0xAE, 0xBE, 0xE1, 0xA6, 0x67, 0x97, 0x5D, + }; + const unsigned char nonce[chacha.nonce_size] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0xDA, 0x31, 0xD5, 0xCF, 0x68, 0x82, 0x21 + }; + const unsigned char encrypted[length] = { + 0xF6, 0x3A, 0x89, 0xB7, 0x5C, 0x22, 0x71, 0xF9, + 0x36, 0x88, 0x16, 0x54, 0x2B, 0xA5, 0x2F, 0x06, + 0xED, 0x49, 0x24, 0x17, 0x92, 0x30, 0x2B, 0x00, + 0xB5, 0xE8, 0xF8, 0x0A, 0xE9, 0xA4, 0x73, 0xAF, + 0xC2, 0x5B, 0x21, 0x8F, 0x51, 0x9A, 0xF0, 0xFD, + 0xD4, 0x06, 0x36, 0x2E, 0x8D, 0x69, 0xDE, 0x7F, + 0x54, 0xC6, 0x04, 0xA6, 0xE0, 0x0F, 0x35, 0x3F, + 0x11, 0x0F, 0x77, 0x1B, 0xDC, 0xA8, 0xAB, 0x92, + + 0xE5, 0xFB, 0xC3, 0x4E, 0x60, 0xA1, 0xD9, 0xA9, + 0xDB, 0x17, 0x34, 0x5B, 0x0A, 0x40, 0x27, 0x36, + 0x85, 0x3B, 0xF9, 0x10, 0xB0, 0x60, 0xBD, 0xF1, + 0xF8, 0x97, 0xB6, 0x29, 0x0F, 0x01, 0xD1, 0x38, + 0xAE, 0x2C, 0x4C, 0x90, 0x22, 0x5B, 0xA9, 0xEA, + 0x14, 0xD5, 0x18, 0xF5, 0x59, 0x29, 0xDE, 0xA0, + 0x98, 0xCA, 0x7A, 0x6C, 0xCF, 0xE6, 0x12, 0x27, + 0x05, 0x3C, 0x84, 0xE4, 0x9A, 0x4A, 0x33, 0x32 + }; + + unsigned char output_encrypt[length]; + chacha.encrypt(plain, length, key, nonce, output_encrypt); + REQUIRE(testbench::is_memory_same(encrypted, output_encrypt, length), "%s != %s\n", hex_2_string(encrypted, length).c_str(), hex_2_string(output_encrypt, length).c_str()); + + // Decryption is the same process. + unsigned char output_decrypt[length]; + chacha.decrypt(output_encrypt, length, key, nonce, output_decrypt); + REQUIRE(testbench::is_memory_same(plain, output_decrypt, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(output_decrypt, length).c_str()); +} + +TEST(chacha, evaluate, verify_ctr_64_rounds_20_key) { + gtl::chacha chacha; + constexpr static const unsigned int length = 64*2; + const unsigned char plain[length] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char key[chacha.key_size] = { + 0xC4, 0x6E, 0xC1, 0xB1, 0x8C, 0xE8, 0xA8, 0x78, + 0x72, 0x5A, 0x37, 0xE7, 0x80, 0xDF, 0xB7, 0x35, + 0x1F, 0x68, 0xED, 0x2E, 0x19, 0x4C, 0x79, 0xFB, + 0xC6, 0xAE, 0xBE, 0xE1, 0xA6, 0x67, 0x97, 0x5D, + }; + const unsigned char nonce[chacha.nonce_size] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0xDA, 0x31, 0xD5, 0xCF, 0x68, 0x82, 0x21 + }; + const unsigned char encrypted[length] = { + 0xF6, 0x3A, 0x89, 0xB7, 0x5C, 0x22, 0x71, 0xF9, + 0x36, 0x88, 0x16, 0x54, 0x2B, 0xA5, 0x2F, 0x06, + 0xED, 0x49, 0x24, 0x17, 0x92, 0x30, 0x2B, 0x00, + 0xB5, 0xE8, 0xF8, 0x0A, 0xE9, 0xA4, 0x73, 0xAF, + 0xC2, 0x5B, 0x21, 0x8F, 0x51, 0x9A, 0xF0, 0xFD, + 0xD4, 0x06, 0x36, 0x2E, 0x8D, 0x69, 0xDE, 0x7F, + 0x54, 0xC6, 0x04, 0xA6, 0xE0, 0x0F, 0x35, 0x3F, + 0x11, 0x0F, 0x77, 0x1B, 0xDC, 0xA8, 0xAB, 0x92, + + 0xE5, 0xFB, 0xC3, 0x4E, 0x60, 0xA1, 0xD9, 0xA9, + 0xDB, 0x17, 0x34, 0x5B, 0x0A, 0x40, 0x27, 0x36, + 0x85, 0x3B, 0xF9, 0x10, 0xB0, 0x60, 0xBD, 0xF1, + 0xF8, 0x97, 0xB6, 0x29, 0x0F, 0x01, 0xD1, 0x38, + 0xAE, 0x2C, 0x4C, 0x90, 0x22, 0x5B, 0xA9, 0xEA, + 0x14, 0xD5, 0x18, 0xF5, 0x59, 0x29, 0xDE, 0xA0, + 0x98, 0xCA, 0x7A, 0x6C, 0xCF, 0xE6, 0x12, 0x27, + 0x05, 0x3C, 0x84, 0xE4, 0x9A, 0x4A, 0x33, 0x32 + }; + + unsigned char output_encrypt[length]; + chacha.encrypt(plain, length, key, nonce, output_encrypt); + REQUIRE(testbench::is_memory_same(encrypted, output_encrypt, length), "%s != %s\n", hex_2_string(encrypted, length).c_str(), hex_2_string(output_encrypt, length).c_str()); + + // Decryption is the same process. + unsigned char output_decrypt[length]; + chacha.decrypt(output_encrypt, length, key, nonce, output_decrypt); + REQUIRE(testbench::is_memory_same(plain, output_decrypt, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(output_decrypt, length).c_str()); +} + +TEST(chacha, evaluate, verify_ctr_32_rounds_20_data) { + gtl::chacha chacha; + constexpr static const unsigned int length = 64*2; + const unsigned char plain[length] = { + 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, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + }; + const unsigned char key[chacha.key_size] = { + 0xC4, 0x6E, 0xC1, 0xB1, 0x8C, 0xE8, 0xA8, 0x78, + 0x72, 0x5A, 0x37, 0xE7, 0x80, 0xDF, 0xB7, 0x35, + 0x1F, 0x68, 0xED, 0x2E, 0x19, 0x4C, 0x79, 0xFB, + 0xC6, 0xAE, 0xBE, 0xE1, 0xA6, 0x67, 0x97, 0x5D, + }; + const unsigned char nonce[chacha.nonce_size] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0xDA, 0x31, 0xD5, 0xCF, 0x68, 0x82, 0x21 + }; + const unsigned char keystream[length] = { + 0xF6, 0x3A, 0x89, 0xB7, 0x5C, 0x22, 0x71, 0xF9, + 0x36, 0x88, 0x16, 0x54, 0x2B, 0xA5, 0x2F, 0x06, + 0xED, 0x49, 0x24, 0x17, 0x92, 0x30, 0x2B, 0x00, + 0xB5, 0xE8, 0xF8, 0x0A, 0xE9, 0xA4, 0x73, 0xAF, + 0xC2, 0x5B, 0x21, 0x8F, 0x51, 0x9A, 0xF0, 0xFD, + 0xD4, 0x06, 0x36, 0x2E, 0x8D, 0x69, 0xDE, 0x7F, + 0x54, 0xC6, 0x04, 0xA6, 0xE0, 0x0F, 0x35, 0x3F, + 0x11, 0x0F, 0x77, 0x1B, 0xDC, 0xA8, 0xAB, 0x92, + + 0xE5, 0xFB, 0xC3, 0x4E, 0x60, 0xA1, 0xD9, 0xA9, + 0xDB, 0x17, 0x34, 0x5B, 0x0A, 0x40, 0x27, 0x36, + 0x85, 0x3B, 0xF9, 0x10, 0xB0, 0x60, 0xBD, 0xF1, + 0xF8, 0x97, 0xB6, 0x29, 0x0F, 0x01, 0xD1, 0x38, + 0xAE, 0x2C, 0x4C, 0x90, 0x22, 0x5B, 0xA9, 0xEA, + 0x14, 0xD5, 0x18, 0xF5, 0x59, 0x29, 0xDE, 0xA0, + 0x98, 0xCA, 0x7A, 0x6C, 0xCF, 0xE6, 0x12, 0x27, + 0x05, 0x3C, 0x84, 0xE4, 0x9A, 0x4A, 0x33, 0x32 + }; + unsigned char encrypted[length]; + for (unsigned int i = 0; i < length; ++i) { + encrypted[i] = plain[i] ^ keystream[i]; + } + + unsigned char output_encrypt[length]; + chacha.encrypt(plain, length, key, nonce, output_encrypt); + REQUIRE(testbench::is_memory_same(encrypted, output_encrypt, length), "%s != %s\n", hex_2_string(encrypted, length).c_str(), hex_2_string(output_encrypt, length).c_str()); + + // Decryption is the same process. + unsigned char output_decrypt[length]; + chacha.decrypt(output_encrypt, length, key, nonce, output_decrypt); + REQUIRE(testbench::is_memory_same(plain, output_decrypt, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(output_decrypt, length).c_str()); +} + +TEST(chacha, evaluate, verify_ctr_64_rounds_20_data) { + gtl::chacha chacha; + constexpr static const unsigned int length = 64*2; + const unsigned char plain[length] = { + 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, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + }; + const unsigned char key[chacha.key_size] = { + 0xC4, 0x6E, 0xC1, 0xB1, 0x8C, 0xE8, 0xA8, 0x78, + 0x72, 0x5A, 0x37, 0xE7, 0x80, 0xDF, 0xB7, 0x35, + 0x1F, 0x68, 0xED, 0x2E, 0x19, 0x4C, 0x79, 0xFB, + 0xC6, 0xAE, 0xBE, 0xE1, 0xA6, 0x67, 0x97, 0x5D, + }; + const unsigned char nonce[chacha.nonce_size] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0xDA, 0x31, 0xD5, 0xCF, 0x68, 0x82, 0x21 + }; + const unsigned char keystream[length] = { + 0xF6, 0x3A, 0x89, 0xB7, 0x5C, 0x22, 0x71, 0xF9, + 0x36, 0x88, 0x16, 0x54, 0x2B, 0xA5, 0x2F, 0x06, + 0xED, 0x49, 0x24, 0x17, 0x92, 0x30, 0x2B, 0x00, + 0xB5, 0xE8, 0xF8, 0x0A, 0xE9, 0xA4, 0x73, 0xAF, + 0xC2, 0x5B, 0x21, 0x8F, 0x51, 0x9A, 0xF0, 0xFD, + 0xD4, 0x06, 0x36, 0x2E, 0x8D, 0x69, 0xDE, 0x7F, + 0x54, 0xC6, 0x04, 0xA6, 0xE0, 0x0F, 0x35, 0x3F, + 0x11, 0x0F, 0x77, 0x1B, 0xDC, 0xA8, 0xAB, 0x92, + + 0xE5, 0xFB, 0xC3, 0x4E, 0x60, 0xA1, 0xD9, 0xA9, + 0xDB, 0x17, 0x34, 0x5B, 0x0A, 0x40, 0x27, 0x36, + 0x85, 0x3B, 0xF9, 0x10, 0xB0, 0x60, 0xBD, 0xF1, + 0xF8, 0x97, 0xB6, 0x29, 0x0F, 0x01, 0xD1, 0x38, + 0xAE, 0x2C, 0x4C, 0x90, 0x22, 0x5B, 0xA9, 0xEA, + 0x14, 0xD5, 0x18, 0xF5, 0x59, 0x29, 0xDE, 0xA0, + 0x98, 0xCA, 0x7A, 0x6C, 0xCF, 0xE6, 0x12, 0x27, + 0x05, 0x3C, 0x84, 0xE4, 0x9A, 0x4A, 0x33, 0x32 + }; + unsigned char encrypted[length]; + for (unsigned int i = 0; i < length; ++i) { + encrypted[i] = plain[i] ^ keystream[i]; + } + + unsigned char output_encrypt[length]; + chacha.encrypt(plain, length, key, nonce, output_encrypt); + REQUIRE(testbench::is_memory_same(encrypted, output_encrypt, length), "%s != %s\n", hex_2_string(encrypted, length).c_str(), hex_2_string(output_encrypt, length).c_str()); + + // Decryption is the same process. + unsigned char output_decrypt[length]; + chacha.decrypt(output_encrypt, length, key, nonce, output_decrypt); + REQUIRE(testbench::is_memory_same(plain, output_decrypt, length), "%s != %s\n", hex_2_string(plain, length).c_str(), hex_2_string(output_decrypt, length).c_str()); +}