All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.sandia.cognition.hash.MD5Hash Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * File:                MD5Hash.java
 * Authors:             Kevin R. Dixon
 * Company:             Sandia National Laboratories
 * Project:             Cognitive Foundry
 *
 * Copyright Jan 26, 2011, Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the U.S. Government.
 * Export of this program may require a license from the United States
 * Government. See CopyrightHistory.txt for complete details.
 *
 */

package gov.sandia.cognition.hash;

import gov.sandia.cognition.annotation.PublicationReference;
import gov.sandia.cognition.annotation.PublicationReferences;
import gov.sandia.cognition.annotation.PublicationType;
import gov.sandia.cognition.util.ObjectUtil;
import java.util.Arrays;

// This class computes MD5 hashes.
// Manually translated by Jon Howell 
// from some public domain C code (md5.c) included with the ssh-1.2.22 source.
// Tue Jan 19 15:55:50 EST 1999
// $Id: MD5Hash.java,v 1.3 2011/06/28 17:07:08 krdixon Exp $
//
// 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.md5final(), which
// will fill a supplied 16-byte array with the digest.
//
// A main() method is included that hashes the data on System.in.
//
// 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).
//
// Comments from md5.c from ssh-1.2.22, the basis for this code:
//
/* This code has been heavily hacked by Tatu Ylonen  to
make it compile on machines like Cray that don't have a 32 bit integer
type. */
/*
 * 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.
 *
 * 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.
 *
 * 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.
 */

/**
 * Implementation of the MD5 128-bit (16-byte) cryptographic hash function.
 * This algorithm is known to be unsafe for cryptographic purposes and
 * is implemented for historic purposes only.  This implementation is based
 * on the Java port of C code by Jon Howell.  According to Howell, it is
 * 25-30 times slower than the optimized C version.  Do not use MD5.
 *
 * @author Kevin R. Dixon
 * @since  3.4.2
 */
