diff --git a/include/boost/crypt/hash/md5.hpp b/include/boost/crypt/hash/md5.hpp index d74061d..cad1710 100644 --- a/include/boost/crypt/hash/md5.hpp +++ b/include/boost/crypt/hash/md5.hpp @@ -1,6 +1,8 @@ // Copyright 2024 Matt Borland // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt +// +// See: https://www.ietf.org/rfc/rfc1321.txt #ifndef BOOST_CRYPT_HASH_MD5_HPP #define BOOST_CRYPT_HASH_MD5_HPP @@ -13,8 +15,10 @@ #include #include #include +#include #ifndef BOOST_CRYPT_BUILD_MODULE +#include #include #include #include @@ -54,7 +58,13 @@ class md5_hasher BOOST_CRYPT_GPU_ENABLED constexpr auto process_byte(ByteType byte) noexcept BOOST_CRYPT_REQUIRES_CONVERSION(ByteType, boost::crypt::uint8_t); - template + template ::value_type) == 1, bool> = true> + BOOST_CRYPT_GPU_ENABLED constexpr auto process_bytes(ForwardIter buffer, boost::crypt::size_t byte_count) noexcept; + + template ::value_type) == 2, bool> = true> + BOOST_CRYPT_GPU_ENABLED constexpr auto process_bytes(ForwardIter buffer, boost::crypt::size_t byte_count) noexcept; + + template ::value_type) == 4, bool> = true> BOOST_CRYPT_GPU_ENABLED constexpr auto process_bytes(ForwardIter buffer, boost::crypt::size_t byte_count) noexcept; BOOST_CRYPT_GPU_ENABLED constexpr auto get_digest() noexcept -> boost::crypt::array; @@ -108,7 +118,8 @@ BOOST_CRYPT_GPU_ENABLED constexpr auto md5_hasher::md5_update(ForwardIter data, low_ += input_bits; if (low_ < old_low) { - ++high_; + // This should never happen as it indicates size_t roll over + ++high_; // LCOV_EXCL_LINE } high_ += size >> 29U; @@ -196,15 +207,50 @@ template BOOST_CRYPT_GPU_ENABLED constexpr auto md5_hasher::process_byte(ByteType byte) noexcept BOOST_CRYPT_REQUIRES_CONVERSION(ByteType, boost::crypt::uint8_t) { - md5_update(&byte, 1UL); + const auto value {static_cast(byte)}; + md5_update(&value, 1UL); } -template +template ::value_type) == 1, bool>> BOOST_CRYPT_GPU_ENABLED constexpr auto md5_hasher::process_bytes(ForwardIter buffer, boost::crypt::size_t byte_count) noexcept { md5_update(buffer, byte_count); } +template ::value_type) == 2, bool>> +BOOST_CRYPT_GPU_ENABLED constexpr auto md5_hasher::process_bytes(ForwardIter buffer, boost::crypt::size_t byte_count) noexcept +{ + #ifndef BOOST_CRYPT_HAS_CUDA + + const auto* char_ptr {reinterpret_cast(std::addressof(*buffer))}; + const auto* data {reinterpret_cast(char_ptr)}; + md5_update(data, byte_count * 2U); + + #else + + const auto* data {reinterpret_cast(buffer)}; + md5_update(data, byte_count * 2U); + + #endif +} + +template ::value_type) == 4, bool>> +BOOST_CRYPT_GPU_ENABLED constexpr auto md5_hasher::process_bytes(ForwardIter buffer, boost::crypt::size_t byte_count) noexcept +{ + #ifndef BOOST_CRYPT_HAS_CUDA + + const auto* char_ptr {reinterpret_cast(std::addressof(*buffer))}; + const auto* data {reinterpret_cast(char_ptr)}; + md5_update(data, byte_count * 4U); + + #else + + const auto* data {reinterpret_cast(buffer)}; + md5_update(data, byte_count * 4U); + + #endif +} + // See: Applied Cryptography - Bruce Schneier // Section 18.5 namespace md5_body_detail { @@ -413,19 +459,120 @@ BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const boost::crypt::uint8_t* str, boo return detail::md5(str, str + len); } +BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char16_t* str) noexcept -> boost::crypt::array +{ + if (str == nullptr) + { + return boost::crypt::array{}; // LCOV_EXCL_LINE + } + + const auto message_len {utility::strlen(str)}; + return detail::md5(str, str + message_len); +} + +BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char16_t* str, boost::crypt::size_t len) noexcept -> boost::crypt::array +{ + if (str == nullptr) + { + return boost::crypt::array{}; // LCOV_EXCL_LINE + } + + return detail::md5(str, str + len); +} + +BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char32_t* str) noexcept -> boost::crypt::array +{ + if (str == nullptr) + { + return boost::crypt::array{}; // LCOV_EXCL_LINE + } + + const auto message_len {utility::strlen(str)}; + return detail::md5(str, str + message_len); +} + +BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const char32_t* str, boost::crypt::size_t len) noexcept -> boost::crypt::array +{ + if (str == nullptr) + { + return boost::crypt::array{}; // LCOV_EXCL_LINE + } + + return detail::md5(str, str + len); +} + +// On some platforms wchar_t is 16 bits and others it's 32 +// Since we check sizeof() the underlying with SFINAE in the actual implementation this is handled transparently +BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const wchar_t* str) noexcept -> boost::crypt::array +{ + if (str == nullptr) + { + return boost::crypt::array{}; // LCOV_EXCL_LINE + } + + const auto message_len {utility::strlen(str)}; + return detail::md5(str, str + message_len); +} + +BOOST_CRYPT_GPU_ENABLED constexpr auto md5(const wchar_t* str, boost::crypt::size_t len) noexcept -> boost::crypt::array +{ + if (str == nullptr) + { + return boost::crypt::array{}; // LCOV_EXCL_LINE + } + + return detail::md5(str, str + len); +} + // ----- String and String view aren't in the libcu++ STL so they so not have device markers ----- +#ifndef BOOST_CRYPT_HAS_CUDA + inline auto md5(const std::string& str) noexcept -> boost::crypt::array { return detail::md5(str.begin(), str.end()); } +inline auto md5(const std::u16string& str) noexcept -> boost::crypt::array +{ + return detail::md5(str.begin(), str.end()); +} + +inline auto md5(const std::u32string& str) noexcept -> boost::crypt::array +{ + return detail::md5(str.begin(), str.end()); +} + +inline auto md5(const std::wstring& str) noexcept -> boost::crypt::array +{ + return detail::md5(str.begin(), str.end()); +} + #ifdef BOOST_CRYPT_HAS_STRING_VIEW + inline auto md5(const std::string_view& str) -> boost::crypt::array { return detail::md5(str.begin(), str.end()); } -#endif + +inline auto md5(const std::u16string_view& str) -> boost::crypt::array +{ + return detail::md5(str.begin(), str.end()); +} + +inline auto md5(const std::u32string_view& str) -> boost::crypt::array +{ + return detail::md5(str.begin(), str.end()); +} + +inline auto md5(const std::wstring_view& str) -> boost::crypt::array +{ + return detail::md5(str.begin(), str.end()); +} + +#endif // BOOST_CRYPT_HAS_STRING_VIEW + +#endif // BOOST_CRYPT_HAS_CUDA } // namespace crypt } // namespace boost diff --git a/include/boost/crypt/utility/iterator.hpp b/include/boost/crypt/utility/iterator.hpp new file mode 100644 index 0000000..8b28764 --- /dev/null +++ b/include/boost/crypt/utility/iterator.hpp @@ -0,0 +1,48 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_CRYPT_UTILITES_ITERATOR_HPP +#define BOOST_CRYPT_UTILITES_ITERATOR_HPP + +#include + +#ifdef BOOST_CRYPT_HAS_CUDA + +#include + +namespace boost { +namespace crypt { + +template +struct iterator_traits : public cuda::std::iterator_traits {}; + +template +struct iterator_traits : public cuda::std::iterator_traits {}; + +} // namespace crypt +} // namespace boost + +#else + +#ifndef BOOST_CRYPT_BUILD_MODULE +#include +#endif + +namespace boost { +namespace crypt { +namespace utility { + +template +struct iterator_traits : public std::iterator_traits {}; + +template +struct iterator_traits : public std::iterator_traits {}; + +} // namespace utility +} // namespace crypt +} // namespace boost + +#endif // BOOST_CRYPT_HAS_CUDA + +#endif //BOOST_CRYPT_UTILITES_ITERATOR_HPP diff --git a/include/boost/crypt/utility/type_traits.hpp b/include/boost/crypt/utility/type_traits.hpp index 61033a2..7d68982 100644 --- a/include/boost/crypt/utility/type_traits.hpp +++ b/include/boost/crypt/utility/type_traits.hpp @@ -161,7 +161,9 @@ using cuda::std::underlying_type_t; #else // STD versions +#ifndef BOOST_CRYPT_BUILD_MODULE #include +#endif namespace boost { namespace crypt { diff --git a/test/generate_random_strings.hpp b/test/generate_random_strings.hpp new file mode 100644 index 0000000..c85f0ff --- /dev/null +++ b/test/generate_random_strings.hpp @@ -0,0 +1,100 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_CRYPT_TEST_GENERATE_RANDOM_STRINGS +#define BOOST_CRYPT_TEST_GENERATE_RANDOM_STRINGS + +#include +#include +#include +#include + +namespace boost { +namespace crypt { + +inline void generate_random_string(char* str, std::size_t length) +{ + + const char charset[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + const std::size_t charset_size = sizeof(charset) - 1; + + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(0, charset_size); + + for (std::size_t i = 0; i < length - 1; ++i) + { + const auto index = dist(rng); + str[i] = charset[index]; + } + + str[length - 1] = '\0'; +} + +inline void generate_random_string(char16_t* str, std::size_t length) +{ + const char16_t charset[] = u"0123456789" + u"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + u"abcdefghijklmnopqrstuvwxyz"; + + const std::size_t charset_size = std::char_traits::length(charset); + + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(0, charset_size - 1); + + for (std::size_t i = 0; i < length - 1; ++i) + { + const auto index = dist(rng); + str[i] = charset[index]; + } + + str[length - 1] = u'\0'; +} + +inline void generate_random_string(char32_t* str, std::size_t length) +{ + const char32_t charset[] = U"0123456789" + U"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + U"abcdefghijklmnopqrstuvwxyz"; + + const std::size_t charset_size = std::char_traits::length(charset); + + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(0, charset_size - 1); + + for (std::size_t i = 0; i < length - 1; ++i) + { + const auto index = dist(rng); + str[i] = charset[index]; + } + + str[length - 1] = u'\0'; +} + +inline void generate_random_string(wchar_t* str, std::size_t length) +{ + const wchar_t charset[] = L"0123456789" + L"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + L"abcdefghijklmnopqrstuvwxyz"; + + const std::size_t charset_size = std::char_traits::length(charset); + + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(0, charset_size - 1); + + for (std::size_t i = 0; i < length - 1; ++i) + { + const auto index = dist(rng); + str[i] = charset[index]; + } + + str[length - 1] = u'\0'; +} + +} // Namespace crypt +} // namespace boost + +#endif // BOOST_CRYPT_TEST_GENERATE_RANDOM_STRINGS diff --git a/test/test_md5.cpp b/test/test_md5.cpp index 67c1e3a..20091ab 100644 --- a/test/test_md5.cpp +++ b/test/test_md5.cpp @@ -1,11 +1,10 @@ // Copyright 2024 Matt Borland // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -// -// Start with the sample hashes from wiki #include #include +#include "generate_random_strings.hpp" #ifdef __clang__ # pragma clang diagnostic push @@ -25,7 +24,6 @@ # pragma GCC diagnostic pop #endif - #include #include #include @@ -35,27 +33,6 @@ #include #include -void generate_random_cstring(char* str, std::size_t length) -{ - - const char charset[] = "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; - - const std::size_t charset_size = sizeof(charset) - 1; - - std::mt19937_64 rng(42); - std::uniform_int_distribution dist(0, charset_size); - - for (std::size_t i = 0; i < length - 1; ++i) - { - int index = dist(rng); - str[i] = charset[index]; - } - - str[length - 1] = '\0'; -} - auto get_boost_uuid_result(const char* str, size_t length) { unsigned char digest[16]; @@ -72,8 +49,9 @@ auto get_boost_uuid_result(const char* str, size_t length) return return_array; } -constexpr std::array>, 9> test_values = +constexpr std::array>, 15> test_values = { + // // Start with the sample hashes from wiki std::make_tuple("The quick brown fox jumps over the lazy dog", std::array{0x9e, 0x10, 0x7d, 0x9d, 0x37, 0x2b, 0xb6, 0x82, 0x6b, 0xd8, 0x1d, 0x35, 0x42, 0xa4, 0x19, 0xd6}), std::make_tuple("The quick brown fox jumps over the lazy dog.", @@ -92,6 +70,20 @@ constexpr std::array>, 9> test_ std::array{0xd3, 0x7e, 0x43, 0x17, 0x49, 0x05, 0xde, 0x70, 0xfb, 0xb5, 0xb0, 0x38, 0xd7, 0x24, 0x7f, 0x57}), std::make_tuple("The Whirlpool Galaxy is about 88% the size of the Milky Way, with a diameter of 76,900 light-years", std::array{0xd5, 0xdf, 0xd7, 0xb4, 0x12, 0x35, 0xab, 0xc7, 0xa9, 0xa3, 0x20, 0x5b, 0x68, 0x96, 0xf3, 0x4d}), + + // From the RFC + std::make_tuple("a", + std::array{0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61}), + std::make_tuple("abc", + std::array{0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72}), + std::make_tuple("message digest", + std::array{0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0}), + std::make_tuple("abcdefghijklmnopqrstuvwxyz", + std::array{0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b}), + std::make_tuple("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + std::array{0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f}), + std::make_tuple("12345678901234567890123456789012345678901234567890123456789012345678901234567890", + std::array{0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a}), }; void basic_tests() @@ -216,6 +208,7 @@ void test_class() } } +template void test_random_values() { constexpr std::size_t max_str_len {65535U}; @@ -228,7 +221,7 @@ void test_random_values() { std::memset(str, '\0', max_str_len); const std::size_t current_str_len {str_len(rng)}; - generate_random_cstring(str, current_str_len); + boost::crypt::generate_random_string(str, current_str_len); const auto uuid_res {get_boost_uuid_result(str, current_str_len)}; const auto crypt_res {boost::crypt::md5(str, current_str_len)}; @@ -247,6 +240,7 @@ void test_random_values() delete[] str; } +template void test_random_piecewise_values() { constexpr std::size_t max_str_len {65535U}; @@ -265,8 +259,8 @@ void test_random_piecewise_values() std::memset(str_2, '\0', max_str_len); const std::size_t current_str_len {str_len(rng)}; - generate_random_cstring(str, current_str_len); - generate_random_cstring(str_2, current_str_len); + boost::crypt::generate_random_string(str, current_str_len); + boost::crypt::generate_random_string(str_2, current_str_len); boost_hasher.process_bytes(str, current_str_len); boost_hasher.process_bytes(str_2, current_str_len); @@ -312,8 +306,17 @@ int main() test_class(); - test_random_values(); - test_random_piecewise_values(); + test_random_values(); + test_random_piecewise_values(); + + test_random_values(); + test_random_piecewise_values(); + + test_random_values(); + test_random_piecewise_values(); + + test_random_values(); + test_random_piecewise_values(); return boost::report_errors(); }