package Tools;

import java.io.*;
import java.net.*;
import java.util.*;
import java.math.*;
import Tools.*;

/**
 * This class computes MD5 hashes.<p>
 * 
 * Introduction: To compute the message digest of a chunk of bytes,
 * create an MD5 object ``md5'', call md5.update() as needed on buffers
 * full of bytes, and then call md5.getValue(), which will fill a
 * supplied 16-byte array with the digest.<p>
 * 
 * A main() method is included that hashes the data on System.in.<p>
 * 
 * It seems to run around 25-30 times slower (JDK1.1.6) than optimized
 * C (gcc -O4, version 2.7.2.3). Measured on a Sun Ultra 5 (SPARC
 * 270MHz).<p>
 * 
 * SOURCE: Manually translated from some public domain C code (md5.c)
 * included with the ssh-1.2.22 source.
 * Comments from ssh-1.2.22/md5.c, the basis for this code:<p>
 * <em>This code has been heavily hacked by Tatu Ylonen
 * <ylo@cs.hut.fi> to make it compile on machines like Cray that don't
 * have a 32 bit integer type.<p>
 * 
 * This code implements the MD5 message-digest algorithm. The algorithm
 * is due to Ron Rivest. This code was written by Colin Plumb in 1993,
 * no copyright is claimed. This code is in the public domain; do with
 * it what you wish.<p>
 * 
 * Equivalent code is available from RSA Data Security, Inc. This code
 * has been tested against that, and is equivalent, except that you
 * don't need to include two pages of legalese with every copy.<p>
 * 
 * To compute the message digest of a chunk of bytes, declare an
 * MD5Context structure, pass it to MD5Init, call MD5Update as needed
 * on buffers full of bytes, and then call MD5Final, which will fill a
 * supplied 16-byte array with the digest.</em> 
 *
 * @deprecated JCE now includes an interface for computing message digests.
 * @author Jon Howell <jonh@cs.dartmouth.edu>
 * @rcs $Id: MD5.java,v 1.4 2000/05/26 06:16:13 jonh Exp $
 *
 * @license This code is Copyright 1999 Jon Howell. It is available for
 * use under the GNU Public License, available at:
 * http://www.gnu.org/copyleft/gpl.html
 */
public class MD5 {
	// The java.util.zip.Checksum interface would be the right
	// one to use (despite its unfortunate location in the namespace),
	// except that its getValue() function returns long; an MD5
	// checksum (16 bytes) is too big to fit in a long (8 bytes).
	// However, we otherwise follow that interface.

	public MD5() {
		accum = new int[4];
		// fill the hash accumulator with a seed value
		accum[0] = 0x67452301;
		accum[1] = 0xefcdab89;
		accum[2] = 0x98badcfe;
		accum[3] = 0x10325476;

		// initially, we've hashed zero bits
		bits = 0L;

		stash = new byte[64];
		stashInt = new int[16];
	}

	public void update(int b) {
		byte[] buf = new byte[1];
		buf[0] = (byte) b;
		update(buf);
	}

	public void update(byte[] buf) {
		update(buf, 0, buf.length);
	}

	public void update(byte[] newbuf, int bufstart, int buflen) {
		int t;
		int len = buflen;

		// shash old bits value for the "Bytes already in" computation
		// just below.
		t = (int) bits;	// (int) cast should just drop high bits, I hope

		/* update bitcount */
		/* the C code used two 32-bit ints separately, and carefully
		 * ensured that the carry carried.
		 * Java has a 64-bit long, which is just what the code really wants.
		 */
		bits += (long)(len<<3);

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

		/* Handle any leading odd-sized chunks */
		/* (that is, any left-over chunk left by last update() */

		if (t!=0) {
			int p = t;
			t = 64 - t;
			if (len < t) {
				System.arraycopy(newbuf, bufstart, stash, p, len);
				return;
			}
			System.arraycopy(newbuf, bufstart, stash, p, t);
			transform();
			bufstart += t;
			len -= t;
		}

		/* Process data in 64-byte chunks */
		while (len >= 64) {
			System.arraycopy(newbuf, bufstart, stash, 0, 64);
			transform();
			bufstart += 64;
			len -= 64;
		}

		/* Handle any remaining bytes of data. */
		/* that is, stash them for the next update(). */
		System.arraycopy(newbuf, bufstart, stash, 0, len);
	}

