diff --git a/include/nil/crypto3/block/detail/idea/idea_functions.hpp b/include/nil/crypto3/block/detail/idea/idea_functions.hpp new file mode 100644 index 0000000..765b7ec --- /dev/null +++ b/include/nil/crypto3/block/detail/idea/idea_functions.hpp @@ -0,0 +1,68 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_IDEA_FUNCTIONS_HPP +#define CRYPTO3_IDEA_FUNCTIONS_HPP + +#include +#include + +namespace nil { + namespace crypto3 { + namespace block { + namespace detail { + template + struct idea_functions : public ::nil::crypto3::detail::basic_functions { + typedef typename ::nil::crypto3::detail::basic_functions::word_type word_type; + + /* + * Multiplication modulo 65537 + */ + static inline word_type mul(word_type x, word_type y) { + const uint32_t P = static_cast(x) * y; + + const word_type Z_mask = static_cast(crypto3::detail::expand_mask(P) & 0xFFFF); + + const uint32_t P_hi = P >> 16; + const uint32_t P_lo = P & 0xFFFF; + + const word_type carry = (P_lo < P_hi); + const word_type r_1 = static_cast((P_lo - P_hi) + carry); + const word_type r_2 = 1 - x - y; + + return crypto3::detail::select(Z_mask, r_1, r_2); + } + + /* + * Find multiplicative inverses modulo 65537 + * + * 65537 is prime; thus Fermat's little theorem tells us that + * x^65537 == x modulo 65537, which means + * x^(65537-2) == x^-1 modulo 65537 since + * x^(65537-2) * x == 1 mod 65537 + * + * Do the exponentiation with a basic square and multiply: all bits are + * of exponent are 1 so we always multiply + */ + static inline word_type mul_inv(word_type x) { + word_type y = x; + + for (size_t i = 0; i != 15; ++i) { + y = mul(y, y); // square + y = mul(y, x); + } + + return y; + } + }; + } // namespace detail + } // namespace block + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_IDEA_FUNCTIONS_CPP_HPP diff --git a/include/nil/crypto3/block/detail/idea/idea_policy.hpp b/include/nil/crypto3/block/detail/idea/idea_policy.hpp new file mode 100644 index 0000000..08f3168 --- /dev/null +++ b/include/nil/crypto3/block/detail/idea/idea_policy.hpp @@ -0,0 +1,39 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_IDEA_POLICY_HPP +#define CRYPTO3_IDEA_POLICY_HPP + +#include + +#include + +namespace nil { + namespace crypto3 { + namespace block { + namespace detail { + struct idea_policy : idea_functions<16> { + constexpr static const std::size_t rounds = 8; + + constexpr static const std::size_t block_bits = 64; + constexpr static const std::size_t block_words = block_bits / word_bits; + typedef std::array block_type; + + constexpr static const std::size_t key_bits = 128; + constexpr static const std::size_t key_words = key_bits / word_bits; + typedef std::array key_type; + + constexpr static const std::size_t key_schedule_size = 52; + typedef std::array key_schedule_type; + }; + } // namespace detail + } // namespace block + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_IDEA_POLICY_HPP diff --git a/include/nil/crypto3/block/idea.hpp b/include/nil/crypto3/block/idea.hpp new file mode 100644 index 0000000..5bec6bd --- /dev/null +++ b/include/nil/crypto3/block/idea.hpp @@ -0,0 +1,172 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// Copyright (c) 2020 Nikita Kaskov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BLOCK_IDEA_HPP +#define CRYPTO3_BLOCK_IDEA_HPP + +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace block { + /*! + * @brief Idea. An older but still unbroken 64-bit cipher with a + * 128-bit key. Somewhat common due to its use in PGP. Avoid in new + * designs. + * + * @ingroup block + */ + class idea { + protected: + typedef detail::idea_policy policy_type; + + constexpr static const std::size_t key_schedule_size = policy_type::key_schedule_size; + typedef typename policy_type::key_schedule_type key_schedule_type; + + public: + constexpr static const std::size_t rounds = policy_type::rounds; + + constexpr static const std::size_t word_bits = policy_type::word_bits; + typedef typename policy_type::word_type word_type; + + constexpr static const std::size_t block_bits = policy_type::block_bits; + constexpr static const std::size_t block_words = policy_type::block_words; + typedef typename policy_type::block_type block_type; + + constexpr static const std::size_t key_bits = policy_type::key_bits; + constexpr static const std::size_t key_words = policy_type::key_words; + typedef typename policy_type::key_type key_type; + + template + struct stream_processor { + struct params_type { + + constexpr static const std::size_t value_bits = ValueBits; + constexpr static const std::size_t length_bits = policy_type::word_bits * 2; + }; + + typedef block_stream_processor type; + }; + + typedef typename stream_endian::little_octet_big_bit endian_type; + + idea(const key_type &key) { + schedule_key(key); + } + + ~idea() { + encryption_key.fill(0); + decryption_key.fill(0); + } + + inline block_type encrypt(const block_type &plaintext) const { + return idea_op(plaintext, encryption_key); + } + + inline block_type decrypt(const block_type &ciphertext) const { + return idea_op(ciphertext, decryption_key); + } + + protected: + key_schedule_type encryption_key, decryption_key; + + static inline block_type idea_op(const block_type &input, const key_schedule_type &key) { + block_type out = {0}; + word_type X1 = boost::endian::native_to_big(input[0]); + word_type X2 = boost::endian::native_to_big(input[1]); + word_type X3 = boost::endian::native_to_big(input[2]); + word_type X4 = boost::endian::native_to_big(input[3]); + + for (size_t j = 0; j != policy_type::rounds; ++j) { + X1 = policy_type::mul(X1, key[6 * j + 0]); + X2 += key[6 * j + 1]; + X3 += key[6 * j + 2]; + X4 = policy_type::mul(X4, key[6 * j + 3]); + + word_type T0 = X3; + X3 = policy_type::mul(X3 ^ X1, key[6 * j + 4]); + + word_type T1 = X2; + X2 = policy_type::mul((X2 ^ X4) + X3, key[6 * j + 5]); + X3 += X2; + + X1 ^= X2; + X4 ^= X3; + X2 ^= T0; + X3 ^= T1; + } + + X1 = policy_type::mul(X1, key[48]); + X2 += key[50]; + X3 += key[49]; + X4 = policy_type::mul(X4, key[51]); + + return {boost::endian::big_to_native(X1), boost::endian::big_to_native(X2), + boost::endian::big_to_native(X3), boost::endian::big_to_native(X4)}; + } + + inline void schedule_key(const key_type &key) { + crypto3::detail::poison(key.data(), 16); + crypto3::detail::poison(encryption_key.data(), key_schedule_size); + crypto3::detail::poison(decryption_key.data(), key_schedule_size); + + std::array K = {0}; + + K[0] = boost::endian::native_to_big(key[0]); + K[1] = boost::endian::native_to_big(key[1]); + + for (size_t off = 0; off != 48; off += 8) { + for (size_t i = 0; i != 8; ++i) { + encryption_key[off + i] = K[i / 4] >> (48 - 16 * (i % 4)); + } + + const uint64_t Kx = (K[0] >> 39); + const uint64_t Ky = (K[1] >> 39); + + K[0] = (K[0] << 25) | Ky; + K[1] = (K[1] << 25) | Kx; + } + + for (size_t i = 0; i != 4; ++i) { + encryption_key[48 + i] = K[i / 4] >> (48 - 16 * (i % 4)); + } + + K.fill(0); + + decryption_key[0] = policy_type::mul_inv(encryption_key[48]); + decryption_key[1] = -encryption_key[49]; + decryption_key[2] = -encryption_key[50]; + decryption_key[3] = policy_type::mul_inv(encryption_key[51]); + + for (size_t i = 0; i != 8 * 6; i += 6) { + decryption_key[i + 4] = encryption_key[46 - i]; + decryption_key[i + 5] = encryption_key[47 - i]; + decryption_key[i + 6] = policy_type::mul_inv(encryption_key[42 - i]); + decryption_key[i + 7] = -encryption_key[44 - i]; + decryption_key[i + 8] = -encryption_key[43 - i]; + decryption_key[i + 9] = policy_type::mul_inv(encryption_key[45 - i]); + } + + std::swap(decryption_key[49], decryption_key[50]); + + crypto3::detail::unpoison(key.data(), 16); + crypto3::detail::unpoison(encryption_key.data(), 52); + crypto3::detail::unpoison(decryption_key.data(), 52); + } + }; + } // namespace block + } // namespace crypto3 +} // namespace nil +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6e6c334..7f4e44b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,6 +39,7 @@ endmacro() set(TESTS_NAMES "pack" "rijndael" + "idea" "md4" "md5" "shacal" diff --git a/test/idea.cpp b/test/idea.cpp new file mode 100644 index 0000000..a302dfd --- /dev/null +++ b/test/idea.cpp @@ -0,0 +1,71 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE idea_cipher_test + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace nil::crypto3::block; + +namespace boost { + namespace test_tools { + namespace tt_detail { + template class P, typename K, typename V> + struct print_log_value> { + void operator()(std::ostream&, P const&) { + } + }; + } // namespace tt_detail + } // namespace test_tools +} // namespace boost + +static const std::unordered_map> valid_data = { + {"Zg==", {0x66}}, + {"Zm8=", {0x66, 0x6F}}, + {"Zm9v", {0x66, 0x6F, 0x6F}}, + {"aGVsbG8gd29ybGQ=", {0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64}}, + {"aGVsbG8gd29ybGQh", {0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21}}, + {"SGVsbG8sIHdvcmxkLg==", {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x2E}}, + {"VGhlIDEyIGNoYXJz", {0x54, 0x68, 0x65, 0x20, 0x31, 0x32, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73}}, + {"VGhlIDEzIGNoYXJzLg==", {0x54, 0x68, 0x65, 0x20, 0x31, 0x33, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x2E}}, + {"VGhlIDE0IGNoYXJzLi4=", {0x54, 0x68, 0x65, 0x20, 0x31, 0x34, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x2E, 0x2E}}, + {"VGhlIDE1IGNoYXJzLi4u", + {0x54, 0x68, 0x65, 0x20, 0x31, 0x35, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x2E, 0x2E, 0x2E}}, + {"QW4gVVRGLTggdXVtbDogw7w=", + {0x41, 0x6E, 0x20, 0x55, 0x54, 0x46, 0x2D, 0x38, 0x20, 0x75, 0x75, 0x6D, 0x6C, 0x3A, 0x20, 0xC3, 0xBC}}, + {"V2VpcmQgR2VybWFuIDIgYnl0ZSB0aGluZzogw58u", + {0x57, 0x65, 0x69, 0x72, 0x64, 0x20, 0x47, 0x65, 0x72, 0x6D, 0x61, 0x6E, 0x20, 0x32, 0x20, + 0x62, 0x79, 0x74, 0x65, 0x20, 0x74, 0x68, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0xC3, 0x9F, 0x2E}}, + {"mw==", {0x9B}}, + {"HGA=", {0x1C, 0x60}}, + {"gTS9", {0x81, 0x34, 0xBD}}, + {"Xmz/3g==", {0x5E, 0x6C, 0xFF, 0xDE}}, + {"ss3w3H8=", {0xb2, 0xcd, 0xf0, 0xdc, 0x7f}}, + {"/FYt2tQO", {0xfc, 0x56, 0x2d, 0xda, 0xd4, 0x0e}}, + {"KbIyLohB6A==", {0x29, 0xb2, 0x32, 0x2e, 0x88, 0x41, 0xe8}}, + {"Dw/O2Ul6r5I=", {0x0f, 0x0f, 0xce, 0xd9, 0x49, 0x7a, 0xaf, 0x92}}, + {"Jw+xiYKADaZA", {0x27, 0x0f, 0xb1, 0x89, 0x82, 0x80, 0x0d, 0xa6, 0x40}}}; + +static const std::vector invalid_data = {"ZOOL!isnotvalidbase64", "Neitheris:this?"}; + +BOOST_AUTO_TEST_SUITE(idea_encode_test_suite) + +BOOST_DATA_TEST_CASE(idea_single_range_encode, boost::unit_test::data::make(valid_data), array_element) { +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file