diff --git a/src/impl/constexpr.inc b/src/impl/constexpr.inc index 7ff5f1a..d2a720f 100644 --- a/src/impl/constexpr.inc +++ b/src/impl/constexpr.inc @@ -2,23 +2,25 @@ namespace md5::impl { -struct md5_ctx_ce { +struct md5_ce_ctx { uint32_t A = value::kA; uint32_t B = value::kB; uint32_t C = value::kC; uint32_t D = value::kD; +}; +struct md5_ce { const char *data; uint64_t data_len, padded_len; - constexpr md5_ctx_ce(const char *data, const uint64_t len) + constexpr md5_ce(const char *data, const uint64_t len) : data(data), data_len(len), padded_len((len + 64 + 8) & ~0b111111ULL) {} }; using Block = std::array; // single md5 block with 64 bytes /// Get the data and padding byte of the specified index. -constexpr uint8_t GetByte(const md5_ctx_ce *ctx, const uint64_t index) { +constexpr uint8_t GetByte(const md5_ce *ctx, const uint64_t index) { if (index < ctx->data_len) // message data return ctx->data[index]; if (index == ctx->data_len) // padding flag @@ -30,7 +32,7 @@ constexpr uint8_t GetByte(const md5_ctx_ce *ctx, const uint64_t index) { } /// Get the MD5 block content at the specified index. -constexpr Block GetBlock(const md5_ctx_ce *ctx, const uint64_t index) { +constexpr Block GetBlock(const md5_ce *ctx, const uint64_t index) { Block block {}; for (int i = 0; i < 16; ++i) { const auto offset = index + i * 4; @@ -54,15 +56,32 @@ constexpr std::array DigestCE(const std::array &ctx) { return result; } -constexpr std::array MD5::HashCE(const char *data, uint64_t len) { - md5_ctx_ce ctx(data, len); - for (uint32_t index = 0; index < ctx.padded_len; index += 64) { - auto block = GetBlock(&ctx, index); - auto A = ctx.A; - auto B = ctx.B; - auto C = ctx.C; - auto D = ctx.D; - MD5_UPDATE +constexpr uint32_t Calc(const md5_ce_ctx &ctx, const int i) { + if (i < 0x10) + return ctx.D ^ (ctx.B & (ctx.C ^ ctx.D)); + if (i < 0x20) + return ctx.C ^ (ctx.D & (ctx.B ^ ctx.C)); + if (i < 0x30) + return ctx.B ^ ctx.C ^ ctx.D; + return ctx.C ^ (ctx.B | ~ctx.D); +} + +constexpr md5_ce_ctx Round(const Block &block, md5_ce_ctx ctx) { + for (int i = 0; i < 64; ++i) { + const auto a = ctx.A + Calc(ctx, i) + block[K(i)] + T(i); + ctx.A = ctx.D; + ctx.D = ctx.C; + ctx.C = ctx.B; + ctx.B += a << S(i) | a >> (32 - S(i)); + } + return ctx; +} + +constexpr std::array MD5::HashCE(const char *data, const uint64_t len) { + md5_ce_ctx ctx; + const md5_ce md5(data, len); + for (uint32_t index = 0; index < md5.padded_len; index += 64) { + const auto [A, B, C, D] = Round(GetBlock(&md5, index), ctx); ctx.A += A; ctx.B += B; ctx.C += C; @@ -71,6 +90,6 @@ constexpr std::array MD5::HashCE(const char *data, uint64_t len) { return DigestCE({ctx.A, ctx.B, ctx.C, ctx.D}); } -static_assert(MD5::HashCE("")[0] == 'd', "invalid constexpr"); +static_assert(MD5::HashCE("")[0] == 'd'); } // namespace md5::impl