/* md5.c: MD5 hashing taken from public domain source
   This has nothing to do with cryptography.
   Copyright (C) 1998 Paul Sheer

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "mostincludes.h"
#include "md5.h"
#include "diffie/compat.h"
#include "src/mad.h"

void md5_transform (unsigned int buf[4], const unsigned int in[16]);

 /* Colin Plumb's original code modified by A.M. Kuchling */

 /* unsigned int must be 4 bytes :-(  paul */

#undef ARCH_BIG_ENDIAN
#undef ARCH_LITTLE_ENDIAN
#define ARCH_BIG_ENDIAN			1
#define ARCH_LITTLE_ENDIAN		2

#define TEST_ENDIANNESS(x)			\
    {						\
	int i = 1;				\
	x = ARCH_BIG_ENDIAN;			\
	if (*((char *) &i) == 1)		\
	    x = ARCH_LITTLE_ENDIAN;		\
    }

/* Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
   initialization constants. */
void md5_init (Md5 * m)
{
    m->buf[0] = 0x67452301UL;
    m->buf[1] = 0xefcdab89UL;
    m->buf[2] = 0x98badcfeUL;
    m->buf[3] = 0x10325476UL;
    m->bits[0] = 0;
    m->bits[1] = 0;
    TEST_ENDIANNESS (m->endianness)
}

#define BYTE_REVERSE(m, field, num)						\
    if (m->endianness == ARCH_BIG_ENDIAN) {					\
	unsigned int my_t, longs = num;						\
	unsigned char *my_buf = (unsigned char *) & (m->field);			\
	do {									\
	    my_t = (unsigned int) ((unsigned int) my_buf[3] << 8 | my_buf[2]) << 16 |	\
		((unsigned) my_buf[1] << 8 | my_buf[0]);				\
	    *(unsigned int *) my_buf = my_t;						\
	    my_buf += 4;								\
	} while (--longs);							\
    }

/* Update context to reflect the concatenation of another buffer full
   of bytes. */
void md5_update (Md5 * m, unsigned char *buf, unsigned int len)
{
    unsigned int t;

    /* Update bitcount */

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

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

    /* Handle any leading odd-sized chunks */

    if (t) {
	unsigned char *p = (unsigned char *) m->in + t;

	t = 64 - t;
	if (len < t) {
	    memcpy (p, buf, len);
	    return;
	}
	memcpy (p, buf, t);
	BYTE_REVERSE (m, in, 16);
	md5_transform (m->buf, (unsigned int *) m->in);
	buf += t;
	len -= t;
    }
    /* Process data in 64-byte chunks */

    while (len >= 64) {
	memcpy (m->in, buf, 64);
	BYTE_REVERSE (m, in, 16);
	md5_transform (m->buf, (unsigned int *) m->in);
	buf += 64;
	len -= 64;
    }

    /* Handle any remaining bytes of data. */

    memcpy (m->in, buf, len);
}

/* Final wrapup - pad to 64-byte boundary with the bit pattern 
   1 0* (64-bit count of bits processed, MSB-first) */
unsigned char *md5_digest (Md5 * m)
{
    unsigned count;
    unsigned char *p;
    Md5 temp;

    memcpy (&temp, m, sizeof (Md5));
    /* Compute number of bytes mod 64 */
    count = (temp.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 = temp.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 */
	memset (p, 0, count);
	BYTE_REVERSE ((&temp), in, 16);
	md5_transform (temp.buf, (unsigned int *) temp.in);

	/* Now fill the next block with 56 bytes */
	memset (temp.in, 0, 56);
    } else {
	/* Pad block to 56 bytes */
	memset (p, 0, count - 8);
    }
    BYTE_REVERSE ((&temp), in, 14);

    /* Append length in bits and transform */
    ((unsigned int *) temp.in)[14] = temp.bits[0];
    ((unsigned int *) temp.in)[15] = temp.bits[1];

    md5_transform (temp.buf, (unsigned int *) temp.in);
    BYTE_REVERSE ((&temp), buf, 4);

    p = malloc (16);
    memcpy (p, temp.buf, 16);
    return p;
}

/* The four core functions - F1 is optimized somewhat */

/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))

/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )

/*
 * The core of the MD5 algorithm, this alters an existing MD5 hash to
 * reflect the addition of 16 longwords of new data.  MD5Update blocks
 * the data and converts bytes into longwords for this routine.
 */
void md5_transform (unsigned int buf[4], const unsigned int in[16])
{
    register unsigned int a, b, c, d;

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

    MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478UL, 7);
    MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756UL, 12);
    MD5STEP (F1, c, d, a, b, in[2] + 0x242070dbUL, 17);
    MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceeeUL, 22);
    MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0fafUL, 7);
    MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62aUL, 12);
    MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613UL, 17);
    MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501UL, 22);
    MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8UL, 7);
    MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7afUL, 12);
    MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1UL, 17);
    MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7beUL, 22);
    MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122UL, 7);
    MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193UL, 12);
    MD5STEP (F1, c, d, a, b, in[14] + 0xa679438eUL, 17);
    MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821UL, 22);

    MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562UL, 5);
    MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340UL, 9);
    MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51UL, 14);
    MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aaUL, 20);
    MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105dUL, 5);
    MD5STEP (F2, d, a, b, c, in[10] + 0x02441453UL, 9);
    MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681UL, 14);
    MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8UL, 20);
    MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6UL, 5);
    MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6UL, 9);
    MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87UL, 14);
    MD5STEP (F2, b, c, d, a, in[8] + 0x455a14edUL, 20);
    MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905UL, 5);
    MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8UL, 9);
    MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9UL, 14);
    MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8aUL, 20);

    MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942UL, 4);
    MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681UL, 11);
    MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122UL, 16);
    MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380cUL, 23);
    MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44UL, 4);
    MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9UL, 11);
    MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60UL, 16);
    MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70UL, 23);
    MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6UL, 4);
    MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127faUL, 11);
    MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085UL, 16);
    MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05UL, 23);
    MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039UL, 4);
    MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5UL, 11);
    MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8UL, 16);
    MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665UL, 23);

    MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244UL, 6);
    MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97UL, 10);
    MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7UL, 15);
    MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039UL, 21);
    MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3UL, 6);
    MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92UL, 10);
    MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47dUL, 15);
    MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1UL, 21);
    MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4fUL, 6);
    MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0UL, 10);
    MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314UL, 15);
    MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1UL, 21);
    MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82UL, 6);
    MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235UL, 10);
    MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bbUL, 15);
    MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391UL, 21);

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

void md5_copy (Md5 * src, Md5 * dest)
{
    dest->endianness = src->endianness;
    memcpy (dest->in, src->in, 64);
    memcpy (&(dest->bits), &(src->bits), 2 * sizeof (unsigned int));
    memcpy (&(dest->buf), &(src->buf), 4 * sizeof (unsigned int));
}

/* does an md5 hash in one go for convenience */
char *md5_hash (const char *in, int len)
{
    Md5 m;
    md5_init (&m);
    md5_update (&m, (unsigned char *) in, len);
    return (char *) md5_digest (&m);
}

