Understanding the Different Message Digest Algorithms

  

The previous section mentioned that there are several parts that make up the message digest algorithm. These parts are initialization, message padding, appending length, computation parts (that include rounds and steps), and the compression to the digest size (that is usually based on the initialization registers). These parts of the message digest algorithm determine the outcome of speed, complexity, integrity, and security. The number of steps and how many operations there are in each step affect the speed of the computation. The size of the blocks and digest size may also affect the speed and security of the algorithm.

Tip  

When the digest has a higher bit size, the algorithm has a higher security and integrity.

A hash algorithm needs to have a high collision resistance , meaning that there should be a very low chance that two different messages can produce the same digest. The only guarantee that two messages can produce different digests is if the message itself is the digest. There is always a chance that a random message could produce the same digest. When different messages produce the same digest, the algorithm has collisions .

Tip  

The higher the bits for the digest, the lower the probability of producing the same digest.

MD algorithms

Ron Rivest, the "R" in RSA security, started many of the message digest algorithms. More information on Mr. Rivest can be found at http://theory.lcs.mit.edu/~rivest/ . Rivest designed MD2 , MD4 , and MD5 for message digest 2, message digest 4, and message digest 5.

Note  

These protocols can be studied in:

RFC 1319: [RFC1319] Rivest, R. RFC1319 "The MD2 Message-Digest Algorithm," Internet Engineering Task Force, http://www.ietf.org/rfc/rfc1319.txt , 1992.

RFC 1320: Rivest, R. RFC1320 "The MD4 Message-Digest Algorithm," Internet Engineering Task Force, http://www.ietf.org/rfc/rfc1320.txt , 1992.

RFC 1321: [RFC1321] Rivest, R. RFC1321 "The MD5 Message-Digest Algorithm," Internet Engineering Task Force, http://www.ietf.org/rfc/rfc1321.txt , 1992.

Many subsequent protocols such as SHA-1 and RIPEMD-160 have been based on MD4. All three MD algorithms used a 128-bit digest size. The MD2 was the fastest , followed by MD4 and then MD5. Collisions have been detected in all three algorithms, and RSA's Laboratories' bulletin number 4 recommends that only MD5 be used when possible. The most collisions were found in the MD2 algorithm, followed by the MD4 algorithm, and then the MD5 algorithm.

The difference between these algorithms is the complexity of their computation. MD2 uses two rounds of 16 steps, MD4 uses three rounds of 16 steps, and MD5 uses four rounds of 16 steps. Thealgorithms with the lowest rounds and least complexity are the ones that are the fastest in computational time. There was also a difference in the initialization values. Only MD5 and MD4 use initialized registers. The initialization values for the MD5 and MD4 that are used as chaining variables are defined as follows :

A = 0x67452301;

B = 0xEFCDAB89;

C = 0x98BADCFE;

D = 0x10325476;

MD4 was designed specifically for implementations on 32-bit machines. Security concerns motivated the design for MD5 shortly thereafter. A 32-bit register is normally a hardware register that processes 32 bits at a time. Because of the breakdown into 32-bit registers, which maps to integers in Java, many algorithms have been modeled after MD4, including MD5. MD5 was designed to strengthen MD4 because it was thought that collisions might occur when using it. Even though collisions have been found, MD5 is widely used because the quickness of the algorithm far outweighs the calculation of the collision. Finding a collision in MD5 is almost as complex as finding the key for a DES cracker, and many believe that it would take a machine that costs $10 million (in 1996 dollars) 24 days to find a collision.

MD5 uses a 512-bit block. Sixty-four bits are used to describe the length of the block, leaving 448 bits for data. Padding is always added, even if the correct size was achieved by the input data. If the message size was originally 448 bits, which is the desired size, a pad must still be applied to add another 512-bit block. The number of padding bits can be anywhere from 1 to 512 bits.

The MD5 computation will execute four rounds with 16 steps each. Each 512-bit block will be executed in the four rounds instead of the MD4 three rounds. Because of the difference in computational power, MD5 is about 25% slower than MD4.

Note  

Here is a brief comparison of MD2, MD4, and MD5:

  1. All use a 128-bit digest size.

  2. They differ on the number of rounds:

    MD2 uses 2 rounds of 16 steps.

    MD4 uses 3 rounds of 16 steps.

    MD5 uses 4 rounds of 16 steps.

  3. MD4 and MD5 use initialized registers (32-bit) and use chaining variables.

  4. MD5 uses a 512-bit block: 64 bits for the length of the block, 448 bits for data.

The reference implementation given in the C language of MD5 is given in RFC 1321. Listing 9-1 gives my Java interpretation of the reference implementation.

