src/native/hashing/hash.cpp (85 lines of code) (raw):
/**
* @file hash.cpp
*
* @copyright Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
#include <string>
#include <cstring>
#include <errors/user_exception.h>
#include "hash.h"
#include "hasher.h"
#include "hexstring_convert.h"
namespace archive_diff::hashing
{
hash::hash(algorithm algorithm, io::reader &reader) : m_algorithm(algorithm)
{
hashing::hasher hasher{hashing::algorithm::sha256};
auto remaining = reader.size();
uint64_t offset{};
const size_t MAX_READ_CHUNK = static_cast<size_t>(64 * 1024);
std::vector<char> read_buffer;
while (remaining)
{
auto to_read = std::min<size_t>(MAX_READ_CHUNK, remaining);
read_buffer.reserve(to_read);
reader.read(offset, std::span<char>{read_buffer.data(), to_read});
hasher.hash_data(std::string_view{read_buffer.data(), to_read});
offset += to_read;
remaining -= to_read;
}
m_hash_data = hasher.get_hash_binary();
}
void hash::read(io::sequential::reader& reader)
{
reader.read_uint32_t(reinterpret_cast<uint32_t *>(&m_algorithm));
size_t hash_data_bytes = get_byte_count_for_algorithm(m_algorithm);
m_hash_data.resize(hash_data_bytes);
memset(m_hash_data.data(), 0, hash_data_bytes);
reader.read(std::span{m_hash_data.data(), hash_data_bytes});
}
void hash::write(io::sequential::writer& writer) const
{
writer.write_uint32_t(static_cast<uint32_t>(m_algorithm));
writer.write(std::string_view{m_hash_data.data(), m_hash_data.size()});
}
std::string hash::get_type_string() const
{
switch (m_algorithm)
{
case algorithm::md5:
return std::string("Md5");
case algorithm::sha256:
return std::string("Sha256");
default:
std::string msg =
"hash::get_type_string(): Unexpected hash type: " + std::to_string(static_cast<int>(m_algorithm));
throw errors::user_exception(errors::error_code::diff_bad_hash_type, msg);
}
}
std::string hash::get_data_string() const
{
return hashing::data_to_hexstring(m_hash_data.data(), m_hash_data.size());
}
void hash::verify_hashes_match(std::string_view actual, std::string_view expected)
{
#ifndef DISABLE_ALL_HASHING
if (actual.size() != expected.size())
{
std::string msg = "hash::verify_hashes_match(): Hash size mismatch. Actual: " + std::to_string(actual.size())
+ ", Expected: " + std::to_string(expected.size());
throw errors::user_exception(errors::error_code::diff_verify_hash_failure, msg);
}
if (memcmp(actual.data(), expected.data(), expected.size()))
{
std::string msg = "Actual and expected buffers mismatch.";
msg += " Actual: " + data_to_hexstring(actual.data(), actual.size());
msg += " Expected: " + data_to_hexstring(expected.data(), expected.size());
throw errors::user_exception(errors::error_code::diff_verify_hash_failure, msg);
}
#endif
}
void hash::verify_data_against_hash(std::string_view data, const hash &hash)
{
hashing::hasher hasher(hashing::algorithm::sha256);
hasher.hash_data(data.data(), data.size());
auto calculated_hash = hasher.get_hash_binary();
std::string_view actual(reinterpret_cast<char *>(calculated_hash.data()), calculated_hash.size());
std::string_view expected(hash.m_hash_data.data(), hash.m_hash_data.size());
verify_hashes_match(actual, expected);
}
} // namespace archive_diff::hashing