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());
+}