diff options
author | Mike Lockwood <lockwood@google.com> | 2012-01-10 14:24:22 -0800 |
---|---|---|
committer | Mike Lockwood <lockwood@google.com> | 2012-01-10 14:25:05 -0800 |
commit | 48ded2421114c4c87ef3f8005c9f793a5d077cbd (patch) | |
tree | be5d899a8b290aa1247c86a5fa57498905d5c584 /src/main/java/ch/ethz/ssh2/crypto/digest | |
parent | dca8ebfb5703cd5d0f6f60e9e9d52854ff13aaf5 (diff) | |
download | ganymed-ssh2-48ded2421114c4c87ef3f8005c9f793a5d077cbd.tar.gz |
ganymed-ssh2-build251beta1
Signed-off-by: Mike Lockwood <lockwood@google.com>
Diffstat (limited to 'src/main/java/ch/ethz/ssh2/crypto/digest')
-rw-r--r-- | src/main/java/ch/ethz/ssh2/crypto/digest/Digest.java | 28 | ||||
-rw-r--r-- | src/main/java/ch/ethz/ssh2/crypto/digest/HMAC.java | 98 | ||||
-rw-r--r-- | src/main/java/ch/ethz/ssh2/crypto/digest/HashForSSH2Types.java | 96 | ||||
-rw-r--r-- | src/main/java/ch/ethz/ssh2/crypto/digest/MAC.java | 91 | ||||
-rw-r--r-- | src/main/java/ch/ethz/ssh2/crypto/digest/MD5.java | 271 | ||||
-rw-r--r-- | src/main/java/ch/ethz/ssh2/crypto/digest/SHA1.java | 667 |
6 files changed, 1251 insertions, 0 deletions
diff --git a/src/main/java/ch/ethz/ssh2/crypto/digest/Digest.java b/src/main/java/ch/ethz/ssh2/crypto/digest/Digest.java new file mode 100644 index 0000000..0b8adb8 --- /dev/null +++ b/src/main/java/ch/ethz/ssh2/crypto/digest/Digest.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. + * Please refer to the LICENSE.txt for licensing details. + */ +package ch.ethz.ssh2.crypto.digest; + +/** + * Digest. + * + * @author Christian Plattner + * @version 2.50, 03/15/10 + */ +public interface Digest +{ + public int getDigestLength(); + + public void update(byte b); + + public void update(byte[] b); + + public void update(byte b[], int off, int len); + + public void reset(); + + public void digest(byte[] out); + + public void digest(byte[] out, int off); +} diff --git a/src/main/java/ch/ethz/ssh2/crypto/digest/HMAC.java b/src/main/java/ch/ethz/ssh2/crypto/digest/HMAC.java new file mode 100644 index 0000000..fcdf43d --- /dev/null +++ b/src/main/java/ch/ethz/ssh2/crypto/digest/HMAC.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. + * Please refer to the LICENSE.txt for licensing details. + */ +package ch.ethz.ssh2.crypto.digest; + +/** + * HMAC. + * + * @author Christian Plattner + * @version 2.50, 03/15/10 + */ +public final class HMAC implements Digest +{ + Digest md; + byte[] k_xor_ipad; + byte[] k_xor_opad; + + byte[] tmp; + + int size; + + public HMAC(Digest md, byte[] key, int size) + { + this.md = md; + this.size = size; + + tmp = new byte[md.getDigestLength()]; + + int blocksize = 64; + + k_xor_ipad = new byte[blocksize]; + k_xor_opad = new byte[blocksize]; + + if (key.length > blocksize) + { + md.reset(); + md.update(key); + md.digest(tmp); + key = tmp; + } + + System.arraycopy(key, 0, k_xor_ipad, 0, key.length); + System.arraycopy(key, 0, k_xor_opad, 0, key.length); + + for (int i = 0; i < blocksize; i++) + { + k_xor_ipad[i] ^= 0x36; + k_xor_opad[i] ^= 0x5C; + } + md.update(k_xor_ipad); + } + + public int getDigestLength() + { + return size; + } + + public void update(byte b) + { + md.update(b); + } + + public void update(byte[] b) + { + md.update(b); + } + + public void update(byte[] b, int off, int len) + { + md.update(b, off, len); + } + + public void reset() + { + md.reset(); + md.update(k_xor_ipad); + } + + public void digest(byte[] out) + { + digest(out, 0); + } + + public void digest(byte[] out, int off) + { + md.digest(tmp); + + md.update(k_xor_opad); + md.update(tmp); + + md.digest(tmp); + + System.arraycopy(tmp, 0, out, off, size); + + md.update(k_xor_ipad); + } +} diff --git a/src/main/java/ch/ethz/ssh2/crypto/digest/HashForSSH2Types.java b/src/main/java/ch/ethz/ssh2/crypto/digest/HashForSSH2Types.java new file mode 100644 index 0000000..45eea70 --- /dev/null +++ b/src/main/java/ch/ethz/ssh2/crypto/digest/HashForSSH2Types.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. + * Please refer to the LICENSE.txt for licensing details. + */ +package ch.ethz.ssh2.crypto.digest; + +import java.math.BigInteger; + +/** + * HashForSSH2Types. + * + * @author Christian Plattner + * @version 2.50, 03/15/10 + */ +public class HashForSSH2Types +{ + Digest md; + + public HashForSSH2Types(Digest md) + { + this.md = md; + } + + public HashForSSH2Types(String type) + { + if (type.equals("SHA1")) + { + md = new SHA1(); + } + else if (type.equals("MD5")) + { + md = new MD5(); + } + else + throw new IllegalArgumentException("Unknown algorithm " + type); + } + + public void updateByte(byte b) + { + /* HACK - to test it with J2ME */ + byte[] tmp = new byte[1]; + tmp[0] = b; + md.update(tmp); + } + + public void updateBytes(byte[] b) + { + md.update(b); + } + + public void updateUINT32(int v) + { + md.update((byte) (v >> 24)); + md.update((byte) (v >> 16)); + md.update((byte) (v >> 8)); + md.update((byte) (v)); + } + + public void updateByteString(byte[] b) + { + updateUINT32(b.length); + updateBytes(b); + } + + public void updateBigInt(BigInteger b) + { + updateByteString(b.toByteArray()); + } + + public void reset() + { + md.reset(); + } + + public int getDigestLength() + { + return md.getDigestLength(); + } + + public byte[] getDigest() + { + byte[] tmp = new byte[md.getDigestLength()]; + getDigest(tmp); + return tmp; + } + + public void getDigest(byte[] out) + { + getDigest(out, 0); + } + + public void getDigest(byte[] out, int off) + { + md.digest(out, off); + } +} diff --git a/src/main/java/ch/ethz/ssh2/crypto/digest/MAC.java b/src/main/java/ch/ethz/ssh2/crypto/digest/MAC.java new file mode 100644 index 0000000..ab40de3 --- /dev/null +++ b/src/main/java/ch/ethz/ssh2/crypto/digest/MAC.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. + * Please refer to the LICENSE.txt for licensing details. + */ +package ch.ethz.ssh2.crypto.digest; + +/** + * MAC. + * + * @author Christian Plattner + * @version 2.50, 03/15/10 + */ +public final class MAC +{ + Digest mac; + int size; + + public static String[] getMacList() + { + /* Higher Priority First */ + + return new String[]{"hmac-sha1-96", "hmac-sha1", "hmac-md5-96", "hmac-md5"}; + } + + public static void checkMacList(String[] macs) + { + for (int i = 0; i < macs.length; i++) + getKeyLen(macs[i]); + } + + public static int getKeyLen(String type) + { + if (type.equals("hmac-sha1")) + return 20; + if (type.equals("hmac-sha1-96")) + return 20; + if (type.equals("hmac-md5")) + return 16; + if (type.equals("hmac-md5-96")) + return 16; + throw new IllegalArgumentException("Unkown algorithm " + type); + } + + public MAC(String type, byte[] key) + { + if (type.equals("hmac-sha1")) + { + mac = new HMAC(new SHA1(), key, 20); + } + else if (type.equals("hmac-sha1-96")) + { + mac = new HMAC(new SHA1(), key, 12); + } + else if (type.equals("hmac-md5")) + { + mac = new HMAC(new MD5(), key, 16); + } + else if (type.equals("hmac-md5-96")) + { + mac = new HMAC(new MD5(), key, 12); + } + else + throw new IllegalArgumentException("Unkown algorithm " + type); + + size = mac.getDigestLength(); + } + + public void initMac(int seq) + { + mac.reset(); + mac.update((byte) (seq >> 24)); + mac.update((byte) (seq >> 16)); + mac.update((byte) (seq >> 8)); + mac.update((byte) (seq)); + } + + public void update(byte[] packetdata, int off, int len) + { + mac.update(packetdata, off, len); + } + + public void getMac(byte[] out, int off) + { + mac.digest(out, off); + } + + public int size() + { + return size; + } +} diff --git a/src/main/java/ch/ethz/ssh2/crypto/digest/MD5.java b/src/main/java/ch/ethz/ssh2/crypto/digest/MD5.java new file mode 100644 index 0000000..bd3c451 --- /dev/null +++ b/src/main/java/ch/ethz/ssh2/crypto/digest/MD5.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. + * Please refer to the LICENSE.txt for licensing details. + */ +package ch.ethz.ssh2.crypto.digest; + +/** + * MD5. Based on the example code in RFC 1321. Optimized (...a little). + * + * @author Christian Plattner + * @version 2.50, 03/15/10 + */ + +/* + * The following disclaimer has been copied from RFC 1321: + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights + * reserved. + * + * License to copy and use this software is granted provided that it is + * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in + * all material mentioning or referencing this software or this function. + * + * License is also granted to make and use derivative works provided that such + * works are identified as "derived from the RSA Data Security, Inc. MD5 + * Message-Digest Algorithm" in all material mentioning or referencing the + * derived work. + * + * RSA Data Security, Inc. makes no representations concerning either the + * merchantability of this software or the suitability of this software for any + * particular purpose. It is provided "as is" without express or implied + * warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + * + */ + +public final class MD5 implements Digest +{ + private int state0, state1, state2, state3; + private long count; + private final byte[] block = new byte[64]; + private final int x[] = new int[16]; + + private static final byte[] padding = new byte[] { (byte) 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + public MD5() + { + reset(); + } + + private static int FF(int a, int b, int c, int d, int x, int s, int ac) + { + a += ((b & c) | ((~b) & d)) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static int GG(int a, int b, int c, int d, int x, int s, int ac) + { + a += ((b & d) | (c & (~d))) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static int HH(int a, int b, int c, int d, int x, int s, int ac) + { + a += (b ^ c ^ d) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static int II(int a, int b, int c, int d, int x, int s, int ac) + { + a += (c ^ (b | (~d))) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static void encode(byte[] dst, int dstoff, int word) + { + dst[dstoff] = (byte) (word); + dst[dstoff + 1] = (byte) (word >> 8); + dst[dstoff + 2] = (byte) (word >> 16); + dst[dstoff + 3] = (byte) (word >> 24); + } + + private void transform(byte[] src, int pos) + { + int a = state0; + int b = state1; + int c = state2; + int d = state3; + + for (int i = 0; i < 16; i++, pos += 4) + { + x[i] = (src[pos] & 0xff) | ((src[pos + 1] & 0xff) << 8) | ((src[pos + 2] & 0xff) << 16) + | ((src[pos + 3] & 0xff) << 24); + } + + /* Round 1 */ + + a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */ + d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */ + c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */ + b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */ + a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */ + d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */ + c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */ + b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */ + a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */ + d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */ + c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */ + b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */ + a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */ + d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */ + c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */ + b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */ + + /* Round 2 */ + a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */ + d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */ + c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */ + b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */ + a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */ + d = GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */ + c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */ + b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */ + a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */ + d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */ + c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */ + b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */ + a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */ + d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */ + c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */ + b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */ + d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */ + c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */ + b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */ + a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */ + d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */ + c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */ + b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */ + a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */ + d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */ + c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */ + b = HH(b, c, d, a, x[6], 23, 0x4881d05); /* 44 */ + a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */ + d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */ + c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */ + b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */ + d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */ + c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */ + b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */ + a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */ + d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */ + c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */ + b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */ + a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */ + d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */ + c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */ + b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */ + a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */ + d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */ + c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */ + b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */ + + state0 += a; + state1 += b; + state2 += c; + state3 += d; + } + + public final void reset() + { + count = 0; + + state0 = 0x67452301; + state1 = 0xefcdab89; + state2 = 0x98badcfe; + state3 = 0x10325476; + + /* Clear traces in memory... */ + + for (int i = 0; i < 16; i++) + x[i] = 0; + } + + public final void update(byte b) + { + final int space = 64 - ((int) (count & 0x3f)); + + count++; + + block[64 - space] = b; + + if (space == 1) + transform(block, 0); + } + + public final void update(byte[] buff, int pos, int len) + { + int space = 64 - ((int) (count & 0x3f)); + + count += len; + + while (len > 0) + { + if (len < space) + { + System.arraycopy(buff, pos, block, 64 - space, len); + break; + } + + if (space == 64) + { + transform(buff, pos); + } + else + { + System.arraycopy(buff, pos, block, 64 - space, space); + transform(block, 0); + } + + pos += space; + len -= space; + space = 64; + } + } + + public final void update(byte[] b) + { + update(b, 0, b.length); + } + + public final void digest(byte[] dst, int pos) + { + byte[] bits = new byte[8]; + + encode(bits, 0, (int) (count << 3)); + encode(bits, 4, (int) (count >> 29)); + + int idx = (int) count & 0x3f; + int padLen = (idx < 56) ? (56 - idx) : (120 - idx); + + update(padding, 0, padLen); + update(bits, 0, 8); + + encode(dst, pos, state0); + encode(dst, pos + 4, state1); + encode(dst, pos + 8, state2); + encode(dst, pos + 12, state3); + + reset(); + } + + public final void digest(byte[] dst) + { + digest(dst, 0); + } + + public final int getDigestLength() + { + return 16; + } +} diff --git a/src/main/java/ch/ethz/ssh2/crypto/digest/SHA1.java b/src/main/java/ch/ethz/ssh2/crypto/digest/SHA1.java new file mode 100644 index 0000000..795005e --- /dev/null +++ b/src/main/java/ch/ethz/ssh2/crypto/digest/SHA1.java @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. + * Please refer to the LICENSE.txt for licensing details. + */ +package ch.ethz.ssh2.crypto.digest; + +/** + * SHA-1 implementation based on FIPS PUB 180-1. + * Highly optimized. + * <p/> + * (http://www.itl.nist.gov/fipspubs/fip180-1.htm) + * + * @author Christian Plattner + * @version 2.50, 03/15/10 + */ +public final class SHA1 implements Digest +{ + private int H0, H1, H2, H3, H4; + + private final int[] w = new int[80]; + private int currentPos; + private long currentLen; + + public SHA1() + { + reset(); + } + + public int getDigestLength() + { + return 20; + } + + public void reset() + { + H0 = 0x67452301; + H1 = 0xEFCDAB89; + H2 = 0x98BADCFE; + H3 = 0x10325476; + H4 = 0xC3D2E1F0; + + currentPos = 0; + currentLen = 0; + + /* In case of complete paranoia, we should also wipe out the + * information contained in the w[] array */ + } + + public void update(byte b[]) + { + update(b, 0, b.length); + } + + public void update(byte b[], int off, int len) + { + if (len >= 4) + { + int idx = currentPos >> 2; + + switch (currentPos & 3) + { + case 0: + w[idx] = (((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff)); + len -= 4; + currentPos += 4; + currentLen += 32; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + break; + case 1: + w[idx] = (w[idx] << 24) | (((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff)); + len -= 3; + currentPos += 3; + currentLen += 24; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + break; + case 2: + w[idx] = (w[idx] << 16) | (((b[off++] & 0xff) << 8) | (b[off++] & 0xff)); + len -= 2; + currentPos += 2; + currentLen += 16; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + break; + case 3: + w[idx] = (w[idx] << 8) | (b[off++] & 0xff); + len--; + currentPos++; + currentLen += 8; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + break; + } + + /* Now currentPos is a multiple of 4 - this is the place to be...*/ + + while (len >= 8) + { + w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) + | (b[off++] & 0xff); + currentPos += 4; + + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + + w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) + | (b[off++] & 0xff); + + currentPos += 4; + + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + + currentLen += 64; + len -= 8; + } + + while (len < 0) //(len >= 4) + { + w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) + | (b[off++] & 0xff); + len -= 4; + currentPos += 4; + currentLen += 32; + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + } + } + + /* Remaining bytes (1-3) */ + + while (len > 0) + { + /* Here is room for further improvements */ + int idx = currentPos >> 2; + w[idx] = (w[idx] << 8) | (b[off++] & 0xff); + + currentLen += 8; + currentPos++; + + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + len--; + } + } + + public void update(byte b) + { + int idx = currentPos >> 2; + w[idx] = (w[idx] << 8) | (b & 0xff); + + currentLen += 8; + currentPos++; + + if (currentPos == 64) + { + perform(); + currentPos = 0; + } + } + + private void putInt(byte[] b, int pos, int val) + { + b[pos] = (byte) (val >> 24); + b[pos + 1] = (byte) (val >> 16); + b[pos + 2] = (byte) (val >> 8); + b[pos + 3] = (byte) val; + } + + public void digest(byte[] out) + { + digest(out, 0); + } + + public void digest(byte[] out, int off) + { + /* Pad with a '1' and 7-31 zero bits... */ + + int idx = currentPos >> 2; + w[idx] = ((w[idx] << 8) | (0x80)) << ((3 - (currentPos & 3)) << 3); + + currentPos = (currentPos & ~3) + 4; + + if (currentPos == 64) + { + currentPos = 0; + perform(); + } + else if (currentPos == 60) + { + currentPos = 0; + w[15] = 0; + perform(); + } + + /* Now currentPos is a multiple of 4 and we can do the remaining + * padding much more efficiently, furthermore we are sure + * that currentPos <= 56. + */ + + for (int i = currentPos >> 2; i < 14; i++) + w[i] = 0; + + w[14] = (int) (currentLen >> 32); + w[15] = (int) currentLen; + + perform(); + + putInt(out, off, H0); + putInt(out, off + 4, H1); + putInt(out, off + 8, H2); + putInt(out, off + 12, H3); + putInt(out, off + 16, H4); + + reset(); + } + + private void perform() + { + for (int t = 16; t < 80; t++) + { + int x = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; + w[t] = ((x << 1) | (x >>> 31)); + } + + int A = H0; + int B = H1; + int C = H2; + int D = H3; + int E = H4; + + /* Here we use variable substitution and loop unrolling + * + * === Original step: + * + * T = s5(A) + f(B,C,D) + E + w[0] + K; + * E = D; D = C; C = s30(B); B = A; A = T; + * + * === Rewritten step: + * + * T = s5(A + f(B,C,D) + E + w[0] + K; + * B = s30(B); + * E = D; D = C; C = B; B = A; A = T; + * + * === Let's rewrite things, introducing new variables: + * + * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A; + * + * T = s5(A0) + f(B0,C0,D0) + E0 + w[0] + K; + * B0 = s30(B0); + * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = T; + * + * T = s5(A1) + f(B1,C1,D1) + E1 + w[1] + K; + * B1 = s30(B1); + * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = T; + * + * E = E2; D = E2; C = C2; B = B2; A = A2; + * + * === No need for 'T', we can write into 'Ex' instead since + * after the calculation of 'T' nobody is interested + * in 'Ex' anymore. + * + * E0 = E; D0 = D; C0 = C; B0 = B; A0 = A; + * + * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K; + * B0 = s30(B0); + * E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0; + * + * E1 = E1 + s5(A1) + f(B1,C1,D1) + w[1] + K; + * B1 = s30(B1); + * E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = E1; + * + * E = Ex; D = Ex; C = Cx; B = Bx; A = Ax; + * + * === Further optimization: get rid of the swap operations + * Idea: instead of swapping the variables, swap the names of + * the used variables in the next step: + * + * E0 = E; D0 = d; C0 = C; B0 = B; A0 = A; + * + * E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K; + * B0 = s30(B0); + * // E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0; + * + * D0 = D0 + s5(E0) + f(A0,B0,C0) + w[1] + K; + * A0 = s30(A0); + * E2 = C0; D2 = B0; C2 = A0; B2 = E0; A2 = D0; + * + * E = E2; D = D2; C = C2; B = B2; A = A2; + * + * === OK, let's do this several times, also, directly + * use A (instead of A0) and B,C,D,E. + * + * E = E + s5(A) + f(B,C,D) + w[0] + K; + * B = s30(B); + * // E1 = D; D1 = C; C1 = B; B1 = A; A1 = E; + * + * D = D + s5(E) + f(A,B,C) + w[1] + K; + * A = s30(A); + * // E2 = C; D2 = B; C2 = A; B2 = E; A2 = D; + * + * C = C + s5(D) + f(E,A,B) + w[2] + K; + * E = s30(E); + * // E3 = B; D3 = A; C3 = E; B3 = D; A3 = C; + * + * B = B + s5(C) + f(D,E,A) + w[3] + K; + * D = s30(D); + * // E4 = A; D4 = E; C4 = D; B4 = C; A4 = B; + * + * A = A + s5(B) + f(C,D,E) + w[4] + K; + * C = s30(C); + * // E5 = E; D5 = D; C5 = C; B5 = B; A5 = A; + * + * //E = E5; D = D5; C = C5; B = B5; A = A5; + * + * === Very nice, after 5 steps each variable + * has the same contents as after 5 steps with + * the original algorithm! + * + * We therefore can easily unroll each interval, + * as the number of steps in each interval is a + * multiple of 5 (20 steps per interval). + */ + + E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[0] + 0x5A827999; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[1] + 0x5A827999; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[2] + 0x5A827999; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[3] + 0x5A827999; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[4] + 0x5A827999; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[5] + 0x5A827999; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[6] + 0x5A827999; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[7] + 0x5A827999; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[8] + 0x5A827999; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[9] + 0x5A827999; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[10] + 0x5A827999; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[11] + 0x5A827999; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[12] + 0x5A827999; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[13] + 0x5A827999; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[14] + 0x5A827999; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[15] + 0x5A827999; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[16] + 0x5A827999; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[17] + 0x5A827999; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[18] + 0x5A827999; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[19] + 0x5A827999; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[20] + 0x6ED9EBA1; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[21] + 0x6ED9EBA1; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[22] + 0x6ED9EBA1; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[23] + 0x6ED9EBA1; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[24] + 0x6ED9EBA1; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[25] + 0x6ED9EBA1; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[26] + 0x6ED9EBA1; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[27] + 0x6ED9EBA1; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[28] + 0x6ED9EBA1; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[29] + 0x6ED9EBA1; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[30] + 0x6ED9EBA1; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[31] + 0x6ED9EBA1; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[32] + 0x6ED9EBA1; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[33] + 0x6ED9EBA1; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[34] + 0x6ED9EBA1; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[35] + 0x6ED9EBA1; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[36] + 0x6ED9EBA1; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[37] + 0x6ED9EBA1; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[38] + 0x6ED9EBA1; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[39] + 0x6ED9EBA1; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[40] + 0x8F1BBCDC; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[41] + 0x8F1BBCDC; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[42] + 0x8F1BBCDC; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[43] + 0x8F1BBCDC; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[44] + 0x8F1BBCDC; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[45] + 0x8F1BBCDC; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[46] + 0x8F1BBCDC; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[47] + 0x8F1BBCDC; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[48] + 0x8F1BBCDC; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[49] + 0x8F1BBCDC; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[50] + 0x8F1BBCDC; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[51] + 0x8F1BBCDC; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[52] + 0x8F1BBCDC; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[53] + 0x8F1BBCDC; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[54] + 0x8F1BBCDC; + C = ((C << 30) | (C >>> 2)); + + E = E + ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[55] + 0x8F1BBCDC; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[56] + 0x8F1BBCDC; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[57] + 0x8F1BBCDC; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[58] + 0x8F1BBCDC; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[59] + 0x8F1BBCDC; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[60] + 0xCA62C1D6; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[61] + 0xCA62C1D6; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[62] + 0xCA62C1D6; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[63] + 0xCA62C1D6; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[64] + 0xCA62C1D6; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[65] + 0xCA62C1D6; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[66] + 0xCA62C1D6; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[67] + 0xCA62C1D6; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[68] + 0xCA62C1D6; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[69] + 0xCA62C1D6; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[70] + 0xCA62C1D6; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[71] + 0xCA62C1D6; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[72] + 0xCA62C1D6; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[73] + 0xCA62C1D6; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[74] + 0xCA62C1D6; + C = ((C << 30) | (C >>> 2)); + + E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[75] + 0xCA62C1D6; + B = ((B << 30) | (B >>> 2)); + + D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[76] + 0xCA62C1D6; + A = ((A << 30) | (A >>> 2)); + + C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[77] + 0xCA62C1D6; + E = ((E << 30) | (E >>> 2)); + + B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[78] + 0xCA62C1D6; + D = ((D << 30) | (D >>> 2)); + + A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[79] + 0xCA62C1D6; + C = ((C << 30) | (C >>> 2)); + + H0 += A; + H1 += B; + H2 += C; + H3 += D; + H4 += E; + + // debug(80, H0, H1, H2, H3, H4); + } + + private static String toHexString(byte[] b) + { + final String hexChar = "0123456789ABCDEF"; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < b.length; i++) + { + sb.append(hexChar.charAt((b[i] >> 4) & 0x0f)); + sb.append(hexChar.charAt(b[i] & 0x0f)); + } + return sb.toString(); + } + + public static void main(String[] args) + { + SHA1 sha = new SHA1(); + + byte[] dig1 = new byte[20]; + byte[] dig2 = new byte[20]; + byte[] dig3 = new byte[20]; + + /* + * We do not specify a charset name for getBytes(), since we assume that + * the JVM's default encoder maps the _used_ ASCII characters exactly as + * getBytes("US-ASCII") would do. (Ah, yes, too lazy to catch the + * exception that can be thrown by getBytes("US-ASCII")). Note: This has + * no effect on the SHA-1 implementation, this is just for the following + * test code. + */ + + sha.update("abc".getBytes()); + sha.digest(dig1); + + sha.update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".getBytes()); + sha.digest(dig2); + + for (int i = 0; i < 1000000; i++) + sha.update((byte) 'a'); + sha.digest(dig3); + + String dig1_res = toHexString(dig1); + String dig2_res = toHexString(dig2); + String dig3_res = toHexString(dig3); + + String dig1_ref = "A9993E364706816ABA3E25717850C26C9CD0D89D"; + String dig2_ref = "84983E441C3BD26EBAAE4AA1F95129E5E54670F1"; + String dig3_ref = "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F"; + + if (dig1_res.equals(dig1_ref)) + System.out.println("SHA-1 Test 1 OK."); + else + System.out.println("SHA-1 Test 1 FAILED."); + + if (dig2_res.equals(dig2_ref)) + System.out.println("SHA-1 Test 2 OK."); + else + System.out.println("SHA-1 Test 2 FAILED."); + + if (dig3_res.equals(dig3_ref)) + System.out.println("SHA-1 Test 3 OK."); + else + System.out.println("SHA-1 Test 3 FAILED."); + + if (dig3_res.equals(dig3_ref)) + System.out.println("SHA-1 Test 3 OK."); + else + System.out.println("SHA-1 Test 3 FAILED."); + } +} |