Skip to content

Commit

Permalink
Merge pull request #8 from cppalliance/file
Browse files Browse the repository at this point in the history
Add file support to MD5
  • Loading branch information
mborland authored Oct 16, 2024
2 parents 19737ff + 6b8d082 commit b7f1c08
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 4 deletions.
72 changes: 68 additions & 4 deletions include/boost/crypt/hash/md5.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <boost/crypt/utility/strlen.hpp>
#include <boost/crypt/utility/cstddef.hpp>
#include <boost/crypt/utility/iterator.hpp>
#include <boost/crypt/utility/file.hpp>

#ifndef BOOST_CRYPT_BUILD_MODULE
#include <memory>
Expand Down Expand Up @@ -550,28 +551,91 @@ inline auto md5(const std::wstring& str) noexcept -> boost::crypt::array<boost::

#ifdef BOOST_CRYPT_HAS_STRING_VIEW

inline auto md5(const std::string_view& str) -> boost::crypt::array<boost::crypt::uint8_t, 16>
inline auto md5(std::string_view str) -> boost::crypt::array<boost::crypt::uint8_t, 16>
{
return detail::md5(str.begin(), str.end());
}

inline auto md5(const std::u16string_view& str) -> boost::crypt::array<boost::crypt::uint8_t, 16>
inline auto md5(std::u16string_view str) -> boost::crypt::array<boost::crypt::uint8_t, 16>
{
return detail::md5(str.begin(), str.end());
}

inline auto md5(const std::u32string_view& str) -> boost::crypt::array<boost::crypt::uint8_t, 16>
inline auto md5(std::u32string_view str) -> boost::crypt::array<boost::crypt::uint8_t, 16>
{
return detail::md5(str.begin(), str.end());
}

inline auto md5(const std::wstring_view& str) -> boost::crypt::array<boost::crypt::uint8_t, 16>
inline auto md5(std::wstring_view str) -> boost::crypt::array<boost::crypt::uint8_t, 16>
{
return detail::md5(str.begin(), str.end());
}

#endif // BOOST_CRYPT_HAS_STRING_VIEW

// ---- CUDA also does not have the ability to consume files -----

namespace detail {

template <boost::crypt::size_t block_size = 64U>
auto md5_file_impl(utility::file_reader<block_size>& reader) noexcept -> boost::crypt::array<boost::crypt::uint8_t, 16>
{
md5_hasher hasher;
while (!reader.eof())
{
const auto buffer_iter {reader.read_next_block()};
const auto len {reader.get_bytes_read()};
hasher.process_bytes(buffer_iter, len);
}

return hasher.get_digest();
}

} // namespace detail

inline auto md5_file(const std::string& filepath) noexcept -> boost::crypt::array<boost::crypt::uint8_t, 16>
{
try
{
utility::file_reader<64U> reader(filepath);
return detail::md5_file_impl(reader);
}
catch (const std::runtime_error&)
{
return boost::crypt::array<boost::crypt::uint8_t, 16>{};
}
}

inline auto md5_file(const char* filepath) noexcept -> boost::crypt::array<boost::crypt::uint8_t, 16>
{
try
{
utility::file_reader<64U> reader(filepath);
return detail::md5_file_impl(reader);
}
catch (const std::runtime_error&)
{
return boost::crypt::array<boost::crypt::uint8_t, 16>{};
}
}

#ifdef BOOST_CRYPT_HAS_STRING_VIEW

inline auto md5_file(std::string_view filepath) noexcept -> boost::crypt::array<boost::crypt::uint8_t, 16>
{
try
{
utility::file_reader<64U> reader(filepath);
return detail::md5_file_impl(reader);
}
catch (const std::runtime_error&)
{
return boost::crypt::array<boost::crypt::uint8_t, 16>{};
}
}

#endif // BOOST_CRYPT_HAS_STRING_VIEW

#endif // BOOST_CRYPT_HAS_CUDA

} // namespace crypt
Expand Down
86 changes: 86 additions & 0 deletions include/boost/crypt/utility/file.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2024 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#ifndef BOOST_CRYPT_UTILITY_FILE_HPP
#define BOOST_CRYPT_UTILITY_FILE_HPP

#include <boost/crypt/utility/config.hpp>
#include <boost/crypt/utility/cstdint.hpp>

