001: package ch.ethz.ssh2.crypto.digest;
002:
003: /**
004: *
005: * SHA-1 implementation based on FIPS PUB 180-1.
006: *
007: * (http://www.itl.nist.gov/fipspubs/fip180-1.htm)
008: *
009: * @author Christian Plattner, plattner@inf.ethz.ch
010: * @version $Id: SHA1.java,v 1.4 2006/02/02 09:11:03 cplattne Exp $
011: */
012: public final class SHA1 implements Digest {
013: private int H0, H1, H2, H3, H4;
014:
015: private final byte msg[] = new byte[64];
016: private final int[] w = new int[80];
017: private int currentPos;
018: private long currentLen;
019:
020: public SHA1() {
021: reset();
022: }
023:
024: public final int getDigestLength() {
025: return 20;
026: }
027:
028: public final void reset() {
029: H0 = 0x67452301;
030: H1 = 0xEFCDAB89;
031: H2 = 0x98BADCFE;
032: H3 = 0x10325476;
033: H4 = 0xC3D2E1F0;
034:
035: currentPos = 0;
036: currentLen = 0;
037: }
038:
039: public final void update(byte b[], int off, int len) {
040: for (int i = off; i < (off + len); i++)
041: update(b[i]);
042: }
043:
044: public final void update(byte b[]) {
045: for (int i = 0; i < b.length; i++)
046: update(b[i]);
047: }
048:
049: public final void update(byte b) {
050: // System.out.println(pos + "->" + b);
051: msg[currentPos++] = b;
052: currentLen += 8;
053: if (currentPos == 64) {
054: perform();
055: currentPos = 0;
056: }
057: }
058:
059: private static final String toHexString(byte[] b) {
060: final String hexChar = "0123456789ABCDEF";
061:
062: StringBuffer sb = new StringBuffer();
063: for (int i = 0; i < b.length; i++) {
064: sb.append(hexChar.charAt((b[i] >> 4) & 0x0f));
065: sb.append(hexChar.charAt(b[i] & 0x0f));
066: }
067: return sb.toString();
068: }
069:
070: private final void putInt(byte[] b, int pos, int val) {
071: b[pos] = (byte) (val >> 24);
072: b[pos + 1] = (byte) (val >> 16);
073: b[pos + 2] = (byte) (val >> 8);
074: b[pos + 3] = (byte) val;
075: }
076:
077: public final void digest(byte[] out) {
078: digest(out, 0);
079: }
080:
081: public final void digest(byte[] out, int off) {
082: long l = currentLen;
083:
084: update((byte) 0x80);
085:
086: // padding could be done more efficiently...
087: while (currentPos != 56)
088: update((byte) 0);
089:
090: update((byte) (l >> 56));
091: update((byte) (l >> 48));
092: update((byte) (l >> 40));
093: update((byte) (l >> 32));
094:
095: update((byte) (l >> 24));
096: update((byte) (l >> 16));
097: update((byte) (l >> 8));
098: update((byte) (l));
099:
100: // debug(80, H0, H1, H2, H3, H4);
101:
102: putInt(out, off, H0);
103: putInt(out, off + 4, H1);
104: putInt(out, off + 8, H2);
105: putInt(out, off + 12, H3);
106: putInt(out, off + 16, H4);
107:
108: reset();
109: }
110:
111: /*
112: * private void debug(int t, int A, int B, int C, int D, int E) {
113: * System.out.println(t + ": " + Integer.toHexString(A).toUpperCase() + ", " +
114: * Integer.toHexString(B).toUpperCase() + ", " +
115: * Integer.toHexString(C).toUpperCase() + "," +
116: * Integer.toHexString(D).toUpperCase() + ", " +
117: * Integer.toHexString(E).toUpperCase()); }
118: */
119: private final void perform() {
120: for (int i = 0; i < 16; i++)
121: w[i] = ((msg[i * 4] & 0xff) << 24)
122: | ((msg[i * 4 + 1] & 0xff) << 16)
123: | ((msg[i * 4 + 2] & 0xff) << 8)
124: | ((msg[i * 4 + 3] & 0xff));
125:
126: for (int t = 16; t < 80; t++) {
127: int x = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
128: w[t] = ((x << 1) | (x >>> 31));
129: }
130:
131: int A = H0;
132: int B = H1;
133: int C = H2;
134: int D = H3;
135: int E = H4;
136:
137: int T;
138:
139: for (int t = 0; t <= 19; t++) {
140: T = ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + E
141: + w[t] + 0x5A827999;
142: E = D;
143: D = C;
144: C = ((B << 30) | (B >>> 2));
145: B = A;
146: A = T;
147: // debug(t, A, B, C, D, E);
148: }
149:
150: for (int t = 20; t <= 39; t++) {
151: T = ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + E + w[t]
152: + 0x6ED9EBA1;
153: E = D;
154: D = C;
155: C = ((B << 30) | (B >>> 2));
156: B = A;
157: A = T;
158: // debug(t, A, B, C, D, E);
159: }
160:
161: for (int t = 40; t <= 59; t++) {
162: T = ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D))
163: + E + w[t] + 0x8F1BBCDC;
164: E = D;
165: D = C;
166: C = ((B << 30) | (B >>> 2));
167: B = A;
168: A = T;
169: // debug(t, A, B, C, D, E);
170: }
171:
172: for (int t = 60; t <= 79; t++) {
173: T = ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + E + w[t]
174: + 0xCA62C1D6;
175: E = D;
176: D = C;
177: C = ((B << 30) | (B >>> 2));
178: B = A;
179: A = T;
180: // debug(t, A, B, C, D, E);
181: }
182:
183: H0 = H0 + A;
184: H1 = H1 + B;
185: H2 = H2 + C;
186: H3 = H3 + D;
187: H4 = H4 + E;
188:
189: // debug(80, H0, H1, H2, H3, H4);
190: }
191:
192: public static void main(String[] args) {
193: SHA1 sha = new SHA1();
194:
195: byte[] dig1 = new byte[20];
196: byte[] dig2 = new byte[20];
197: byte[] dig3 = new byte[20];
198:
199: /*
200: * We do not specify a charset name for getBytes(), since we assume that
201: * the JVM's default encoder maps the _used_ ASCII characters exactly as
202: * getBytes("US-ASCII") would do. (Ah, yes, too lazy to catch the
203: * exception that can be thrown by getBytes("US-ASCII")). Note: This has
204: * no effect on the SHA-1 implementation, this is just for the following
205: * test code.
206: */
207:
208: sha.update("abc".getBytes());
209: sha.digest(dig1);
210:
211: sha
212: .update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
213: .getBytes());
214: sha.digest(dig2);
215:
216: for (int i = 0; i < 1000000; i++)
217: sha.update((byte) 'a');
218: sha.digest(dig3);
219:
220: String dig1_res = toHexString(dig1);
221: String dig2_res = toHexString(dig2);
222: String dig3_res = toHexString(dig3);
223:
224: String dig1_ref = "A9993E364706816ABA3E25717850C26C9CD0D89D";
225: String dig2_ref = "84983E441C3BD26EBAAE4AA1F95129E5E54670F1";
226: String dig3_ref = "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F";
227:
228: if (dig1_res.equals(dig1_ref))
229: System.out.println("SHA-1 Test 1 OK.");
230: else
231: System.out.println("SHA-1 Test 1 FAILED.");
232:
233: if (dig2_res.equals(dig2_ref))
234: System.out.println("SHA-1 Test 2 OK.");
235: else
236: System.out.println("SHA-1 Test 2 FAILED.");
237:
238: if (dig3_res.equals(dig3_ref))
239: System.out.println("SHA-1 Test 3 OK.");
240: else
241: System.out.println("SHA-1 Test 3 FAILED.");
242:
243: }
244: }
|