From: Chuck McManis Subject: SHA-1 in Java Date: 7 Oct 1996 00:53:00 +0300 Several people have asked for it, here's my version of Steve Reid's version of SHA-1, written in Java. Note that its a straight port so it isn't terribly fast (about 120K bytes/second on a P133) The other classes (MessageDigest.java, MDx.java, etc) are available at http://www.professionals.com/~cmcmanis/java/ /* * SHA1.java - An implementation of the SHA-1 Algorithm * * This version by Chuck McManis (cmcmanis@netcom.com) and * still public domain. * * Based on the C code that Steve Reid wrote his header * was : * SHA-1 in C * By Steve Reid * 100% Public Domain * * Test Vectors (from FIPS PUB 180-1) * "abc" * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 * A million repetitions of "a" * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ package util.crypt; import java.util.*; /** * This is a simple port of Steve Reid's SHA-1 code into Java. * I've run his test vectors through the code and they all pass. * */ public final class SHA1 extends MessageDigest { private int state[] = new int[5]; private long count; public SHA1() { state = new int[5]; count = 0; if (block == null) block = new int[16]; digestBits = new byte[20]; digestValid = false; } /* * The following array forms the basis for the transform * buffer. Update puts bytes into this buffer and then * transform adds it into the state of the digest. */ private int block[] = new int[16]; private int blockIndex; /* * These functions are taken out of #defines in Steve's * code. Java doesn't have a preprocessor so the first * step is to just promote them to real methods. * Later we can optimize them out into inline code, * note that by making them final some compilers will * inline them when given the -O flag. */ final int rol(int value, int bits) { int q = (value << bits) | (value >>> (32 - bits)); return q; } final int blk0(int i) { block[i] = (rol(block[i],24)&0xFF00FF00) | (rol(block[i],8)&0x00FF00FF); return block[i]; } final int blk(int i) { block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15]^ block[(i+2)&15]^block[i&15], 1); return (block[i&15]); } final void R0(int data[], int v, int w, int x , int y, int z, int i) { data[z] += ((data[w] & (data[x] ^ data[y] )) ^ data[y]) + blk0(i) + 0x5A827999 + rol(data[v] ,5); data[w] = rol(data[w], 30); } final void R1(int data[], int v, int w, int x, int y, int z, int i) { data[z] += ((data[w] & (data[x] ^ data[y])) ^ data[y]) + blk(i) + 0x5A827999 + rol(data[v] ,5); data[w] = rol(data[w], 30); } final void R2(int data[], int v, int w, int x, int y, int z, int i) { data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0x6ED9EBA1 + rol(data[v] ,5); data[w] = rol(data[w], 30); } final void R3(int data[], int v, int w, int x, int y, int z, int i) { data[z] += (((data[w] | data[x]) & data[y]) | (data[w] & data[x])) + blk(i) + 0x8F1BBCDC + rol(data[v] ,5); data[w] = rol(data[w], 30); } final void R4(int data[], int v, int w, int x, int y, int z, int i) { data[z] += (data[w] ^ data[x] ^ data[y]) + blk(i) + 0xCA62C1D6 + rol(data[v] ,5); data[w] = rol(data[w], 30); } /* * Steve's original code and comments : * * blk0() and blk() perform the initial expand. * I got the idea of expanding during the round function from SSLeay * * #define blk0(i) block->l[i] * #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ * ^block->l[(i+2)&15]^block->l[i&15],1)) * * (R0+R1), R2, R3, R4 are the different operations used in SHA1 * #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); * #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); * #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); * #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); * #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); */ int dd[] = new int[5]; /** * Hash a single 512-bit block. This is the core of the algorithm. * * Note that working with arrays is very inefficent in Java as it * does a class cast check each time you store into the array. * */ void transform() { /* Copy context->state[] to working vars */ dd[0] = state[0]; dd[1] = state[1]; dd[2] = state[2]; dd[3] = state[3]; dd[4] = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(dd,0,1,2,3,4, 0); R0(dd,4,0,1,2,3, 1); R0(dd,3,4,0,1,2, 2); R0(dd,2,3,4,0,1, 3); R0(dd,1,2,3,4,0, 4); R0(dd,0,1,2,3,4, 5); R0(dd,4,0,1,2,3, 6); R0(dd,3,4,0,1,2, 7); R0(dd,2,3,4,0,1, 8); R0(dd,1,2,3,4,0, 9); R0(dd,0,1,2,3,4,10); R0(dd,4,0,1,2,3,11); R0(dd,3,4,0,1,2,12); R0(dd,2,3,4,0,1,13); R0(dd,1,2,3,4,0,14); R0(dd,0,1,2,3,4,15); R1(dd,4,0,1,2,3,16); R1(dd,3,4,0,1,2,17); R1(dd,2,3,4,0,1,18); R1(dd,1,2,3,4,0,19); R2(dd,0,1,2,3,4,20); R2(dd,4,0,1,2,3,21); R2(dd,3,4,0,1,2,22); R2(dd,2,3,4,0,1,23); R2(dd,1,2,3,4,0,24); R2(dd,0,1,2,3,4,25); R2(dd,4,0,1,2,3,26); R2(dd,3,4,0,1,2,27); R2(dd,2,3,4,0,1,28); R2(dd,1,2,3,4,0,29); R2(dd,0,1,2,3,4,30); R2(dd,4,0,1,2,3,31); R2(dd,3,4,0,1,2,32); R2(dd,2,3,4,0,1,33); R2(dd,1,2,3,4,0,34); R2(dd,0,1,2,3,4,35); R2(dd,4,0,1,2,3,36); R2(dd,3,4,0,1,2,37); R2(dd,2,3,4,0,1,38); R2(dd,1,2,3,4,0,39); R3(dd,0,1,2,3,4,40); R3(dd,4,0,1,2,3,41); R3(dd,3,4,0,1,2,42); R3(dd,2,3,4,0,1,43); R3(dd,1,2,3,4,0,44); R3(dd,0,1,2,3,4,45); R3(dd,4,0,1,2,3,46); R3(dd,3,4,0,1,2,47); R3(dd,2,3,4,0,1,48); R3(dd,1,2,3,4,0,49); R3(dd,0,1,2,3,4,50); R3(dd,4,0,1,2,3,51); R3(dd,3,4,0,1,2,52); R3(dd,2,3,4,0,1,53); R3(dd,1,2,3,4,0,54); R3(dd,0,1,2,3,4,55); R3(dd,4,0,1,2,3,56); R3(dd,3,4,0,1,2,57); R3(dd,2,3,4,0,1,58); R3(dd,1,2,3,4,0,59); R4(dd,0,1,2,3,4,60); R4(dd,4,0,1,2,3,61); R4(dd,3,4,0,1,2,62); R4(dd,2,3,4,0,1,63); R4(dd,1,2,3,4,0,64); R4(dd,0,1,2,3,4,65); R4(dd,4,0,1,2,3,66); R4(dd,3,4,0,1,2,67); R4(dd,2,3,4,0,1,68); R4(dd,1,2,3,4,0,69); R4(dd,0,1,2,3,4,70); R4(dd,4,0,1,2,3,71); R4(dd,3,4,0,1,2,72); R4(dd,2,3,4,0,1,73); R4(dd,1,2,3,4,0,74); R4(dd,0,1,2,3,4,75); R4(dd,4,0,1,2,3,76); R4(dd,3,4,0,1,2,77); R4(dd,2,3,4,0,1,78); R4(dd,1,2,3,4,0,79); /* Add the working vars back into context.state[] */ state[0] += dd[0]; state[1] += dd[1]; state[2] += dd[2]; state[3] += dd[3]; state[4] += dd[4]; } /** * * SHA1Init - Initialize new context */ public void init() { /* SHA1 initialization constants */ state[0] = 0x67452301; state[1] = 0xEFCDAB89; state[2] = 0x98BADCFE; state[3] = 0x10325476; state[4] = 0xC3D2E1F0; count = 0; digestBits = new byte[20]; digestValid = false; blockIndex = 0; } /** * Add one byte to the digest. When this is implemented * all of the abstract class methods end up calling * this method for types other than bytes. */ public synchronized void update(byte b) { int mask = (8 * (blockIndex & 3)); count += 8; block[blockIndex >> 2] &= ~(0xff << mask); block[blockIndex >> 2] |= (b & 0xff) << mask; blockIndex++; if (blockIndex == 64) { transform(); blockIndex = 0; } } /** * Complete processing on the message digest. */ public void finish() { byte bits[] = new byte[8]; int i, j; for (i = 0; i < 8; i++) { bits[i] = (byte)((count >>> (((7 - i) * 8))) & 0xff); } update((byte) 128); while (blockIndex != 56) update((byte) 0); // This should cause a transform to happen. update(bits); for (i = 0; i < 20; i++) { digestBits[i] = (byte) ((state[i>>2] >> ((3-(i & 3)) * 8) ) & 0xff); } digestValid = true; } /** Return a string that identifies this algorithm */ public String getAlg() { return "SHA1"; } /** * Print out the digest in a form that can be easily compared * to the test vectors. */ private String digout() { StringBuffer sb = new StringBuffer(); for (int i = 0; i < 20; i++) { char c1, c2; c1 = (char) ((digestBits[i] >>> 4) & 0xf); c2 = (char) (digestBits[i] & 0xf); c1 = (char) ((c1 > 9) ? 'A' + (c1 - 10) : '0' + c1); c2 = (char) ((c2 > 9) ? 'A' + (c2 - 10) : '0' + c2); sb.append(c1); sb.append(c2); if (((i+1) % 4) == 0) sb.append(' '); } return sb.toString(); } /** * This is a test program for the SHA1 algorithm. It puts * the three test vectors through the algorithm and prints * out the results (they should match.) Then it runs the * MessageDigest benchmark method to see how fast it is. * on my P133 its about 110 - 120K bytes/second. * * It then compares it to MD5, which is about 150K bytes/second. * * The reference to MoreOutputStream can be deleted. This is a * small class that opens a window to display the results. This * works around Symantec Cafe's tendency to shut down the DOS * window after showing the output, and the inability to scroll * back in a DOS window. */ public static void main(String args[]) { int i, j; SHA1 s = new SHA1(); // This line may be safely deleted, its to make it easy to see // the output of the program. System.out = new java.io.PrintStream(new util.MoreOutputStream()); System.out.println("SHA-1 Test PROGRAM."); System.out.println("This code runs the test vectors through the code."); /* "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D */ System.out.println("First test is 'abc'"); String z = "abc"; s.init(); s.update((byte) 'a'); s.update((byte) 'b'); s.update((byte) 'c'); s.finish(); System.out.println(s.digout()); System.out.println("A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D"); /* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 */ System.out.println("Next Test is 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'"); z = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; s.init(); s.updateASCII(z); s.finish(); System.out.println(s.digout()); System.out.println("84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1"); /* A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ System.out.println("Last test is 1 million 'a' characters."); s.init(); for (i = 0; i < 1000000; i++) s.update((byte) 'a'); s.finish(); System.out.println(s.digout()); System.out.println("34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"); MessageDigest.benchmark(s); MD5 mm = new MD5(); MessageDigest.benchmark(mm); } } -- --Chuck McManis http://www.professionals.com/~cmcmanis/index.html All opinions in this message are those of the author. No warranty as to the suitability or accuracy is stated or implied. Use at your own risk. cmcmanis@netcom.com +1.408.524.4805