#ifndef BOOST_CRYPT_BUILD_MODULE
#include <fstream>
#include <string>
#include <ios>
#include <exception>
#include <array>
#endif

namespace boost {
namespace crypt {
namespace utility {

template <std::size_t block_size = 64U>
class file_reader
{
private:
std::ifstream fd;
std::array<std::uint8_t, block_size> buffer_ {};

public:
explicit file_reader(const std::string& filename) : fd(filename, std::ios::binary | std::ios::in)
{
if (!fd.is_open())
{
throw std::runtime_error("Error opening file: " + filename);
}
}

explicit file_reader(const char* filename) : fd(filename, std::ios::binary | std::ios::in)
{
if (!fd.is_open())
{
throw std::runtime_error("Error opening file");
}
}

#ifdef BOOST_CRYPT_HAS_STRING_VIEW
explicit file_reader(std::string_view filename) : fd(filename.data(), std::ios::binary | std::ios::in)
{
if (!fd.is_open())
{
throw std::runtime_error("Error opening file");
}
}
#endif

auto read_next_block()
{
fd.read(reinterpret_cast<char*>(buffer_.data()), block_size);
return buffer_.begin();
}

auto get_bytes_read() const -> std::size_t
{
return static_cast<std::size_t>(fd.gcount());
}

auto eof() const -> bool
{
return fd.eof();
}

~file_reader()
{
if (fd.is_open())
{
fd.close();
}
}
};

} // namespace utility
} // namespace crypt
} // namespace boost

#endif //BOOST_CRYPT_UTILITY_FILE_HPP
1 change: 1 addition & 0 deletions test/test_file_1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The quick brown fox jumps over the lazy dog.
9 changes: 9 additions & 0 deletions test/test_file_2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras leo purus, faucibus id risus pulvinar, condimentum molestie justo. Nulla sit amet pulvinar magna. In at quam id leo scelerisque posuere. Nullam porttitor auctor vestibulum. Nullam ac velit orci. Quisque semper hendrerit tortor, eu vulputate mauris vestibulum eget. Quisque nunc neque, posuere ut tortor a, suscipit tristique diam. Maecenas nec elit turpis. Nullam sapien enim, rhoncus et aliquet id, accumsan ac justo. Maecenas eu diam eget lorem vehicula lacinia eget sit amet orci. Ut lobortis magna arcu, pharetra lacinia est lobortis ut. Nam sagittis ex et magna maximus volutpat. Praesent lacinia felis neque. Vestibulum velit nisl, ullamcorper eu hendrerit eget, aliquet vitae ante. Mauris scelerisque blandit felis sed pharetra.

Etiam a sapien at arcu cursus malesuada. Mauris ut quam velit. Praesent rutrum, neque ut vehicula hendrerit, lorem libero malesuada neque, sed sodales turpis lorem fermentum erat. Vestibulum in eleifend erat, nec blandit libero. Etiam aliquam lacus sit amet nisl cursus, ut consequat orci dignissim. Proin varius lectus augue, a euismod quam euismod at. Nullam augue sapien, finibus viverra ante ac, ultricies eleifend ipsum. Mauris ullamcorper eros nulla, sed porta nulla imperdiet sed. Sed ac massa dui. Pellentesque tellus ligula, posuere quis enim hendrerit, suscipit eleifend felis. Vivamus consectetur feugiat orci a faucibus. Morbi tristique, ex sit amet mollis laoreet, ligula ligula tempor lorem, sed posuere nisl sem at nunc. Morbi a sodales justo. Sed efficitur nibh vitae turpis aliquam semper nec in urna.

In neque nisl, malesuada eu tristique non, euismod a diam. Sed mattis scelerisque consectetur. Sed in molestie libero, quis porta felis. Curabitur vel augue mauris. Aliquam dignissim facilisis bibendum. Vestibulum ut dignissim metus, ut fermentum elit. Etiam eu arcu id massa tristique semper. Ut lobortis neque eget hendrerit pretium. Nulla congue justo nec nibh cursus mattis. Morbi libero urna, sagittis ac risus vel, rutrum finibus purus.