	public void getValue(byte[] digest) {
		md5final(digest);
	}

	public byte[] getValue() {
		byte[] digest = new byte[16];
		md5final(digest);
		return digest;
	}

	/**
	 * The hashCode of an MD5 hash is useful in that it doesn't reveal
	 * any more information about the original hashed object that does
	 * MD5. It's not as useful for its uniqueness, since it's only 32
	 * bits long and not 128.
	 */
	public int hashCode() {
		byte[] md5value = getValue();
		int hashCode = 0;
		for (int i=0; i<md5value.length; i++) {
			int byteVal = ((int) md5value[i]) & 0x0ff;
			hashCode ^= (byteVal << (i&0x03));
		}
		return hashCode;
	}

	public static void main(String args[]) {
		// This main() method was created to easily test
		// this class. It hashes whatever's on System.in.

		byte buf[] = new byte[397];
			// arbitrary buffer length designed to irritate update()
		int rc;
		MD5 md = new MD5();
		byte out[] = new byte[16];
		int i;
		int len = 0;

		try {
			while ((rc = System.in.read(buf, 0, 397)) > 0) {
				md.update(buf, 0, rc);
				len += rc;
			}
		} catch (IOException ex) {
			ex.printStackTrace();
			return;
		}
		md.getValue(out);

		System.out.println("file length: "+len);
		System.out.println("hash: "+Arrays.dumpBytes(out));
	}

	// instance variables
	int accum[];	// accum[] is where the hash accumulates.
	long bits;		// This is the count of bits hashed so far; it is
					// involved at the final computation so that the hash
					// encodes the length of its input.
	byte stash[];	// This is a buffer where we stash bytes until we have
					// enough (64) to perform a transform operation.
	int stashInt[];
				// stashInt[] is used and discarded inside transform(),
				// but why allocate it over and over?
				// (In the C version this is allocated on the stack.)

	/////////////////////////////////////////////////////////////////////
	// Below here ye will only finde private functions                 //
	/////////////////////////////////////////////////////////////////////

	// In the C version, a call to MD5STEP is a macro-in-a-macro.
	// In this Java version, we pass an Fcore object to represent the
	// inner macro, and the MD5STEP() method performs the work of
	// the outer macro. It would be good if this could all get
	// inlined, but it would take a pretty aggressive compiler to
	// inline away the dynamic method lookup made by MD5STEP to
	// get to the Fcore.f function.
	// TODO: We could explicitly inline/specialize this code ourselves. Eeew.

	private abstract class Fcore {
		abstract int f(int x, int y, int z);
	}
	private Fcore F1 = new Fcore() {
		int f(int x, int y, int z) { return (z ^ (x & (y ^ z))); }};
	private Fcore F2 = new Fcore() {
		int f(int x, int y, int z) { return (y ^ (z & (x ^ y))); }};
	private Fcore F3 = new Fcore() {
		int f(int x, int y, int z) { return (x ^ y ^ z); }};
	private Fcore F4 = new Fcore() {
		int f(int x, int y, int z) { return (y ^ (x | ~z)); }};

	private int MD5STEP(Fcore f, int w, int x, int y, int z, int data, int s) {
		w += f.f(x, y, z) + data;
		w = w<<s | w>>>(32-s);
		w += x;
		return w;
	}

