00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 package Acme.Crypto;
00030
00031 import java.io.*;
00032
00034
00035
00036
00037
00038
00039
00040
00041 public class ShaHash extends Hash
00042 {
00043
00045 public ShaHash()
00046 {
00047 super( SHA_DIGESTSIZE );
00048 reset();
00049 }
00050
00052 public void reset()
00053 {
00054 shaInit();
00055 }
00056
00058 public void add( byte b )
00059 {
00060
00061 byte[] data = new byte[1];
00062 data[0] = b;
00063 add( data, 0, 1 );
00064 }
00065
00067 public void add( byte[] data, int off, int len )
00068 {
00069 shaUpdate( data, off, len );
00070 }
00071
00073 protected void prepare()
00074 {
00075 shaFinal();
00076 spreadIntsToBytes( digest, 0, hashBytes, 0, SHA_DIGESTSIZE/4 );
00077 }
00078
00079 private static final boolean USE_MODIFIED_SHA = true;
00080 private static final int SHA_BLOCKSIZE = 64;
00081 private static final int SHA_DIGESTSIZE = 20;
00082
00083 private int[] digest = new int[SHA_DIGESTSIZE/4];
00084 private long bitCount;
00085 private byte[] dataB = new byte[SHA_BLOCKSIZE];
00086 private int[] dataI = new int[SHA_BLOCKSIZE/4];
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114 private static int f1( int x, int y, int z )
00115 {
00116 return ( x & y ) | ( ~x & z );
00117 }
00118
00119
00120 private static int f2( int x, int y, int z )
00121 {
00122 return x ^ y ^ z;
00123 }
00124
00125
00126 private static int f3( int x, int y, int z )
00127 {
00128 return ( x & y ) | ( x & z ) | ( y & z );
00129 }
00130
00131
00132 private static int f4( int x, int y, int z )
00133 {
00134 return x ^ y ^ z;
00135 }
00136
00137
00138 private static final int K1 = 0x5a827999;
00139 private static final int K2 = 0x6ed9eba1;
00140 private static final int K3 = 0x8f1bbcdc;
00141 private static final int K4 = 0xca62c1d6;
00142
00143
00144 private static final int h0init = 0x67452301;
00145 private static final int h1init = 0xefcdab89;
00146 private static final int h2init = 0x98badcfe;
00147 private static final int h3init = 0x10325476;
00148 private static final int h4init = 0xc3d2e1f0;
00149
00150
00151 private static int rotateL( int x, int n )
00152 {
00153 return ( x << n ) | ( x >>> ( 32 - n ) );
00154 }
00155
00156
00157
00158
00159 private void subRound1( int count )
00160 {
00161 int temp = rotateL( A, 5 ) + f1( B, C, D ) + E + W[count] + K1;
00162 E = D;
00163 D = C;
00164 C = rotateL( B, 30 );
00165 B = A;
00166 A = temp;
00167 }
00168
00169 private void subRound2( int count )
00170 {
00171 int temp = rotateL( A, 5 ) + f2( B, C, D ) + E + W[count] + K2;
00172 E = D;
00173 D = C;
00174 C = rotateL( B, 30 );
00175 B = A;
00176 A = temp;
00177 }
00178
00179 private void subRound3( int count )
00180 {
00181 int temp = rotateL( A, 5 ) + f3( B, C, D ) + E + W[count] + K3;
00182 E = D;
00183 D = C;
00184 C = rotateL( B, 30 );
00185 B = A;
00186 A = temp;
00187 }
00188
00189 private void subRound4( int count )
00190 {
00191 int temp = rotateL( A, 5 ) + f4( B, C, D ) + E + W[count] + K4;
00192 E = D;
00193 D = C;
00194 C = rotateL( B, 30 );
00195 B = A;
00196 A = temp;
00197 }
00198
00199
00200 private int h0, h1, h2, h3, h4;
00201 private int A, B, C, D, E;
00202
00204 private void shaInit()
00205 {
00206
00207 digest[0] = h0init;
00208 digest[1] = h1init;
00209 digest[2] = h2init;
00210 digest[3] = h3init;
00211 digest[4] = h4init;
00212
00213
00214 bitCount = 0;
00215 }
00216
00217 private int[] W = new int[80];
00218
00220 private void shaTransform()
00221 {
00222 int i;
00223
00224
00225 for( i = 0; i < SHA_BLOCKSIZE/4; ++i )
00226 W[i] = dataI[i];
00227
00228
00229 for ( ; i < 80; ++i )
00230 {
00231 W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
00232 if ( USE_MODIFIED_SHA )
00233 W[i] = rotateL( W[i], 1 );
00234 }
00235
00236
00237 A = digest[0];
00238 B = digest[1];
00239 C = digest[2];
00240 D = digest[3];
00241 E = digest[4];
00242
00243
00244 for ( i = 0; i < 20; ++i )
00245 subRound1( i );
00246 for ( ; i < 40; ++i )
00247 subRound2( i );
00248 for ( ; i < 60; ++i )
00249 subRound3( i );
00250 for ( ; i < 80; ++i )
00251 subRound4( i );
00252
00253
00254 digest[0] += A;
00255 digest[1] += B;
00256 digest[2] += C;
00257 digest[3] += D;
00258 digest[4] += E;
00259 }
00260
00262
00263
00264
00265 private void shaUpdate( byte[] buffer, int offset, int count )
00266 {
00267
00268 bitCount += count << 3;
00269
00270
00271 while ( count >= SHA_BLOCKSIZE )
00272 {
00273 copyBlock( buffer, offset, dataB, 0, SHA_BLOCKSIZE );
00274 squashBytesToInts( dataB, 0, dataI, 0, SHA_BLOCKSIZE/4 );
00275 shaTransform();
00276 offset += SHA_BLOCKSIZE;
00277 count -= SHA_BLOCKSIZE;
00278 }
00279
00280
00281
00282 copyBlock( buffer, offset, dataB, 0, count );
00283 }
00284
00285 private void shaFinal()
00286 {
00287 int count;
00288
00289
00290 count = (int) ( bitCount >>> 3 ) & 0x3F;
00291
00292
00293
00294 dataB[count++] = (byte) 0x80;
00295
00296
00297 if ( count > SHA_BLOCKSIZE - 8 )
00298 {
00299
00300 fillBlock( dataB, count, (byte) 0, SHA_BLOCKSIZE - count );
00301 squashBytesToInts( dataB, 0, dataI, 0, SHA_BLOCKSIZE/4 );
00302 shaTransform();
00303
00304
00305 fillBlock( dataB, 0, (byte) 0, SHA_BLOCKSIZE - 8 );
00306 }
00307 else
00308
00309 fillBlock( dataB, count, (byte) 0, SHA_BLOCKSIZE - 8 - count );
00310 squashBytesToInts( dataB, 0, dataI, 0, SHA_BLOCKSIZE/4 );
00311
00312
00313 dataI[14] = (int) ( bitCount >>> 32 );
00314 dataI[15] = (int) ( bitCount & 0xffffffff );
00315
00316 shaTransform();
00317 }
00318
00319
00320
00321
00322
00323 private static int TEST_BLOCK_SIZE = SHA_DIGESTSIZE * 100;
00324
00325
00326 private static int TEST_BYTES = 10000000;
00327 private static int TEST_BLOCKS = TEST_BYTES / TEST_BLOCK_SIZE;
00328
00329 public static void main( String[] args )
00330 {
00331 ShaHash h = new ShaHash();
00332
00333
00334
00335
00336 h.addASCII( "abc" );
00337 byte[] hb = h.get();
00338 byte[] oldCorrect = {
00339 (byte) 0x01, (byte) 0x64, (byte) 0xb8, (byte) 0xa9,
00340 (byte) 0x14, (byte) 0xcd, (byte) 0x2a, (byte) 0x5e,
00341 (byte) 0x74, (byte) 0xc4, (byte) 0xf7, (byte) 0xff,
00342 (byte) 0x08, (byte) 0x2c, (byte) 0x4d, (byte) 0x97,
00343 (byte) 0xf1, (byte) 0xed, (byte) 0xf8, (byte) 0x80
00344 };
00345 byte[] newCorrect = {
00346 (byte) 0xa9, (byte) 0x99, (byte) 0x3e, (byte) 0x36,
00347 (byte) 0x47, (byte) 0x06, (byte) 0x81, (byte) 0x6a,
00348 (byte) 0xba, (byte) 0x3e, (byte) 0x25, (byte) 0x71,
00349 (byte) 0x78, (byte) 0x50, (byte) 0xc2, (byte) 0x6c,
00350 (byte) 0x9c, (byte) 0xd0, (byte) 0xd8, (byte) 0x9d
00351 };
00352 byte[] correct;
00353 if ( USE_MODIFIED_SHA )
00354 correct = newCorrect;
00355 else
00356 correct = oldCorrect;
00357 System.out.println( "Got: " + toStringBlock( hb ) );
00358 System.out.println( "Want: " + toStringBlock( correct ) );
00359 if ( ! equalsBlock( hb, correct ) )
00360 {
00361 System.err.println( "Error in SHA implementation." );
00362 System.exit( 1 );
00363 }
00364
00365
00366
00367 byte[] data = new byte[TEST_BLOCK_SIZE];
00368 int i;
00369
00370 fillBlock( data, (byte) 0 );
00371
00372 System.out.println( "SHA time trial. Processing " + TEST_BYTES + " characters..." );
00373
00374
00375 h.reset();
00376 for ( i = TEST_BLOCKS; i > 0; i-- )
00377 h.add( data, 0, TEST_BLOCK_SIZE );
00378 h.get();
00379
00380 System.out.println( "Done." );
00381
00382 }
00383
00384 }