Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   Java (http://www.programmingforums.org/forum17.html)
-   -   Trouble with an MD5 implementation (http://www.programmingforums.org/showthread.php?t=10928)

grimpirate Aug 2nd, 2006 5:01 PM

Trouble with an MD5 implementation
 
Well I recently made a small md5 hash calculator program and posted it on the finished projects section. However, upon reading some replies Sane said that perhaps I should try implementing it myself, and so I did. However, I'm unsure why my implementation isn't working. Here's the code, it's two java classes, a driver called MD5Test and the MD5 class:

MD5.java
:

public class MD5
{
        private static final int[] T = new int[64];

        public MD5()
        {
                int i = 0;
                T[i++] = 0xd76aa478;
                T[i++] = 0xe8c7b756;
                T[i++] = 0x242070db;
                T[i++] = 0xc1bdceee;
                T[i++] = 0xf57c0faf;
                T[i++] = 0x4787c62a;
                T[i++] = 0xa8304613;
                T[i++] = 0xfd469501;
                T[i++] = 0x698098d8;
                T[i++] = 0x8b44f7af;
                T[i++] = 0xffff5bb1;
                T[i++] = 0x895cd7be;
                T[i++] = 0x6b901122;
                T[i++] = 0xfd987193;
                T[i++] = 0xa679438e;
                T[i++] = 0x49b40821;
                T[i++] = 0xf61e2562;
                T[i++] = 0xc040b340;
                T[i++] = 0x265e5a51;
                T[i++] = 0xe9b6c7aa;
                T[i++] = 0xd62f105d;
                T[i++] = 0x2441453;
                T[i++] = 0xd8a1e681;
                T[i++] = 0xe7d3fbc8;
                T[i++] = 0x21e1cde6;
                T[i++] = 0xc33707d6;
                T[i++] = 0xf4d50d87;
                T[i++] = 0x455a14ed;
                T[i++] = 0xa9e3e905;
                T[i++] = 0xfcefa3f8;
                T[i++] = 0x676f02d9;
                T[i++] = 0x8d2a4c8a;
                T[i++] = 0xfffa3942;
                T[i++] = 0x8771f681;
                T[i++] = 0x6d9d6122;
                T[i++] = 0xfde5380c;
                T[i++] = 0xa4beea44;
                T[i++] = 0x4bdecfa9;
                T[i++] = 0xf6bb4b60;
                T[i++] = 0xbebfbc70;
                T[i++] = 0x289b7ec6;
                T[i++] = 0xeaa127fa;
                T[i++] = 0xd4ef3085;
                T[i++] = 0x4881d05;
                T[i++] = 0xd9d4d039;
                T[i++] = 0xe6db99e5;
                T[i++] = 0x1fa27cf8;
                T[i++] = 0xc4ac5665;
                T[i++] = 0xf4292244;
                T[i++] = 0x432aff97;
                T[i++] = 0xab9423a7;
                T[i++] = 0xfc93a039;
                T[i++] = 0x655b59c3;
                T[i++] = 0x8f0ccc92;
                T[i++] = 0xffeff47d;
                T[i++] = 0x85845dd1;
                T[i++] = 0x6fa87e4f;
                T[i++] = 0xfe2ce6e0;
                T[i++] = 0xa3014314;
                T[i++] = 0x4e0811a1;
                T[i++] = 0xf7537e82;
                T[i++] = 0xbd3af235;
                T[i++] = 0x2ad7d2bb;
                T[i++] = 0xeb86d391;
        }

        public String hash()
        {
                int A = 0x67452301;
                int B = 0xefcdab89;
                int C = 0x98badcfe;
                int D = 0x10325476;

                int[] M = new int[16];
                int[] X = new int[16];

                // A message of no length
                // Starts here
                M[0] = 0x80000000;

                for(int i = 1; i < M.length; i++)
                {
                        M[i] = 0;
                }
                // Ends here

                for(int i = 0; i < M.length / 16; i++)
                {
                        for(int j = 0; j < 16; j++)
                        {
                                X[j] = M[i * 16 + j];
                        }

                        int AA = A;
                        int BB = B;
                        int CC = C;
                        int DD = D;

                        A = round1(A, B, C, D, X[0], 7, 1 - 1);
                        D = round1(D, A, B, C, X[1], 12, 2 - 1);
                        C = round1(C, D, A, B, X[2], 17, 3 - 1);
                        B = round1(B, C, D, A, X[3], 22, 4 - 1);
                        A = round1(A, B, C, D, X[4], 7, 5 - 1);
                        D = round1(D, A, B, C, X[5], 12, 6 - 1);
                        C = round1(C, D, A, B, X[6], 17, 7 - 1);
                        B = round1(B, C, D, A, X[7], 22, 8 - 1);
                        A = round1(A, B, C, D, X[8], 7, 9 - 1 );
                        D = round1(D, A, B, C, X[9], 12, 10 - 1);
                        C = round1(C, D, A, B, X[10], 17, 11 - 1);
                        B = round1(B, C, D, A, X[11], 22, 12 - 1);
                        A = round1(A, B, C, D, X[12], 7, 13 - 1);
                        D = round1(D, A, B, C, X[13], 12, 14 - 1);
                        C = round1(C, D, A, B, X[14], 17, 15 - 1);
                        B = round1(B, C, D, A, X[15], 22, 16 - 1);
                        A = round2(A, B, C, D, X[1], 5, 17 - 1);
                        D = round2(D, A, B, C, X[6], 9, 18 - 1);
                        C = round2(C, D, A, B, X[11], 14, 19 - 1);
                        B = round2(B, C, D, A, X[0], 20, 20 - 1);
                        A = round2(A, B, C, D, X[5], 5, 21 - 1);
                        D = round2(D, A, B, C, X[10], 9, 22 - 1);
                        C = round2(C, D, A, B, X[15], 14, 23 - 1);
                        B = round2(B, C, D, A, X[4], 20, 24 - 1);
                        A = round2(A, B, C, D, X[9], 5, 25 - 1);
                        D = round2(D, A, B, C, X[14], 9, 26 - 1);
                        C = round2(C, D, A, B, X[3], 14, 27 - 1);
                        B = round2(B, C, D, A, X[8], 20, 28 - 1);
                        A = round2(A, B, C, D, X[13], 5, 29 - 1);
                        D = round2(D, A, B, C, X[2], 9, 30 - 1);
                        C = round2(C, D, A, B, X[7], 14, 31 - 1);
                        B = round2(B, C, D, A, X[12], 20, 32 - 1);
                        A = round3(A, B, C, D, X[5], 4, 33 - 1);
                        D = round3(D, A, B, C, X[8], 11, 34 - 1);
                        C = round3(C, D, A, B, X[11], 16, 35 - 1);
                        B = round3(B, C, D, A, X[14], 23, 36 - 1);
                        A = round3(A, B, C, D, X[1], 4, 37 - 1);
                        D = round3(D, A, B, C, X[4], 11, 38 - 1);
                        C = round3(C, D, A, B, X[7], 16, 39 - 1);
                        B = round3(B, C, D, A, X[10], 23, 40 - 1);
                        A = round3(A, B, C, D, X[13], 4, 41 - 1);
                        D = round3(D, A, B, C, X[0], 11, 42 - 1);
                        C = round3(C, D, A, B, X[3], 16, 43 - 1);
                        B = round3(B, C, D, A, X[6], 23, 44 - 1);
                        A = round3(A, B, C, D, X[9], 4, 45 - 1);
                        D = round3(D, A, B, C, X[12], 11, 46 - 1);
                        C = round3(C, D, A, B, X[15], 16, 47 - 1);
                        B = round3(B, C, D, A, X[2], 23, 48 - 1);
                        A = round4(A, B, C, D, X[0], 6, 49 - 1);
                        D = round4(D, A, B, C, X[7], 10, 50 - 1);
                        C = round4(C, D, A, B, X[14], 15, 51 - 1);
                        B = round4(B, C, D, A, X[5], 21, 52 - 1);
                        A = round4(A, B, C, D, X[12], 6, 53 - 1);
                        D = round4(D, A, B, C, X[3], 10, 54 - 1);
                        C = round4(C, D, A, B, X[10], 15, 55 - 1);
                        B = round4(B, C, D, A, X[1], 21, 56 - 1);
                        A = round4(A, B, C, D, X[8], 6, 57 - 1);
                        D = round4(D, A, B, C, X[15], 10, 58 - 1);
                        C = round4(C, D, A, B, X[6], 15, 59 - 1);
                        B = round4(B, C, D, A, X[13], 21, 60 - 1);
                        A = round4(A, B, C, D, X[4], 6, 61 - 1);
                        D = round4(D, A, B, C, X[11], 10, 62 - 1);
                        C = round4(C, D, A, B, X[2], 15, 63 - 1);
                        B = round4(B, C, D, A, X[9], 21, 64 - 1);

                        A += AA;
                        B += BB;
                        C += CC;
                        D += DD;
                }

                return Integer.toHexString(A) + Integer.toHexString(B) + Integer.toHexString(C) + Integer.toHexString(D);
        }

        private int round1(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + lShiftRotate((a + F(b, c, d) + x + T[i]), s);
        }

        private int round2(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + lShiftRotate((a + G(b, c, d) + x + T[i]), s);
        }

        private int round3(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + lShiftRotate((a + H(b, c, d) + x + T[i]), s);
        }

        private int round4(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + lShiftRotate((a + I(b, c, d) + x + T[i]), s);
        }

        private int F(int X, int Y, int Z)
        {
                return X & Y | ~X & Z;
        }

        private int G(int X, int Y, int Z)
        {
                return X & Z | Y & ~Z;
        }

        private int H(int X, int Y, int Z)
        {
                return X ^ Y ^ Z;
        }

        private int I(int X, int Y, int Z)
        {
                return Y ^ (X | ~Z);
        }

        public static int lShiftRotate(int x, int y)
        {
                int mask = 0x80000000;

                for(int i = 0; i < y; i++)
                {
                        if((x & mask) == 0)
                        {
                                x = x << 1;
                        }
                        else
                        {
                                x = (x << 1) + 1;
                        }
                }

                return x;
        }
}

MD5Test.java
:

public class MD5Test
{
        public static void main(String[] args)
        {
                MD5 message = new MD5();
                System.out.println(message.hash());
        }
}

Right now the program is only supposed to compute the has/checksum of an empty string/file or whatever. It's the array listed as M, which is originally 0 but as I understand it, if a message has 0 length then it should start with a 1 and follow with 511 0's, which is what I did there to the best of my knowledge. I'm not sure whether or not A, B, C, and D are their proper values as there's some reference to big-endian or little-endian notation and I'm unsure as how to proceed. I copied the values from the RFC reference implementation, as well as the values for T which are supposed to be obtainable by performing a calculation along the lines of Math.floor(Math.abs(Math.sin(i)) * Math.pow(2, 32)) where {i: 1...64}, buuuuuuuuuuut those values weren't anywhere even remotely close when I actually computed it to the hex values given in the reference implementation. If anybody could point out where I have an error, or for that matter a series of cascading errors lol, I'd appreciate it.

P.S. As stated in the RFC the hash for a string of "" should be
MD5 ("") = d41d8cd98f00b204e9800998ecf8427e

grimpirate Aug 2nd, 2006 5:49 PM

uhh... yeah I typed in those values for nothing in the constructor 'cause I actually figured out how to work the formula, anyways here's how to determine the values of T according to the stipulation in the RFC:
:

        public MD5()
        {
                for(int i = 0; i < T.length; i++)
                {
                        double temp = Math.abs(Math.sin(i + 1)) * Math.pow(2, 32);
                        if(temp > Math.pow(2, 32) / 2 - 1)
                        {
                                T[i] = (int)Math.floor(temp - Math.pow(2, 32));
                        }
                        else
                        {
                                T[i] = (int)Math.floor(temp);
                        }
                }
        }

That's what the constructor should look like, which betters the code visually I suppose, but I assume it also slows down the program since having the values readily available take less clock cycles. I leave it to you to choose which one you like. I'm sure there's also an easier way than the one that I chose, some sort of casting using a long of some sort.

DaWei Aug 2nd, 2006 11:02 PM

No wonder pirates are grim.

grimpirate Aug 3rd, 2006 12:36 AM

The C is a harsh mistress. That's why we drink plenty of Java. ahahaha

AntiNinja Aug 3rd, 2006 12:43 AM

Quote:

Originally Posted by grimpirate
The C is a harsh mistress. That's why we drink plenty of Java.

:rolleyes:

grimpirate Aug 4th, 2006 3:23 PM

Breakthrough!
 
yes I discovered what the problem was with my code. As it turns out the implementation was correct there were no little tiny errors or anything. However, what happens is that Rivest fails to mention in the RFC that input is assumed to be in a little-endian format and therefore needs to be decoded into big-endian format. Furthermore, the outputs A, B, C, and D are in little-endian format and need to be output into big-endian format. Why did he do this? I have no idea but as far as I'm concerned the man's an idiot, especially since it just seems to be wasting clock cycles. In my code you'll note the two major changes: the array value's inital value M[0] is now 0x00000080 instead of 0x80000000 and I added a function called encode which transforms from little-endian into big-endian. This is also why the values of A, B, C, and D, when initialized look mirrored to how they are written in the RFC. Here's the updated MD5 class
MD5.java
:

import java.lang.Math;

public class MD5
{
        private static int[] T = new int[64];

        public MD5()
        {
                for(int i = 0; i < T.length; i++)
                {
                        double temp = Math.abs(Math.sin(i + 1)) * Math.pow(2, 32);
                        if(temp > Math.pow(2, 32) / 2 - 1)
                        {
                                T[i] = (int)Math.floor(temp - Math.pow(2, 32));
                        }
                        else
                        {
                                T[i] = (int)Math.floor(temp);
                        }
                }
        }

        public String hash()
        {
                int A = 0x67452301;
                int B = 0xefcdab89;
                int C = 0x98badcfe;
                int D = 0x10325476;

                int[] M = new int[16];
                int[] X = new int[16];

                // A message of no length
                // Starts here

                // This was reversed because the input is supposed to come in little-endian format
                // while computations are performed in a big-endian style
                M[0] = 0x00000080;

                for(int i = 1; i < M.length; i++)
                {
                        M[i] = 0;
                }
                // Ends here

                for(int i = 0; i < M.length / X.length; i++)
                {
                        for(int j = 0; j < X.length; j++)
                        {
                                X[j] = M[i * 16 + j];
                        }

                        int AA = A;
                        int BB = B;
                        int CC = C;
                        int DD = D;

                        A = round1(A, B, C, D, X[0], 7, 1 - 1);
                        D = round1(D, A, B, C, X[1], 12, 2 - 1);
                        C = round1(C, D, A, B, X[2], 17, 3 - 1);
                        B = round1(B, C, D, A, X[3], 22, 4 - 1);

                        A = round1(A, B, C, D, X[4], 7, 5 - 1);
                        D = round1(D, A, B, C, X[5], 12, 6 - 1);
                        C = round1(C, D, A, B, X[6], 17, 7 - 1);
                        B = round1(B, C, D, A, X[7], 22, 8 - 1);

                        A = round1(A, B, C, D, X[8], 7, 9 - 1 );
                        D = round1(D, A, B, C, X[9], 12, 10 - 1);
                        C = round1(C, D, A, B, X[10], 17, 11 - 1);
                        B = round1(B, C, D, A, X[11], 22, 12 - 1);

                        A = round1(A, B, C, D, X[12], 7, 13 - 1);
                        D = round1(D, A, B, C, X[13], 12, 14 - 1);
                        C = round1(C, D, A, B, X[14], 17, 15 - 1);
                        B = round1(B, C, D, A, X[15], 22, 16 - 1);

                        A = round2(A, B, C, D, X[1], 5, 17 - 1);
                        D = round2(D, A, B, C, X[6], 9, 18 - 1);
                        C = round2(C, D, A, B, X[11], 14, 19 - 1);
                        B = round2(B, C, D, A, X[0], 20, 20 - 1);

                        A = round2(A, B, C, D, X[5], 5, 21 - 1);
                        D = round2(D, A, B, C, X[10], 9, 22 - 1);
                        C = round2(C, D, A, B, X[15], 14, 23 - 1);
                        B = round2(B, C, D, A, X[4], 20, 24 - 1);

                        A = round2(A, B, C, D, X[9], 5, 25 - 1);
                        D = round2(D, A, B, C, X[14], 9, 26 - 1);
                        C = round2(C, D, A, B, X[3], 14, 27 - 1);
                        B = round2(B, C, D, A, X[8], 20, 28 - 1);

                        A = round2(A, B, C, D, X[13], 5, 29 - 1);
                        D = round2(D, A, B, C, X[2], 9, 30 - 1);
                        C = round2(C, D, A, B, X[7], 14, 31 - 1);
                        B = round2(B, C, D, A, X[12], 20, 32 - 1);

                        A = round3(A, B, C, D, X[5], 4, 33 - 1);
                        D = round3(D, A, B, C, X[8], 11, 34 - 1);
                        C = round3(C, D, A, B, X[11], 16, 35 - 1);
                        B = round3(B, C, D, A, X[14], 23, 36 - 1);

                        A = round3(A, B, C, D, X[1], 4, 37 - 1);
                        D = round3(D, A, B, C, X[4], 11, 38 - 1);
                        C = round3(C, D, A, B, X[7], 16, 39 - 1);
                        B = round3(B, C, D, A, X[10], 23, 40 - 1);

                        A = round3(A, B, C, D, X[13], 4, 41 - 1);
                        D = round3(D, A, B, C, X[0], 11, 42 - 1);
                        C = round3(C, D, A, B, X[3], 16, 43 - 1);
                        B = round3(B, C, D, A, X[6], 23, 44 - 1);

                        A = round3(A, B, C, D, X[9], 4, 45 - 1);
                        D = round3(D, A, B, C, X[12], 11, 46 - 1);
                        C = round3(C, D, A, B, X[15], 16, 47 - 1);
                        B = round3(B, C, D, A, X[2], 23, 48 - 1);

                        A = round4(A, B, C, D, X[0], 6, 49 - 1);
                        D = round4(D, A, B, C, X[7], 10, 50 - 1);
                        C = round4(C, D, A, B, X[14], 15, 51 - 1);
                        B = round4(B, C, D, A, X[5], 21, 52 - 1);

                        A = round4(A, B, C, D, X[12], 6, 53 - 1);
                        D = round4(D, A, B, C, X[3], 10, 54 - 1);
                        C = round4(C, D, A, B, X[10], 15, 55 - 1);
                        B = round4(B, C, D, A, X[1], 21, 56 - 1);

                        A = round4(A, B, C, D, X[8], 6, 57 - 1);
                        D = round4(D, A, B, C, X[15], 10, 58 - 1);
                        C = round4(C, D, A, B, X[6], 15, 59 - 1);
                        B = round4(B, C, D, A, X[13], 21, 60 - 1);

                        A = round4(A, B, C, D, X[4], 6, 61 - 1);
                        D = round4(D, A, B, C, X[11], 10, 62 - 1);
                        C = round4(C, D, A, B, X[2], 15, 63 - 1);
                        B = round4(B, C, D, A, X[9], 21, 64 - 1);

                        A += AA;
                        B += BB;
                        C += CC;
                        D += DD;
                }

                return Integer.toHexString(encode(A)) + Integer.toHexString(encode(B)) + Integer.toHexString(encode(C)) + Integer.toHexString(encode(D));
        }

        private int round1(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + Integer.rotateLeft((a + F(b, c, d) + x + T[i]), s);
        }

        private int round2(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + Integer.rotateLeft((a + G(b, c, d) + x + T[i]), s);
        }

        private int round3(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + Integer.rotateLeft((a + H(b, c, d) + x + T[i]), s);
        }

        private int round4(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + Integer.rotateLeft((a + I(b, c, d) + x + T[i]), s);
        }

        private int F(int X, int Y, int Z)
        {
                return X & Y | ~X & Z;
        }

        private int G(int X, int Y, int Z)
        {
                return X & Z | Y & ~Z;
        }

        private int H(int X, int Y, int Z)
        {
                return X ^ Y ^ Z;
        }

        private int I(int X, int Y, int Z)
        {
                return Y ^ (X | ~Z);
        }

        // This method converts an input from little-endian into big-endian
        private int encode(int x)
        {
                int mask[] = new int[4];

                mask[0] = 0xff000000;
                mask[1] = 0x00ff0000;
                mask[2] = 0x0000ff00;
                mask[3] = 0x000000ff;

                return Integer.rotateRight(x & mask[0], 24) + Integer.rotateRight(x & mask[1], 8) + Integer.rotateLeft(x & mask[2], 8) + Integer.rotateLeft(x & mask[3], 24);
        }
}

I'll keep posting whatever advancements I make with it. Like accepting InputStreams or strings or whatever. But for now it at least produces the correct output for an input of no length. Pirates rule!

Infinite Recursion Aug 4th, 2006 4:13 PM

[aside]
"Pirates rule!"

Today was deemed "Pirate's Day" by my employer. The upper management dressed up as pirates and came around the offices (aka Cubevile, aka Dilbertville) handing out "stolen treasure".... I threw all of the crap aside to try and find the gold, but no gold and no check... Management needs to learn how to code, they have too much free time on their hands... bah.

[/aside]

grimpirate Aug 8th, 2006 5:52 AM

Well I got it to accept bytes, so to all you programmers out there please try as many different strings as you can come up with and lemme know if they produce the wrong output, assuming you care to that is. I tried it out with Rivest's test suite and it worked fine and with some of my own so I'm gonna say it's working. Here are the two classes again:
MD5.java
:

import java.lang.Math;

public class MD5
{
        private int[] T = new int[64];

        private int A = 0x67452301;
        private        int B = 0xefcdab89;
        private        int C = 0x98badcfe;
        private int D = 0x10325476;

        private long bytesTransferred = 0;
        private int bytesSwitch = 0;

        private int[] X;

        private int entrySwitch = 0;

        private int transferredWord = 0;

        public MD5()
        {
                for(int i = 0; i < T.length; i++)
                {
                        double temp = Math.abs(Math.sin(i + 1)) * Math.pow(2, 32);
                        if(temp > Math.pow(2, 32) / 2 - 1)
                        {
                                T[i] = (int)Math.floor(temp - Math.pow(2, 32));
                        }
                        else
                        {
                                T[i] = (int)Math.floor(temp);
                        }
                }

                X = new int[16];
        }

        public void update(byte chunk)
        {
                bytesTransferred++;
                bytesSwitch++;

                int temp = 0;

                switch(bytesSwitch)
                {
                        case 1:
                                temp = chunk & 0x000000ff;
                                break;
                        case 2:
                                temp = chunk & 0x000000ff;
                                temp = temp << 8;
                                break;
                        case 3:
                                temp = chunk & 0x000000ff;
                                temp = temp << 16;
                                break;
                        case 4:
                                temp = chunk & 0x000000ff;
                                temp = temp << 24;
                                break;
                }

                transferredWord = transferredWord ^ temp;
                if(bytesSwitch == 4)
                {
                        bytesSwitch = 0;
                        X[entrySwitch] = transferredWord;
                        entrySwitch++;
                        transferredWord = 0;
                }

//                int[] M = new int[16];

                // A message of no length
                // Starts here

                // This was reversed because the input is supposed to come in little-endian format
                // while computations are performed in a big-endian style
//                M[0] = 0x80636261;        // "abc"
//                M[1] = 0;
//                M[2] = 0;
//                M[3] = 0;
//                M[4] = 0;
//                M[5] = 0;
//                M[6] = 0;
//                M[7] = 0;
//                M[8] = 0;
//                M[9] = 0;
//                M[10] = 0;
//                M[11] = 0;
//                M[12] = 0;
//                M[13] = 0;
//                M[14] = 0x00000018;
//                M[15] = 0x00000000;
                // Ends here

//                for(int i = 0; i < M.length / X.length; i++)
//                {
//                        for(int j = 0; j < X.length; j++)
//                        {
//                                X[j] = M[i * 16 + j];
//                        }
                if(entrySwitch == 16)
                {
                        entrySwitch = 0;

                        int AA = A;
                        int BB = B;
                        int CC = C;
                        int DD = D;

                        A = round1(A, B, C, D, X[0], 7, 1 - 1);
                        D = round1(D, A, B, C, X[1], 12, 2 - 1);
                        C = round1(C, D, A, B, X[2], 17, 3 - 1);
                        B = round1(B, C, D, A, X[3], 22, 4 - 1);

                        A = round1(A, B, C, D, X[4], 7, 5 - 1);
                        D = round1(D, A, B, C, X[5], 12, 6 - 1);
                        C = round1(C, D, A, B, X[6], 17, 7 - 1);
                        B = round1(B, C, D, A, X[7], 22, 8 - 1);

                        A = round1(A, B, C, D, X[8], 7, 9 - 1 );
                        D = round1(D, A, B, C, X[9], 12, 10 - 1);
                        C = round1(C, D, A, B, X[10], 17, 11 - 1);
                        B = round1(B, C, D, A, X[11], 22, 12 - 1);

                        A = round1(A, B, C, D, X[12], 7, 13 - 1);
                        D = round1(D, A, B, C, X[13], 12, 14 - 1);
                        C = round1(C, D, A, B, X[14], 17, 15 - 1);
                        B = round1(B, C, D, A, X[15], 22, 16 - 1);

                        A = round2(A, B, C, D, X[1], 5, 17 - 1);
                        D = round2(D, A, B, C, X[6], 9, 18 - 1);
                        C = round2(C, D, A, B, X[11], 14, 19 - 1);
                        B = round2(B, C, D, A, X[0], 20, 20 - 1);

                        A = round2(A, B, C, D, X[5], 5, 21 - 1);
                        D = round2(D, A, B, C, X[10], 9, 22 - 1);
                        C = round2(C, D, A, B, X[15], 14, 23 - 1);
                        B = round2(B, C, D, A, X[4], 20, 24 - 1);

                        A = round2(A, B, C, D, X[9], 5, 25 - 1);
                        D = round2(D, A, B, C, X[14], 9, 26 - 1);
                        C = round2(C, D, A, B, X[3], 14, 27 - 1);
                        B = round2(B, C, D, A, X[8], 20, 28 - 1);

                        A = round2(A, B, C, D, X[13], 5, 29 - 1);
                        D = round2(D, A, B, C, X[2], 9, 30 - 1);
                        C = round2(C, D, A, B, X[7], 14, 31 - 1);
                        B = round2(B, C, D, A, X[12], 20, 32 - 1);

                        A = round3(A, B, C, D, X[5], 4, 33 - 1);
                        D = round3(D, A, B, C, X[8], 11, 34 - 1);
                        C = round3(C, D, A, B, X[11], 16, 35 - 1);
                        B = round3(B, C, D, A, X[14], 23, 36 - 1);

                        A = round3(A, B, C, D, X[1], 4, 37 - 1);
                        D = round3(D, A, B, C, X[4], 11, 38 - 1);
                        C = round3(C, D, A, B, X[7], 16, 39 - 1);
                        B = round3(B, C, D, A, X[10], 23, 40 - 1);

                        A = round3(A, B, C, D, X[13], 4, 41 - 1);
                        D = round3(D, A, B, C, X[0], 11, 42 - 1);
                        C = round3(C, D, A, B, X[3], 16, 43 - 1);
                        B = round3(B, C, D, A, X[6], 23, 44 - 1);

                        A = round3(A, B, C, D, X[9], 4, 45 - 1);
                        D = round3(D, A, B, C, X[12], 11, 46 - 1);
                        C = round3(C, D, A, B, X[15], 16, 47 - 1);
                        B = round3(B, C, D, A, X[2], 23, 48 - 1);

                        A = round4(A, B, C, D, X[0], 6, 49 - 1);
                        D = round4(D, A, B, C, X[7], 10, 50 - 1);
                        C = round4(C, D, A, B, X[14], 15, 51 - 1);
                        B = round4(B, C, D, A, X[5], 21, 52 - 1);

                        A = round4(A, B, C, D, X[12], 6, 53 - 1);
                        D = round4(D, A, B, C, X[3], 10, 54 - 1);
                        C = round4(C, D, A, B, X[10], 15, 55 - 1);
                        B = round4(B, C, D, A, X[1], 21, 56 - 1);

                        A = round4(A, B, C, D, X[8], 6, 57 - 1);
                        D = round4(D, A, B, C, X[15], 10, 58 - 1);
                        C = round4(C, D, A, B, X[6], 15, 59 - 1);
                        B = round4(B, C, D, A, X[13], 21, 60 - 1);

                        A = round4(A, B, C, D, X[4], 6, 61 - 1);
                        D = round4(D, A, B, C, X[11], 10, 62 - 1);
                        C = round4(C, D, A, B, X[2], 15, 63 - 1);
                        B = round4(B, C, D, A, X[9], 21, 64 - 1);

                        A += AA;
                        B += BB;
                        C += CC;
                        D += DD;
                }
        }

        public String finish()
        {
                long fileSize = bytesTransferred * 8;        // in bits
                byte sizePad[] = new byte[8];

                sizePad[0] = (byte)(fileSize & 0x00000000000000ffL);
                sizePad[1] = (byte)((fileSize >> 8) & 0x00000000000000ffL);
                sizePad[2] = (byte)((fileSize >> 16) & 0x00000000000000ffL);
                sizePad[3] = (byte)((fileSize >> 24) & 0x00000000000000ffL);
                sizePad[4] = (byte)((fileSize >> 32) & 0x00000000000000ffL);
                sizePad[5] = (byte)((fileSize >> 40) & 0x00000000000000ffL);
                sizePad[6] = (byte)((fileSize >> 48) & 0x00000000000000ffL);
                sizePad[7] = (byte)((fileSize >> 56) & 0x00000000000000ffL);

                update((byte)0x80);

                long incongruent = bytesTransferred % 64;

                if(incongruent < 57)
                {
                        for(long i = incongruent; i < 56; i++)
                        {
                                update((byte)0x00);
                        }
                }
                else
                {
                        for(long i = incongruent; i < 64; i++)
                        {
                                update((byte)0x00);
                        }
                        for(int i = 0; i < 56; i++)
                        {
                                update((byte)0x00);
                        }
                }

                for(int i = 0; i < sizePad.length; i++)
                {
                        update(sizePad[i]);
                }

                return parseHash();
        }

        private String parseHash()
        {
                String part[] = new String[4];

                part[0] = Integer.toHexString(encode(A));
                part[1] = Integer.toHexString(encode(B));
                part[2] = Integer.toHexString(encode(C));
                part[3] = Integer.toHexString(encode(D));

                for(int j = 0; j < part.length; j++)
                {
                        for(int i = 0; i < 8 - part[j].length(); i++)
                        {
                                part[j] = "0" + part[j];
                        }
                }

                return part[0] + part[1] + part[2] + part[3];
        }

        private int round1(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + Integer.rotateLeft((a + F(b, c, d) + x + T[i]), s);
        }

        private int round2(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + Integer.rotateLeft((a + G(b, c, d) + x + T[i]), s);
        }

        private int round3(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + Integer.rotateLeft((a + H(b, c, d) + x + T[i]), s);
        }

        private int round4(int a, int b, int c, int d, int x, int s, int i)
        {
                return b + Integer.rotateLeft((a + I(b, c, d) + x + T[i]), s);
        }

        private int F(int X, int Y, int Z)
        {
                return X & Y | ~X & Z;
        }

        private int G(int X, int Y, int Z)
        {
                return X & Z | Y & ~Z;
        }

        private int H(int X, int Y, int Z)
        {
                return X ^ Y ^ Z;
        }

        private int I(int X, int Y, int Z)
        {
                return Y ^ (X | ~Z);
        }

        // This method converts an input from little-endian into big-endian
        private int encode(int x)
        {
                int mask[] = new int[4];

                mask[0] = 0xff000000;
                mask[1] = 0x00ff0000;
                mask[2] = 0x0000ff00;
                mask[3] = 0x000000ff;

                return Integer.rotateRight(x & mask[0], 24) + Integer.rotateRight(x & mask[1], 8) + Integer.rotateLeft(x & mask[2], 8) + Integer.rotateLeft(x & mask[3], 24);
        }
}

And its driver:
MD5Test.java
:

import java.lang.Math;
public class MD5Test
{
        public static void main(String[] args)
        {
                MD5 message = new MD5();
                String foo = new String("a"); // Place test string here
                byte bar[] = foo.getBytes();
                for(int i = 0; i < bar.length; i++)
                {
                        message.update(bar[i]);
                }
                System.out.println(message.finish());

        }
}


grimpirate Aug 8th, 2006 5:57 AM

I think I'm going to take Rivest's idea and just make my own algorithm by ignoring the whole little-endian big-endian conversion. It seems like a grandiose waste of processing cycles and of no significant mathematical importance. Onward with pirate recklessness.


All times are GMT -5. The time now is 12:50 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC