diff --git a/CMakeLists.txt b/CMakeLists.txt index b500302..74041e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,9 @@ option(MD5_ENABLE_BENCHMARK "Enable benchmark of the md5sum library." ON) set(CMAKE_CXX_STANDARD 20) add_compile_options(-Wall -Wextra) -add_library(md5sum STATIC src/core.cc src/wrapper.cc) +add_library(md5sum STATIC src/impl/core.cc src/impl/wrapper.cc) target_compile_options(md5sum PRIVATE -fno-rtti -fno-exceptions) -target_include_directories(md5sum INTERFACE src/) +target_include_directories(md5sum PUBLIC src/) include(third_party/ThirdParty.cmake) add_library(md5sum::md5sum ALIAS md5sum) diff --git a/src/core.cc b/src/core.cc deleted file mode 100644 index f22ca04..0000000 --- a/src/core.cc +++ /dev/null @@ -1,114 +0,0 @@ -#include - -#include "md5.h" -#include "md5_math.h" - -#define RA A, B, C, D -#define RB D, A, B, C -#define RC C, D, A, B -#define RD B, C, D, A - -#define F(x, y, z) (z ^ (x & (y ^ z))) -#define G(x, y, z) (y ^ (z & (x ^ y))) -#define H(x, y, z) (x ^ y ^ z) -#define I(x, y, z) (y ^ (x | ~z)) - -#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) - -#define STEP(i, f, a, b, c, d) \ - do { \ - a += f(b, c, d) + block[K(i)] + T(i); \ - CYCLIC(a, S(i)); \ - a += b; \ - } while (0) - -#define FF(i, ...) STEP((0x00 | i), F, __VA_ARGS__) -#define GG(i, ...) STEP((0x10 | i), G, __VA_ARGS__) -#define HH(i, ...) STEP((0x20 | i), H, __VA_ARGS__) -#define II(i, ...) STEP((0x30 | i), I, __VA_ARGS__) - -#define MD5_ROUND(OP) \ - OP(0x0, RA); OP(0x1, RB); OP(0x2, RC); OP(0x3, RD); \ - OP(0x4, RA); OP(0x5, RB); OP(0x6, RC); OP(0x7, RD); \ - OP(0x8, RA); OP(0x9, RB); OP(0xa, RC); OP(0xb, RD); \ - OP(0xc, RA); OP(0xd, RB); OP(0xe, RC); OP(0xf, RD); - -namespace md5 { - -static const unsigned char Padding[64] {0x80, /* 0x00, ... */}; - -consteval int K(int index) { // index -> [0, 64) - const int step[] = {1, 5, 3, 7}; - const int begin[] = {0, 1, 5, 0}; - return (begin[index >> 4] + step[index >> 4] * index) & 0b1111; -} - -consteval int S(int index) { // index -> [0, 64) - const int map[][4] = { - {7, 12, 17, 22}, - {5, 9, 14, 20}, - {4, 11, 16, 23}, - {6, 10, 15, 21}, - }; - return map[index >> 4][index & 0b11]; -} - -consteval uint32_t T(int index) { // index -> [0, 64) - auto val = math::sin(index + 1); - return static_cast(std::abs(val) * 0x100000000); -} - -const void* MD5::UpdateImpl(const void *data, uint64_t len) { - auto *block = static_cast(data); - auto *limit = block + ((len &= ~0b111111ULL) >> 2); - auto A = ctx_.A; - auto B = ctx_.B; - auto C = ctx_.C; - auto D = ctx_.D; - - while (block < limit) { - auto A_ = A; - auto B_ = B; - auto C_ = C; - auto D_ = D; - MD5_ROUND(FF) - MD5_ROUND(GG) - MD5_ROUND(HH) - MD5_ROUND(II) - A += A_; - B += B_; - C += C_; - D += D_; - block += 16; // move to next block - } - - ctx_.A = A; - ctx_.B = B; - ctx_.C = C; - ctx_.D = D; - ctx_.size += len; - return static_cast(limit); -} - -void MD5::FinalImpl(const void *data, uint64_t len) { - if (len >= 120) { // len -> [64 + 56, INF) - data = UpdateImpl(data, len); - len &= 0b111111; // len -> [0, 64) - } - - unsigned char buffer[128]; // 2 blocks - std::memcpy(buffer, data, len); - uint64_t total = (ctx_.size + len) << 3; // total number in bit - - if (len < 56) { // len -> [0, 56) - std::memcpy(buffer + len, Padding, 56 - len); - std::memcpy(buffer + 56, &total, 8); - UpdateImpl(buffer, 64); // update 1 block - } else { // len -> [56, 64 + 56) - std::memcpy(buffer + len, Padding, 120 - len); - std::memcpy(buffer + 120, &total, 8); - UpdateImpl(buffer, 128); // update 2 blocks - } -} - -} // namespace md5 diff --git a/src/impl/algorithm.inc b/src/impl/algorithm.inc new file mode 100644 index 0000000..f099f28 --- /dev/null +++ b/src/impl/algorithm.inc @@ -0,0 +1,62 @@ +#pragma once + +#define MD5_R1 A, B, C, D +#define MD5_R2 D, A, B, C +#define MD5_R3 C, D, A, B +#define MD5_R4 B, C, D, A + +#define MD5_F(x, y, z) (z ^ (x & (y ^ z))) +#define MD5_G(x, y, z) (y ^ (z & (x ^ y))) +#define MD5_H(x, y, z) (x ^ y ^ z) +#define MD5_I(x, y, z) (y ^ (x | ~z)) + +#define MD5_CYC(w, s) (w = (w << s) | (w >> (32 - s))) + +#define MD5_STEP(i, f, a, b, c, d) \ + do { \ + a += f(b, c, d) + MD5_DATA(i) + MD5_T(i); \ + MD5_CYC(a, MD5_S(i)); \ + a += b; \ + } while (0) + +#define MD5_FF(i, ...) MD5_STEP((0x00 | i), MD5_F, __VA_ARGS__) +#define MD5_GG(i, ...) MD5_STEP((0x10 | i), MD5_G, __VA_ARGS__) +#define MD5_HH(i, ...) MD5_STEP((0x20 | i), MD5_H, __VA_ARGS__) +#define MD5_II(i, ...) MD5_STEP((0x30 | i), MD5_I, __VA_ARGS__) + +#define MD5_ROUND(OP) \ + OP(0x0, MD5_R1); OP(0x1, MD5_R2); OP(0x2, MD5_R3); OP(0x3, MD5_R4); \ + OP(0x4, MD5_R1); OP(0x5, MD5_R2); OP(0x6, MD5_R3); OP(0x7, MD5_R4); \ + OP(0x8, MD5_R1); OP(0x9, MD5_R2); OP(0xa, MD5_R3); OP(0xb, MD5_R4); \ + OP(0xc, MD5_R1); OP(0xd, MD5_R2); OP(0xe, MD5_R3); OP(0xf, MD5_R4); + +#define MD5_UPDATE \ + MD5_ROUND(MD5_FF) MD5_ROUND(MD5_GG) MD5_ROUND(MD5_HH) MD5_ROUND(MD5_II) + +namespace md5::value { + +/// MD5 data block index, input between 0 and 63. +consteval int K(int i) { + const int step[] = {1, 5, 3, 7}; + const int begin[] = {0, 1, 5, 0}; + return (begin[i >> 4] + step[i >> 4] * i) & 0b1111; +} + +/// MD5 circular shift times, input between 0 and 63. +consteval int S(int i) { + const int shift[][4] = { + {7, 12, 17, 22}, + {5, 9, 14, 20}, + {4, 11, 16, 23}, + {6, 10, 15, 21}, + }; + return shift[i >> 4][i & 0b11]; +} + +/// MD5 T-table constant, input between 0 and 63. +consteval uint32_t T(int i) { + auto val = math::sin(i + 1); + return static_cast(std::abs(val) * 0x100000000); +} + +} // namespace md5::value diff --git a/src/impl/core.cc b/src/impl/core.cc new file mode 100644 index 0000000..ce2b7f2 --- /dev/null +++ b/src/impl/core.cc @@ -0,0 +1,66 @@ +#include + +#include "md5.h" +#include "sine.inc" +#include "algorithm.inc" + +#define MD5_S(i) value::S(i) +#define MD5_T(i) value::T(i) +#define MD5_DATA(i) block[value::K(i)] + +namespace md5 { + +static constexpr unsigned char Padding[64] { 0x80, /* 0x00, ... */ }; + +const void* MD5::UpdateImpl(const void *data, uint64_t len) { + auto *block = static_cast(data); + auto *limit = block + ((len &= ~0b111111ULL) >> 2); + + auto A = ctx_.A; + auto B = ctx_.B; + auto C = ctx_.C; + auto D = ctx_.D; + + while (block < limit) { + auto A_ = A; + auto B_ = B; + auto C_ = C; + auto D_ = D; + MD5_UPDATE + A += A_; + B += B_; + C += C_; + D += D_; + block += 16; // move to next block + } + + ctx_.A = A; + ctx_.B = B; + ctx_.C = C; + ctx_.D = D; + ctx_.size += len; + return static_cast(limit); +} + +void MD5::FinalImpl(const void *data, uint64_t len) { + if (len >= 120) { // len -> [64 + 56, INF) + data = UpdateImpl(data, len); + len &= 0b111111; // len -> [0, 64) + } + + unsigned char buffer[128]; // 2 blocks + std::memcpy(buffer, data, len); + uint64_t total = (ctx_.size + len) << 3; // total number in bit + + if (len < 56) { // len -> [0, 56) + std::memcpy(buffer + len, Padding, 56 - len); + std::memcpy(buffer + 56, &total, 8); + UpdateImpl(buffer, 64); // update 1 block + } else { // len -> [56, 64 + 56) + std::memcpy(buffer + len, Padding, 120 - len); + std::memcpy(buffer + 120, &total, 8); + UpdateImpl(buffer, 128); // update 2 blocks + } +} + +} // namespace md5 diff --git a/src/md5.inc b/src/impl/inline.inc similarity index 100% rename from src/md5.inc rename to src/impl/inline.inc diff --git a/src/md5_math.h b/src/impl/sine.inc similarity index 94% rename from src/md5_math.h rename to src/impl/sine.inc index 7506ad8..77e76cb 100644 --- a/src/md5_math.h +++ b/src/impl/sine.inc @@ -4,7 +4,7 @@ namespace md5::math { -constexpr double PI = 3.141592653589793238462643L; +constexpr double PI = 3.14159265358979323846264338327950; consteval double pow(double x, int n) { double res = 1; diff --git a/src/wrapper.cc b/src/impl/wrapper.cc similarity index 85% rename from src/wrapper.cc rename to src/impl/wrapper.cc index bad6c30..dc9a3c0 100644 --- a/src/wrapper.cc +++ b/src/impl/wrapper.cc @@ -4,6 +4,22 @@ namespace md5 { +static constexpr char HexTable[] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f', +}; + +std::string MD5::Digest() const { + std::string result {}; + result.resize(32); + auto *ptr = reinterpret_cast(&ctx_); + for (int i = 0; i < 32; ++ptr) { + result[i++] = HexTable[*ptr >> 4]; + result[i++] = HexTable[*ptr & 0b1111]; + } + return result; +} + MD5& MD5::Update(const void *data, uint64_t len) { if (buffer_size_ != 0) { if (buffer_size_ + len < 64) { // buffer not filled @@ -22,7 +38,6 @@ MD5& MD5::Update(const void *data, uint64_t len) { data = UpdateImpl(data, len); len &= 0b111111; // len -> [0, 64) - if (len != 0) { std::memcpy(buffer_, data, len); // save remain data into buffer buffer_size_ = len; @@ -30,20 +45,4 @@ MD5& MD5::Update(const void *data, uint64_t len) { return *this; } -static constexpr char HexTable[] = { - '0','1','2','3','4','5','6','7', - '8','9','a','b','c','d','e','f', -}; - -std::string MD5::Digest() const { - std::string result {}; - result.resize(32); - auto *src = reinterpret_cast(&ctx_); - for (int i = 0; i < 32; ++src) { - result[i++] = HexTable[*src >> 4]; - result[i++] = HexTable[*src & 0b1111]; - } - return result; -} - } // namespace md5 diff --git a/src/md5.h b/src/md5.h index 3f89d09..a7d682c 100644 --- a/src/md5.h +++ b/src/md5.h @@ -65,4 +65,4 @@ private: } // namespace md5 -#include "md5.inc" +#include "impl/inline.inc"