diff --git a/fuzzing/fuzz_sha512.cpp b/fuzzing/fuzz_sha512.cpp new file mode 100644 index 00000000..97fc847d --- /dev/null +++ b/fuzzing/fuzz_sha512.cpp @@ -0,0 +1,41 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, std::size_t size) +{ + try + { + auto c_data = reinterpret_cast(data); + std::string c_data_str {c_data, size}; // Guarantee null termination since we can't pass the size argument + + boost::crypt::sha512(c_data_str); + + std::string_view view {c_data_str}; + boost::crypt::sha512(view); + + std::span data_span {c_data, size}; + boost::crypt::sha512(data_span); + + // Fuzz the hasher object + boost::crypt::sha512_hasher hasher; + hasher.process_bytes(data_span); + hasher.process_bytes(data_span); + hasher.process_bytes(data_span); + hasher.finalize(); + hasher.get_digest(); + hasher.process_bytes(data_span); // State is invalid but should not crash + } + catch(...) + { + std::cerr << "Error with: " << data << std::endl; + std::terminate(); + } + + return 0; +} diff --git a/include/boost/crypt/utility/config.hpp b/include/boost/crypt/utility/config.hpp index c4ab9198..a46640c6 100644 --- a/include/boost/crypt/utility/config.hpp +++ b/include/boost/crypt/utility/config.hpp @@ -58,7 +58,7 @@ // ---- Constexpr arrays ----- // ----- Assertions ----- - +#ifndef BOOST_CRYPT_ASSERT #ifdef BOOST_CRYPT_NO_EXCEPTIONS # define BOOST_CRYPT_ASSERT(x) # define BOOST_CRYPT_ASSERT_MSG(expr, msg) @@ -74,6 +74,7 @@ # define BOOST_CRYPT_ASSERT_MSG(expr, msg) #endif #endif +#endif // ----- Assertions ----- // ----- Has something ----- diff --git a/include/boost/crypt2/detail/assert.hpp b/include/boost/crypt2/detail/assert.hpp new file mode 100644 index 00000000..1a2d498d --- /dev/null +++ b/include/boost/crypt2/detail/assert.hpp @@ -0,0 +1,180 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_CRYPT2_DETAIL_ASSERT_HPP +#define BOOST_CRYPT2_DETAIL_ASSERT_HPP + +#include + +// Check for C++23 stacktrace support +// TODO(mborland): Not ready for the mainstream as of 01/25. Manually enable for now +#ifdef BOOST_CRYPT_ENABLE_STACKTRACE +# if __has_include() +# ifndef BOOST_CRYPT_BUILD_MODULE +# include +# endif +# endif +# if defined(__cpp_lib_stacktrace) && __cpp_lib_stacktrace >= 202011L +# define BOOST_CRYPT_HAS_STACKTRACE 1 +# else +# define BOOST_CRYPT_HAS_STACKTRACE 0 +# endif +#else +# define BOOST_CRYPT_HAS_STACKTRACE 0 +#endif + +#ifndef BOOST_CRYPT_BUILD_MODULE + +#include +#include +#include +#include +#include +#include + +#endif + +#if !defined(NDEBUG) && !defined(BOOST_CRYPT_HAS_CUDA) + +namespace boost::crypt::assert_detail { + +struct assertion_error : std::runtime_error +{ + using std::runtime_error::runtime_error; +}; + +template +[[nodiscard]] std::string format_assertion_message( + const char* condition_str, + const std::source_location& location, + #if BOOST_CRYPT_HAS_STACKTRACE + const std::stacktrace& trace, + #endif + Args&&... args) +{ + std::stringstream ss; + ss << "Assertion failed: " << condition_str << '\n' + << "File: " << location.file_name() << '\n' + << "Line: " << location.line() << '\n' + << "Function: " << location.function_name() << '\n' + << "Column: " << location.column() << '\n'; + + // Fold expression to handle optional message + ((ss << "Message: " << args << '\n'), ...); + + #if BOOST_CRYPT_HAS_STACKTRACE + // Add stacktrace + ss << "Stacktrace:\n" << trace; + #endif + + return ss.str(); +} + +// Version without message +constexpr void constexpr_assert_impl( + bool condition, + const char* condition_str, + const std::source_location& location = std::source_location::current()) +{ + if (!condition) + { + #if BOOST_CRYPT_HAS_STACKTRACE + if (!std::is_constant_evaluated()) + { + throw assertion_error( + format_assertion_message( + condition_str, + location, + std::stacktrace::current() + ) + ); + } + else + { + throw assertion_error( + format_assertion_message( + condition_str, + location, + std::stacktrace{} + ) + ); + } + #else + throw assertion_error( + format_assertion_message( + condition_str, + location + ) + ); + #endif + } +} + +// Version with message +constexpr void constexpr_assert_impl( + bool condition, + const char* condition_str, + const char* message, + const std::source_location& location = std::source_location::current()) +{ + if (!condition) + { + #if BOOST_CRYPT_HAS_STACKTRACE + if (!std::is_constant_evaluated()) + { + throw assertion_error( + format_assertion_message( + condition_str, + location, + std::stacktrace::current(), + message + ) + ); + } + else + { + throw assertion_error( + format_assertion_message( + condition_str, + location, + std::stacktrace{}, + message + ) + ); + } + #else + throw assertion_error( + format_assertion_message( + condition_str, + location, + message + ) + ); + #endif + } +} + +} // namespace boost::crypt::assert_detail + +// Macro overloading based on argument count +#define BOOST_CRYPT_ASSERT_1(condition) \ + boost::crypt::assert_detail::constexpr_assert_impl((condition), #condition) + +#define BOOST_CRYPT_ASSERT_2(condition, message) \ + boost::crypt::assert_detail::constexpr_assert_impl((condition), #condition, message) + +// Helper macros for argument counting +#define BOOST_CRYPT_ASSERT_GET_MACRO(_1, _2, NAME, ...) NAME + +// Main macro that selects the appropriate version +#define BOOST_CRYPT_ASSERT(...) \ + BOOST_CRYPT_ASSERT_GET_MACRO(__VA_ARGS__, BOOST_CRYPT_ASSERT_2, BOOST_CRYPT_ASSERT_1)(__VA_ARGS__) + +#else + +#define BOOST_CRYPT_ASSERT(...) + +#endif // NDEBUG and CUDA + +#endif // BOOST_CRYPT2_DETAIL_ASSERT_HPP diff --git a/include/boost/crypt2/detail/compat.hpp b/include/boost/crypt2/detail/compat.hpp index 03bdf148..3bbfba6e 100644 --- a/include/boost/crypt2/detail/compat.hpp +++ b/include/boost/crypt2/detail/compat.hpp @@ -42,9 +42,11 @@ namespace boost::crypt::compat { #ifdef BOOST_CRYPT_HAS_CUDA using size_t = cuda::std::size_t; using uint32_t = cuda::std::uint32_t; +using uint64_t = cuda::std::uint64_t; #else using size_t = std::size_t; using uint32_t = std::uint32_t; +using uint64_t = std::uint64_t; #endif // Arrays and spans diff --git a/include/boost/crypt2/hash/detail/sha224_256_hasher.hpp b/include/boost/crypt2/hash/detail/sha224_256_hasher.hpp index 2d4f6803..f014ae04 100644 --- a/include/boost/crypt2/hash/detail/sha224_256_hasher.hpp +++ b/include/boost/crypt2/hash/detail/sha224_256_hasher.hpp @@ -41,7 +41,7 @@ class sha_224_256_hasher final : public sha_1_2_hasher_base namespace sha256_detail { -// On the host device we prefere this array to be static, +// On the host device we prefer this array to be static, // but in a CUDA environment we move it into the function to make it available to host and device #ifndef BOOST_CRYPT_HAS_CUDA inline constexpr compat::array sha256_k { diff --git a/include/boost/crypt2/hash/detail/sha512_base.hpp b/include/boost/crypt2/hash/detail/sha512_base.hpp new file mode 100644 index 00000000..0161c8ab --- /dev/null +++ b/include/boost/crypt2/hash/detail/sha512_base.hpp @@ -0,0 +1,534 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// See: https://datatracker.ietf.org/doc/html/rfc6234 +// See: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf#page=31 + +#ifndef BOOST_CRYPT2_HASH_DETAIL_SHA512_BASE_HPP +#define BOOST_CRYPT2_HASH_DETAIL_SHA512_BASE_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost::crypt::hash_detail { + +template +class sha512_base final +{ +public: + + using return_type = compat::array; + + static constexpr compat::size_t block_size {128U}; + +private: + + static_assert(digest_size == 28U || digest_size == 32U || digest_size == 48U || digest_size == 64U, + "Digest size must be 28 (SHA512/224), 32 (SHA512/256), 48 (SHA384), or 64 (SHA512)"); + + compat::array intermediate_hash_ {}; + compat::array buffer_ {}; + compat::size_t buffer_index_ {}; + compat::uint64_t low_ {}; + compat::uint64_t high_ {}; + bool computed_ {}; + bool corrupted_ {}; + + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto process_message_block() noexcept -> void; + + [[nodiscard]] BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto update(compat::span data) noexcept -> state; + + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto pad_message() noexcept -> void; + + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto get_digest_impl(compat::span data) const -> state; + +public: + + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR sha512_base() noexcept { init(); } + + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR ~sha512_base() noexcept; + + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto init() noexcept -> void; + + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto process_bytes(compat::span data) noexcept -> state; + + template + BOOST_CRYPT_GPU_ENABLED auto process_bytes(SizedRange&& data) noexcept -> state; + + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto finalize() noexcept -> state; + + [[nodiscard("Digest is the function return value")]] BOOST_CRYPT_GPU_ENABLED_CONSTEXPR + auto get_digest() const noexcept -> return_type; + + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto get_digest(compat::span data) const noexcept -> state; + + template + BOOST_CRYPT_GPU_ENABLED auto get_digest(Range&& data) const noexcept -> state; +}; + +template +template +BOOST_CRYPT_GPU_ENABLED auto sha512_base::get_digest(Range&& data) const noexcept -> state +{ + using value_type = compat::range_value_t; + + auto data_span {compat::span(compat::forward(data))}; + + if (data_span.size() * sizeof(value_type) < digest_size) + { + return state::insufficient_output_length; + } + + #if defined(__clang__) && __clang_major__ >= 19 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-container" + #endif + + return get_digest_impl(compat::span( + compat::as_writable_bytes(data_span).data(), + digest_size + )); + + #if defined(__clang__) && __clang_major__ >= 19 + #pragma clang diagnostic pop + #endif +} + +template +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha512_base::get_digest(compat::span data) const noexcept -> state +{ + if (data.size() >= digest_size) + { + // We have verified the length of the span is correct so using a fixed length section of it is safe + #if defined(__clang__) && __clang_major__ >= 19 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-container" + #endif + + return get_digest_impl(compat::span(data.data(), digest_size)); + + #if defined(__clang__) && __clang_major__ >= 19 + #pragma clang diagnostic pop + #endif + } + + return state::insufficient_output_length; +} + +template +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha512_base::get_digest() const noexcept -> sha512_base::return_type +{ + return_type digest {}; + get_digest_impl(digest); + return digest; +} + +template +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha512_base::get_digest_impl(compat::span data) const -> state +{ + if (corrupted_ || !computed_) + { + return state::state_error; + } + + BOOST_CRYPT_ASSERT(data.size() == digest_size); + BOOST_CRYPT_ASSERT(intermediate_hash_.size() >= (digest_size - 1U) >> 3U); + for (compat::size_t i {}; i < digest_size; ++i) + { + data[i] = static_cast(intermediate_hash_[i >> 3U] >> 8U * (7 - (i % 8U))); + } + + return state::success; +} + +template +constexpr auto sha512_base::finalize() noexcept -> state +{ + if (computed_) + { + corrupted_ = true; + } + if (corrupted_) + { + return state::state_error; + } + + pad_message(); + + detail::clear_mem(buffer_); + low_ = 0UL; + high_ = 0UL; + computed_ = true; + + return state::success; +} + +template +[[nodiscard]] BOOST_CRYPT_GPU_ENABLED_CONSTEXPR +auto sha512_base::update(compat::span data) noexcept -> state +{ + if (data.empty()) + { + return state::success; + } + if (computed_) + { + corrupted_ = true; + } + if (corrupted_) + { + return state::state_error; + } + + for (const auto val : data) + { + buffer_[buffer_index_++] = val; + + low_ += 8U; + + if (low_ == 0U) [[unlikely]] + { + // Would indicate size_t rollover which should not happen on a single data stream + // LCOV_EXCL_START + ++high_; + if (high_ == 0U) [[unlikely]] + { + corrupted_ = true; + return state::input_too_long; + } + // LCOV_EXCL_STOP + } + + if (buffer_index_ == buffer_.size()) + { + process_message_block(); + } + } + + return state::success; +} + +template +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR sha512_base::~sha512_base() noexcept +{ + detail::clear_mem(intermediate_hash_); + detail::clear_mem(buffer_); + buffer_index_ = 0U; + low_ = 0ULL; + high_ = 0ULL; + computed_ = false; + corrupted_ = false; +}; + +template +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha512_base::init() noexcept -> void +{ + intermediate_hash_.fill(0ULL); + buffer_.fill(compat::byte{}); + buffer_index_ = 0U; + low_ = 0ULL; + high_ = 0ULL; + computed_ = false; + corrupted_ = false; + + if constexpr (digest_size == 28U) + { + // Constants for SHA512/224 + intermediate_hash_[0] = 0x8C3D37C819544DA2ULL; + intermediate_hash_[1] = 0x73E1996689DCD4D6ULL; + intermediate_hash_[2] = 0x1DFAB7AE32FF9C82ULL; + intermediate_hash_[3] = 0x679DD514582F9FCFULL; + intermediate_hash_[4] = 0x0F6D2B697BD44DA8ULL; + intermediate_hash_[5] = 0x77E36F7304C48942ULL; + intermediate_hash_[6] = 0x3F9D85A86A1D36C8ULL; + intermediate_hash_[7] = 0x1112E6AD91D692A1ULL; + } + else if constexpr (digest_size == 32U) + { + // Constants for SHA512/256 + intermediate_hash_[0] = 0x22312194FC2BF72CULL; + intermediate_hash_[1] = 0x9F555FA3C84C64C2ULL; + intermediate_hash_[2] = 0x2393B86B6F53B151ULL; + intermediate_hash_[3] = 0x963877195940EABDULL; + intermediate_hash_[4] = 0x96283EE2A88EFFE3ULL; + intermediate_hash_[5] = 0xBE5E1E2553863992ULL; + intermediate_hash_[6] = 0x2B0199FC2C85B8AAULL; + intermediate_hash_[7] = 0x0EB72DDC81C52CA2ULL; + } + else if constexpr (digest_size == 48U) + { + // Constants for SHA384 + intermediate_hash_[0] = 0xcbbb9d5dc1059ed8ULL; + intermediate_hash_[1] = 0x629a292a367cd507ULL; + intermediate_hash_[2] = 0x9159015a3070dd17ULL; + intermediate_hash_[3] = 0x152fecd8f70e5939ULL; + intermediate_hash_[4] = 0x67332667ffc00b31ULL; + intermediate_hash_[5] = 0x8eb44a8768581511ULL; + intermediate_hash_[6] = 0xdb0c2e0d64f98fa7ULL; + intermediate_hash_[7] = 0x47b5481dbefa4fa4ULL; + } + else + { + // Constants for SHA512 + intermediate_hash_[0] = 0x6a09e667f3bcc908ULL; + intermediate_hash_[1] = 0xbb67ae8584caa73bULL; + intermediate_hash_[2] = 0x3c6ef372fe94f82bULL; + intermediate_hash_[3] = 0xa54ff53a5f1d36f1ULL; + intermediate_hash_[4] = 0x510e527fade682d1ULL; + intermediate_hash_[5] = 0x9b05688c2b3e6c1fULL; + intermediate_hash_[6] = 0x1f83d9abfb41bd6bULL; + intermediate_hash_[7] = 0x5be0cd19137e2179ULL; + } +} + +namespace sha512_detail { + +#ifndef BOOST_CRYPT_HAS_CUDA + +// On the host device we prefer this array to be static, +// but in a CUDA environment we move it into the function to make it available to host and device +inline constexpr compat::array sha512_k { + 0x428A2F98D728AE22ULL, 0x7137449123EF65CDULL, 0xB5C0FBCFEC4D3B2FULL, + 0xE9B5DBA58189DBBCULL, 0x3956C25BF348B538ULL, 0x59F111F1B605D019ULL, + 0x923F82A4AF194F9BULL, 0xAB1C5ED5DA6D8118ULL, 0xD807AA98A3030242ULL, + 0x12835B0145706FBEULL, 0x243185BE4EE4B28CULL, 0x550C7DC3D5FFB4E2ULL, + 0x72BE5D74F27B896FULL, 0x80DEB1FE3B1696B1ULL, 0x9BDC06A725C71235ULL, + 0xC19BF174CF692694ULL, 0xE49B69C19EF14AD2ULL, 0xEFBE4786384F25E3ULL, + 0x0FC19DC68B8CD5B5ULL, 0x240CA1CC77AC9C65ULL, 0x2DE92C6F592B0275ULL, + 0x4A7484AA6EA6E483ULL, 0x5CB0A9DCBD41FBD4ULL, 0x76F988DA831153B5ULL, + 0x983E5152EE66DFABULL, 0xA831C66D2DB43210ULL, 0xB00327C898FB213FULL, + 0xBF597FC7BEEF0EE4ULL, 0xC6E00BF33DA88FC2ULL, 0xD5A79147930AA725ULL, + 0x06CA6351E003826FULL, 0x142929670A0E6E70ULL, 0x27B70A8546D22FFCULL, + 0x2E1B21385C26C926ULL, 0x4D2C6DFC5AC42AEDULL, 0x53380D139D95B3DFULL, + 0x650A73548BAF63DEULL, 0x766A0ABB3C77B2A8ULL, 0x81C2C92E47EDAEE6ULL, + 0x92722C851482353BULL, 0xA2BFE8A14CF10364ULL, 0xA81A664BBC423001ULL, + 0xC24B8B70D0F89791ULL, 0xC76C51A30654BE30ULL, 0xD192E819D6EF5218ULL, + 0xD69906245565A910ULL, 0xF40E35855771202AULL, 0x106AA07032BBD1B8ULL, + 0x19A4C116B8D2D0C8ULL, 0x1E376C085141AB53ULL, 0x2748774CDF8EEB99ULL, + 0x34B0BCB5E19B48A8ULL, 0x391C0CB3C5C95A63ULL, 0x4ED8AA4AE3418ACBULL, + 0x5B9CCA4F7763E373ULL, 0x682E6FF3D6B2B8A3ULL, 0x748F82EE5DEFB2FCULL, + 0x78A5636F43172F60ULL, 0x84C87814A1F0AB72ULL, 0x8CC702081A6439ECULL, + 0x90BEFFFA23631E28ULL, 0xA4506CEBDE82BDE9ULL, 0xBEF9A3F7B2C67915ULL, + 0xC67178F2E372532BULL, 0xCA273ECEEA26619CULL, 0xD186B8C721C0C207ULL, + 0xEADA7DD6CDE0EB1EULL, 0xF57D4F7FEE6ED178ULL, 0x06F067AA72176FBAULL, + 0x0A637DC5A2C898A6ULL, 0x113F9804BEF90DAEULL, 0x1B710B35131C471BULL, + 0x28DB77F523047D84ULL, 0x32CAAB7B40C72493ULL, 0x3C9EBE0A15C9BEBCULL, + 0x431D67C49C100D4CULL, 0x4CC5D4BECB3E42B6ULL, 0x597F299CFC657E2AULL, + 0x5FCB6FAB3AD6FAECULL, 0x6C44198C4A475817ULL +}; + +#endif // BOOST_CRYPT_HAS_CUDA + +// See section 4.1.3 +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto big_sigma0(const compat::uint64_t x) noexcept -> compat::uint64_t +{ + return compat::rotr(x, 28) ^ compat::rotr(x, 34) ^ compat::rotr(x, 39); +} + +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto big_sigma1(const compat::uint64_t x) noexcept -> compat::uint64_t +{ + return compat::rotr(x, 14) ^ compat::rotr(x, 18) ^ compat::rotr(x, 41); +} + +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto little_sigma0(const compat::uint64_t x) noexcept -> compat::uint64_t +{ + return compat::rotr(x, 1) ^ compat::rotr(x, 8) ^ (x >> 7); +} + +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto little_sigma1(const compat::uint64_t x) noexcept -> compat::uint64_t +{ + return compat::rotr(x, 19) ^ compat::rotr(x, 61) ^ (x >> 6); +} + +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha_ch(const compat::uint64_t x, + const compat::uint64_t y, + const compat::uint64_t z) -> compat::uint64_t +{ + return (x & y) ^ ((~x) & z); +} + +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha_maj(const compat::uint64_t x, + const compat::uint64_t y, + const compat::uint64_t z) -> compat::uint64_t +{ + return (x & y) ^ (x & z) ^ (y & z); +} + +} // namespace sha512_detail + +template +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha512_base::process_message_block() noexcept -> void +{ + #ifdef BOOST_CRYPT_HAS_CUDA + + constexpr compat::array sha512_k = { + 0x428A2F98D728AE22ULL, 0x7137449123EF65CDULL, 0xB5C0FBCFEC4D3B2FULL, + 0xE9B5DBA58189DBBCULL, 0x3956C25BF348B538ULL, 0x59F111F1B605D019ULL, + 0x923F82A4AF194F9BULL, 0xAB1C5ED5DA6D8118ULL, 0xD807AA98A3030242ULL, + 0x12835B0145706FBEULL, 0x243185BE4EE4B28CULL, 0x550C7DC3D5FFB4E2ULL, + 0x72BE5D74F27B896FULL, 0x80DEB1FE3B1696B1ULL, 0x9BDC06A725C71235ULL, + 0xC19BF174CF692694ULL, 0xE49B69C19EF14AD2ULL, 0xEFBE4786384F25E3ULL, + 0x0FC19DC68B8CD5B5ULL, 0x240CA1CC77AC9C65ULL, 0x2DE92C6F592B0275ULL, + 0x4A7484AA6EA6E483ULL, 0x5CB0A9DCBD41FBD4ULL, 0x76F988DA831153B5ULL, + 0x983E5152EE66DFABULL, 0xA831C66D2DB43210ULL, 0xB00327C898FB213FULL, + 0xBF597FC7BEEF0EE4ULL, 0xC6E00BF33DA88FC2ULL, 0xD5A79147930AA725ULL, + 0x06CA6351E003826FULL, 0x142929670A0E6E70ULL, 0x27B70A8546D22FFCULL, + 0x2E1B21385C26C926ULL, 0x4D2C6DFC5AC42AEDULL, 0x53380D139D95B3DFULL, + 0x650A73548BAF63DEULL, 0x766A0ABB3C77B2A8ULL, 0x81C2C92E47EDAEE6ULL, + 0x92722C851482353BULL, 0xA2BFE8A14CF10364ULL, 0xA81A664BBC423001ULL, + 0xC24B8B70D0F89791ULL, 0xC76C51A30654BE30ULL, 0xD192E819D6EF5218ULL, + 0xD69906245565A910ULL, 0xF40E35855771202AULL, 0x106AA07032BBD1B8ULL, + 0x19A4C116B8D2D0C8ULL, 0x1E376C085141AB53ULL, 0x2748774CDF8EEB99ULL, + 0x34B0BCB5E19B48A8ULL, 0x391C0CB3C5C95A63ULL, 0x4ED8AA4AE3418ACBULL, + 0x5B9CCA4F7763E373ULL, 0x682E6FF3D6B2B8A3ULL, 0x748F82EE5DEFB2FCULL, + 0x78A5636F43172F60ULL, 0x84C87814A1F0AB72ULL, 0x8CC702081A6439ECULL, + 0x90BEFFFA23631E28ULL, 0xA4506CEBDE82BDE9ULL, 0xBEF9A3F7B2C67915ULL, + 0xC67178F2E372532BULL, 0xCA273ECEEA26619CULL, 0xD186B8C721C0C207ULL, + 0xEADA7DD6CDE0EB1EULL, 0xF57D4F7FEE6ED178ULL, 0x06F067AA72176FBAULL, + 0x0A637DC5A2C898A6ULL, 0x113F9804BEF90DAEULL, 0x1B710B35131C471BULL, + 0x28DB77F523047D84ULL, 0x32CAAB7B40C72493ULL, 0x3C9EBE0A15C9BEBCULL, + 0x431D67C49C100D4CULL, 0x4CC5D4BECB3E42B6ULL, 0x597F299CFC657E2AULL, + 0x5FCB6FAB3AD6FAECULL, 0x6C44198C4A475817ULL + }; + + #endif + + using namespace sha512_detail; + + compat::array W {}; + + // Init the first 16 words of W + BOOST_CRYPT_ASSERT(8U * 16U == buffer_.size()); + for (compat::size_t i {}; i < 16U; ++i) + { + W[i] = (static_cast(buffer_[i * 8U]) << 56U) | + (static_cast(buffer_[i * 8U + 1U]) << 48U) | + (static_cast(buffer_[i * 8U + 2U]) << 40U) | + (static_cast(buffer_[i * 8U + 3U]) << 32U) | + (static_cast(buffer_[i * 8U + 4U]) << 24U) | + (static_cast(buffer_[i * 8U + 5U]) << 16U) | + (static_cast(buffer_[i * 8U + 6U]) << 8U) | + (static_cast(buffer_[i * 8U + 7U])); + } + + // Init the last 64 + for (compat::size_t i {16U}; i < W.size(); ++i) + { + W[i] = little_sigma1(W[i - 2U]) + W[i - 7U] + + little_sigma0(W[i - 15U]) + W[i - 16U]; + } + + auto A {intermediate_hash_[0]}; + auto B {intermediate_hash_[1]}; + auto C {intermediate_hash_[2]}; + auto D {intermediate_hash_[3]}; + auto E {intermediate_hash_[4]}; + auto F {intermediate_hash_[5]}; + auto G {intermediate_hash_[6]}; + auto H {intermediate_hash_[7]}; + + BOOST_CRYPT_ASSERT(sha512_k.size() == W.size()); + for (compat::size_t i {}; i < W.size(); ++i) + { + const auto temp1 {H + big_sigma1(E) + sha_ch(E, F, G) + sha512_k[i] + W[i]}; + const auto temp2 {big_sigma0(A) + sha_maj(A, B, C)}; + H = G; + G = F; + F = E; + E = D + temp1; + D = C; + C = B; + B = A; + A = temp1 + temp2; + } + + intermediate_hash_[0] += A; + intermediate_hash_[1] += B; + intermediate_hash_[2] += C; + intermediate_hash_[3] += D; + intermediate_hash_[4] += E; + intermediate_hash_[5] += F; + intermediate_hash_[6] += G; + intermediate_hash_[7] += H; + + // Reset the buffer index + buffer_index_ = 0U; +} + +template +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha512_base::process_bytes(compat::span data) noexcept -> state +{ + return update(data); +} + +template +template +BOOST_CRYPT_GPU_ENABLED auto sha512_base::process_bytes(SizedRange&& data) noexcept -> state +{ + auto data_span {compat::make_span(compat::forward(data))}; + return update(compat::as_bytes(data_span)); +} + +template +BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha512_base::pad_message() noexcept -> void +{ + constexpr compat::size_t message_length_start_index {112U}; + + // We don't have enough space for everything we need + if (buffer_index_ >= message_length_start_index) + { + buffer_[buffer_index_++] = compat::byte{0x80}; + while (buffer_index_ < buffer_.size()) + { + buffer_[buffer_index_++] = compat::byte{0x00}; + } + + process_message_block(); + + while (buffer_index_ < message_length_start_index) + { + buffer_[buffer_index_++] = compat::byte{0x00}; + } + } + else + { + buffer_[buffer_index_++] = compat::byte{0x80}; + while (buffer_index_ < message_length_start_index) + { + buffer_[buffer_index_++] = compat::byte{0x00}; + } + } + + // Add the message length to the end of the buffer + BOOST_CRYPT_ASSERT(buffer_index_ == message_length_start_index); + + buffer_[112U] = static_cast(high_ >> 56U); + buffer_[113U] = static_cast(high_ >> 48U); + buffer_[114U] = static_cast(high_ >> 40U); + buffer_[115U] = static_cast(high_ >> 32U); + buffer_[116U] = static_cast(high_ >> 24U); + buffer_[117U] = static_cast(high_ >> 16U); + buffer_[118U] = static_cast(high_ >> 8U); + buffer_[119U] = static_cast(high_); + + buffer_[120U] = static_cast(low_ >> 56U); + buffer_[121U] = static_cast(low_ >> 48U); + buffer_[122U] = static_cast(low_ >> 40U); + buffer_[123U] = static_cast(low_ >> 32U); + buffer_[124U] = static_cast(low_ >> 24U); + buffer_[125U] = static_cast(low_ >> 16U); + buffer_[126U] = static_cast(low_ >> 8U); + buffer_[127U] = static_cast(low_); + + // Finally we process the message block with our filled buffer + process_message_block(); +} + +} // namespace boost::crypt::hash_detail + +#endif //BOOST_CRYPT2_HASH_DETAIL_SHA512_BASE_HPP diff --git a/include/boost/crypt2/hash/detail/sha_1_2_hasher_base.hpp b/include/boost/crypt2/hash/detail/sha_1_2_hasher_base.hpp index 4181805f..594a4d7e 100644 --- a/include/boost/crypt2/hash/detail/sha_1_2_hasher_base.hpp +++ b/include/boost/crypt2/hash/detail/sha_1_2_hasher_base.hpp @@ -47,11 +47,12 @@ class sha_1_2_hasher_base BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto process_bytes(compat::span data) noexcept -> state; - template - BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto process_bytes(Range&& data) noexcept -> state; + template + BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto process_bytes(SizedRange&& data) noexcept -> state; BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto finalize() noexcept -> state; + // TODO(mborland): Allow this to take dynamic extent, check the length and then use a fixed amount. See sha512_base [[nodiscard("Digest is the function return value")]] BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto get_digest() noexcept -> return_type; BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto get_digest(compat::span data) noexcept -> state; diff --git a/include/boost/crypt2/hash/sha512.hpp b/include/boost/crypt2/hash/sha512.hpp new file mode 100644 index 00000000..b0c8ba53 --- /dev/null +++ b/include/boost/crypt2/hash/sha512.hpp @@ -0,0 +1,90 @@ +// Copyright 2024 - 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// See: https://datatracker.ietf.org/doc/html/rfc4634 +// See: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf + +#ifndef BOOST_CRYPT2_SHA512_HPP +#define BOOST_CRYPT2_SHA512_HPP + +#include +#include +#include +#include + +namespace boost::crypt { + +BOOST_CRYPT_EXPORT using sha512_hasher = hash_detail::sha512_base<64U>; + +// One shot functions +BOOST_CRYPT_EXPORT BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha512(compat::span data) noexcept -> sha512_hasher::return_type +{ + sha512_hasher hasher; + hasher.process_bytes(data); + hasher.finalize(); + return hasher.get_digest(); +} + +template +BOOST_CRYPT_EXPORT BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto sha512(SizedRange&& data) noexcept -> sha512_hasher::return_type +{ + sha512_hasher hasher; + hasher.process_bytes(data); + hasher.finalize(); + return hasher.get_digest(); +} + +#ifndef BOOST_CRYPT_HAS_CUDA + +// Error: the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information [-Werror,-Wunsafe-buffer-usage-in-container] +// Since this is the way the file streams report sizing information we must use it +// If a bad read occurs an exception is thrown so there's little risk of a bad region +#if defined(__clang__) && __clang_major__ >= 19 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-container" +#endif + +namespace detail { + +inline auto sha512_file_impl(detail::file_reader<64U>& reader) -> sha512_hasher::return_type +{ + sha512_hasher hasher; + while (!reader.eof()) + { + const auto buffer_iter {reader.read_next_block()}; + const auto len {reader.get_bytes_read()}; + const auto buffer_span {std::span(buffer_iter, len)}; + hasher.process_bytes(buffer_span); + } + + hasher.finalize(); + return hasher.get_digest(); +} + +} // namespace detail + +template +BOOST_CRYPT_EXPORT inline auto sha512_file(const T& filepath) +{ + if constexpr (std::is_pointer_v>) + { + if (filepath == nullptr) + { + throw std::runtime_error("Invalid file path"); + } + } + + detail::file_reader<64U> reader(filepath); + return detail::sha512_file_impl(reader); +} + +#if defined(__clang__) && __clang_major__ >= 19 +#pragma clang diagnostic pop +#endif + +#endif + +} // namespace boost::crypt + +#endif //BOOST_CRYPT2_SHA256_HPP diff --git a/include/boost/crypt2/state.hpp b/include/boost/crypt2/state.hpp index 9b304493..273f87f9 100644 --- a/include/boost/crypt2/state.hpp +++ b/include/boost/crypt2/state.hpp @@ -20,6 +20,7 @@ BOOST_CRYPT_EXPORT enum class state uninitialized, // Random bits can not be provided since the generator is uninitialized requested_too_many_bits, // 2^19 bits is all that's allowed per request insufficient_key_length, // The key is not of proscribed length + insufficient_output_length, // The output will not fit in the provided container state_error // added more input after get_digest without re-init }; diff --git a/test/Jamfile b/test/Jamfile index b3915988..11427d45 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -49,13 +49,16 @@ project : requirements # ODR Violations #run link_1.cpp link_2.cpp link_3.cpp ; +# Helpers +run test_assert.cpp ; + # Basic Testing run quick.cpp ; run test_sha1.cpp ; run test_sha224.cpp ; run test_sha256.cpp ; #run test_sha384.cpp ; -#run test_sha512.cpp ; +run test_sha512.cpp ; #run test_sha512_224.cpp ; #run test_sha512_256.cpp ; #run test_sha3_512.cpp ; @@ -98,8 +101,8 @@ run test_nist_cavs_sha256_short_long.cpp ; #run test_nist_cavs_sha384_hmac_drbg.cpp ; #run test_nist_cavs_sha384_hash_drbg.cpp ; -#run test_nist_cavs_sha512_monte.cpp ; -#run test_nist_cavs_sha512_short_long.cpp ; +run test_nist_cavs_sha512_monte.cpp ; +run test_nist_cavs_sha512_short_long.cpp ; #run test_nist_cavs_sha512_hmac.cpp ; #run test_nist_cavs_sha512_hmac_drbg.cpp ; #run test_nist_cavs_sha512_hash_drbg.cpp ; diff --git a/test/test_assert.cpp b/test/test_assert.cpp new file mode 100644 index 00000000..fbc5f94e --- /dev/null +++ b/test/test_assert.cpp @@ -0,0 +1,19 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +int main() +{ + try + { + BOOST_CRYPT_ASSERT(false); + } + catch (...) + { + std::cerr << "Caught exception as expected" << std::endl; + } + + return 0; +} diff --git a/test/test_nist_cavs_detail.hpp b/test/test_nist_cavs_detail.hpp index 1c1d2eb6..de9c796c 100644 --- a/test/test_nist_cavs_detail.hpp +++ b/test/test_nist_cavs_detail.hpp @@ -1700,7 +1700,7 @@ auto test_vectors_oneshot(const test_vector_container_type& test_vectors) -> boo #endif this_hash.process_bytes(data_span); - + this_hash.finalize(); const local_result_type result_01 { this_hash.get_digest() }; //const bool result_hash_01_is_ok { std::equal(test_vector.my_result.cbegin(), test_vector.my_result.cend(), result_01.cbegin()) }; @@ -1721,7 +1721,7 @@ auto test_vectors_oneshot(const test_vector_container_type& test_vectors) -> boo this_hash.init(); this_hash.process_bytes(data_span); - + this_hash.finalize(); const local_result_type result_02 { this_hash.get_digest() }; bool result_hash_02_is_ok { true }; @@ -1878,7 +1878,7 @@ auto test_vectors_monte(const nist::cavs::test_vector_container_type& test_vecto #endif this_hash.process_bytes(current_data); - + this_hash.finalize(); MDi = this_hash.get_digest(); MD[0U] = MD[1U]; diff --git a/test/test_nist_cavs_sha512_monte.cpp b/test/test_nist_cavs_sha512_monte.cpp index b796fe94..7e02c797 100644 --- a/test/test_nist_cavs_sha512_monte.cpp +++ b/test/test_nist_cavs_sha512_monte.cpp @@ -3,13 +3,13 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -#include +#include #include "test_nist_cavs_detail.hpp" auto main() -> int { - bool result_is_ok { true }; + bool result_is_ok { true }; { nist::cavs::test_vector_container_type my_test_vectors_monte { }; diff --git a/test/test_nist_cavs_sha512_short_long.cpp b/test/test_nist_cavs_sha512_short_long.cpp index 249e930e..85969326 100644 --- a/test/test_nist_cavs_sha512_short_long.cpp +++ b/test/test_nist_cavs_sha512_short_long.cpp @@ -3,7 +3,7 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -#include +#include #include "test_nist_cavs_detail.hpp" diff --git a/test/test_sha256.cpp b/test/test_sha256.cpp index f04e5457..05e13fd7 100644 --- a/test/test_sha256.cpp +++ b/test/test_sha256.cpp @@ -250,37 +250,6 @@ void files_test() BOOST_TEST_THROWS(boost::crypt::sha256_file(bad_path), std::runtime_error); } -// This ends up being completely calculated in a constexpr fashion so Codecov complains -// LCOV_EXCL_START -void test_span() -{ - #ifdef BOOST_CRYPT_HAS_SPAN - - // "abc" in hex - const std::byte vals[] = {std::byte{0x61}, std::byte{0x62}, std::byte{0x63}}; - std::span byte_span {vals}; - const auto expected_res = std::array{0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; - const auto res = boost::crypt::sha256(byte_span); - - for (std::size_t i {}; i < res.size(); ++i) - { - BOOST_TEST_EQ(res[i], expected_res[i]); - } - - boost::crypt::sha256_hasher hasher; - auto current_state = hasher.process_bytes(byte_span); - BOOST_TEST(current_state == boost::crypt::state::success); - const auto res_2 = hasher.get_digest(); - - for (std::size_t i {}; i < res.size(); ++i) - { - BOOST_TEST_EQ(res_2[i], expected_res[i]); - } - - #endif // BOOST_CRYPT_HAS_SPAN -} -// LCOV_EXCL_STOP - consteval bool immediate_test() { constexpr std::array vals = {std::byte{0x61}, std::byte{0x62}, std::byte{0x63}}; diff --git a/test/test_sha512.cpp b/test/test_sha512.cpp index 3c83a6d6..dd860a66 100644 --- a/test/test_sha512.cpp +++ b/test/test_sha512.cpp @@ -2,9 +2,19 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -#include +#include + +#if defined(__clang__) && __clang_major__ >= 19 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + #include -#include "generate_random_strings.hpp" + +#if defined(__clang__) && __clang_major__ >= 19 +#pragma clang diagnostic pop +#endif + #include #include #include @@ -15,10 +25,12 @@ #include #include -constexpr std::array, 3> test_values = +using std::byte; + +const std::array>, 3> test_values = { std::make_tuple("", - boost::crypt::sha512_hasher::return_type { + std::array { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, @@ -29,7 +41,7 @@ constexpr std::array { 0x07, 0xe5, 0x47, 0xd9, 0x58, 0x6f, 0x6a, 0x73, 0xf7, 0x3f, 0xba, 0xc0, 0x43, 0x5e, 0xd7, 0x69, 0x51, 0x21, 0x8f, 0xb7, 0xd0, 0xc8, 0xd7, 0x88, @@ -40,7 +52,7 @@ constexpr std::array { 0x91, 0xea, 0x12, 0x45, 0xf2, 0x0d, 0x46, 0xae, 0x9a, 0x03, 0x7a, 0x98, 0x9f, 0x54, 0xf1, 0xf7, 0x90, 0xf0, 0xa4, 0x76, 0x07, 0xee, 0xb8, 0xa1, @@ -60,7 +72,7 @@ void basic_tests() const auto valid_result {std::get<1>(test_value)}; for (std::size_t i {}; i < message_result.size(); ++i) { - if (!BOOST_TEST_EQ(message_result[i], valid_result[i])) + if (!BOOST_TEST(message_result[i] == static_cast(valid_result[i]))) { // LCOV_EXCL_START std::cerr << "Failure with: " << std::get<0>(test_value) << '\n'; @@ -80,7 +92,7 @@ void string_test() const auto valid_result {std::get<1>(test_value)}; for (std::size_t i {}; i < message_result.size(); ++i) { - if (!BOOST_TEST_EQ(message_result[i], valid_result[i])) + if (!BOOST_TEST(message_result[i] == static_cast(valid_result[i]))) { // LCOV_EXCL_START std::cerr << "Failure with: " << std::get<0>(test_value) << '\n'; @@ -93,7 +105,6 @@ void string_test() void string_view_test() { - #ifdef BOOST_CRYPT_HAS_STRING_VIEW for (const auto& test_value : test_values) { const std::string string_message {std::get<0>(test_value)}; @@ -102,7 +113,7 @@ void string_view_test() const auto valid_result {std::get<1>(test_value)}; for (std::size_t i {}; i < message_result.size(); ++i) { - if (!BOOST_TEST_EQ(message_result[i], valid_result[i])) + if (!BOOST_TEST(message_result[i] == static_cast(valid_result[i]))) { // LCOV_EXCL_START std::cerr << "Failure with: " << std::get<0>(test_value) << '\n'; @@ -114,10 +125,11 @@ void string_view_test() boost::crypt::sha512_hasher hasher; const auto current_state = hasher.process_bytes(string_view_message); BOOST_TEST(current_state == boost::crypt::state::success); + hasher.finalize(); const auto result2 = hasher.get_digest(); for (std::size_t i {}; i < message_result.size(); ++i) { - if (!BOOST_TEST_EQ(result2[i], valid_result[i])) + if (!BOOST_TEST(result2[i] == static_cast(valid_result[i]))) { // LCOV_EXCL_START std::cerr << "Failure with: " << std::get<0>(test_value) << '\n'; @@ -126,42 +138,8 @@ void string_view_test() } } } - #endif } -void bad_input() -{ - const auto null_message {boost::crypt::sha512(static_cast(nullptr))}; - BOOST_TEST_EQ(null_message[0], 0x0); - BOOST_TEST_EQ(null_message[1], 0x0); - BOOST_TEST_EQ(null_message[2], 0x0); - BOOST_TEST_EQ(null_message[3], 0x0); - - const auto null_message_len {boost::crypt::sha512(static_cast(nullptr), 100)}; - BOOST_TEST_EQ(null_message_len[0], 0x0); - BOOST_TEST_EQ(null_message_len[1], 0x0); - BOOST_TEST_EQ(null_message_len[2], 0x0); - BOOST_TEST_EQ(null_message_len[3], 0x0); - - const auto unsigned_null_message {boost::crypt::sha512(static_cast(nullptr))}; - BOOST_TEST_EQ(unsigned_null_message[0], 0x0); - BOOST_TEST_EQ(unsigned_null_message[1], 0x0); - BOOST_TEST_EQ(unsigned_null_message[2], 0x0); - BOOST_TEST_EQ(unsigned_null_message[3], 0x0); - - const auto unsigned_null_message_len {boost::crypt::sha512(static_cast(nullptr), 100)}; - BOOST_TEST_EQ(unsigned_null_message_len[0], 0x0); - BOOST_TEST_EQ(unsigned_null_message_len[1], 0x0); - BOOST_TEST_EQ(unsigned_null_message_len[2], 0x0); - BOOST_TEST_EQ(unsigned_null_message_len[3], 0x0); - - std::string test_str {"Test string"}; - const auto reveresed_input {boost::crypt::detail::sha512(test_str.end(), test_str.begin())}; - BOOST_TEST_EQ(reveresed_input[0], 0x0); - BOOST_TEST_EQ(reveresed_input[1], 0x0); - BOOST_TEST_EQ(reveresed_input[2], 0x0); - BOOST_TEST_EQ(reveresed_input[3], 0x0); -} void test_class() { @@ -169,14 +147,16 @@ void test_class() for (const auto& test_value : test_values) { + hasher.init(); const auto msg {std::get<0>(test_value)}; - hasher.process_bytes(msg, std::strlen(msg)); + hasher.process_bytes(msg); + hasher.finalize(); const auto message_result {hasher.get_digest()}; const auto valid_result {std::get<1>(test_value)}; for (std::size_t i {}; i < message_result.size(); ++i) { - if (!BOOST_TEST_EQ(message_result[i], valid_result[i])) + if (!BOOST_TEST(message_result[i] == static_cast(valid_result[i]))) { // LCOV_EXCL_START std::cerr << "Failure with: " << std::get<0>(test_value) << '\n'; @@ -184,38 +164,22 @@ void test_class() // LCOV_EXCL_STOP } } - - hasher.init(); } -} - -template -void test_file(T filename, const boost::crypt::sha512_hasher::return_type& res) -{ - const auto crypt_res {boost::crypt::sha512_file(filename)}; - for (std::size_t j {}; j < crypt_res.size(); ++j) - { - if (!BOOST_TEST_EQ(res[j], crypt_res[j])) - { - // LCOV_EXCL_START - std::cerr << "Failure with file: " << filename << std::endl; - break; - // LCOV_EXCL_STOP - } - } + const std::string bad_update_msg {"bad"}; + BOOST_TEST(hasher.process_bytes(bad_update_msg) == boost::crypt::state::state_error); + BOOST_TEST(hasher.finalize() == boost::crypt::state::state_error); + BOOST_TEST(hasher.get_digest() == boost::crypt::sha512_hasher::return_type{}); } template -void test_invalid_file(T filename) +void test_file(T filename, const std::array& res) { - constexpr boost::crypt::sha512_hasher::return_type res{}; - const auto crypt_res {boost::crypt::sha512_file(filename)}; for (std::size_t j {}; j < crypt_res.size(); ++j) { - if (!BOOST_TEST_EQ(res[j], crypt_res[j])) + if (!BOOST_TEST(static_cast(res[j]) == static_cast(crypt_res[j]))) { // LCOV_EXCL_START std::cerr << "Failure with file: " << filename << std::endl; @@ -275,118 +239,81 @@ void files_test() // On macOS 15 // sha512 test_file_1.txt // sha512 (test_file_1.txt) = 020da0f4d8a4c8bfbc98274027740061d7df52ee07091ed6595a083e0f45327bbe59424312d86f218b74ed2e25507abaf5c7a5fcf4cafcf9538b705808fd55ec - constexpr boost::crypt::sha512_hasher::return_type res{0x02, 0x0d, 0xa0, 0xf4, 0xd8, 0xa4, 0xc8, 0xbf, 0xbc, 0x98, 0x27, 0x40, 0x27, 0x74, 0x00, 0x61, 0xd7, 0xdf, 0x52, 0xee, 0x07, 0x09, 0x1e, 0xd6, 0x59, 0x5a, 0x08, 0x3e, 0x0f, 0x45, 0x32, 0x7b, 0xbe, 0x59, 0x42, 0x43, 0x12, 0xd8, 0x6f, 0x21, 0x8b, 0x74, 0xed, 0x2e, 0x25, 0x50, 0x7a, 0xba, 0xf5, 0xc7, 0xa5, 0xfc, 0xf4, 0xca, 0xfc, 0xf9, 0x53, 0x8b, 0x70, 0x58, 0x08, 0xfd, 0x55, 0xec}; + constexpr std::array res{0x02, 0x0d, 0xa0, 0xf4, 0xd8, 0xa4, 0xc8, 0xbf, 0xbc, 0x98, 0x27, 0x40, 0x27, 0x74, 0x00, 0x61, 0xd7, 0xdf, 0x52, 0xee, 0x07, 0x09, 0x1e, 0xd6, 0x59, 0x5a, 0x08, 0x3e, 0x0f, 0x45, 0x32, 0x7b, 0xbe, 0x59, 0x42, 0x43, 0x12, 0xd8, 0x6f, 0x21, 0x8b, 0x74, 0xed, 0x2e, 0x25, 0x50, 0x7a, 0xba, 0xf5, 0xc7, 0xa5, 0xfc, 0xf4, 0xca, 0xfc, 0xf9, 0x53, 0x8b, 0x70, 0x58, 0x08, 0xfd, 0x55, 0xec}; test_file(filename, res); const std::string str_filename {filename}; test_file(str_filename, res); - - #ifdef BOOST_CRYPT_HAS_STRING_VIEW + const std::string_view str_view_filename {str_filename}; test_file(str_view_filename, res); - #endif const auto invalid_filename = "broken.bin"; - test_invalid_file(invalid_filename); + BOOST_TEST_THROWS(boost::crypt::sha512_file(invalid_filename), std::runtime_error); const std::string str_invalid_filename {invalid_filename}; - test_invalid_file(str_invalid_filename); + BOOST_TEST_THROWS(boost::crypt::sha512_file(str_invalid_filename), std::runtime_error); - #ifdef BOOST_CRYPT_HAS_STRING_VIEW const std::string_view str_view_invalid_filename {str_invalid_filename}; - test_invalid_file(str_view_invalid_filename); - #endif - + BOOST_TEST_THROWS(boost::crypt::sha512_file(str_view_invalid_filename), std::runtime_error); // On macOS 15 // sha512 test_file_2.txt // sha512 (test_file_2.txt) = 83e18fae406f58d67f459ef301dc236639c44d4c10928e38363021ba037159e73b00a86820607a1595653129b52b284543714834816dd00a33f49cbf16ee5d77 - constexpr boost::crypt::sha512_hasher::return_type res_2{0x83, 0xe1, 0x8f, 0xae, 0x40, 0x6f, 0x58, 0xd6, 0x7f, 0x45, 0x9e, 0xf3, 0x01, 0xdc, 0x23, 0x66, 0x39, 0xc4, 0x4d, 0x4c, 0x10, 0x92, 0x8e, 0x38, 0x36, 0x30, 0x21, 0xba, 0x03, 0x71, 0x59, 0xe7, 0x3b, 0x00, 0xa8, 0x68, 0x20, 0x60, 0x7a, 0x15, 0x95, 0x65, 0x31, 0x29, 0xb5, 0x2b, 0x28, 0x45, 0x43, 0x71, 0x48, 0x34, 0x81, 0x6d, 0xd0, 0x0a, 0x33, 0xf4, 0x9c, 0xbf, 0x16, 0xee, 0x5d, 0x77}; + constexpr std::array res_2{0x83, 0xe1, 0x8f, 0xae, 0x40, 0x6f, 0x58, 0xd6, 0x7f, 0x45, 0x9e, 0xf3, 0x01, 0xdc, 0x23, 0x66, 0x39, 0xc4, 0x4d, 0x4c, 0x10, 0x92, 0x8e, 0x38, 0x36, 0x30, 0x21, 0xba, 0x03, 0x71, 0x59, 0xe7, 0x3b, 0x00, 0xa8, 0x68, 0x20, 0x60, 0x7a, 0x15, 0x95, 0x65, 0x31, 0x29, 0xb5, 0x2b, 0x28, 0x45, 0x43, 0x71, 0x48, 0x34, 0x81, 0x6d, 0xd0, 0x0a, 0x33, 0xf4, 0x9c, 0xbf, 0x16, 0xee, 0x5d, 0x77}; test_file(filename_2, res_2); const char* test_null_file = nullptr; - test_invalid_file(test_null_file); -} - -void test_invalid_state() -{ - boost::crypt::sha512_hasher hasher; - auto current_state = hasher.process_bytes("test", 4); - BOOST_TEST(current_state == boost::crypt::state::success); - - hasher.get_digest(); - - const auto bad_state = hasher.process_bytes("test", 4); - BOOST_TEST(bad_state == boost::crypt::state::state_error); - - const auto digest = hasher.get_digest(); - - for (const auto& val : digest) - { - BOOST_TEST_EQ(val, static_cast(0)); - } - - hasher.init(); + BOOST_TEST_THROWS(boost::crypt::sha512_file(test_null_file), std::runtime_error); - current_state = hasher.process_bytes("test", 4); - BOOST_TEST(current_state == boost::crypt::state::success); - current_state = hasher.process_byte(0x03); - BOOST_TEST(current_state == boost::crypt::state::success); - const char* ptr = nullptr; - current_state = hasher.process_bytes(ptr, 4); - BOOST_TEST(current_state == boost::crypt::state::null); - - const char16_t* ptr16 = nullptr; - current_state = hasher.process_bytes(ptr16, 4); - BOOST_TEST(current_state == boost::crypt::state::null); - - const char32_t* ptr32 = nullptr; - current_state = hasher.process_bytes(ptr32, 4); - BOOST_TEST(current_state == boost::crypt::state::null); - - const wchar_t* wptr = nullptr; - current_state = hasher.process_bytes(wptr, 4); - BOOST_TEST(current_state == boost::crypt::state::null); + std::filesystem::path bad_path = "path.txt"; + BOOST_TEST_THROWS(boost::crypt::sha512_file(bad_path), std::runtime_error); } -// This ends up being completely calculated in a constexpr fashion so Codecov complains -// LCOV_EXCL_START -void test_span() +consteval bool immediate_test() { - #ifdef BOOST_CRYPT_HAS_SPAN + constexpr std::array vals = {std::byte{0x61}, std::byte{0x62}, std::byte{0x63}}; + constexpr std::array expected_res { + std::byte{0xdd}, std::byte{0xaf}, std::byte{0x35}, std::byte{0xa1}, std::byte{0x93}, std::byte{0x61}, + std::byte{0x7a}, std::byte{0xba}, std::byte{0xcc}, std::byte{0x41}, std::byte{0x73}, std::byte{0x49}, + std::byte{0xae}, std::byte{0x20}, std::byte{0x41}, std::byte{0x31}, std::byte{0x12}, std::byte{0xe6}, + std::byte{0xfa}, std::byte{0x4e}, std::byte{0x89}, std::byte{0xa9}, std::byte{0x7e}, std::byte{0xa2}, + std::byte{0x0a}, std::byte{0x9e}, std::byte{0xee}, std::byte{0xe6}, std::byte{0x4b}, std::byte{0x55}, + std::byte{0xd3}, std::byte{0x9a}, std::byte{0x21}, std::byte{0x92}, std::byte{0x99}, std::byte{0x2a}, + std::byte{0x27}, std::byte{0x4f}, std::byte{0xc1}, std::byte{0xa8}, std::byte{0x36}, std::byte{0xba}, + std::byte{0x3c}, std::byte{0x23}, std::byte{0xa3}, std::byte{0xfe}, std::byte{0xeb}, std::byte{0xbd}, + std::byte{0x45}, std::byte{0x4d}, std::byte{0x44}, std::byte{0x23}, std::byte{0x64}, std::byte{0x3c}, + std::byte{0xe8}, std::byte{0x0e}, std::byte{0x2a}, std::byte{0x9a}, std::byte{0xc9}, std::byte{0x4f}, + std::byte{0xa5}, std::byte{0x4c}, std::byte{0xa4}, std::byte{0x9f} + }; - // "abc" in hex - const std::byte vals[] = {std::byte{0x61}, std::byte{0x62}, std::byte{0x63}}; std::span byte_span {vals}; - const auto expected_res = std::array{0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f}; - const auto res = boost::crypt::sha512(byte_span); - - for (std::size_t i {}; i < res.size(); ++i) - { - BOOST_TEST_EQ(res[i], expected_res[i]); - } boost::crypt::sha512_hasher hasher; - auto current_state = hasher.process_bytes(byte_span); - BOOST_TEST(current_state == boost::crypt::state::success); - const auto res_2 = hasher.get_digest(); + hasher.init(); + hasher.process_bytes(byte_span); + hasher.finalize(); + const auto res = hasher.get_digest(); + bool correct {true}; for (std::size_t i {}; i < res.size(); ++i) { - BOOST_TEST_EQ(res_2[i], expected_res[i]); + if (res[i] != expected_res[i]) + { + correct = false; + break; + } } - #endif // BOOST_CRYPT_HAS_SPAN + return correct; } -// LCOV_EXCL_STOP int main() { basic_tests(); string_test(); string_view_test(); - bad_input(); test_class(); // The Windows file system returns a different result than on UNIX platforms @@ -394,9 +321,10 @@ int main() files_test(); #endif - test_invalid_state(); - - test_span(); + // GCC-14 has an internal compiler error here + #if defined(__GNUC__) && __GNUC__ != 14 + static_assert(immediate_test()); + #endif return boost::report_errors(); }