#ifndef HASH_MD2_ALTERNATE_2019_04_11_H #define HASH_MD2_ALTERNATE_2019_04_11_H #include #include #include // See also: https://tools.ietf.org/html/rfc1319 #if(__cplusplus >= 201703L) namespace al::crypto::stream::hash { #else namespace al { namespace crypto { namespace stream { namespace hash { #endif class hash_md2_alternate : public al::crypto::stream::hash::hash_base<128U, 384U, std::uint8_t, 16U> { private: using base_class_type = al::crypto::stream::hash::hash_base<128U, 384U, std::uint8_t, 16U>; public: hash_md2_alternate() : my_checksum() { my_checksum.fill(0U); } hash_md2_alternate(const hash_md2_alternate&) = default; hash_md2_alternate(hash_md2_alternate&&) noexcept = default; ~hash_md2_alternate() override = default; auto operator=(const hash_md2_alternate&) -> hash_md2_alternate& = default; auto operator=(hash_md2_alternate&&) noexcept -> hash_md2_alternate& = default; auto initialize() -> void override { base_class_type::initialize(); my_checksum.fill(0U); } auto finalize() -> void override { const auto value_to_pad = static_cast ( base_class_type::message_buffer_static_size - base_class_type::message_index ); std::fill(base_class_type::message_buffer.begin() + static_cast(base_class_type::message_index), base_class_type::message_buffer.end(), value_to_pad); transform(); base_class_type::message_buffer = my_checksum; transform(); } AL_CRYPTO_NODISCARD auto get_result() const -> base_class_type::result_type override { base_class_type::result_type result; std::copy(base_class_type::transform_context.cbegin(), base_class_type::transform_context.cbegin() + result.size(), result.begin()); return result; } static auto hash_md2_sbox_table() -> const std::uint8_t* { static const std::array local_hash_md2_sbox_table {{ 0x29U, 0x2EU, 0x43U, 0xC9U, 0xA2U, 0xD8U, 0x7CU, 0x01U, 0x3DU, 0x36U, 0x54U, 0xA1U, 0xECU, 0xF0U, 0x06U, 0x13U, 0x62U, 0xA7U, 0x05U, 0xF3U, 0xC0U, 0xC7U, 0x73U, 0x8CU, 0x98U, 0x93U, 0x2BU, 0xD9U, 0xBCU, 0x4CU, 0x82U, 0xCAU, 0x1EU, 0x9BU, 0x57U, 0x3CU, 0xFDU, 0xD4U, 0xE0U, 0x16U, 0x67U, 0x42U, 0x6FU, 0x18U, 0x8AU, 0x17U, 0xE5U, 0x12U, 0xBEU, 0x4EU, 0xC4U, 0xD6U, 0xDAU, 0x9EU, 0xDEU, 0x49U, 0xA0U, 0xFBU, 0xF5U, 0x8EU, 0xBBU, 0x2FU, 0xEEU, 0x7AU, 0xA9U, 0x68U, 0x79U, 0x91U, 0x15U, 0xB2U, 0x07U, 0x3FU, 0x94U, 0xC2U, 0x10U, 0x89U, 0x0BU, 0x22U, 0x5FU, 0x21U, 0x80U, 0x7FU, 0x5DU, 0x9AU, 0x5AU, 0x90U, 0x32U, 0x27U, 0x35U, 0x3EU, 0xCCU, 0xE7U, 0xBFU, 0xF7U, 0x97U, 0x03U, 0xFFU, 0x19U, 0x30U, 0xB3U, 0x48U, 0xA5U, 0xB5U, 0xD1U, 0xD7U, 0x5EU, 0x92U, 0x2AU, 0xACU, 0x56U, 0xAAU, 0xC6U, 0x4FU, 0xB8U, 0x38U, 0xD2U, 0x96U, 0xA4U, 0x7DU, 0xB6U, 0x76U, 0xFCU, 0x6BU, 0xE2U, 0x9CU, 0x74U, 0x04U, 0xF1U, 0x45U, 0x9DU, 0x70U, 0x59U, 0x64U, 0x71U, 0x87U, 0x20U, 0x86U, 0x5BU, 0xCFU, 0x65U, 0xE6U, 0x2DU, 0xA8U, 0x02U, 0x1BU, 0x60U, 0x25U, 0xADU, 0xAEU, 0xB0U, 0xB9U, 0xF6U, 0x1CU, 0x46U, 0x61U, 0x69U, 0x34U, 0x40U, 0x7EU, 0x0FU, 0x55U, 0x47U, 0xA3U, 0x23U, 0xDDU, 0x51U, 0xAFU, 0x3AU, 0xC3U, 0x5CU, 0xF9U, 0xCEU, 0xBAU, 0xC5U, 0xEAU, 0x26U, 0x2CU, 0x53U, 0x0DU, 0x6EU, 0x85U, 0x28U, 0x84U, 0x09U, 0xD3U, 0xDFU, 0xCDU, 0xF4U, 0x41U, 0x81U, 0x4DU, 0x52U, 0x6AU, 0xDCU, 0x37U, 0xC8U, 0x6CU, 0xC1U, 0xABU, 0xFAU, 0x24U, 0xE1U, 0x7BU, 0x08U, 0x0CU, 0xBDU, 0xB1U, 0x4AU, 0x78U, 0x88U, 0x95U, 0x8BU, 0xE3U, 0x63U, 0xE8U, 0x6DU, 0xE9U, 0xCBU, 0xD5U, 0xFEU, 0x3BU, 0x00U, 0x1DU, 0x39U, 0xF2U, 0xEFU, 0xB7U, 0x0EU, 0x66U, 0x58U, 0xD0U, 0xE4U, 0xA6U, 0x77U, 0x72U, 0xF8U, 0xEBU, 0x75U, 0x4BU, 0x0AU, 0x31U, 0x44U, 0x50U, 0xB4U, 0x8FU, 0xEDU, 0x1FU, 0x1AU, 0xDBU, 0x99U, 0x8DU, 0x33U, 0x9FU, 0x11U, 0x83U, 0x14U, }}; return local_hash_md2_sbox_table.data(); } static auto hash_md2_sbox_generated() -> const std::uint8_t* { static std::array local_hash_md2_sbox_generated; static bool is_init; if(!is_init) { is_init = true; for(auto i = static_cast(UINT8_C(0)); i < local_hash_md2_sbox_generated.size(); ++i) { local_hash_md2_sbox_generated[i] = static_cast(i); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) } for(auto i = static_cast(UINT8_C(2)); i <= local_hash_md2_sbox_generated.size(); ++i) { const auto j = static_cast(hash_md2_sbox_randomizer(std::uint32_t(i))); std::swap(local_hash_md2_sbox_generated[j], // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) local_hash_md2_sbox_generated[i - 1U]); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) } } return local_hash_md2_sbox_generated.data(); } private: std::array my_checksum; auto transform() -> void override { for(std::size_t j = 0U; j < base_class_type::message_buffer_static_size; ++j) { base_class_type::transform_context[j + 16U] = base_class_type::message_buffer[j + 0U]; base_class_type::transform_context[j + 32U] = ( base_class_type::transform_context[j + 16U] ^ base_class_type::transform_context[j + 0U]); } std::uint_fast8_t index = UINT8_C(0); // Perform the MD2 permutation of the transform context. // In other words, permutes each byte in the auxiliary // block 18 times. for(std::size_t j = 0U; j < 18U; ++j) { std::for_each(base_class_type::transform_context.begin(), base_class_type::transform_context.end(), [&index](std::uint8_t& transform_context_element) -> void { transform_context_element = std::uint8_t(transform_context_element ^ hash_md2_sbox_table()[index]); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) index = transform_context_element; }); index = static_cast( base_class_type::transform_context.back() + static_cast(j)); } // Add the checksum to the message digest. index = my_checksum.back(); std::transform( my_checksum.cbegin(), my_checksum.cend(), base_class_type::message_buffer.cbegin(), my_checksum.begin(), [&index](const std::uint8_t& checksum_element, const std::uint8_t& buffer_element) -> std::uint8_t { index ^= buffer_element; const auto next_checksum_value = static_cast ( checksum_element ^ hash_md2_sbox_table()[index] // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) ); index = next_checksum_value; return next_checksum_value; }); base_class_type::message_index = 0U; base_class_type::message_buffer.fill(0U); } static auto hash_md2_sbox_randomizer(std::uint32_t n) -> std::uint8_t // NOLINT(misc-no-recursion) { static std::vector pi_out; if(pi_out.empty()) { using pi_spigot_type = al::crypto::math::constants::pi::pi_spigot; static pi_spigot_type::input_container_type pi_in(pi_spigot_type::input_size()); pi_out.resize(pi_spigot_type::output_size()); std::uintmax_t operation_count = UINTMAX_C(0); pi_spigot_type::calculate(pi_in.begin(), pi_in.begin() + std::ptrdiff_t(pi_in.size()), pi_out.begin(), &operation_count); } static std::size_t pi_pointer; auto x = static_cast(pi_out[pi_pointer]); ++pi_pointer; auto y = static_cast(UINT8_C(10)); if(n > 10) { x = (x * 10U) + std::uint32_t(pi_out[pi_pointer]); ++pi_pointer; y = static_cast(UINT8_C(100)); } if(n > 100U) { x = (x * 10U) + std::uint32_t(pi_out[pi_pointer]); ++pi_pointer; y = static_cast(UINT32_C(1000)); } return ((x < (n * (y / n))) ? static_cast(x % n) : hash_md2_sbox_randomizer(n)); } }; // MD2 ("") = 8350e5a3e24c153df2275c9f80692773 // MD2 ("a") = 32ec01ec4a6dac72c0ab96fb34c0b5d1 // MD2 ("abc") = da853b0d3f88d99b30283a69e6ded6bb // MD2 ("message digest") = ab4f496bfb2a530b219ff33031fe06b0 // MD2 ("abcdefghijklmnopqrstuvwxyz") = 4e8ddff3650292ab5a4108c3aa47940b // MD2 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = da33def2a42df13975352846c30338cd // MD2 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = d5976f79d83d3a0dc9806c3c66f3efd8 #if(__cplusplus >= 201703L) } // namespace al::crypto::stream::hash #else } // namespace hash } // namespace stream } // namespace crypto } // namespace al #endif #endif // HASH_MD2_ALTERNATE_2019_04_11_H