Skip to content

Instantly share code, notes, and snippets.

@Loki-Astari
Last active September 19, 2022 19:57
Show Gist options
  • Save Loki-Astari/264f47bdc2685bf8e220a24607c5f0e8 to your computer and use it in GitHub Desktop.
Save Loki-Astari/264f47bdc2685bf8e220a24607c5f0e8 to your computer and use it in GitHub Desktop.
#include "md5.h"
#include <iostream>
#include <cmath>
#include <bit>
const std::array<std::uint32_t, 64> pstl::cryptography::hashing::md5::s_array = {
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
};
constexpr std::array<std::uint32_t, 64> pstl::cryptography::hashing::md5::make_k_array()
{
std::array<std::uint32_t, 64> output;
for (int i = 0; i < output.max_size(); i++)
output[i] = std::floor(0x100000000 * std::abs(std::sin(i + 1)));
return output;
}
const std::array<std::uint32_t, 64> pstl::cryptography::hashing::md5::k_array = pstl::cryptography::hashing::md5::make_k_array();
// Based on RFC 1321
// https://www.ietf.org/rfc/rfc1321.txt
// 1992
pstl::cryptography::hashing::Hash const& pstl::cryptography::hashing::md5::digest(std::string str)
{
add(str);
return hash();
}
pstl::cryptography::hashing::Hash const& pstl::cryptography::hashing::md5::hash()
{
if (complete) {
return result;
}
complete = true;
buffer.emplace_back(0x80);
if (buffer.size() >= 56) {
buffer.resize(64, 0x0);
addBuffer();
buffer.clear();
}
buffer.resize(64, 0x0);
std::uint32_t* placeForMsgSize = reinterpret_cast<std::uint32_t*>(&buffer[56]);
(*placeForMsgSize) = msgSize;
addBuffer();
for (auto& var : result)
{
var = (((var) >> 24) | (((var) & 0x00FF0000) >> 8) | (((var) & 0x0000FF00) << 8) | ((var) << 24));
}
return result;
}
void pstl::cryptography::hashing::md5::add(std::string const& str)
{
std::uint8_t const* begin = reinterpret_cast<std::uint8_t const*>(str.data());
std::uint8_t const* end = begin + str.size();
add(begin, end);
}
void pstl::cryptography::hashing::md5::add(std::uint8_t const* begin, std::uint8_t const* end)
{
if (complete) {
throw std::runtime_error("Can not add Data to a completed Hash");
}
std::uint32_t size = std::distance(begin, end);
msgSize += (size * 8);
if (buffer.size() != 0) {
std::uint32_t needed = 64 - buffer.size();
std::uint32_t add = std::min(needed, size);
buffer.insert(std::end(buffer), begin, begin + add);
if (buffer.size() != 64) {
return;
}
addBuffer();
buffer.clear();
begin += add;
}
for (std::uint8_t const* endChunk = begin + 64; endChunk < end; begin = endChunk, endChunk += 64) {
addChunk(reinterpret_cast<std::uint32_t const*>(&(*begin)));
}
buffer.insert(std::end(buffer), begin, end);
}
void pstl::cryptography::hashing::md5::addBuffer()
{
addChunk(reinterpret_cast<std::uint32_t*>(buffer.data()));
}
void pstl::cryptography::hashing::md5::addChunk(std::uint32_t const* data)
{
std::uint32_t A = result[0];
std::uint32_t B = result[1];
std::uint32_t C = result[2];
std::uint32_t D = result[3];
for (int j = 0; j < 64; j++)
{
std::uint32_t F, g;
if (j < 16)
{
F = (B & C) | ((~B) & D);
g = j;
}
else if (j < 32)
{
F = (B & D) | (C & (~D));
g = (5 * j + 1) % 16;
}
else if (j < 48)
{
F = B ^ C ^ D;
g = (3 * j + 5) % 16;
}
else if (j < 64)
{
F = C ^ (B | (~D));
g = (7 * j) % 16;
}
std::uint32_t token = data[g];
F = F + A + token + k_array[j];
A = D;
D = C;
C = B;
B = B + std::rotl(F, s_array[j]);
}
result[0] += A;
result[1] += B;
result[2] += C;
result[3] += D;
}
#include <string>
#include <array>
#include <vector>
#include <ostream>
#include <cstdlib>
#include <iomanip>
namespace pstl::cryptography::hashing
{
struct Hash: std::array<std::uint32_t,4>
{
Hash(std::uint32_t a1, std::uint32_t a2, std::uint32_t a3, std::uint32_t a4)
: array{a1, a2, a3, a4}
{}
Hash(char const* str)
{
std::string chunk;
for (int loop = 0; loop < 4; ++loop) {
chunk = std::string_view(str + loop * 8, 8);
(*this)[loop] = std::stoul(chunk.data(), nullptr, 16);
}
}
friend std::ostream& operator<<(std::ostream& str, Hash const& data)
{
for (auto var : data) {
str << std::setw(8) << std::setfill('0') << std::hex << var;
}
return str;
}
};
class md5
{
public:
md5()
: result{0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476}
, complete(false)
, msgSize(0)
{
buffer.reserve(64);
}
md5(md5 const&) = delete;
md5& operator=(md5 const&) = delete;
void reset()
{
result = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
complete = false;
msgSize = 0;
buffer.clear();
}
// Original Interface.
Hash const& digest(std::string str);
// New Interface
Hash const& hash();
void add(std::string const& str);
void add(std::uint8_t const* begin, std::uint8_t const* end);
private:
constexpr static std::array<std::uint32_t, 64> make_k_array();
static const std::array<std::uint32_t, 64> k_array;
static const std::array<std::uint32_t, 64> s_array;
void addChunk(std::uint32_t const* data);
void addBuffer();
Hash result;
bool complete;
std::vector<uint8_t> buffer;
std::uint32_t msgSize = 0;
};
}
#include "md5.h"
#include <iostream>
using pstl::cryptography::hashing::md5;
using pstl::cryptography::hashing::Hash;
void test2(std::string const& input1, std::string const& input2, Hash const& expected)
{
md5 hash;
hash.add(input1);
hash.add(input2);
Hash actual = hash.hash();
std::cout << actual;
if (actual != expected) {
std::cout << " FAIL: (" << expected << ")";
}
std::cout << "\n";
}
void test(std::string const& input1, Hash const& expected)
{
md5 hash;
Hash actual = hash.digest(input1);
std::cout << actual;
if (actual != expected) {
std::cout << " FAIL: (" << expected << ")";
}
std::cout << "\n";
}
int main()
{
test("", "d41d8cd98f00b204e9800998ecf8427e");
test("a", "0cc175b9c0f1b6a831c399e269772661");
test("abc", "900150983cd24fb0d6963f7d28e17f72");
test("message digest", "f96b697d7cb7938d525a2f31aaf161d0");
test("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b");
test("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "d174ab98d277d9f5a5611c2c9f419d9f");
test("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a");
test2("", "", "d41d8cd98f00b204e9800998ecf8427e");
test2("a", "", "0cc175b9c0f1b6a831c399e269772661");
test2("ab", "c", "900150983cd24fb0d6963f7d28e17f72");
test2("message di", "gest", "f96b697d7cb7938d525a2f31aaf161d0");
test2("abcdefghijklmnopqrstuvwx", "yz", "c3fcd3d76192e4007dfb496cca67e13b");
test2("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "", "d174ab98d277d9f5a5611c2c9f419d9f");
test2("12", "345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment