/*
 *    Copyright (C) 2001 Nikos Mavroyanopoulos
 *
 *    This library is free software; you can redistribute it and/or modify it 
 *    under the terms of the GNU Library General Public License as published 
 *    by the Free Software Foundation; either version 2 of the License, or 
 *    (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Library General Public License for more details.
 *
 *    You should have received a copy of the GNU Library General Public
 *    License along with this library; if not, write to the
 *    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *    Boston, MA 02111-1307, USA.
 */


#include <libdefs.h>

#ifdef ENABLE_MD4

/* 
 * The algorithm is due to Ron Rivest.  This code is based on code
 * written by Colin Plumb in 1993.
 */
 

/*
 * This code implements the MD4 message-digest algorithm.
 */

#include "mhash_md4.h"

#define rotl32(x,n)   (((x) << ((mutils_word32)(n))) | ((x) >> (32 - (mutils_word32)(n))))

/*
 * Start MD4 accumulation.  Set bit count to 0 and buffer to mysterious
 * initialization __constants.
 */
void MD4Init(struct MD4Context *ctx)
{
	ctx->buf[0] = 0x67452301;
	ctx->buf[1] = 0xefcdab89;
	ctx->buf[2] = 0x98badcfe;
	ctx->buf[3] = 0x10325476;

	ctx->bits[0] = 0;
	ctx->bits[1] = 0;
}

/*
 * Update context to reflect the concatenation of another buffer full
 * of bytes.
 */
void MD4Update(struct MD4Context *ctx, mutils_word8 __const *buf, mutils_word32 len)
{
	register mutils_word32 t;

	/* Update bitcount */

	t = ctx->bits[0];
	if ((ctx->bits[0] = t + ((mutils_word32) len << 3)) < t)
		ctx->bits[1]++;	/* Carry from low to high */
	ctx->bits[1] += len >> 29;

	t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */

	/* Handle any leading odd-sized chunks */

	if (t) {
		mutils_word8 *p = (mutils_word8 *) ctx->in + t;

		t = 64 - t;
		if (len < t) {
			mutils_memcpy(p, buf, len);
			return;
		}
		mutils_memcpy(p, buf, t);
		mutils_word32nswap((mutils_word32 *) ctx->in, 16, MUTILS_TRUE);
		MD4Transform(ctx->buf, (mutils_word32 *) ctx->in);
		buf += t;
		len -= t;
	}
	/* Process data in 64-byte chunks */

	while (len >= 64) {
		mutils_memcpy(ctx->in, buf, 64);
		mutils_word32nswap((mutils_word32 *) ctx->in, 16, MUTILS_TRUE);
		MD4Transform(ctx->buf, (mutils_word32 *) ctx->in);
		buf += 64;
		len -= 64;
	}

	/* Handle any remaining bytes of data. */

	mutils_memcpy(ctx->in, buf, len);
}

/*
 * Final wrapup - pad to 64-byte boundary with the bit pattern 
 * 1 0* (64-bit count of bits processed, MSB-first)
 */
void MD4Final(struct MD4Context *ctx, mutils_word8 *digest)
{
	mutils_word32 count;
	mutils_word8 *p;

	/* Compute number of bytes mod 64 */
	count = (ctx->bits[0] >> 3) & 0x3F;

	/* Set the first char of padding to 0x80.  This is safe since there is
	   always at least one byte free */
	p = ctx->in + count;
	*p++ = 0x80;

	/* Bytes of padding needed to make 64 bytes */
	count = 64 - 1 - count;

	/* Pad out to 56 mod 64 */
	if (count < 8) {
		/* Two lots of padding:  Pad the first block to 64 bytes */
		mutils_bzero(p, count);
		mutils_word32nswap((mutils_word32 *) ctx->in, 16, MUTILS_TRUE);
		MD4Transform(ctx->buf, (mutils_word32 *) ctx->in);

		/* Now fill the next block with 56 bytes */
		mutils_bzero(ctx->in, 56);
	} else {
		/* Pad block to 56 bytes */
		mutils_bzero(p, count - 8);
	}
	mutils_word32nswap((mutils_word32 *) ctx->in, 14, MUTILS_TRUE);

	/* Append length in bits and transform */
	((mutils_word32 *) ctx->in)[14] = ctx->bits[0];
	((mutils_word32 *) ctx->in)[15] = ctx->bits[1];

	MD4Transform(ctx->buf, (mutils_word32 *) ctx->in);
	mutils_word32nswap((mutils_word32 *) ctx->buf, 4, MUTILS_TRUE);
	
	if (digest!=NULL)
		mutils_memcpy(digest, ctx->buf, 16);
	mutils_bzero(ctx, sizeof(ctx));	/* In case it's sensitive */
}

