/*============================================================================= Copyright (c) 2011-2014 Bolero MURAKAMI https://github.com/bolero-MURAKAMI/Sprout 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 SPROUT_CHECKSUM_MD5_HPP #define SPROUT_CHECKSUM_MD5_HPP #include #include #include #include #include #include #include #include #include #include #include namespace sprout { static_assert(CHAR_BIT == 8, "CHAR_BIT == 8"); namespace md5_detail { inline SPROUT_CONSTEXPR std::uint32_t func_f(std::uint32_t x, std::uint32_t y, std::uint32_t z) { return (x & y) | (~x & z); } inline SPROUT_CONSTEXPR std::uint32_t func_g(std::uint32_t x, std::uint32_t y, std::uint32_t z) { return (x & z) | (y & ~z); } inline SPROUT_CONSTEXPR std::uint32_t func_h(std::uint32_t x, std::uint32_t y, std::uint32_t z) { return x ^ y ^ z; } inline SPROUT_CONSTEXPR std::uint32_t func_i(std::uint32_t x, std::uint32_t y, std::uint32_t z) { return y ^ (x | ~z); } struct round1_op { public: SPROUT_CONSTEXPR std::uint32_t operator()(std::uint32_t a, std::uint32_t b, std::uint32_t c, std::uint32_t d, std::uint32_t x, std::uint32_t t, std::uint32_t s) const { return b + sprout::left_rotate(a + sprout::md5_detail::func_f(b, c, d) + x + t, s); } }; struct round2_op { public: inline SPROUT_CONSTEXPR std::uint32_t operator()(std::uint32_t a, std::uint32_t b, std::uint32_t c, std::uint32_t d, std::uint32_t x, std::uint32_t t, std::uint32_t s) const { return b + sprout::left_rotate(a + sprout::md5_detail::func_g(b, c, d) + x + t, s); } }; struct round3_op { public: inline SPROUT_CONSTEXPR std::uint32_t operator()(std::uint32_t a, std::uint32_t b, std::uint32_t c, std::uint32_t d, std::uint32_t x, std::uint32_t t, std::uint32_t s) const { return b + sprout::left_rotate(a + sprout::md5_detail::func_h(b, c, d) + x + t, s); } }; struct round4_op { public: inline SPROUT_CONSTEXPR std::uint32_t operator()(std::uint32_t a, std::uint32_t b, std::uint32_t c, std::uint32_t d, std::uint32_t x, std::uint32_t t, std::uint32_t s) const { return b + sprout::left_rotate(a + sprout::md5_detail::func_i(b, c, d) + x + t, s); } }; template inline SPROUT_CONSTEXPR sprout::array round_x_impl( std::uint32_t a, std::uint32_t b, std::uint32_t c, std::uint32_t d, sprout::array const& x, RoundOp round_op, sprout::array const& xis, sprout::array const& ts, sprout::array const& ss, std::size_t i = 0 ) { return i == 16 ? sprout::array{{a, b, c, d}} : i % 4 == 0 ? sprout::md5_detail::round_x_impl(round_op(a, b, c, d, x[xis[i]], ts[i], ss[i]), b, c, d, x, round_op, xis, ts, ss, i + 1) : i % 4 == 1 ? sprout::md5_detail::round_x_impl(a, b, c, round_op(d, a, b, c, x[xis[i]], ts[i], ss[i]), x, round_op, xis, ts, ss, i + 1) : i % 4 == 2 ? sprout::md5_detail::round_x_impl(a, b, round_op(c, d, a, b, x[xis[i]], ts[i], ss[i]), d, x, round_op, xis, ts, ss, i + 1) : sprout::md5_detail::round_x_impl(a, round_op(b, c, d, a, x[xis[i]], ts[i], ss[i]), c, d, x, round_op, xis, ts, ss, i + 1) ; } template inline SPROUT_CONSTEXPR sprout::array round_x( sprout::array const& k, sprout::array const& x, RoundOp round_op, sprout::array const& xis, sprout::array const& ts, sprout::array const& ss ) { return sprout::md5_detail::round_x_impl(k[0], k[1], k[2], k[3], x, round_op, xis, ts, ss); } template struct round_table; # define SPROUT_MD5_DETAIL_ROUND_TABLE_DEF(N, XIS, TS, SS) \ template<> \ struct round_table { \ public: \ typedef sprout::array xis_type; \ typedef sprout::array ts_type; \ typedef sprout::array ss_type; \ public: \ SPROUT_STATIC_CONSTEXPR xis_type xis \ SPROUT_STATIC_CONSTEXPR_DATA_MEMBER_INNER(XIS) \ ; \ SPROUT_STATIC_CONSTEXPR ts_type ts \ SPROUT_STATIC_CONSTEXPR_DATA_MEMBER_INNER(TS) \ ; \ SPROUT_STATIC_CONSTEXPR ss_type ss \ SPROUT_STATIC_CONSTEXPR_DATA_MEMBER_INNER(SS) \ ; \ }; \ SPROUT_CONSTEXPR_OR_CONST typename sprout::md5_detail::round_table::xis_type sprout::md5_detail::round_table::xis \ SPROUT_STATIC_CONSTEXPR_DATA_MEMBER_OUTER(XIS) \ ; \ SPROUT_CONSTEXPR_OR_CONST typename sprout::md5_detail::round_table::ts_type sprout::md5_detail::round_table::ts \ SPROUT_STATIC_CONSTEXPR_DATA_MEMBER_OUTER(TS) \ ; \ SPROUT_CONSTEXPR_OR_CONST typename sprout::md5_detail::round_table::ss_type sprout::md5_detail::round_table::ss \ SPROUT_STATIC_CONSTEXPR_DATA_MEMBER_OUTER(SS) SPROUT_MD5_DETAIL_ROUND_TABLE_DEF( \ 1, \ (xis_type{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}), \ (ts_type{{ \ 0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE, 0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501, \ 0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE, 0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821 \ }}), \ (ss_type{{7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22}}) \ ); SPROUT_MD5_DETAIL_ROUND_TABLE_DEF( \ 2, \ (xis_type{{1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12}}), \ (ts_type{{ \ 0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA, 0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8, \ 0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED, 0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A \ }}), \ (ss_type{{5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20}}) \ ); SPROUT_MD5_DETAIL_ROUND_TABLE_DEF( \ 3, \ (xis_type{{5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2}}), \ (ts_type{{ \ 0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C, 0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70, \ 0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05, 0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665 \ }}), \ (ss_type{{4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23}}) \ ); SPROUT_MD5_DETAIL_ROUND_TABLE_DEF( \ 4, \ (xis_type{{0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9}}), \ (ts_type{{ \ 0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039, 0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1, \ 0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1, 0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391 \ }}), \ (ss_type{{6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}}) \ ); # undef SPROUT_MD5_DETAIL_ROUND_TABLE_DEF inline SPROUT_CONSTEXPR sprout::array round1(sprout::array const& k, sprout::array const& x) { return sprout::md5_detail::round_x( k, x, sprout::md5_detail::round1_op(), sprout::md5_detail::round_table<1>::xis, sprout::md5_detail::round_table<1>::ts, sprout::md5_detail::round_table<1>::ss ); } inline SPROUT_CONSTEXPR sprout::array round2(sprout::array const& k, sprout::array const& x) { return sprout::md5_detail::round_x( k, x, sprout::md5_detail::round2_op(), sprout::md5_detail::round_table<2>::xis, sprout::md5_detail::round_table<2>::ts, sprout::md5_detail::round_table<2>::ss ); } inline SPROUT_CONSTEXPR sprout::array round3(sprout::array const& k, sprout::array const& x) { return sprout::md5_detail::round_x( k, x, sprout::md5_detail::round3_op(), sprout::md5_detail::round_table<3>::xis, sprout::md5_detail::round_table<3>::ts, sprout::md5_detail::round_table<3>::ss ); } inline SPROUT_CONSTEXPR sprout::array round4(sprout::array const& k, sprout::array const& x) { return sprout::md5_detail::round_x( k, x, sprout::md5_detail::round4_op(), sprout::md5_detail::round_table<4>::xis, sprout::md5_detail::round_table<4>::ts, sprout::md5_detail::round_table<4>::ss ); } inline SPROUT_CONSTEXPR sprout::array round_all(sprout::array const& k, sprout::array const& x) { return sprout::md5_detail::round4( sprout::md5_detail::round3( sprout::md5_detail::round2( sprout::md5_detail::round1(k, x), x ), x ), x ); } } // namespace detail // // md5 // class md5 { public: typedef sprout::array value_type; typedef md5 const const_type; private: sprout::array k_; sprout::array block_; std::uint64_t bit_count_; private: SPROUT_CONSTEXPR md5( sprout::array const& k, std::uint64_t bit_count ) : k_(k) , block_{{}} , bit_count_(bit_count) {} SPROUT_CONSTEXPR md5( sprout::array const& k, sprout::array const& block, std::uint64_t bit_count ) : k_(k) , block_(block) , bit_count_(bit_count) {} SPROUT_CONSTEXPR md5 const process( sprout::array const& k, sprout::array const& block, std::uint64_t bit_count ) const { return bit_count % (64 * 8) != 0 ? const_type(k, block, bit_count) : const_type(k, block, bit_count).process_block() ; } SPROUT_CONSTEXPR md5 const process_block_1(sprout::array const& x) const { return md5( sprout::array{{k_[0] + x[0], k_[1] + x[1], k_[2] + x[2], k_[3] + x[3]}}, bit_count_ ); } SPROUT_CONSTEXPR md5 const process_block() const { return process_block_1(sprout::md5_detail::round_all(k_, block_)); } SPROUT_CONSTEXPR md5 const process_bit_impl(bool bit, std::size_t index, std::size_t offset) const { return process( k_, sprout::fixed::set( block_, block_.begin() + index, block_[index] | static_cast(bit) << (offset / 8 * 8 + (7 - offset % 8)) ), bit_count_ + 1 ); } SPROUT_CONSTEXPR md5 const process_bits_impl(std::uint8_t bits, std::size_t i) const { return i == 0 ? *this : process_bit(((bits >> (i - 1)) & 1) != 0).process_bits_impl(bits, i - 1) ; } SPROUT_CONSTEXPR md5 const process_byte_impl(std::uint8_t byte, std::size_t index, std::size_t offset) const { return process( k_, sprout::fixed::set( block_, block_.begin() + index, block_[index] | static_cast(((byte >> 7) & 1) != 0) << (offset / 8 * 8 + (7 - offset % 8)) | static_cast(((byte >> 6) & 1) != 0) << ((offset + 1) / 8 * 8 + (7 - (offset + 1) % 8)) | static_cast(((byte >> 5) & 1) != 0) << ((offset + 2) / 8 * 8 + (7 - (offset + 2) % 8)) | static_cast(((byte >> 4) & 1) != 0) << ((offset + 3) / 8 * 8 + (7 - (offset + 3) % 8)) | static_cast(((byte >> 3) & 1) != 0) << ((offset + 4) / 8 * 8 + (7 - (offset + 4) % 8)) | static_cast(((byte >> 2) & 1) != 0) << ((offset + 5) / 8 * 8 + (7 - (offset + 5) % 8)) | static_cast(((byte >> 1) & 1) != 0) << ((offset + 6) / 8 * 8 + (7 - (offset + 6) % 8)) | static_cast((byte & 1) != 0) << ((offset + 7) / 8 * 8 + (7 - (offset + 7) % 8)) ), bit_count_ + 8 ); } template SPROUT_CONSTEXPR md5 const process_block_impl(InputIterator first, InputIterator last) const { return first == last ? *this : process_byte(*first).process_block_impl(sprout::next(first), last) ; } SPROUT_CONSTEXPR std::uint64_t pad_size() const { return static_cast((511 + 448 - bit_count_) % 512); } SPROUT_CONSTEXPR md5 const process_padding(std::uint64_t pad_size) const { return pad_size == 0 ? *this : process_bit(false).process_padding(pad_size - 1) ; } SPROUT_CONSTEXPR md5 const process_length(std::uint64_t bit_count) const { return process_byte(static_cast(bit_count & 0xFF)) .process_byte(static_cast((bit_count >> 8) & 0xFF)) .process_byte(static_cast((bit_count >> 16) & 0xFF)) .process_byte(static_cast((bit_count >> 24) & 0xFF)) .process_byte(static_cast((bit_count >> 32) & 0xFF)) .process_byte(static_cast((bit_count >> 40) & 0xFF)) .process_byte(static_cast((bit_count >> 48) & 0xFF)) .process_byte(static_cast((bit_count >> 56) & 0xFF)) ; } SPROUT_CONSTEXPR value_type make_value() const { return value_type{{ static_cast((k_[0]) & 0xFF), static_cast((k_[0] >> 8) & 0xFF), static_cast((k_[0] >> 16) & 0xFF), static_cast((k_[0] >> 24) & 0xFF), static_cast((k_[1]) & 0xFF), static_cast((k_[1] >> 8) & 0xFF), static_cast((k_[1] >> 16) & 0xFF), static_cast((k_[1] >> 24) & 0xFF), static_cast((k_[2]) & 0xFF), static_cast((k_[2] >> 8) & 0xFF), static_cast((k_[2] >> 16) & 0xFF), static_cast((k_[2] >> 24) & 0xFF), static_cast((k_[3]) & 0xFF), static_cast((k_[3] >> 8) & 0xFF), static_cast((k_[3] >> 16) & 0xFF), static_cast((k_[3] >> 24) & 0xFF) }}; } SPROUT_CXX14_CONSTEXPR void process_block() { sprout::array x = sprout::md5_detail::round_all(k_, block_); k_[0] += x[0]; k_[1] += x[1]; k_[2] += x[2]; k_[3] += x[3]; block_.assign(0); } template SPROUT_CXX14_CONSTEXPR void process_block_impl(InputIterator first, InputIterator last) { for(; first != last; ++first) { process_byte(*first); } } public: SPROUT_CONSTEXPR md5() : k_{{0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476}} , block_{{}} , bit_count_() {} SPROUT_CXX14_CONSTEXPR void reset() { k_[0] = 0x67452301; k_[1] = 0xEFCDAB89; k_[2] = 0x98BADCFE; k_[3] = 0x10325476; block_.assign(0); bit_count_ = 0; } SPROUT_CONSTEXPR md5 const process_bit(bool bit) const { return process_bit_impl( bit, static_cast(bit_count_ % (64 * 8) / 32), static_cast(bit_count_ % 32) ); } SPROUT_CONSTEXPR md5 const process_bits(std::uint8_t bits, std::size_t bit_count) const { return process_bits_impl(bits, bit_count); } SPROUT_CONSTEXPR md5 const process_byte(std::uint8_t byte) const { return bit_count_ % 8 == 0 ? process_byte_impl( byte, static_cast(bit_count_ % (64 * 8) / 32), static_cast(bit_count_ % 32) ) : process_bit(((byte >> 7) & 1) != 0) .process_bit(((byte >> 6) & 1) != 0) .process_bit(((byte >> 5) & 1) != 0) .process_bit(((byte >> 4) & 1) != 0) .process_bit(((byte >> 3) & 1) != 0) .process_bit(((byte >> 2) & 1) != 0) .process_bit(((byte >> 1) & 1) != 0) .process_bit((byte & 1) != 0) ; } template SPROUT_CONSTEXPR md5 const process_block(InputIterator bytes_begin, InputIterator bytes_end) const { return process_block_impl( sprout::make_bytes_iterator(bytes_begin), sprout::make_bytes_iterator(bytes_end) ); } template SPROUT_CONSTEXPR md5 const process_bytes(InputIterator buffer, std::size_t byte_count) const { return process_block(buffer, sprout::next(buffer, byte_count)); } template SPROUT_CONSTEXPR md5 const process_range(InputRange const& bytes_range) const { return process_block(sprout::begin(bytes_range), sprout::end(bytes_range)); } SPROUT_CXX14_CONSTEXPR void process_bit(bool bit) { std::size_t index = static_cast(bit_count_ % (64 * 8) / 32); std::size_t offset = static_cast(bit_count_ % 32); block_[index] |= static_cast(bit) << (offset / 8 * 8 + (7 - offset % 8)); if (++bit_count_ % 512 == 0) { process_block(); } } SPROUT_CXX14_CONSTEXPR void process_bits(std::uint8_t bits, std::size_t bit_count) { while (bit_count--) { process_bit(((bits >> bit_count) & 1) != 0); } } SPROUT_CXX14_CONSTEXPR void process_byte(std::uint8_t byte) { process_bits(byte, 8); } template SPROUT_CXX14_CONSTEXPR void process_block(InputIterator bytes_begin, InputIterator bytes_end) { process_block_impl( sprout::make_bytes_iterator(bytes_begin), sprout::make_bytes_iterator(bytes_end) ); } template SPROUT_CXX14_CONSTEXPR void process_bytes(InputIterator buffer, std::size_t byte_count) { process_block(buffer, sprout::next(buffer, byte_count)); } template SPROUT_CXX14_CONSTEXPR void process_range(InputRange const& bytes_range) { process_block(sprout::begin(bytes_range), sprout::end(bytes_range)); } SPROUT_CONSTEXPR value_type checksum() const { return process_bit(true).process_padding(pad_size()).process_length(bit_count_).make_value(); } SPROUT_CONSTEXPR value_type operator()() const { return checksum(); } }; } // namespace sprout #endif // #ifndef SPROUT_CHECKSUM_MD5_HPP