	private void transform() {
		// process the 64 bytes in stash[]

		// stashInt is an internal representation of stash as a
		// block of 16 4-byte integers, rather than 64 1-byte bytes.
		for (int i=0; i<16; i++) {
			stashInt[i] = Endian.LittleGetInt(stash, 4*i);
		}

		int a, b, c, d;
		a = accum[0];
		b = accum[1];
		c = accum[2];
		d = accum[3];

	    a = MD5STEP(F1, a, b, c, d, stashInt[0]		+ 0xd76aa478, 7);
	    d = MD5STEP(F1, d, a, b, c, stashInt[1]		+ 0xe8c7b756, 12);
	    c = MD5STEP(F1, c, d, a, b, stashInt[2]		+ 0x242070db, 17);
	    b = MD5STEP(F1, b, c, d, a, stashInt[3]		+ 0xc1bdceee, 22);
	    a = MD5STEP(F1, a, b, c, d, stashInt[4]		+ 0xf57c0faf, 7);
	    d = MD5STEP(F1, d, a, b, c, stashInt[5]		+ 0x4787c62a, 12);
	    c = MD5STEP(F1, c, d, a, b, stashInt[6]		+ 0xa8304613, 17);
	    b = MD5STEP(F1, b, c, d, a, stashInt[7]		+ 0xfd469501, 22);
	    a = MD5STEP(F1, a, b, c, d, stashInt[8]		+ 0x698098d8, 7);
	    d = MD5STEP(F1, d, a, b, c, stashInt[9]		+ 0x8b44f7af, 12);
	    c = MD5STEP(F1, c, d, a, b, stashInt[10]	+ 0xffff5bb1, 17);
	    b = MD5STEP(F1, b, c, d, a, stashInt[11]	+ 0x895cd7be, 22);
	    a = MD5STEP(F1, a, b, c, d, stashInt[12]	+ 0x6b901122, 7);
	    d = MD5STEP(F1, d, a, b, c, stashInt[13]	+ 0xfd987193, 12);
	    c = MD5STEP(F1, c, d, a, b, stashInt[14]	+ 0xa679438e, 17);
	    b = MD5STEP(F1, b, c, d, a, stashInt[15]	+ 0x49b40821, 22);
	
	    a = MD5STEP(F2, a, b, c, d, stashInt[1]		+ 0xf61e2562, 5);
	    d = MD5STEP(F2, d, a, b, c, stashInt[6]		+ 0xc040b340, 9);
	    c = MD5STEP(F2, c, d, a, b, stashInt[11]	+ 0x265e5a51, 14);
	    b = MD5STEP(F2, b, c, d, a, stashInt[0]		+ 0xe9b6c7aa, 20);
	    a = MD5STEP(F2, a, b, c, d, stashInt[5]		+ 0xd62f105d, 5);
	    d = MD5STEP(F2, d, a, b, c, stashInt[10]	+ 0x02441453, 9);
	    c = MD5STEP(F2, c, d, a, b, stashInt[15]	+ 0xd8a1e681, 14);
	    b = MD5STEP(F2, b, c, d, a, stashInt[4]		+ 0xe7d3fbc8, 20);
	    a = MD5STEP(F2, a, b, c, d, stashInt[9]		+ 0x21e1cde6, 5);
	    d = MD5STEP(F2, d, a, b, c, stashInt[14]	+ 0xc33707d6, 9);
	    c = MD5STEP(F2, c, d, a, b, stashInt[3]		+ 0xf4d50d87, 14);
	    b = MD5STEP(F2, b, c, d, a, stashInt[8]		+ 0x455a14ed, 20);
	    a = MD5STEP(F2, a, b, c, d, stashInt[13]	+ 0xa9e3e905, 5);
	    d = MD5STEP(F2, d, a, b, c, stashInt[2]		+ 0xfcefa3f8, 9);
	    c = MD5STEP(F2, c, d, a, b, stashInt[7]		+ 0x676f02d9, 14);
	    b = MD5STEP(F2, b, c, d, a, stashInt[12]	+ 0x8d2a4c8a, 20);
	
	    a = MD5STEP(F3, a, b, c, d, stashInt[5]		+ 0xfffa3942, 4);
	    d = MD5STEP(F3, d, a, b, c, stashInt[8]		+ 0x8771f681, 11);
	    c = MD5STEP(F3, c, d, a, b, stashInt[11]	+ 0x6d9d6122, 16);
	    b = MD5STEP(F3, b, c, d, a, stashInt[14]	+ 0xfde5380c, 23);
	    a = MD5STEP(F3, a, b, c, d, stashInt[1]		+ 0xa4beea44, 4);
	    d = MD5STEP(F3, d, a, b, c, stashInt[4]		+ 0x4bdecfa9, 11);
	    c = MD5STEP(F3, c, d, a, b, stashInt[7]		+ 0xf6bb4b60, 16);
	    b = MD5STEP(F3, b, c, d, a, stashInt[10]	+ 0xbebfbc70, 23);
	    a = MD5STEP(F3, a, b, c, d, stashInt[13]	+ 0x289b7ec6, 4);
	    d = MD5STEP(F3, d, a, b, c, stashInt[0]		+ 0xeaa127fa, 11);
	    c = MD5STEP(F3, c, d, a, b, stashInt[3]		+ 0xd4ef3085, 16);
	    b = MD5STEP(F3, b, c, d, a, stashInt[6]		+ 0x04881d05, 23);
	    a = MD5STEP(F3, a, b, c, d, stashInt[9]		+ 0xd9d4d039, 4);
	    d = MD5STEP(F3, d, a, b, c, stashInt[12]	+ 0xe6db99e5, 11);
	    c = MD5STEP(F3, c, d, a, b, stashInt[15]	+ 0x1fa27cf8, 16);
	    b = MD5STEP(F3, b, c, d, a, stashInt[2]		+ 0xc4ac5665, 23);
	
	    a = MD5STEP(F4, a, b, c, d, stashInt[0]		+ 0xf4292244, 6);
	    d = MD5STEP(F4, d, a, b, c, stashInt[7]		+ 0x432aff97, 10);
	    c = MD5STEP(F4, c, d, a, b, stashInt[14]	+ 0xab9423a7, 15);
	    b = MD5STEP(F4, b, c, d, a, stashInt[5]		+ 0xfc93a039, 21);
	    a = MD5STEP(F4, a, b, c, d, stashInt[12]	+ 0x655b59c3, 6);
	    d = MD5STEP(F4, d, a, b, c, stashInt[3]		+ 0x8f0ccc92, 10);
	    c = MD5STEP(F4, c, d, a, b, stashInt[10]	+ 0xffeff47d, 15);
	    b = MD5STEP(F4, b, c, d, a, stashInt[1]		+ 0x85845dd1, 21);
	    a = MD5STEP(F4, a, b, c, d, stashInt[8]		+ 0x6fa87e4f, 6);
	    d = MD5STEP(F4, d, a, b, c, stashInt[15]	+ 0xfe2ce6e0, 10);
	    c = MD5STEP(F4, c, d, a, b, stashInt[6]		+ 0xa3014314, 15);
	    b = MD5STEP(F4, b, c, d, a, stashInt[13]	+ 0x4e0811a1, 21);
	    a = MD5STEP(F4, a, b, c, d, stashInt[4]		+ 0xf7537e82, 6);
	    d = MD5STEP(F4, d, a, b, c, stashInt[11]	+ 0xbd3af235, 10);
	    c = MD5STEP(F4, c, d, a, b, stashInt[2]		+ 0x2ad7d2bb, 15);
	    b = MD5STEP(F4, b, c, d, a, stashInt[9]		+ 0xeb86d391, 21);
	
	    accum[0] += a;
	    accum[1] += b;
	    accum[2] += c;
	    accum[3] += d;
	}