@PublicationReferences(
    references={
        @PublicationReference(
            author="Wikipedia",
            title="MD5",
            type=PublicationType.WebPage,
            year=2010
        )
        ,
        @PublicationReference(
            author="Jon Howell",
            title="MD5 Java class",
            type=PublicationType.Misc,
            year=1999,
            url="http://www.jonh.net/~jonh/md5/MD5.java",
            notes="A fairly inefficient implementation, but a correct one!"
        )
    }
)
public class MD5Hash
    extends AbstractHashFunction
{

    /**
     * MD5 is a 128-bit (16-byte) length hash.
     */
    public static final int LENGTH = 16;

    /**
     * Default seed
     */
    protected static final byte[] DEFAULT_SEED = {
        (byte)0x67, (byte)0x45, (byte)0x23, (byte)0x01,
        (byte)0xef, (byte)0xcd, (byte)0xab, (byte)0x89,
        (byte)0x98, (byte)0xba, (byte)0xdc, (byte)0xfe,
        (byte)0x10, (byte)0x32, (byte)0x54, (byte)0x76
    };

    /**
     * These were originally unsigned ints.
     * This Java code makes an effort to avoid sign traps.
     * buf[] is where the hash accumulates.
     */
    private int buf[];

    /**
     * This is the count of bits hashed so far.
     */
    private long bits;

    /**
     * This is a buffer where we stash bytes until we have
     * enough (64) to perform a transform operation.
     */
    private byte in[];

    /**
     * Default constructor
     */
    public MD5Hash()
    {
        this.buf = new int[4];

        this.in = new byte[64];
    }

    @Override
    public void evaluateInto(
        byte[] input,
        byte[] output,
        byte[] seed)
    {

        if( output.length != LENGTH )
        {
            throw new IllegalArgumentException(
                "Output must be of length " + LENGTH );
        }

        if( seed.length != LENGTH )
        {
            throw new IllegalArgumentException(
                "Expected seed to be of length: " + LENGTH );
        }

        if( input == null )
        {
            Arrays.fill( output, (byte) 0 );
            return;
        }


        // no bits yet.
        this.bits = 0L;

        // fill the hash accumulator with a seed value
        this.buf[0] = HashFunctionUtil.toInteger(seed, 0*4);
        this.buf[1] = HashFunctionUtil.toInteger(seed, 1*4);
        this.buf[2] = HashFunctionUtil.toInteger(seed, 2*4);
        this.buf[3] = HashFunctionUtil.toInteger(seed, 3*4);

        this.update(input);
        this.md5final(output);
    }

    @Override
    public int length()
    {
        return LENGTH;
    }

    @Override
    public byte[] getDefaultSeed()
    {
        return ObjectUtil.deepCopy(DEFAULT_SEED);
    }

    /**
     * Main MD5 update step
     * @param newbuf
     * new buffer to update
     */
    private void update(
        byte[] newbuf)
    {
        update(newbuf, 0, newbuf.length);
    }


    /**
     * Interior MD5 update step
     * @param newbuf
     * new buffer to update
     * @param bufstart
     * Offset into the buffer to update
     * @param buflen
     * how many bytes to update (not necessarily all of them!)
     */
    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->in */

        /* 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, in, p, len);
                return;
            }
            System.arraycopy(newbuf, bufstart, in, p, t);
            transform();
            bufstart += t;
            len -= t;
        }

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

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

    /**
     * Final wrapup - pad to 64-byte boundary with the bit pattern
     * 1 0* (64-bit count of bits processed, MSB-first)
     * @param digest
     * Output value from the hash code
     */
    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;
        in[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 */
            zeroByteArray(in, p, count);
            transform();

            /* Now fill the next block with 56 bytes */
            zeroByteArray(in, 0, 56);
        }
        else
        {
            /* Pad block to 56 bytes */
            zeroByteArray(in, 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.
        int lowbits = (int) bits;
        int highbits = (int) (bits >>> 32);
        PUT_32BIT_LSB_FIRST(in, 56, lowbits);
        PUT_32BIT_LSB_FIRST(in, 60, highbits);

        transform();
        PUT_32BIT_LSB_FIRST(digest, 0, buf[0]);
        PUT_32BIT_LSB_FIRST(digest, 4, buf[1]);
        PUT_32BIT_LSB_FIRST(digest, 8, buf[2]);
        PUT_32BIT_LSB_FIRST(digest, 12, buf[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.fill( in, (byte) 0 );
        Arrays.fill( buf, 0 );
        bits = 0;
    }

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

    /**
     * Zeros the given portion of the array
     * @param a
     * Array
     * @param start
     * start offset
     * @param length
     * how many bytes to zero
     */
    private void zeroByteArray(
        byte[] a,
        int start,
        int length)
    {
        setByteArray(a, (byte) 0, start, length);
    }

    /**
     * Assigns a portion of the array to the given value
     * @param a
     * Array
     * @param val
     * value to assign
     * @param start
     * start offset
     * @param length
     * how many bytes to zero
     */
    private void setByteArray(
        byte[] a,
        byte val,
        int start,
        int length)
    {
        int i;
        int end = start + length;
        for (i = start; i < end; i++)
        {
            a[i] = val;
        }
    } 

    /**
     * MD5 main step
     * @param w
     * w
     * @param x
     * x
     * @param y
     * y
     * @param z
     * z
     * @param data
     * data
     * @param s
     * s
     * @return
     * step value
     */
    private int MD5STEP1(
        int w,
        int x,
        int y,
        int z,
        int data,
        int s )
    {
        w += data + (z ^ (x & (y ^ z)));
        return x + ((w << s) | (w >>> (32 - s)));
    }

    /**
     * MD5 main step
     * @param w
     * w
     * @param x
     * x
     * @param y
     * y
     * @param z
     * z
     * @param data
     * data
     * @param s
     * s
     * @return
     * step value
     */
    private int MD5STEP2(
        int w,
        int x,
        int y,
        int z,
        int data,
        int s )
    {
        w += data + (y ^ (z & (x ^ y)));
        return x + ((w << s) | (w >>> (32 - s)));
    }

    /**
     * MD5 main step
     * @param w
     * w
     * @param x
     * x
     * @param y
     * y
     * @param z
     * z
     * @param data
     * data
     * @param s
     * s
     * @return
     * step value
     */
    private int MD5STEP3(
        int w,
        int x,
        int y,
        int z,
        int data,
        int s )
    {
        w += data + (x ^ y ^ z);
        return x + ((w << s) | (w >>> (32 - s)));
    }

    /**
     * MD5 main step
     * @param w
     * w
     * @param x
     * x
     * @param y
     * y
     * @param z
     * z
     * @param data
     * data
     * @param s
     * s
     * @return
     * step value
     */
    private int MD5STEP4(
        int w,
        int x,
        int y,
        int z,
        int data,
        int s )
    {
        w += data + (y ^ (x | ~z));
        return x + ((w << s) | (w >>> (32 - s)));
    }

    /**
     * transforms the MD5 data
     */
    private void transform()
    {
        final int ii00 = ((in[ 0] & 0xff) | ((in[ 1] & 0xff) << 8) |  ((in[ 2] & 0xff) << 16) | ((in[ 3] & 0xff) << 24));
        final int ii01 = ((in[ 4] & 0xff) | ((in[ 5] & 0xff) << 8) |  ((in[ 6] & 0xff) << 16) | ((in[ 7] & 0xff) << 24));
        final int ii02 = ((in[ 8] & 0xff) | ((in[ 9] & 0xff) << 8) |  ((in[10] & 0xff) << 16) | ((in[11] & 0xff) << 24));
        final int ii03 = ((in[12] & 0xff) | ((in[13] & 0xff) << 8) |  ((in[14] & 0xff) << 16) | ((in[15] & 0xff) << 24));
        final int ii04 = ((in[16] & 0xff) | ((in[17] & 0xff) << 8) |  ((in[18] & 0xff) << 16) | ((in[19] & 0xff) << 24));
        final int ii05 = ((in[20] & 0xff) | ((in[21] & 0xff) << 8) |  ((in[22] & 0xff) << 16) | ((in[23] & 0xff) << 24));
        final int ii06 = ((in[24] & 0xff) | ((in[25] & 0xff) << 8) |  ((in[26] & 0xff) << 16) | ((in[27] & 0xff) << 24));
        final int ii07 = ((in[28] & 0xff) | ((in[29] & 0xff) << 8) |  ((in[30] & 0xff) << 16) | ((in[31] & 0xff) << 24));
        final int ii08 = ((in[32] & 0xff) | ((in[33] & 0xff) << 8) |  ((in[34] & 0xff) << 16) | ((in[35] & 0xff) << 24));
        final int ii09 = ((in[36] & 0xff) | ((in[37] & 0xff) << 8) |  ((in[38] & 0xff) << 16) | ((in[39] & 0xff) << 24));
        final int ii10 = ((in[40] & 0xff) | ((in[41] & 0xff) << 8) |  ((in[42] & 0xff) << 16) | ((in[43] & 0xff) << 24));
        final int ii11 = ((in[44] & 0xff) | ((in[45] & 0xff) << 8) |  ((in[46] & 0xff) << 16) | ((in[47] & 0xff) << 24));
        final int ii12 = ((in[48] & 0xff) | ((in[49] & 0xff) << 8) |  ((in[50] & 0xff) << 16) | ((in[51] & 0xff) << 24));
        final int ii13 = ((in[52] & 0xff) | ((in[53] & 0xff) << 8) |  ((in[54] & 0xff) << 16) | ((in[55] & 0xff) << 24));
        final int ii14 = ((in[56] & 0xff) | ((in[57] & 0xff) << 8) |  ((in[58] & 0xff) << 16) | ((in[59] & 0xff) << 24));
        final int ii15 = ((in[60] & 0xff) | ((in[61] & 0xff) << 8) |  ((in[62] & 0xff) << 16) | ((in[63] & 0xff) << 24));

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

        a = MD5STEP1( a, b, c, d, ii00 + 0xd76aa478,  7);
        d = MD5STEP1( d, a, b, c, ii01 + 0xe8c7b756, 12);
        c = MD5STEP1( c, d, a, b, ii02 + 0x242070db, 17);
        b = MD5STEP1( b, c, d, a, ii03 + 0xc1bdceee, 22);
        a = MD5STEP1( a, b, c, d, ii04 + 0xf57c0faf,  7);
        d = MD5STEP1( d, a, b, c, ii05 + 0x4787c62a, 12);
        c = MD5STEP1( c, d, a, b, ii06 + 0xa8304613, 17);
        b = MD5STEP1( b, c, d, a, ii07 + 0xfd469501, 22);
        a = MD5STEP1( a, b, c, d, ii08 + 0x698098d8,  7);
        d = MD5STEP1( d, a, b, c, ii09 + 0x8b44f7af, 12);
        c = MD5STEP1( c, d, a, b, ii10 + 0xffff5bb1, 17);
        b = MD5STEP1( b, c, d, a, ii11 + 0x895cd7be, 22);
        a = MD5STEP1( a, b, c, d, ii12 + 0x6b901122,  7);
        d = MD5STEP1( d, a, b, c, ii13 + 0xfd987193, 12);
        c = MD5STEP1( c, d, a, b, ii14 + 0xa679438e, 17);
        b = MD5STEP1( b, c, d, a, ii15 + 0x49b40821, 22);

        a = MD5STEP2( a, b, c, d, ii01 + 0xf61e2562,  5);
        d = MD5STEP2( d, a, b, c, ii06 + 0xc040b340,  9);
        c = MD5STEP2( c, d, a, b, ii11 + 0x265e5a51, 14);
        b = MD5STEP2( b, c, d, a, ii00 + 0xe9b6c7aa, 20);
        a = MD5STEP2( a, b, c, d, ii05 + 0xd62f105d,  5);
        d = MD5STEP2( d, a, b, c, ii10 + 0x02441453,  9);
        c = MD5STEP2( c, d, a, b, ii15 + 0xd8a1e681, 14);
        b = MD5STEP2( b, c, d, a, ii04 + 0xe7d3fbc8, 20);
        a = MD5STEP2( a, b, c, d, ii09 + 0x21e1cde6,  5);
        d = MD5STEP2( d, a, b, c, ii14 + 0xc33707d6,  9);
        c = MD5STEP2( c, d, a, b, ii03 + 0xf4d50d87, 14);
        b = MD5STEP2( b, c, d, a, ii08 + 0x455a14ed, 20);
        a = MD5STEP2( a, b, c, d, ii13 + 0xa9e3e905,  5);
        d = MD5STEP2( d, a, b, c, ii02 + 0xfcefa3f8,  9);
        c = MD5STEP2( c, d, a, b, ii07 + 0x676f02d9, 14);
        b = MD5STEP2( b, c, d, a, ii12 + 0x8d2a4c8a, 20);

        a = MD5STEP3( a, b, c, d, ii05 + 0xfffa3942,  4);
        d = MD5STEP3( d, a, b, c, ii08 + 0x8771f681, 11);
        c = MD5STEP3( c, d, a, b, ii11 + 0x6d9d6122, 16);
        b = MD5STEP3( b, c, d, a, ii14 + 0xfde5380c, 23);
        a = MD5STEP3( a, b, c, d, ii01 + 0xa4beea44,  4);
        d = MD5STEP3( d, a, b, c, ii04 + 0x4bdecfa9, 11);
        c = MD5STEP3( c, d, a, b, ii07 + 0xf6bb4b60, 16);
        b = MD5STEP3( b, c, d, a, ii10 + 0xbebfbc70, 23);
        a = MD5STEP3( a, b, c, d, ii13 + 0x289b7ec6,  4);
        d = MD5STEP3( d, a, b, c, ii00 + 0xeaa127fa, 11);
        c = MD5STEP3( c, d, a, b, ii03 + 0xd4ef3085, 16);
        b = MD5STEP3( b, c, d, a, ii06 + 0x04881d05, 23);
        a = MD5STEP3( a, b, c, d, ii09 + 0xd9d4d039,  4);
        d = MD5STEP3( d, a, b, c, ii12 + 0xe6db99e5, 11);
        c = MD5STEP3( c, d, a, b, ii15 + 0x1fa27cf8, 16);
        b = MD5STEP3( b, c, d, a, ii02 + 0xc4ac5665, 23);

        a = MD5STEP4( a, b, c, d, ii00 + 0xf4292244,  6);
        d = MD5STEP4( d, a, b, c, ii07 + 0x432aff97, 10);
        c = MD5STEP4( c, d, a, b, ii14 + 0xab9423a7, 15);
        b = MD5STEP4( b, c, d, a, ii05 + 0xfc93a039, 21);
        a = MD5STEP4( a, b, c, d, ii12 + 0x655b59c3,  6);
        d = MD5STEP4( d, a, b, c, ii03 + 0x8f0ccc92, 10);
        c = MD5STEP4( c, d, a, b, ii10 + 0xffeff47d, 15);
        b = MD5STEP4( b, c, d, a, ii01 + 0x85845dd1, 21);
        a = MD5STEP4( a, b, c, d, ii08 + 0x6fa87e4f,  6);
        d = MD5STEP4( d, a, b, c, ii15 + 0xfe2ce6e0, 10);
        c = MD5STEP4( c, d, a, b, ii06 + 0xa3014314, 15);
        b = MD5STEP4( b, c, d, a, ii13 + 0x4e0811a1, 21);
        a = MD5STEP4( a, b, c, d, ii04 + 0xf7537e82,  6);
        d = MD5STEP4( d, a, b, c, ii11 + 0xbd3af235, 10);
        c = MD5STEP4( c, d, a, b, ii02 + 0x2ad7d2bb, 15);
        b = MD5STEP4( b, c, d, a, ii09 + 0xeb86d391, 21);

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

    /**
     * Sets the LSB fist
     * @param b
     * bytes
     * @param off
     * offset
     * @param value
     * LSB value
     */
    private void PUT_32BIT_LSB_FIRST(
        byte[] b,
        int off,
        int value)
    {
        b[off + 0] = (byte) (value);
        b[off + 1] = (byte) (value >>> 8);
        b[off + 2] = (byte) (value >>> 16);
        b[off + 3] = (byte) (value >>> 24);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy