// Derived from the "RSA Data Security, Inc. MD5 Message-Digest Algorithm": // src: https://github.com/freebsd/freebsd-src/blob/release/14.2.0/sys/kern/md5c.c #include #include #include #include "md5.h" #if defined(_MSC_VER) #define bswap_u16 _byteswap_ushort #define bswap_u32 _byteswap_ulong #define bswap_u64 _byteswap_uint64 #else #define bswap_u16 __builtin_bswap16 #define bswap_u32 __builtin_bswap32 #define bswap_u64 __builtin_bswap64 #endif template void Encode(uint8_t* output, const T input) requires(std::is_integral_v && (sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8)) { if constexpr (std::endian::native == std::endian::little || sizeof(T) == 1) { memcpy(output, &input, sizeof(T)); // ReSharper disable once CppDFAUnreachableCode } else if constexpr (sizeof(T) == 2) { *reinterpret_cast(output) = bswap_u16(input); } else if constexpr (sizeof(T) == 4) { *reinterpret_cast(output) = bswap_u32(input); } else if constexpr (sizeof(T) == 8) { *reinterpret_cast(output) = bswap_u64(input); } } template void Decode(T* output, const uint8_t* input) requires(std::is_integral_v && (sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8)) { if constexpr (std::endian::native == std::endian::little) { memcpy(output, input, sizeof(T)); // ReSharper disable once CppDFAUnreachableCode } else if constexpr (sizeof(T) == 2) { *output = bswap_u16(*reinterpret_cast(input)); } else if constexpr (sizeof(T) == 4) { *output = bswap_u32(*reinterpret_cast(input)); } else if constexpr (sizeof(T) == 8) { *output = bswap_u64(*reinterpret_cast(input)); } } inline void Encode(unsigned char* output, const uint32_t* input, const unsigned int len) { if constexpr (std::endian::native == std::endian::little) { memcpy(output, input, len); } else { // ReSharper disable once CppDFAUnreachableCode for (unsigned int i = 0; i < len; i += 4, output += 4) { Encode(output, input[i]); } } } inline void Decode(uint32_t* output, const unsigned char* input, const unsigned int len) { // ReSharper disable once CppDFAUnreachableCode if constexpr (std::endian::native == std::endian::little) { memcpy(output, input, len); } else { for (unsigned int i = 0; i < len; i += 4, ++output) { Decode(output, &input[i]); } } } void MD5Transform(uint32_t state[4], const unsigned char block[64]); static unsigned char PADDING[64] = {0x80}; /* F, G, H and I are basic MD5 functions. */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits. */ #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. * Rotation is separate from addition to prevent recomputation. */ #define FF(a, b, c, d, x, s, ac) \ { \ (a) += F((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = ROTATE_LEFT((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) \ { \ (a) += G((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = ROTATE_LEFT((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) \ { \ (a) += H((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = ROTATE_LEFT((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) \ { \ (a) += I((b), (c), (d)) + (x) + (uint32_t)(ac); \ (a) = ROTATE_LEFT((a), (s)); \ (a) += (b); \ } /* * MD5 block update operation. Continues an MD5 message-digest * operation, processing another message block, and updating the * context. */ void md5_update(MD5_CTX* ctx, const uint8_t* in, const size_t len) { unsigned int i{0}; const unsigned char* input = in; /* Compute number of bytes mod 64 */ unsigned int index = ctx->count % 64; ctx->count += len; // ReSharper disable once CppTooWideScopeInitStatement const unsigned int partLen = 64 - index; /* Transform as many times as possible. */ if (len >= partLen) { memcpy(&ctx->buffer[index], input, partLen); MD5Transform(ctx->state, ctx->buffer); for (i = partLen; i + 63 < len; i += 64) { MD5Transform(ctx->state, &input[i]); } index = 0; } /* Buffer remaining input */ memcpy(&ctx->buffer[index], &input[i], len - i); } /* * MD5 padding. Adds padding followed by original length. */ static void MD5Pad(MD5_CTX* context) { unsigned char bits[8]; /* Save number of bits */ Encode(bits, context->count << 3); /* Pad out to 56 mod 64. */ const unsigned int index = context->count % 64; const unsigned int padLen = index < 56 ? 56 - index : 120 - index; md5_update(context, PADDING, padLen); /* Append length (before padding) */ md5_update(context, bits, 8); } /* * MD5 finalization. Ends an MD5 message-digest operation, writing * the message digest and zeroizing the context. */ void md5_final(MD5_CTX* ctx, uint8_t* digest) { /* Do padding. */ MD5Pad(ctx); /* Store state in digest */ Encode(digest, ctx->state, MD5_DIGEST_LENGTH); /* Zeroize sensitive information. */ memset(ctx, 0, sizeof(*ctx)); } /* MD5 basic transformation. Transforms state based on block. */ void MD5Transform(uint32_t state[4], const unsigned char block[64]) { uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16]; Decode(x, block, 64); /* Round 1 */ constexpr int S11 = 7; constexpr int S12 = 12; constexpr int S13 = 17; constexpr int S14 = 22; FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ constexpr int S21 = 5; constexpr int S22 = 9; constexpr int S23 = 14; constexpr int S24 = 20; GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ constexpr int S31 = 4; constexpr int S32 = 11; constexpr int S33 = 16; constexpr int S34 = 23; HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ constexpr int S41 = 6; constexpr int S42 = 10; constexpr int S43 = 15; constexpr int S44 = 21; II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; /* Zeroize sensitive information. */ memset(x, 0, sizeof(x)); }