Browse Source

We don't need file hashing yet

default_compile_flags
vector-of-bool 5 years ago
parent
commit
c2b95d3ee6
4 changed files with 0 additions and 289 deletions
  1. +0
    -1
      src/browns/md5.cpp
  2. +0
    -186
      src/browns/md5.hpp
  3. +0
    -30
      src/browns/md5.test.cpp
  4. +0
    -72
      src/browns/output.hpp

+ 0
- 1
src/browns/md5.cpp View File

@@ -1 +0,0 @@
#include "./md5.hpp"

+ 0
- 186
src/browns/md5.hpp View File

@@ -1,186 +0,0 @@
#pragma once

#include <neo/buffer_algorithm.hpp>
#include <neo/const_buffer.hpp>

#include <array>
#include <cassert>
#include <cstddef>

namespace browns {

class md5 {
public:
using digest_type = std::array<std::byte, 16>;

private:
// clang-format off
static constexpr std::array<std::uint32_t, 64> magic_numbers_sierra = {
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
};
static constexpr std::array<std::uint32_t, 64> magic_numbers_kilo = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
};
// clang-format on

std::array<std::uint32_t, 4> _running_digest = {
0x67452301,
0xefcdab89,
0x98badcfe,
0x10325476,
};

constexpr static std::size_t bits_per_block = 512;
constexpr static std::size_t bits_per_byte = 8;
constexpr static std::size_t bytes_per_block = bits_per_block / bits_per_byte;

using chunk_type = std::array<std::byte, bytes_per_block>;

chunk_type _pending_blocks = {};
std::size_t _write_offset = 0;
std::uint64_t _msg_length = 0;

std::size_t _num_pending_blocks = 0;

constexpr void _consume_block() noexcept {
_write_offset = 0;
std::uint32_t alpha = _running_digest[0];
std::uint32_t bravo = _running_digest[1];
std::uint32_t charlie = _running_digest[2];
std::uint32_t delta = _running_digest[3];

const std::uint32_t* data
= static_cast<const std::uint32_t*>(static_cast<const void*>(_pending_blocks.data()));
for (int idx = 0; idx < 64; ++idx) {
std::uint32_t F = 0;
std::uint32_t g = 0;
if (idx < 16) {
F = (bravo & charlie) | ((~bravo) & delta);
g = idx;
} else if (idx < 32) {
F = (delta & bravo) | ((~delta) & charlie);
g = ((5 * idx) + 1) % 16;
} else if (idx < 48) {
F = bravo ^ charlie ^ delta;
g = ((3 * idx) + 5) % 16;
} else {
F = charlie ^ (bravo | (~delta));
g = (7 * idx) % 16;
}
F = F + alpha + magic_numbers_kilo[idx] + data[g];
alpha = delta;
delta = charlie;
charlie = bravo;
bravo = bravo
+ ((F << magic_numbers_sierra[idx]) | (F >> (32 - magic_numbers_sierra[idx])));
}
_running_digest[0] += alpha;
_running_digest[1] += bravo;
_running_digest[2] += charlie;
_running_digest[3] += delta;
}

constexpr static std::byte* _le_copy(std::uint64_t n, std::byte* ptr) noexcept {
auto n_ptr = neo::byte_pointer(&n);
auto n_end = n_ptr + sizeof n;
while (n_ptr != n_end) {
*ptr++ = *n_ptr++;
}
return ptr;
}

public:
constexpr md5() = default;

constexpr void feed(neo::const_buffer buf) noexcept {
feed(buf.data(), buf.data() + buf.size());
}

template <typename Iter, typename Sent>
constexpr void feed(Iter it, const Sent s) noexcept {
using source_val_type = typename std::iterator_traits<Iter>::value_type;
static_assert(std::disjunction_v<std::is_same<source_val_type, char>,
std::is_same<source_val_type, unsigned char>,
std::is_same<source_val_type, signed char>,
std::is_same<source_val_type, std::byte>>,
"Type fed to hash must have be std::byte-sized");
while (it != s) {
auto write_head = std::next(_pending_blocks.begin(), _write_offset);
const auto write_stop = _pending_blocks.end();
while (write_head != write_stop && it != s) {
*write_head++ = static_cast<std::byte>(*it);
++it;
++_write_offset;
_msg_length += 1;
}

if (write_head == write_stop) {
_consume_block();
}
}
}

constexpr void pad() noexcept {
// Length is recored in bits
const std::uint64_t len_nbits = _msg_length * 8;

// Calc how many bytes of padding need to be inserted
std::size_t n_to_next_boundary = bytes_per_block - _write_offset;
if (n_to_next_boundary < sizeof(len_nbits)) {
// We don't have enough room to the next boundary for the message
// length. Go another entire block of padding
n_to_next_boundary += bytes_per_block;
}

// Create an array for padding.
std::array<std::byte, bytes_per_block * 2> pad = {};
// Set the lead bit, as per spec
pad[0] = std::byte{0b1000'0000};

// Calc how many bytes from our pad object we should copy. We need
// to leave room at the end for the message length integer.
std::size_t n_to_copy = n_to_next_boundary - sizeof(_msg_length);

// Feed the padding
feed(neo::const_buffer(pad.data(), n_to_copy));
// Now feed the message length integer
feed(neo::const_buffer(neo::byte_pointer(&len_nbits), sizeof(len_nbits)));

assert(_write_offset == 0);
}

constexpr digest_type digest() const noexcept {
if (_write_offset != 0) {
assert(false && "Requested digest of incomplete md5. Be sure you called pad()!");
std::terminate();
}
digest_type ret = {};
auto data = neo::byte_pointer(_running_digest.data());
auto dest = ret.begin();
auto dest_end = ret.end();
while (dest != dest_end) {
*dest++ = *data++;
}
return ret;
}
};

} // namespace browns