Listing 9-1: The MD5 implementation
start example
 package com.richware.chap09;     import java.security.*; /**  * Class RichMD5  * Description: This is an example  * implementation of the MD5  * algorithm.  *  * Copyright:    Copyright (c) 2002 Wiley Publishing, Inc.  * @author Rich Helton <rhelton@richware.com>  * @version 1.0   * DISCLAIMER: Please refer to the disclaimer at the beginning of this book. */ public class RichMD5  {   public final static String[][] testData =    {     //    data string, md hex     { "", "D41D8CD98F00B204E9800998ECF8427E" },     // A.5 1     { "a", "0CC175B9C0F1B6A831C399E269772661" },    // A.5 2     { "aa", "4124BC0A9335C27F086F24BA207A4912" },     { "abc", "900150983CD24FB0D6963F7D28E17F72" },  // A.5 3     { "aaa", "47BCE5C74F589F4867DBD57E9CA9F808" },     { "bbb", "08F8E0260C64418510CEFB2B06EEE5CD" },     { "ccc", "9DF62E693988EB4E1E1444ECE0578579" },     { "message digest", "F96B697D7CB7938D525A2F31AAF161D0" },  // A.5 4     { "abcdefg", "7AC66C0F148DE9519B8BD264312C4D64" },     { "abcdefghijk", "92B9CCCC0B98C3A0B8D0DF25A421C0E3" },      {                                              // A.5 5       "abcdefghijklmnopqrstuvwxyz",       "C3FCD3D76192E4007DFB496CCA67E13B"     },      {                                              // A.5 6       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",       "D174AB98D277D9F5A5611C2C9F419D9F"     },      {                                              // A.5 7       "12345678901234567890123456789012345678901234567890123456789012345678901234567890",       "57EDF4A22BE3C955AC49DA2E2107B67A"     },   };   public static final char[] hexDigits =    {     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',     'C', 'D', 'E', 'F'   };   private static final int   S11       = 7;   private static final int   S12       = 12;   private static final int   S13       = 17;   private static final int   S14       = 22;   private static final int   S21       = 5;   private static final int   S22       = 9;   private static final int   S23       = 14;   private static final int   S24       = 20;   private static final int   S31       = 4;   private static final int   S32       = 11;   private static final int   S33       = 16;   private static final int   S34       = 23;    private static final int   S41       = 6;   private static final int   S42       = 10;   private static final int   S43       = 15;   private static final int   S44       = 21;   private long               count;       /** 4 32-bit words (interim result) */   private int[] context = new int[4];       /** 512 bits work buffer = 16 x 32-bit words */   private int[] x = new int[16];   private byte  digestBits[];   private byte  buffer[];       /**    * Constructor RichMD5    */   public RichMD5()    {     MD5Init();   }       protected void MD5Init()    {     // initial values of MD5 i.e. A, B, C, D     context[0] = 0x67452301;     context[1] = 0xEFCDAB89;     context[2] = 0x98BADCFE;     context[3] = 0x10325476;     buffer     = new byte[64];     count      = 0L;      digestBits = new byte[16];     for (int i = 0; i < digestBits.length; i++)      {       digestBits[i] = 0;     }   }       /**    * Method MD5Update    * @param input    * @param inputLen    *    */   public void MD5Update(byte[] input, int inputLen)    {     int k = 0;     int j = inputLen;     while (j > 0)      {       int l = (int) (count >>> 3 & 63L);       if ((l == 0) && (j > 64))        {         count += 512L;         MD5Transform(input, k);         j -= 64;         k += 64;       }       else        {         count     += 8L;         buffer[l] = input[k];         if (l >= 63)          {           MD5Transform(buffer, 0);         }         k++;         j--;       }     }   }       /**    * Method MD5Final    * @return    *    */   public byte[] MD5Final()    {     byte abyte0[] = new byte[8];     for (int i = 0; i < 8; i++)      {       abyte0[i] = (byte) (int) (count >>> i * 8 & 255L);     }         /* Pad out to 56 mod 64. */     int index = (int) (count >> 3) & 0x3f;     /*      * Apply the padding      */     int  padLen   = (index >= 56)                     ? 120 - index                     : 56 - index;     byte abyte1[] = new byte[padLen];     abyte1[0] = -128;     MD5Update(abyte1, abyte1.length);      MD5Update(abyte0, abyte0.length);     for (int j = 0; j < 4; j++)      {       for (int i1 = 0; i1 < 4; i1++)        {         digestBits[j * 4 + i1] = (byte) (context[j] >>> i1 * 8                                          & 0xff);       }     }         /* Store state in digest */     byte abyte2[] = new byte[16];     System.arraycopy(digestBits, 0, abyte2, 0, 16);     MD5Init();     return abyte2;   }       protected void MD5Transform(byte[] block, int offset)    {     /*      * Decodes 64 bytes from      * input block into an array of      * 16 32-bit entities.      * Decode function in reference      */     for (int i = 0; i < 16; i++)      {       x[i] = (block[offset++] & 0xFF)               (block[offset++] & 0xFF) << 8               (block[offset++] & 0xFF) << 16               (block[offset++] & 0xFF) << 24;     }     int a = context[0];      int b = context[1];     int c = context[2];     int d = context[3];         /* Round 1 */     a = FF(a, b, c, d, x[0], S11, 0xd76aa478);   /* 1 */     d = FF(d, a, b, c, x[1], S12, 0xe8c7b756);   /* 2 */     c = FF(c, d, a, b, x[2], S13, 0x242070db);   /* 3 */     b = FF(b, c, d, a, x[3], S14, 0xc1bdceee);   /* 4 */     a = FF(a, b, c, d, x[4], S11, 0xf57c0faf);   /* 5 */     d = FF(d, a, b, c, x[5], S12, 0x4787c62a);   /* 6 */     c = FF(c, d, a, b, x[6], S13, 0xa8304613);   /* 7 */     b = FF(b, c, d, a, x[7], S14, 0xfd469501);   /* 8 */     a = FF(a, b, c, d, x[8], S11, 0x698098d8);   /* 9 */     d = FF(d, a, b, c, x[9], S12, 0x8b44f7af);   /* 10 */     c = FF(c, d, a, b, x[10], S13, 0xffff5bb1);  /* 11 */     b = FF(b, c, d, a, x[11], S14, 0x895cd7be);  /* 12 */     a = FF(a, b, c, d, x[12], S11, 0x6b901122);  /* 13 */     d = FF(d, a, b, c, x[13], S12, 0xfd987193);  /* 14 */     c = FF(c, d, a, b, x[14], S13, 0xa679438e);  /* 15 */     b = FF(b, c, d, a, x[15], S14, 0x49b40821);  /* 16 */         /* Round 2 */     a = GG(a, b, c, d, x[1], S21, 0xf61e2562);   /* 17 */     d = GG(d, a, b, c, x[6], S22, 0xc040b340);   /* 18 */     c = GG(c, d, a, b, x[11], S23, 0x265e5a51);  /* 19 */     b = GG(b, c, d, a, x[0], S24, 0xe9b6c7aa);   /* 20 */     a = GG(a, b, c, d, x[5], S21, 0xd62f105d);   /* 21 */     d = GG(d, a, b, c, x[10], S22, 0x2441453);   /* 22 */     c = GG(c, d, a, b, x[15], S23, 0xd8a1e681);  /* 23 */     b = GG(b, c, d, a, x[4], S24, 0xe7d3fbc8);   /* 24 */     a = GG(a, b, c, d, x[9], S21, 0x21e1cde6);   /* 25 */     d = GG(d, a, b, c, x[14], S22, 0xc33707d6);  /* 26 */     c = GG(c, d, a, b, x[3], S23, 0xf4d50d87);   /* 27 */     b = GG(b, c, d, a, x[8], S24, 0x455a14ed);   /* 28 */     a = GG(a, b, c, d, x[13], S21, 0xa9e3e905);  /* 29 */     d = GG(d, a, b, c, x[2], S22, 0xfcefa3f8);   /* 30 */     c = GG(c, d, a, b, x[7], S23, 0x676f02d9);   /* 31 */     b = GG(b, c, d, a, x[12], S24, 0x8d2a4c8a);  /* 32 */         /* Round 3 */     a = HH(a, b, c, d, x[5], S31, 0xfffa3942);   /* 33 */     d = HH(d, a, b, c, x[8], S32, 0x8771f681);   /* 34 */     c = HH(c, d, a, b, x[11], S33, 0x6d9d6122);  /* 35 */     b = HH(b, c, d, a, x[14], S34, 0xfde5380c);  /* 36 */     a = HH(a, b, c, d, x[1], S31, 0xa4beea44);   /* 37 */     d = HH(d, a, b, c, x[4], S32, 0x4bdecfa9);   /* 38 */     c = HH(c, d, a, b, x[7], S33, 0xf6bb4b60);   /* 39 */     b = HH(b, c, d, a, x[10], S34, 0xbebfbc70);  /* 40 */     a = HH(a, b, c, d, x[13], S31, 0x289b7ec6);  /* 41 */     d = HH(d, a, b, c, x[0], S32, 0xeaa127fa);   /* 42 */     c = HH(c, d, a, b, x[3], S33, 0xd4ef3085);   /* 43 */     b = HH(b, c, d, a, x[6], S34, 0x4881d05);    /* 44 */     a = HH(a, b, c, d, x[9], S31, 0xd9d4d039);   /* 45 */     d = HH(d, a, b, c, x[12], S32, 0xe6db99e5);  /* 46 */     c = HH(c, d, a, b, x[15], S33, 0x1fa27cf8);  /* 47 */     b = HH(b, c, d, a, x[2], S34, 0xc4ac5665);   /* 48 */         /* Round 4 */     a = II(a, b, c, d, x[0], S41, 0xf4292244);  /* 49 */     d = II(d, a, b, c, x[7], S42, 0x432aff97);  /* 50 */     c = II(c, d, a, b, x[14], S43, 0xab9423a7);  /* 51 */     b = II(b, c, d, a, x[5], S44, 0xfc93a039);  /* 52 */     a = II(a, b, c, d, x[12], S41, 0x655b59c3);  /* 53 */     d = II(d, a, b, c, x[3], S42, 0x8f0ccc92);  /* 54 */     c = II(c, d, a, b, x[10], S43, 0xffeff47d);  /* 55 */     b = II(b, c, d, a, x[1], S44, 0x85845dd1);  /* 56 */     a = II(a, b, c, d, x[8], S41, 0x6fa87e4f);  /* 57 */     d = II(d, a, b, c, x[15], S42, 0xfe2ce6e0);  /* 58 */     c = II(c, d, a, b, x[6], S43, 0xa3014314);  /* 59 */     b = II(b, c, d, a, x[13], S44, 0x4e0811a1);  /* 60 */     a = II(a, b, c, d, x[4], S41, 0xf7537e82);  /* 61 */     d = II(d, a, b, c, x[11], S42, 0xbd3af235);  /* 62 */     c = II(c, d, a, b, x[2], S43, 0x2ad7d2bb);  /* 63 */     b = II(b, c, d, a, x[9], S44, 0xeb86d391);  /* 64 */     context[0] += a;     context[1] += b;     context[2] += c;     context[3] += d;   }       private static int F(int x, int y, int z)    {     return (z ^ (x & (y ^ z)));   }       private static int G(int x, int y, int z)    {     return (y ^ (z & (x ^ y)));   }       private static int H(int x, int y, int z)    {     return (x ^ y ^ z);   }       private static int I(int x, int y, int z)    {     return (y ^ (x  ~z));   }       private static int FF(int a, int b, int c, int d, int k,                         int s, int t)     {     a += k + t + F(b, c, d);     a = (a << s  a >>> -s);     return a + b;   }       private static int GG(int a, int b, int c, int d, int k,                         int s, int t)    {     a += k + t + G(b, c, d);     a = (a << s  a >>> -s);     return a + b;   }       private static int HH(int a, int b, int c, int d, int k,                         int s, int t)    {     a += k + t + H(b, c, d);     a = (a << s  a >>> -s);     return a + b;   }       private int II(int a, int b, int c, int d, int k, int s,                  int t)    {     a += k + t + I(b, c, d);     a = (a << s  a >>> -s);     return a + b;    }       /**    * Method main    * Description: This is a test driver    * @param args none    *    */   public static void main(String[] args)    {     try      {       /*        * Create a new MD5 and test data        */       RichMD5 md5 = new RichMD5();       /*        * Create a JDK MD5 algorithm        */       MessageDigest md5MD = MessageDigest.getInstance("MD5");       for (int index = 0; index < testData.length; index++)        {         System.out.println("");         System.out.println("MD5 Digesting...");         System.out.println(testData[index][0]);         byte[] testBytes = testData[index][0].getBytes();             /*          * Update the digest with data          * normally the data can be updated          * at different times          */         System.out.println("Test Length :" + testBytes.length);         md5.MD5Update(testBytes, testBytes.length);         byte[] digest = md5.MD5Final();         System.out.println("Trusted Digest :"                            + testData[index][1]);         byte[] testDigest = testData[0][1].getBytes();         char[] buf        = new char[digest.length * 2];         int    j          = 0;         int    k;         for (int i = 0; i < digest.length; i++)          {           k        = digest[i];           buf[j++] = hexDigits[(k >>> 4) & 0x0F];           buf[j++] = hexDigits[k & 0x0F];         }         String buffer = new String(buf);         System.out.println("New Digest     :" + buffer);       }           /*        *  Test the JDK Version        */       for (int index = 0; index < testData.length; index++)        {         System.out.println("");         System.out.println("MD5 Digesting with JDK...");         System.out.println(testData[index][0]);         byte[] testBytes = testData[index][0].getBytes();             /*          * Update the digest with data          * normally the data can be updated          * at different times          */         System.out.println("Test Length :" + testBytes.length);         md5MD.update(testBytes, 0, testBytes.length);         byte[] digest = md5MD.digest();         System.out.println("Trusted Digest :"                            + testData[index][1]);         byte[] testDigest = testData[0][1].getBytes();         char[] buf        = new char[digest.length * 2];         int    j          = 0;         int    k;         for (int i = 0; i < digest.length; i++)          {           k        = digest[i];           buf[j++] = hexDigits[(k >>> 4) & 0x0F];           buf[j++] = hexDigits[k & 0x0F];         }             String buffer = new String(buf);         System.out.println("New Digest     :" + buffer);       }     }         /*      * Catches      */     catch (Exception ex)      {       ex.printStackTrace();     }   } } 
end example
 

The output of Listing 9-1 demonstrates that the expected result is achieved. That is, the Trusted Digest and the New Digest values are the same for the same input, as the following fragment shows:

 >java RichMD5 MD5 Digesting... Test Length :0 Trusted Digest :D41D8CD98F00B204E9800998ECF8427E New Digest     :D41D8CD98F00B204E9800998ECF8427E MD5 Digesting... a Test Length :1 Trusted Digest :0CC175B9C0F1B6A831C399E269772661 New Digest     :0CC175B9C0F1B6A831C399E269772661 

The SHA-1 algorithm

Another algorithm that has become just as popular as the MD5 algorithm is the SHA-1 algorithm. In 1995, the Federal Information Processing Standards (FIPS) published publication 180-1 for the standard of the Secure Hash Standard .

Note  

For more information see FIPS 180-1 "Secure Hash Standard," Federal Information Publication Standards, http://www.itl.nist.gov/fipspubs/fip180-1.htm , 1995.

The algorithm from the 180-1 publication is defined as the Secure Hash Algorithm number 1 (SHA-1). The National Institute of Standards (NIST) and the National Security Agency (NSA) developed the SHA-1 algorithm. SHA-1 is closely modeled after the MD4 algorithm. There is currently a draft by FIPS 180-2 publication to update the SHA-1 algorithm. The SHA-1 was designed for use with the Digital Signature Algorithm (DSA) in mind.

The algorithm can take the input of data of not more than 2 64 bits and produce a digest of 160 bits. The input is broken down into 512-bit blocks and is processed individually. A 160-bit buffer is used to hold intermediate and final results of the hash function. The buffer can be represented by five 32-bit registers (A, B, C, D, and E). The initialization values for the SHA-1 are defined as follows:

A = 67 45 23 01

B = EF CD AB 89

C = 98 BA DC FE

D = 10 32 54 76

E = C3 D2 E1 F0

SHA-1 consists of four rounds of processing 20 operations. Each round will use a specific constant:

  • Round 1 will use K 1 = 0x5a827999, which is equivalent to 2 30 X 2.

  • Round 2 will use K 2 = 0x6ed9eba1, which is equivalent to 2 30 X 3.

  • Round 3 will use K 3 = 0x8f1bbcdc, which is equivalent to 2 30 X 5.

  • Round 4 will use K 4 = 0xca62c1d6, which is equivalent to 2 30 X 10.

All four rounds will be applied to each of the 512-bit blocks that are used for the input buffer. Because the complexity and collision resistance is higher in SHA-1, it is about 30% slower than MD5. See Listing 9-2 for an example implementation of the SHA-1 algorithm.

Note  

Some of the differences between the SHA-1 algorithm and the MD4 are:

  1. The digest value for SHA-1 is 160 bits, and for MD4 it is 128 bits.

  2. SHA-1 has 40 rounds of 20 steps each while MD4 has 3 rounds of 16 steps each.

  3. SHA-1 has 5 chaining variables that are initialized with values while MD4 only has 4 registers.

Listing 9-2: An example SHA-1 algorithm
start example
 package com.richware.chap09; import java.security.*;     /**  * Class RichSHA  * Description: This is an example  * implementation of the SHA-1  * algorithm.  *  * Copyright:    Copyright (c) 2002 Wiley Publishing, Inc.  * @author Rich Helton <rhelton@richware.com>  * @version 1.0  * DISCLAIMER: Please refer to the disclaimer at the beginning of this book.  */ public class RichSHA  {   /* Temoporary Buffer */   public int W_[];       /* counter for the bytes */   private long count_ = 0L;        /* Initial Register Values */   private int[] H_;       /* Input buffer */   private byte[] INPUT_;       /*    * The round constants    */   private final int round1_kt = 0x5a827999;   private final int round2_kt = 0x6ed9eba1;   private final int round3_kt = 0x8f1bbcdc;   private final int round4_kt = 0xca62c1d6;       /*    * Trusted digests    */   public final static String[][] testData =    {     { "", "da39a3ee5e6b4b0d3255bfef95601890afd80709" },     { "1", "356a192b7913b04c54574d18c28d46e6395428ab" },     { "a", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8" },     { "abc", "a9993e364706816aba3e25717850c26c9cd0d89d" },     { "abcdefghijklmnopqrstuvwxyz",       "32d10c7b8cf96570ca04ce37f2a19d84240d3a89" },     { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",       "84983E441C3BD26EBAAE4AA1F95129E5E54670F1" },           { "Anyone got any SHA-1 test data?",       "09b9e9c04a84ce274942048acf3a6f2ff4a8a39c" },         { "Of cabbages and kings",         "5f093d74a9cb1f2f14537bcf3a8a1ffd59b038a2" }    };       /*    * For hex conversion    */   public static final char[] hexDigits =    {     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',     'C', 'D', 'E', 'F'   };       /**    * Constructor RichSHA    * Description: The Main constructor    */   public RichSHA()    {     W_     = new int[80];     H_     = new int[5];     INPUT_ = new byte[64];         initSHA();   }       /**    * Method: initSHA    * Description: Initialize the variables    * for SHA    */   protected void initSHA()    {     // initial values of SHA i.e. A, B, C, D     H_[0] = 0x67452301;     H_[1] = 0xefcdab89;     H_[2] = 0x98badcfe;     H_[3] = 0x10325476;     H_[4] = 0xc3d2e1f0;          for (int i = 0; i < 80; i++)      {       W_[i] = 0;     }         for (int i = 0; i < 64; i++)      {       INPUT_[i] = 0;     }         count_ = 0L;   }       /**    * Method updateSHA    * Description: Updates the SHA values    * with a byte array    * @param data the data to add    * @param offset offset to add data    * @param len length of bytes    *    */   public void updateSHA(byte data[], int offset, int len)    {     if ((offset < 0)  (len < 0)              (offset + len > data.length))      {       throw new ArrayIndexOutOfBoundsException();     }         /*      * SAVE Input and compute      * when the block is done      */     for (int index = 0; index < len; index++)      {       INPUT_[(int) count_ & 63] = data[offset + index];           if ((int) (count_ & 63) == 63)        {         computeSHA();       }           count_++;     }   }       /**    * Method digestSHA    * Description: Calculates final digest    * @return the byte array with final digest    *    */   public byte[] digestSHA()    {     byte digest[] = new byte[20];     try      {       pad();           for (int i = 0; i < 5; i++)        {         digest[4 * i]     = (byte) ((H_[i] >>> 24) & 255);         digest[4 * i + 1] = (byte) ((H_[i] >>> 16) & 255);         digest[4 * i + 2] = (byte) ((H_[i] >>> 8) & 255);         digest[4 * i + 3] = (byte) (H_[i] & 255);       }           initSHA();     }         /*      * Catches      */     catch (Exception ex)      {       ex.printStackTrace();     }         return digest;   }       /**    * Method computeSHA    * Description: The SHA algorithm    */   private void computeSHA()    {     /* step a */     for (int k1 = 0; k1 < 16; k1++)      {       W_[k1] =         (((((int) INPUT_[4 * k1] & 255) << 8) + ((int) INPUT_[4 * k1 +  1] & 255) << 8) + ((int) INPUT_[4 * k1 + 2] & 255) << 8)         + ((int) INPUT_[4 * k1 + 3] & 255);     }         /* step b */     /*      * 32 bit Word values being derived      * from the 512-bit Message      */     for (int k2 = 16; k2 <= 79; k2++)      {       int i = W_[k2 - 3] ^ W_[k2 - 8] ^ W_[k2 - 14]               ^ W_[k2 - 16];           W_[k2] = i << 1  i >>> 31;     }         int a    = H_[0];     int b    = H_[1];     int c    = H_[2];     int d    = H_[3];     int e    = H_[4];     int temp = 0;         for (int index = 0; index < 80; index++)      {       /*        * Round 1        */       if (index < 20)        {         temp = (a << 5  a >>> 27) + (b & c  ~b & d) + e                + W_[index] + round1_kt;       }           /*        * Round 2        */       else if (index < 40)        {         temp = (a << 5  a >>> 27) + (b ^ c ^ d) + e                + W_[index] + round2_kt;       }           /*        * Round 3        */       else if (index < 60)        {         temp = (a << 5  a >>> 27) + (b & c  b & d  c & d)                + e + W_[index] + round3_kt;       }           /*        * Round 4        */       else if (index < 80)        {         temp = (a << 5  a >>> 27) + (b ^ c ^ d) + e                + W_[index] + round4_kt;        }           /*        * All Rounds        */       e = d;       d = c;       c = b << 30  b >>> 2;       b = a;       a = temp;     }         /*      * Add the values back      * to the registers      */     H_[0] += a;     H_[1] += b;     H_[2] += c;     H_[3] += d;     H_[4] += e;   }       /**    * Method pad    * Description: Pads the bytes    */   private void pad()    {     int  i;      long bitlength;     bitlength                 = count_ << 3;     INPUT_[(int) count_ & 63] = (byte) 128;     count_++;         if ((int) (count_ & 63) >= 56)      {       for (i = ((int) count_ & 63); i < 64; i++)        {         INPUT_[i] = 0;         count_++;       }       computeSHA();     }         for (i = ((int) count_ & 63); i < 56; i++)      {       INPUT_[i] = 0;     }         INPUT_[56] = (byte) ((bitlength >>> 56) & 255);     INPUT_[57] = (byte) ((bitlength >>> 48) & 255);     INPUT_[58] = (byte) ((bitlength >>> 40) & 255);     INPUT_[59] = (byte) ((bitlength >>> 32) & 255);     INPUT_[60] = (byte) ((bitlength >>> 24) & 255);     INPUT_[61] = (byte) ((bitlength >>> 16) & 255);     INPUT_[62] = (byte) ((bitlength >>> 8) & 255);     INPUT_[63] = (byte) (bitlength & 255);         computeSHA();   }       /**    * Method main    * Description: This is a test driver    * @param args none    *    */   public static void main(String[] args)    {     try      {       /*        * Create a new local SHA1 and test data        */       RichSHA sha = new RichSHA();           /*        * Create a JDK SHA aalgorithm        */       MessageDigest shaMD = MessageDigest.getInstance("SHA1");           /*        * Loop through the test data        */       for (int index = 0; index < testData.length; index++)        {         System.out.println("");         System.out.println("SHA1 Digesting...");         System.out.println(testData[index][0]);         byte[] testBytes = testData[index][0].getBytes();             /*          * Update the digest with data          * normally the data can be updated          * at different times          */         System.out.println("Test Length :" + testBytes.length);         sha.updateSHA(testBytes, 0, testBytes.length);         byte[] digest = sha.digestSHA();         System.out.println("Trusted Digest :"                            + testData[index][1]);             byte[] testDigest = testData[0][1].getBytes();         char[] buf        = new char[digest.length * 2];         int    j          = 0;         int    k;             for (int i = 0; i < digest.length; i++)          {           k        = digest[i];           buf[j++] = hexDigits[(k >>> 4) & 0x0F];           buf[j++] = hexDigits[k & 0x0F];         }             String buffer = new String(buf);         System.out.println("New Digest     :" + buffer);       }           /*        *  Test the JDK Version        */       for (int index = 0; index < testData.length; index++)        {         System.out.println("");         System.out.println("SHA1 Digesting with JDK...");         System.out.println(testData[index][0]);         byte[] testBytes = testData[index][0].getBytes();             /*          * Update the digest with data          * normally the data can be updated          * at different times          */         System.out.println("Test Length :" + testBytes.length);         shaMD.update(testBytes, 0, testBytes.length);         byte[] digest = shaMD.digest();         System.out.println("Trusted Digest :"                            + testData[index][1]);         byte[] testDigest = testData[0][1].getBytes();         char[] buf        = new char[digest.length * 2];         int    j          = 0;         int    k;             for (int i = 0; i < digest.length; i++)          {           k        = digest[i];           buf[j++] = hexDigits[(k >>> 4) & 0x0F];           buf[j++] = hexDigits[k & 0x0F];         }             String buffer = new String(buf);         System.out.println("New Digest     :" + buffer);       }     }         /*      * Catches      */     catch (Exception ex)      {       ex.printStackTrace();     }   } } 
end example
 

In Listing 9-2, the computeSHA method demonstrates the four rounds. The rounds are initialized with the K constants. Each round has much of the same functionality with slight differences in the b, c, and d logical operations. After the rounds are computed and the digest needs to be done, a compression of the digest takes place to shift the register values of A, B, C, D, and E into 20 bytes of digest represented in the H array. An example output of Listing 9-2 is given in Listing 9-3. Notice that the different input messages produce different digests. Also, a comparison is done with the JDK 1.4 SHA-1 message digest, and they match.

Listing 9-3: Output from Listing 9-2
start example
 SHA1 Digesting...     Test Length :0 Trusted Digest :da39a3ee5e6b4b0d3255bfef95601890afd80709 New Digest     :DA39A3EE5E6B4B0D3255BFEF95601890AFD80709     SHA1 Digesting... 1 Test Length :1 Trusted Digest :356a192b7913b04c54574d18c28d46e6395428ab New Digest     :356A192B7913B04C54574D18C28D46E6395428AB     SHA1 Digesting... a Test Length :1 Trusted Digest :86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 New Digest     :86F7E437FAA5A7FCE15D1DDCB9EAEAEA377667B8     SHA1 Digesting... abc Test Length :3 Trusted Digest :a9993e364706816aba3e25717850c26c9cd0d89d New Digest     :A9993E364706816ABA3E25717850C26C9CD0D89D     SHA1 Digesting... abcdefghijklmnopqrstuvwxyz Test Length :26 Trusted Digest :32d10c7b8cf96570ca04ce37f2a19d84240d3a89 New Digest     :32D10C7B8CF96570CA04CE37F2A19D84240D3A89     SHA1 Digesting... abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq Test Length :56 Trusted Digest :84983E441C3BD26EBAAE4AA1F95129E5E54670F1 New Digest     :84983E441C3BD26EBAAE4AA1F95129E5E54670F1     SHA1 Digesting... Anyone got any SHA-1 test data? Test Length :31 Trusted Digest :09b9e9c04a84ce274942048acf3a6f2ff4a8a39c New Digest     :09B9E9C04A84CE274942048ACF3A6F2FF4A8A39C     SHA1 Digesting... Of cabbages and kings Test Length :21 Trusted Digest :5f093d74a9cb1f2f14537bcf3a8a1ffd59b038a2 New Digest     :5F093D74A9CB1F2F14537BCF3A8A1FFD59B038A2     SHA1 Digesting with JDK...     Test Length :0 Trusted Digest :da39a3ee5e6b4b0d3255bfef95601890afd80709 New Digest     :DA39A3EE5E6B4B0D3255BFEF95601890AFD80709     SHA1 Digesting with JDK... 1 Test Length :1 Trusted Digest :356a192b7913b04c54574d18c28d46e6395428ab New Digest     :356A192B7913B04C54574D18C28D46E6395428AB     SHA1 Digesting with JDK...  a Test Length :1 Trusted Digest :86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 New Digest     :86F7E437FAA5A7FCE15D1DDCB9EAEAEA377667B8     SHA1 Digesting with JDK... abc Test Length :3 Trusted Digest :a9993e364706816aba3e25717850c26c9cd0d89d New Digest     :A9993E364706816ABA3E25717850C26C9CD0D89D     SHA1 Digesting with JDK... abcdefghijklmnopqrstuvwxyz Test Length :26 Trusted Digest :32d10c7b8cf96570ca04ce37f2a19d84240d3a89 New Digest     :32D10C7B8CF96570CA04CE37F2A19D84240D3A89     SHA1 Digesting with JDK... abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq Test Length :56 Trusted Digest :84983E441C3BD26EBAAE4AA1F95129E5E54670F1 New Digest     :84983E441C3BD26EBAAE4AA1F95129E5E54670F1     SHA1 Digesting with JDK... Anyone got any SHA-1 test data? Test Length :31 Trusted Digest :09b9e9c04a84ce274942048acf3a6f2ff4a8a39c New Digest     :09B9E9C04A84CE274942048ACF3A6F2FF4A8A39C     SHA1 Digesting with JDK... Of cabbages and kings Test Length :21 Trusted Digest :5f093d74a9cb1f2f14537bcf3a8a1ffd59b038a2 New Digest     :5F093D74A9CB1F2F14537BCF3A8A1FFD59B038A2 
end example
 

The RIPEMD-160

Another algorithm that is very popular is the RIPEMD-160 ( Race Integrity Primitives Evaluation Message Digest for 160 ). The RIPEMD-160 algorithm is also based on MD4. The "160" in RIPEMD-160 means that the algorithm produces a 160-bit digest. The computations are much more complex, and the speed of the algorithm is almost 25% slower than MD4.

The RIPEMD-160 has five rounds with 16 steps. The orders of the input words are processed and are modified along with the type of rotations on the words. There is also a parallel computation where the words are run twice through the computation differing only in the constants in the steps. Because of these differences, the algorithm makes it less prone to collisions and harder to break due to its complexity. The RIPEMD-160 uses five registers to initialize the hashing algorithm just like the SHA-1:

A = 67 45 23 01

B = EF CD AB 89

C = 98 BA DC FE

D = 10 32 54 76

E = C3 D2 E1 F0

The RIPEMD-160 extends five chaining registers just like SHA-1. The blocks are split into 512-bit blocks, and there is a 64-bit message length. There are many similarities between SHA-1 and RIPEMD-160, but the computation method and speed of the algorithm are different. The RIPEMD-160 is about 15% slower than SHA-1 because of the higher complexity of the algorithm of RIPEMD-160. RIPEMD-160 has a higher number of rounds, and each round rotates the hash left and then right using different constants.

  • The left rotation constants for each round are 0, 2 30 X 2, 2 30 X 3, and 2 30 X 5.

  • The respective rounds for each on the right shift are 2 30 X 3 2, 2 30 X 3 3, 2 30 X 3 5, and 0.

The RIPEMD-160 is a much more complex algorithm than the other algorithms discussed so far. Because of its complexity, it is indeed the slowest of the algorithms. Some organizations, especially banks and brokerage companies, use it because its security and collision resistance is much higher, so being slower is acceptable.

  


Java Security Solutions
Java Security Solutions
ISBN: 0764549286
EAN: 2147483647
Year: 2001
Pages: 222

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net