/* The three core functions */

#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))

#define FF(a, b, c, d, x, s) { \
    (a) += F ((b), (c), (d)) + (x); \
    (a) = rotl32 ((a), (s)); \
  }
#define GG(a, b, c, d, x, s) { \
    (a) += G ((b), (c), (d)) + (x) + (mutils_word32)0x5a827999; \
    (a) = rotl32 ((a), (s)); \
  }
#define HH(a, b, c, d, x, s) { \
    (a) += H ((b), (c), (d)) + (x) + (mutils_word32)0x6ed9eba1; \
    (a) = rotl32 ((a), (s)); \
  }


/*
 * The core of the MD4 algorithm
 */
void MD4Transform(mutils_word32 buf[4], __const mutils_word32 in[16])
{
	register mutils_word32 a, b, c, d;

	a = buf[0];
	b = buf[1];
	c = buf[2];
	d = buf[3];

	FF(a, b, c, d, in[0], 3);	/* 1 */
	FF(d, a, b, c, in[1], 7);	/* 2 */
	FF(c, d, a, b, in[2], 11);	/* 3 */
	FF(b, c, d, a, in[3], 19);	/* 4 */
	FF(a, b, c, d, in[4], 3);	/* 5 */
	FF(d, a, b, c, in[5], 7);	/* 6 */
	FF(c, d, a, b, in[6], 11);	/* 7 */
	FF(b, c, d, a, in[7], 19);	/* 8 */
	FF(a, b, c, d, in[8], 3);	/* 9 */
	FF(d, a, b, c, in[9], 7);	/* 10 */
	FF(c, d, a, b, in[10], 11);	/* 11 */
	FF(b, c, d, a, in[11], 19);	/* 12 */
	FF(a, b, c, d, in[12], 3);	/* 13 */
	FF(d, a, b, c, in[13], 7);	/* 14 */
	FF(c, d, a, b, in[14], 11);	/* 15 */
	FF(b, c, d, a, in[15], 19);	/* 16 */

	GG(a, b, c, d, in[0], 3);	/* 17 */
	GG(d, a, b, c, in[4], 5);	/* 18 */
	GG(c, d, a, b, in[8], 9);	/* 19 */
	GG(b, c, d, a, in[12], 13);	/* 20 */
	GG(a, b, c, d, in[1], 3);	/* 21 */
	GG(d, a, b, c, in[5], 5);	/* 22 */
	GG(c, d, a, b, in[9], 9);	/* 23 */
	GG(b, c, d, a, in[13], 13);	/* 24 */
	GG(a, b, c, d, in[2], 3);	/* 25 */
	GG(d, a, b, c, in[6], 5);	/* 26 */
	GG(c, d, a, b, in[10], 9);	/* 27 */
	GG(b, c, d, a, in[14], 13);	/* 28 */
	GG(a, b, c, d, in[3], 3);	/* 29 */
	GG(d, a, b, c, in[7], 5);	/* 30 */
	GG(c, d, a, b, in[11], 9);	/* 31 */
	GG(b, c, d, a, in[15], 13);	/* 32 */

	HH(a, b, c, d, in[0], 3);	/* 33 */
	HH(d, a, b, c, in[8], 9);	/* 34 */
	HH(c, d, a, b, in[4], 11);	/* 35 */
	HH(b, c, d, a, in[12], 15);	/* 36 */
	HH(a, b, c, d, in[2], 3);	/* 37 */
	HH(d, a, b, c, in[10], 9);	/* 38 */
	HH(c, d, a, b, in[6], 11);	/* 39 */
	HH(b, c, d, a, in[14], 15);	/* 40 */
	HH(a, b, c, d, in[1], 3);	/* 41 */
	HH(d, a, b, c, in[9], 9);	/* 42 */
	HH(c, d, a, b, in[5], 11);	/* 43 */
	HH(b, c, d, a, in[13], 15);	/* 44 */
	HH(a, b, c, d, in[3], 3);	/* 45 */
	HH(d, a, b, c, in[11], 9);	/* 46 */
	HH(c, d, a, b, in[7], 11);	/* 47 */
	HH(b, c, d, a, in[15], 15);	/* 48 */


	buf[0] += a;
	buf[1] += b;
	buf[2] += c;
	buf[3] += d;
}

#endif /* ENABLE_MD4 */