Nam mattis fringilla justo eget pretium. Vivamus quis facilisis tortor. Aenean blandit elit eu mollis lacinia. Cras orci odio, aliquet eget lacus ac, feugiat lacinia magna. Pellentesque vel urna congue metus faucibus accumsan. Donec ac tortor feugiat nibh maximus imperdiet non id lectus. Duis commodo, purus eu suscipit porttitor, lorem purus faucibus enim, ac ultricies nulla massa vitae magna. Vestibulum maximus enim ante, quis lacinia magna molestie nec. Donec eleifend sapien at risus iaculis rhoncus. Phasellus sit amet urna pulvinar, commodo mi ut, feugiat sapien. Nullam id felis nec turpis commodo laoreet.

Vivamus quis felis pretium, bibendum turpis vitae, maximus nulla. Phasellus a elit id erat lacinia lacinia. Nunc in feugiat libero. Morbi sodales quam eget sem egestas varius ut quis orci. In hac habitasse platea dictumst. Sed sollicitudin vestibulum faucibus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam rhoncus nulla elit, id egestas arcu posuere vulputate. Nullam sit amet leo iaculis, viverra nisi nec, efficitur nisi. Fusce nec dui ultricies, ultricies est ac, pretium est.
125 changes: 125 additions & 0 deletions test/test_md5.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <string>
#include <array>
#include <tuple>
#include <fstream>
#include <cstdlib>
#include <ctime>
#include <cstring>
Expand Down Expand Up @@ -295,6 +296,125 @@ void test_random_piecewise_values()
delete[] str_2;
}

template <typename T>
void test_file(T filename, const std::array<std::uint16_t, 16>& res)
{
const auto crypt_res {boost::crypt::md5_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
}
}
}

template <typename T>
void test_invalid_file(T filename)
{
constexpr std::array<std::uint16_t, 16> res{};

const auto crypt_res {boost::crypt::md5_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
}
}
}

void files_test()
{
// Based off where we are testing from (test vs boost_root) we need to adjust our filepath
const char* filename;
const char* filename_2;

// Boost-root
std::ifstream fd("libs/crypt/test/test_file_1.txt", std::ios::binary | std::ios::in);
filename = "libs/crypt/test/test_file_1.txt";
filename_2 = "libs/crypt/test/test_file_2.txt";

// LCOV_EXCL_START
if (!fd.is_open())
{
// Local test directory or IDE
std::ifstream fd2("test_file_1.txt", std::ios::binary | std::ios::in);
filename = "test_file_1.txt";
filename_2 = "test_file_2.txt";

if (!fd2.is_open())
{
// test/cover
std::ifstream fd3("../test_file_1.txt", std::ios::binary | std::ios::in);
filename = "../test_file_1.txt";
filename_2 = "../test_file_2.txt";

if (!fd3.is_open())
{
std::cerr << "Test not run due to file system issues" << std::endl;
return;
}
else
{
fd3.close();
}
}
else
{
fd2.close();
}
}
else
{
fd.close();
}
// LCOV_EXCL_STOP

// On macOS 15
// md5 test_file_1.txt
// MD5 (test_file_1.txt) = 0d7006cd055e94cf614587e1d2ae0c8e
constexpr std::array<std::uint16_t, 16> res{0x0d, 0x70, 0x06, 0xcd, 0x05, 0x5e, 0x94, 0xcf,
0x61, 0x45, 0x87, 0xe1, 0xd2, 0xae, 0x0c, 0x8e};

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);

const std::string str_invalid_filename {invalid_filename};
test_invalid_file(str_invalid_filename);

#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

// On macOS 15
// md5 test_file_2.txt
// MD5 (test_file_2.txt) = 530e67fa4b01e3ccaee8eca9916a814c
constexpr std::array<std::uint16_t, 16> res_2{0x53, 0x0e, 0x67, 0xfa, 0x4b, 0x01, 0xe3, 0xcc,
0xae, 0xe8, 0xec, 0xa9, 0x91, 0x6a, 0x81, 0x4c};

test_file(filename_2, res_2);
}

int main()
{
basic_tests();
Expand All @@ -318,5 +438,10 @@ int main()
test_random_values<wchar_t>();
test_random_piecewise_values<wchar_t>();

// The Windows file system returns a different result than on UNIX platforms
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
files_test();
#endif

return boost::report_errors();
}

0 comments on commit b7f1c08

Please sign in to comment.