diff --git a/include/boost/crypt/hash/sha1.hpp b/include/boost/crypt/hash/sha1.hpp index 9378698..0443fd7 100644 --- a/include/boost/crypt/hash/sha1.hpp +++ b/include/boost/crypt/hash/sha1.hpp @@ -32,25 +32,6 @@ namespace crypt { class sha1_hasher { -private: - - boost::crypt::array intermediate_hash_ {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0}; - boost::crypt::array buffer_ {}; - - boost::crypt::size_t buffer_index_ {}; - boost::crypt::size_t low_ {}; - boost::crypt::size_t high_ {}; - - bool computed {}; - bool corrupted {}; - - BOOST_CRYPT_GPU_ENABLED constexpr auto sha1_process_message_block() -> void; - - template - BOOST_CRYPT_GPU_ENABLED constexpr auto sha1_update(ForwardIter data, boost::crypt::size_t size) noexcept -> hasher_state; - - BOOST_CRYPT_GPU_ENABLED constexpr auto pad_message() noexcept -> void; - public: using return_type = boost::crypt::array; @@ -71,6 +52,25 @@ class sha1_hasher BOOST_CRYPT_GPU_ENABLED constexpr auto get_digest() noexcept -> return_type ; + +private: + + boost::crypt::array intermediate_hash_ { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; + boost::crypt::array buffer_ {}; + + boost::crypt::size_t buffer_index_ {}; + boost::crypt::size_t low_ {}; + boost::crypt::size_t high_ {}; + + bool computed {}; + bool corrupted {}; + + BOOST_CRYPT_GPU_ENABLED constexpr auto sha1_process_message_block() -> void; + + template + BOOST_CRYPT_GPU_ENABLED constexpr auto sha1_update(ForwardIter data, boost::crypt::size_t size) noexcept -> hasher_state; + + BOOST_CRYPT_GPU_ENABLED constexpr auto pad_message() noexcept -> void; }; namespace detail { diff --git a/test/Jamfile b/test/Jamfile index db44fff..7223a96 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -45,5 +45,6 @@ project : requirements run quick.cpp ; run test_md5.cpp ; -run test_nist_cavs_sha1_short.cpp ; +run test_nist_cavs_sha1_monte.cpp ; +run test_nist_cavs_sha1_short_long.cpp ; run test_sha1.cpp ; diff --git a/test/nist_cavs/vectors/shavs.pdf b/test/nist_cavs/vectors/shavs.pdf new file mode 100644 index 0000000..36dfffd Binary files /dev/null and b/test/nist_cavs/vectors/shavs.pdf differ diff --git a/test/test_cavs_nist_detail.h b/test/test_cavs_nist_detail.h index 18102b4..649af50 100644 --- a/test/test_cavs_nist_detail.h +++ b/test/test_cavs_nist_detail.h @@ -73,7 +73,7 @@ struct test_object_hash using test_vector_container_type = std::deque; -auto where_file(const std::string& test_vectors_filename) -> std::string +auto where_file_shabytesvectors(const std::string& test_vectors_filename) -> std::string { // Try to open the file in each of the known relative paths // in order to find out where it is located. @@ -159,7 +159,7 @@ auto parse_file_vectors(const std::string& test_vectors_filename, test_vector_co { bool result_parse_is_ok { false }; - const std::string test_vectors_filename_relative { where_file(test_vectors_filename) }; + const std::string test_vectors_filename_relative { where_file_shabytesvectors(test_vectors_filename) }; const bool result_filename_plausible_is_ok { (!test_vectors_filename_relative.empty()) }; diff --git a/test/test_nist_cavs_detail.hpp b/test/test_nist_cavs_detail.hpp index 50f98d5..d05d913 100644 --- a/test/test_nist_cavs_detail.hpp +++ b/test/test_nist_cavs_detail.hpp @@ -46,6 +46,23 @@ struct test_object_hash test_object_hash() = delete; + // Construct this hash test object by setting the result only. + // There is no message and there is no length available for + // this hash test object. + + explicit test_object_hash(const std::string& str_result) + : my_result // LCOV_EXCL_LINE + { + [&str_result]() + { + const auto byte_data { detail::convert_hex_string_to_byte_container(str_result) }; + return message_type(byte_data.cbegin(), byte_data.cend()); + }() + } + { } + + // Construct this hash test object with all of message, length and result. + explicit test_object_hash(const std::string& str_data, const std::string& str_result) : my_length { str_data.size() / static_cast(UINT8_C(2)) }, my_msg @@ -61,7 +78,7 @@ struct test_object_hash [&str_result]() { const auto byte_data { detail::convert_hex_string_to_byte_container(str_result) }; - return message_type(byte_data.cbegin(), byte_data.cend()); + return message_type(byte_data.cbegin(), byte_data.cend()); }() } { } @@ -73,7 +90,7 @@ struct test_object_hash using test_vector_container_type = std::deque; -auto where_file(const std::string& test_vectors_filename) -> std::string +auto where_file_shabytesvectors(const std::string& test_vectors_filename) -> std::string { // Try to open the file in each of the known relative paths // in order to find out where it is located. @@ -159,10 +176,12 @@ auto parse_file_vectors(const std::string& test_vectors_filename, test_vector_co { bool result_parse_is_ok { false }; - const std::string test_vectors_filename_relative { where_file(test_vectors_filename) }; + const std::string test_vectors_filename_relative { where_file_shabytesvectors(test_vectors_filename) }; const bool result_filename_plausible_is_ok { (!test_vectors_filename_relative.empty()) }; + BOOST_TEST(result_filename_plausible_is_ok); + if(result_filename_plausible_is_ok) { std::string str_message { }; @@ -232,6 +251,77 @@ auto parse_file_vectors(const std::string& test_vectors_filename, test_vector_co } } + BOOST_TEST(result_parse_is_ok); + + return result_parse_is_ok; +} + +auto parse_file_monte(const std::string& test_monte_filename, test_vector_container_type& test_vectors_to_get) -> bool +{ + bool result_parse_is_ok { false }; + + const std::string test_vectors_filename_relative { where_file_shabytesvectors(test_monte_filename) }; + + const bool result_filename_plausible_is_ok { (!test_vectors_filename_relative.empty()) }; + + BOOST_TEST(result_filename_plausible_is_ok); + + if(result_filename_plausible_is_ok) + { + std::string str_result { }; + + // Read the file for creating the test cases. + std::ifstream in(test_vectors_filename_relative.c_str()); + + const bool file_is_open = in.is_open(); + + unsigned count { }; + + if(file_is_open) + { + result_parse_is_ok = true; + + std::string line { }; + std::string result { }; + + while(getline(in, line)) + { + const std::string::size_type pos_cnt = line.find("COUNT =", 0U); + const std::string::size_type pos_md = line.find("MD =", 0U); + + const bool line_is_representation_is_cnt = (pos_cnt != std::string::npos); + const bool line_is_representation_is_md = (pos_md != std::string::npos); + + // Get the next count. + if(line_is_representation_is_cnt) + { + const std::string str_cnt = line.substr(8U, line.length() - 8U); + + const unsigned long count_from_file = std::strtoul(str_cnt.c_str(), nullptr, 10U); + + count = static_cast(count_from_file); + } + + // Get the next (expected) result. + if(line_is_representation_is_md) + { + result = line.substr(5U, line.length() - 5U); + + // Add the new test object to v. + const test_object_hash test_obj(result); + + test_vectors_to_get.push_back(test_obj); + } + } + + in.close(); + + result_parse_is_ok = ((!test_vectors_to_get.empty()) && (count == 99U) && result_parse_is_ok); + } + } + + BOOST_TEST(result_parse_is_ok); + return result_parse_is_ok; } @@ -246,6 +336,8 @@ auto test_vectors_oneshot(const test_vector_container_type& test_vectors) -> boo using local_hasher_type = HasherType; using local_result_type = typename local_hasher_type::return_type; + BOOST_TEST((!test_vectors.empty())); + bool result_is_ok { true }; for(const auto& test_vector : test_vectors) @@ -267,6 +359,9 @@ auto test_vectors_oneshot(const test_vector_container_type& test_vectors) -> boo // Make pass 2 through the messages. // Use the triple-combination of init/process/get-result functions. + // Even though this is not required in CAVS testing, it is + // done in order to ensure that the init() function properly + // puts the hasher-object into its initialized state. this_hash.init(); @@ -287,6 +382,95 @@ auto test_vectors_oneshot(const test_vector_container_type& test_vectors) -> boo return result_is_ok; } +template +auto test_vectors_monte(const nist::cavs::test_vector_container_type& test_vectors_monte, const std::vector& seed_init) -> bool +{ + using local_hasher_type = HasherType; + using local_result_type = typename local_hasher_type::return_type; + + using local_array_type = local_result_type; + + // Obtain the test-specific initial seed. + local_array_type Seed { }; + + const std::size_t + copy_len + { + (std::min)(static_cast(Seed.size()), static_cast(seed_init.size())) + }; + + std::copy + ( + seed_init.cbegin(), + seed_init.cbegin() + static_cast::difference_type>(copy_len), + Seed.begin() + ); + + bool result_is_ok { (!test_vectors_monte.empty()) }; + + if(result_is_ok) + { + local_array_type MD[3U] { { }, { }, { } }; + + local_array_type MDi { }; + local_array_type MDj { }; + + constexpr local_array_type dummy_array { }; + + // See pseudocode on page 9 of "The Secure Hash Algorithm Validation System (SHAVS)". + + for(std::size_t j { }; j < 100U; ++j) + { + MD[0U] = MD[1U] = MD[2U] = Seed; + + for(std::size_t i { 3U } ; i < 1003U; ++i) + { + using local_wide_array_type = boost::crypt::array; + + std::vector result_vector; + + result_vector.reserve(dummy_array.size() * 3U); + + result_vector.insert(result_vector.end(), MD[0U].cbegin(), MD[0U].cend()); + result_vector.insert(result_vector.end(), MD[1U].cbegin(), MD[1U].cend()); + result_vector.insert(result_vector.end(), MD[2U].cbegin(), MD[2U].cend()); + + local_wide_array_type Mi { }; + + std::copy(result_vector.cbegin(), result_vector.cend(), Mi.begin()); + + local_hasher_type this_hash { }; + + this_hash.init(); + + this_hash.process_bytes(Mi.data(), Mi.size()); + + MDi = this_hash.get_digest(); + + MD[0U] = MD[1U]; + MD[1U] = MD[2U]; + MD[2U] = MDi; + } + + MDj = Seed = MDi; + + const bool result_this_monte_step_is_ok = + std::equal + ( + MDj.cbegin(), + MDj.cend(), + test_vectors_monte[j].my_result.cbegin() + ); + + result_is_ok = (result_this_monte_step_is_ok && result_is_ok); + + BOOST_TEST(result_this_monte_step_is_ok); + } + } + + return result_is_ok; +} + } // namespace cavs } // namespace nist diff --git a/test/test_nist_cavs_sha1_monte.cpp b/test/test_nist_cavs_sha1_monte.cpp new file mode 100644 index 0000000..8f2a6f3 --- /dev/null +++ b/test/test_nist_cavs_sha1_monte.cpp @@ -0,0 +1,35 @@ +// Copyright 2024 Matt Borland +// Copyright 2024 Christopher Kormanyos +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#include "test_nist_cavs_detail.hpp" + +auto main() -> int +{ + bool result_is_ok { true }; + + { + nist::cavs::test_vector_container_type my_test_vectors_monte { }; + + std::vector + seed_init + ( + { + 0xDDU, 0x4DU, 0xF6U, 0x44U, 0xEAU, 0xF3U, 0xD8U, 0x5BU, + 0xACU, 0xE2U, 0xB2U, 0x1AU, 0xCCU, 0xAAU, 0x22U, 0xB2U, + 0x88U, 0x21U, 0xF5U, 0xCDU + } + ); + + static_cast(nist::cavs::detail::parse_file_monte("SHA1Monte.rsp", my_test_vectors_monte)); + + result_is_ok = (nist::cavs::test_vectors_monte(my_test_vectors_monte, seed_init) && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + return boost::report_errors(); +} diff --git a/test/test_nist_cavs_sha1_short.cpp b/test/test_nist_cavs_sha1_short.cpp deleted file mode 100644 index 8677eb9..0000000 --- a/test/test_nist_cavs_sha1_short.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2024 Matt Borland -// Copyright 2024 Christopher Kormanyos -// Distributed under the Boost Software License, Version 1.0. -// https://www.boost.org/LICENSE_1_0.txt - -#include "test_nist_cavs_detail.hpp" - -#include - -auto main() -> int -{ - nist::cavs::test_vector_container_type test_vectors { }; - - static_cast(nist::cavs::detail::parse_file_vectors("SHA1ShortMsg.rsp", test_vectors)); - - const bool result_is_ok { nist::cavs::test_vectors_oneshot(test_vectors) }; - - static_cast(result_is_ok); - - return boost::report_errors(); -} diff --git a/test/test_nist_cavs_sha1_short_long.cpp b/test/test_nist_cavs_sha1_short_long.cpp new file mode 100644 index 0000000..ba8024f --- /dev/null +++ b/test/test_nist_cavs_sha1_short_long.cpp @@ -0,0 +1,36 @@ +// Copyright 2024 Matt Borland +// Copyright 2024 Christopher Kormanyos +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#include "test_nist_cavs_detail.hpp" + +auto main() -> int +{ + bool result_is_ok { true }; + + { + nist::cavs::test_vector_container_type test_vectors_short { }; + + static_cast(nist::cavs::detail::parse_file_vectors("SHA1ShortMsg.rsp", test_vectors_short)); + + result_is_ok = (nist::cavs::test_vectors_oneshot(test_vectors_short) && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + + { + nist::cavs::test_vector_container_type test_vectors_long { }; + + static_cast(nist::cavs::detail::parse_file_vectors("SHA1LongMsg.rsp", test_vectors_long)); + + result_is_ok = (nist::cavs::test_vectors_oneshot(test_vectors_long) && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + return boost::report_errors(); +}