+ 0
- 30
src/browns/md5.test.cpp View File

@@ -1,30 +0,0 @@
#include <browns/md5.hpp>

#include <browns/output.hpp>
#include <neo/as_buffer.hpp>

#include <catch2/catch.hpp>

#include <iostream>

auto md5_hash_str(std::string_view s) {
browns::md5 hash;
hash.feed(neo::as_buffer(s));
hash.pad();
return browns::format_digest(hash.digest());
}

void check_hash(std::string_view str, std::string_view digest) {
INFO("Hashed string: " << str);
CHECK(md5_hash_str(str) == digest);
}

TEST_CASE("Known hashes") {
check_hash("1234abcd1234abcd1234abcd1234abcd", "67aa636d72b967157c363f0acdf7011b");
check_hash("The quick brown fox jumps over the lazy dog", "9e107d9d372bb6826bd81d3542a419d6");
check_hash("", "d41d8cd98f00b204e9800998ecf8427e");
check_hash(
"WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW",
"967ce152b23edc20ebd23b7eba57277c");
check_hash("WWWWWWWWWWWWWWWWWWWWWWWWWWWWW", "9aff577d3248b8889b22f24ee9665c17");
}

+ 0
- 72
src/browns/output.hpp View File

@@ -1,72 +0,0 @@
#pragma once

#include <array>
#include <cstddef>
#include <string>
#include <string_view>

namespace browns {

template <std::size_t N>
std::string format_digest(const std::array<std::byte, N>& dig) {
std::string ret;
ret.resize(N * 2);
for (std::size_t pos = 0; pos < N; ++pos) {
std::byte b = dig[pos];
char& c1 = ret[pos * 2];
char& c2 = ret[pos * 2 + 1];
const char tab[17] = "0123456789abcdef";
auto high = (static_cast<char>(b) & 0xf0) >> 4;
auto low = (static_cast<char>(b) & 0x0f);
c1 = tab[high];
c2 = tab[low];
}
return ret;
}

template <typename Digest>
Digest parse_digest(std::string_view str) {
Digest ret;
std::byte* out_ptr = ret.data();
std::byte* const out_end = out_ptr + ret.size();
auto str_ptr = str.begin();
const auto str_end = str.end();

auto invalid = [&] { //
throw std::runtime_error("Invalid hash digest string: " + std::string(str));
};

auto nibble = [&](char c) -> std::byte {
if (c >= 'A' && c <= 'F') {
c = static_cast<char>(c + ('a' - 'A'));
}
std::byte nib{0};
if (c >= '0' && c <= '9') {
nib = std::byte(c - '0');
} else if (c >= 'a' && c <= 'f') {
nib = std::byte(c - 'a');
} else {
invalid();
}
return nib;
};

// We must have an even number of chars to form full octets
if (str.size() % 2) {
invalid();
}

while (str_ptr != str_end && out_ptr != out_end) {
std::byte high = nibble(*str_ptr++);
std::byte low = nibble(*str_ptr++);
std::byte octet = (high << 4) | low;
*out_ptr++ = octet;
}

if (str_ptr != str_end || out_ptr != out_end) {
invalid();
}
return ret;
}

} // namespace browns

Loading…
Cancel
Save