/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifdef SUPPORT_MD5
static const uint32_t S[64] = {
0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL,
0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL,
0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL,
0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL,
0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL,
0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL,
0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL,
0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL
};
static uint32_t rol32(uint32_t n, int k) { return (n << k) | (n >> (32 - k)); } /* k != 0, 32 */
static void
process_block(uint32_t h[4], const uint8_t *x, uint32_t w[16])
{
#define F(x, y, z) (z ^ (x & (y ^ z)))
#define G(x, y, z) (y ^ (z & (y ^ x)))
#define H(x, y, z) (x ^ y ^ z)
#define I(x, y, z) (y ^ (x | ~z))
#define FF(a, b, c, d, w, s, t) (a += F(b, c, d) + w + t, a = rol32(a, s) + b)
#define GG(a, b, c, d, w, s, t) (a += G(b, c, d) + w + t, a = rol32(a, s) + b)
#define HH(a, b, c, d, w, s, t) (a += H(b, c, d) + w + t, a = rol32(a, s) + b)
#define II(a, b, c, d, w, s, t) (a += I(b, c, d) + w + t, a = rol32(a, s) + b)
register uint32_t a, b, c, d;
unsigned i;
for (i = 0; i < 16U; i++) {
w[i] = (uint32_t)x[4U * i + 0U] << 0;
w[i] |= (uint32_t)x[4U * i + 1U] << 8;
w[i] |= (uint32_t)x[4U * i + 2U] << 16;
w[i] |= (uint32_t)x[4U * i + 3U] << 24;
}
a = h[0];
b = h[1];
c = h[2];
d = h[3];
i = 0U;
while (i < 16U) {
FF(a, b, c, d, w[i], 7, S[i]), i++;
FF(d, a, b, c, w[i], 12, S[i]), i++;
FF(c, d, a, b, w[i], 17, S[i]), i++;
FF(b, c, d, a, w[i], 22, S[i]), i++;
}
while (i < 32U) {
GG(a, b, c, d, w[(5 * i + 1) % 16], 5, S[i]), i++;
GG(d, a, b, c, w[(5 * i + 1) % 16], 9, S[i]), i++;
GG(c, d, a, b, w[(5 * i + 1) % 16], 14, S[i]), i++;
GG(b, c, d, a, w[(5 * i + 1) % 16], 20, S[i]), i++;
}
while (i < 48U) {
HH(a, b, c, d, w[(3 * i + 5) % 16], 4, S[i]), i++;
HH(d, a, b, c, w[(3 * i + 5) % 16], 11, S[i]), i++;
HH(c, d, a, b, w[(3 * i + 5) % 16], 16, S[i]), i++;
HH(b, c, d, a, w[(3 * i + 5) % 16], 23, S[i]), i++;
}
while (i < 64U) {
II(a, b, c, d, w[7 * i % 16], 6, S[i]), i++;
II(d, a, b, c, w[7 * i % 16], 10, S[i]), i++;
II(c, d, a, b, w[7 * i % 16], 15, S[i]), i++;
II(b, c, d, a, w[7 * i % 16], 21, S[i]), i++;
}
h[0] += a;
h[1] += b;
h[2] += c;
h[3] += d;
}
LIBHASHSUM_1_NONNULL_
static size_t
process(struct libhashsum_hasher *this, const void *data, size_t bytes)
{
const uint8_t *m = data;
size_t off = 0;
for (; bytes - off >= 64U; off += 64U)
process_block(this->state.md5.h.h32, &m[off], this->state.md5.w);
this->state.md5.count += off;
return off;
}
LIBHASHSUM_1_NONNULL_
static int
finalise_common(struct libhashsum_hasher *this, uint8_t *m, size_t bytes, unsigned extra_bits)
{
uint8_t mask;
unsigned i;
register uint32_t hi;
if (extra_bits > 7U) {
errno = EINVAL;
return -1;
}
this->state.md5.count += bytes;
this->state.md5.count *= 8U;
this->state.md5.count += (size_t)extra_bits;
if (extra_bits)
m[bytes] = libhashsum_reverse_byte__(m[bytes]);
mask = (uint8_t)(1U << (7U - extra_bits));
m[bytes] |= mask;
m[bytes] &= (uint8_t)~(mask - 1U); /* keep high bits (original value was reversed) */
bytes++;
if (bytes > 56U) {
memset(&m[bytes], 0, 64U - bytes);
process_block(this->state.md5.h.h32, m, this->state.md5.w);
bytes = 0;
}
memset(&m[bytes], 0, 56U - bytes);
m[56] = (uint8_t)(this->state.md5.count >> 0);
m[57] = (uint8_t)(this->state.md5.count >> 8);
m[58] = (uint8_t)(this->state.md5.count >> 16);
m[59] = (uint8_t)(this->state.md5.count >> 24);
m[60] = (uint8_t)(this->state.md5.count >> 32);
m[61] = (uint8_t)(this->state.md5.count >> 40);
m[62] = (uint8_t)(this->state.md5.count >> 48);
m[63] = (uint8_t)(this->state.md5.count >> 56);
process_block(this->state.md5.h.h32, m, this->state.md5.w);
memset(this->state.md5.m, 0, sizeof(this->state.md5.m));
memset(this->state.md5.w, 0, sizeof(this->state.md5.w));
this->state.md5.count = 0;
for (i = 0; i < 4U; i++) {
hi = this->state.md5.h.h32[i];
this->state.md5.h.sum[i * 4U + 0U] = (uint8_t)(hi >> 0);
this->state.md5.h.sum[i * 4U + 1U] = (uint8_t)(hi >> 8);
this->state.md5.h.sum[i * 4U + 2U] = (uint8_t)(hi >> 16);
this->state.md5.h.sum[i * 4U + 3U] = (uint8_t)(hi >> 24);
}
this->hash_output = this->state.md5.h.sum;
return 0;
}
LIBHASHSUM_1_NONNULL_
static int
finalise_const(struct libhashsum_hasher *this, const void *data, size_t bytes, unsigned extra_bits)
{
const uint8_t *m = data;
size_t r;
r = process(this, m, bytes);
m = &m[r];
bytes -= r;
this->state.md5.m[bytes] = 0;
memcpy(this->state.md5.m, m, bytes + (size_t)(extra_bits > 0U));
return finalise_common(this, this->state.md5.m, bytes, extra_bits);
}
LIBHASHSUM_1_NONNULL_
static int
finalise(struct libhashsum_hasher *this, void *data, size_t bytes, unsigned extra_bits, size_t size)
{
uint8_t *m = data;
size_t r;
r = process(this, m, bytes);
m = &m[r];
bytes -= r;
size -= r;
if (size < 64U) {
memcpy(this->state.md5.m, m, bytes + (size_t)(extra_bits > 0U));
m = this->state.md5.m;
}
return finalise_common(this, m, bytes, extra_bits);
}
int
libhashsum_init_md5_hasher(struct libhashsum_hasher *this)
{
this->algorithm = LIBHASHSUM_MD5;
this->input_block_size = 64U;
this->hash_size = sizeof(this->state.md5.h.sum);
this->hash_output = NULL;
this->supports_non_whole_bytes = 1;
this->process = &process;
this->finalise_const = &finalise_const;
this->finalise = &finalise;
memset(&this->state.md5, 0, sizeof(this->state.md5));
this->state.md5.h.h32[0] = UINT32_C(0x67452301);
this->state.md5.h.h32[1] = UINT32_C(0xefcdab89);
this->state.md5.h.h32[2] = UINT32_C(0x98badcfe);
this->state.md5.h.h32[3] = UINT32_C(0x10325476);
return 0;
}
#else
int
libhashsum_init_md5_hasher(struct libhashsum_hasher *this)
{
(void) this;
errno = ENOSYS;
return -1;
}
#endif