	/*
	 * Final wrapup - pad to 64-byte boundary with the bit pattern
	 * 1 0* (64-bit count of bits processed, MSB-first)
	 */
	private void md5final(byte[] digest) {
		/* "final" is a poor method name in Java. :v) */
		int count;
		int p;		// in original code, this is a pointer; in this java code
					// it's an index into the array this->in.

	    /* Compute number of bytes mod 64 */
	    count = (int) ((bits >>> 3) & 0x3F);
	
	    /* Set the first char of padding to 0x80.  This is safe since there is
	       always at least one byte free */
		p = count;
		stash[p++] = (byte) 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 */
			Arrays.zeroByteArray(stash, p, count);
			transform();
		
			/* Now fill the next block with 56 bytes */
			Arrays.zeroByteArray(stash, 0, 56);
	    } else {
			/* Pad block to 56 bytes */
			Arrays.zeroByteArray(stash, p, count - 8);
	    }
	
	    /* Append length in bits and transform */
		// Could use a PUT_64BIT... func here. This is a fairly
		// direct translation from the C code, where bits was an array
		// of two 32-bit ints.
		Endian.LittlePutLong(stash, 56, bits);
	
		transform();
		Endian.LittlePutInt(digest,  0, accum[0]);
		Endian.LittlePutInt(digest,  4, accum[1]);
		Endian.LittlePutInt(digest,  8, accum[2]);
		Endian.LittlePutInt(digest, 12, accum[3]);

		/* zero sensitive data */
		/* notice this misses any sneaking out on the stack. The C
		 * version uses registers in some spots, perhaps because
		 * they care about this.
		 */
		Arrays.zeroByteArray(stash);
		Arrays.zeroIntArray(accum);
		bits = 0;
		Arrays.zeroIntArray(stashInt);
	}
}
