/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifdef SUPPORT_MD4
#define LETO32(X)\
(((uint32_t)(X)[0] << 0) |\
((uint32_t)(X)[1] << 8) |\
((uint32_t)(X)[2] << 16) |\
((uint32_t)(X)[3] << 24))
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 uint32_t *x)
{
#define FGH(A, BCD, I, C, S) (A = rol32(x[I] + A + (BCD) + UINT32_C(C), S))
#define F(A, B, C, D, I, S) FGH(A, (B & C) | (~B & D), I, 0x00000000, S)
#define G(A, B, C, D, I, S) FGH(A, (B & C) | (B & D) | (C & D), I, 0x5a827999, S)
#define H(A, B, C, D, I, S) FGH(A, B ^ C ^ D, I, 0x6ed9eba1, S)
#define FOUR(M, I1, S1, I2, S2, I3, S3, I4, S4)\
(M(a, b, c, d, I1, S1),\
M(d, a, b, c, I2, S2),\
M(c, d, a, b, I3, S3),\
M(b, c, d, a, I4, S4))
#define SIXTEEN(F, S1, S2, S3, S4, I11, I12, I13, I14,\
I21, I22, I23, I24,\
I31, I32, I33, I34,\
I41, I42, I43, I44)\
(FOUR(F, I11, S1, I12, S2, I13, S3, I14, S4),\
FOUR(F, I21, S1, I22, S2, I23, S3, I24, S4),\
FOUR(F, I31, S1, I32, S2, I33, S3, I34, S4),\
FOUR(F, I41, S1, I42, S2, I43, S3, I44, S4))
uint32_t a = h[0];
uint32_t b = h[1];
uint32_t c = h[2];
uint32_t d = h[3];
SIXTEEN(F, 3, 7, 11, 19, 0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15);
SIXTEEN(G, 3, 5, 9, 13, 0, 4, 8, 12,
1, 5, 9, 13,
2, 6, 10, 14,
3, 7, 11, 15);
SIXTEEN(H, 3, 9, 11, 15, 0, 8, 4, 12,
2, 10, 6, 14,
1, 9, 5, 13,
3, 11, 7, 15);
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;
size_t i;
for (; bytes - off >= 64U; off += 64U) {
for (i = 0; i < 16U; i++)
this->state.md4.m.m32[i] = LETO32(&m[off + i * 4U]);
process_block(this->state.md4.h.h32, this->state.md4.m.m32);
}
this->state.md4.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.md4.count += bytes;
this->state.md4.count *= 8U;
this->state.md4.count += (size_t)extra_bits;
if (extra_bits)
m[bytes] = libhashsum_reverse_byte__(m[bytes]);
memset(&m[bytes + 1U], 0, 63U - bytes);
mask = (uint8_t)(1U << (7U - extra_bits));
m[bytes] |= mask;
m[bytes] &= (uint8_t)~(mask - 1U); /* keep high bits (original value was reversed) */
for (i = 0; i < 14; i++)
this->state.md4.m.m32[i] = LETO32(&m[i * 4U]);
if (bytes > 55U) {
this->state.md4.m.m32[14] = LETO32(&m[14U * 4U]);
this->state.md4.m.m32[15] = LETO32(&m[15U * 4U]);
process_block(this->state.md4.h.h32, this->state.md4.m.m32);
memset(this->state.md4.m.m32, 0, 56U);
}
this->state.md4.m.m32[14] = (uint32_t)(this->state.md4.count >> 0);
this->state.md4.m.m32[15] = (uint32_t)(this->state.md4.count >> 32);
process_block(this->state.md4.h.h32, this->state.md4.m.m32);
memset(&this->state.md4.m, 0, sizeof(this->state.md4.m));
this->state.md4.count = 0;
if (!this->hash_output)
this->hash_output = this->state.md4.h.sum;
for (i = 0; i < 4U; i++) {
hi = this->state.md4.h.h32[i];
this->hash_output[i * 4U + 0U] = (uint8_t)(hi >> 0);
this->hash_output[i * 4U + 1U] = (uint8_t)(hi >> 8);
this->hash_output[i * 4U + 2U] = (uint8_t)(hi >> 16);
this->hash_output[i * 4U + 3U] = (uint8_t)(hi >> 24);
}
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.md4.m.m8[bytes] = 0;
memcpy(this->state.md4.m.m8, m, bytes + (size_t)(extra_bits > 0U));
return finalise_common(this, this->state.md4.m.m8, 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) {
this->state.md4.m.m8[bytes] = 0;
memcpy(this->state.md4.m.m8, m, bytes + (size_t)(extra_bits > 0U));
m = this->state.md4.m.m8;
}
return finalise_common(this, m, bytes, extra_bits);
}
int
libhashsum_init_md4_hasher(struct libhashsum_hasher *this)
{
this->algorithm = LIBHASHSUM_MD4;
this->algorithm_string = "MD4";
this->input_block_size = 64U;
this->hash_size = sizeof(this->state.md4.h.sum);
this->hash_output = NULL;
this->supports_non_whole_bytes = 1;
this->process = &process;
this->finalise_const = &finalise_const;
this->finalise = &finalise;
this->stretch = NULL;
this->destroy = NULL;
memset(&this->state.md4, 0, sizeof(this->state.md4));
this->state.md4.h.h32[0] = UINT32_C(0x67452301);
this->state.md4.h.h32[1] = UINT32_C(0xefcdab89);
this->state.md4.h.h32[2] = UINT32_C(0x98badcfe);
this->state.md4.h.h32[3] = UINT32_C(0x10325476);
return 0;
}
#else
int
libhashsum_init_md4_hasher(struct libhashsum_hasher *this)
{
(void) this;
errno = ENOSYS;
return -1;
}
#endif