View Javadoc

1   /**
2    * Copyright Santeri Paavolainen <sjpaavol@cc.helsinki.fi> and Timothy W Macinta
3    * (twm@alum.mit.edu) (optimizations and bug fixes) and individual contributors
4    * by the @author tags. See the COPYRIGHT.txt in the distribution for a full
5    * listing of individual contributors.
6    *
7    * This is free software; you can redistribute it and/or modify it under the
8    * terms of the GNU Lesser General Public License as published by the Free
9    * Software Foundation; either version 2.1 of the License, or (at your option)
10   * any later version.
11   *
12   * This software is distributed in the hope that it will be useful, but WITHOUT
13   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15   * details.
16   *
17   * You should have received a copy of the GNU Lesser General Public License
18   * along with this software; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
20   * site: http://www.fsf.org.
21   */
22  package goldengate.common.digest;
23  
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileNotFoundException;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.nio.ByteBuffer;
30  import java.nio.channels.FileChannel;
31  import java.util.Arrays;
32  
33  import org.jboss.netty.buffer.ChannelBuffer;
34  
35  /**
36   * Fast implementation of RSA's MD5 hash generator in Java JDK Beta-2 or higher.
37   * <p>
38   * Originally written by Santeri Paavolainen, Helsinki Finland 1996.<br>
39   * (c) Santeri Paavolainen, Helsinki Finland 1996<br>
40   * Many changes Copyright (c) 2002 - 2005 Timothy W Macinta<br>
41   * <p>
42   * This library is free software; you can redistribute it and/or modify it under
43   * the terms of the GNU Library General Public License as published by the Free
44   * Software Foundation; either version 2.1 of the License, or (at your option)
45   * any later version.
46   * <p>
47   * This library is distributed in the hope that it will be useful, but WITHOUT
48   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
49   * FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
50   * details.
51   * <p>
52   * You should have received a copy of the GNU Library General Public License
53   * along with this library; if not, write to the Free Software Foundation, Inc.,
54   * 675 Mass Ave, Cambridge, MA 02139, USA.
55   * <p>
56   * See http://www.twmacinta.com/myjava/fast_md5.php for more information on this
57   * file and the related files.
58   * <p>
59   * This was originally a rather straight re-implementation of the reference
60   * implementation given in RFC1321 by RSA. It passes the MD5 test suite as
61   * defined in RFC1321.
62   * <p>
63   * Many optimizations made by Timothy W Macinta. Reduced time to checksum a test
64   * file in Java alone to roughly half the time taken compared with
65   * java.security.MessageDigest (within an intepretter). Also added an optional
66   * native method to reduce the time even further. See
67   * http://www.twmacinta.com/myjava/fast_md5.php for further information on the
68   * time improvements achieved.
69   * <p>
70   * Some bug fixes also made by Timothy W Macinta.
71   * <p>
72   * Please note: I (Timothy Macinta) have put this code in the com.twmacinta.util
73   * package only because it came without a package. I was not the the original
74   * author of the code, although I did optimize it (substantially) and fix some
75   * bugs.
76   * <p>
77   * This Java class has been derived from the RSA Data Security, Inc. MD5
78   * Message-Digest Algorithm and its reference implementation.
79   * <p>
80   * This class will attempt to use a native method to quickly compute checksums
81   * when the appropriate native library is available. On Linux, this library
82   * should be named "MD5.so" and on Windows it should be named "MD5.dll". The
83   * code will attempt to locate the library in the following locations in the
84   * order given:
85   *
86   * <ol>
87   * <li>The path specified by the system property
88   * "com.twmacinta.util.MD5.NATIVE_LIB_FILE" (be sure to include "MD5.so" or
89   * "MD5.dll" as appropriate at the end of the path).
90   * <li>A platform specific directory beneath the "lib/arch/" directory. On Linux
91   * for x86, this is "lib/arch/linux_x86/". On Windows for x86, this is
92   * "lib/arch/win32_x86/".
93   * <li>Within the "lib/" directory.
94   * <li>Within the current directory.
95   * </ol>
96   *
97   * <p>
98   * If the library is not found, the code will fall back to the default (slower)
99   * Java code.
100  * <p>
101  * As a side effect of having the code search for the native library,
102  * SecurityExceptions might be thrown on JVMs that have a restrictive
103  * SecurityManager. The initialization code attempts to silently discard these
104  * exceptions and continue, but many SecurityManagers will attempt to notify the
105  * user directly of all SecurityExceptions thrown. Consequently, the code has
106  * provisions for skipping the search for the native library. Any of these
107  * provisions may be used to skip the search as long as they are performed
108  * <i>before</i> the first instance of a com.twmacinta.util.MD5 object is
109  * constructed (note that the convenience stream objects will implicitly create
110  * an MD5 object).
111  * <p>
112  * The first option is to set the system property
113  * "com.twmacinta.util.MD5.NO_NATIVE_LIB" to "true" or "1". Unfortunately,
114  * SecurityManagers may also choose to disallow system property setting, so this
115  * won't be of use in all cases.
116  * <p>
117  * The second option is to call com.twmacinta.util.MD5.initNativeLibrary(true)
118  * before any MD5 objects are constructed.
119  *
120  * @author Santeri Paavolainen <sjpaavol@cc.helsinki.fi>
121  * @author Timothy W Macinta (twm@alum.mit.edu) (optimizations and bug fixes)
122  * @author Frederic Bregier Bregier (add NIO support and dynamic library path
123  *         loading)
124  */
125 
126 public class MD5 {
127     /**
128      * MD5 state
129      */
130     private MD5State state;
131 
132     /**
133      * If Final() has been called, finals is set to the current finals state.
134      * Any Update() causes this to be set to null.
135      */
136     private MD5State finals;
137 
138     /**
139      * Padding for Final()
140      */
141     private static byte padding[] = {
142             (byte) 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
144             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
145 
146     private static boolean native_lib_loaded = false;
147 
148     private static boolean native_lib_init_pending = true;
149 
150     private static String native_lib_path = "D:/somewhere/goldengate/lib/arch/win32_x86/MD5.dll";
151 
152     /**
153      * Initialize MD5 internal state (object can be reused just by calling
154      * Init() after every Final()
155      */
156     public synchronized void Init() {
157         state = new MD5State();
158         finals = null;
159     }
160 
161     /**
162      * Class constructor
163      */
164     public MD5() {
165         if (native_lib_init_pending) {
166             _initNativeLibrary();
167         }
168         Init();
169     }
170 
171     /**
172      * Initialize class, and update hash with ob.toString()
173      *
174      * @param ob
175      *            Object, ob.toString() is used to update hash after
176      *            initialization
177      */
178     public MD5(Object ob) {
179         this();
180         Update(ob.toString());
181     }
182 
183     private void Decode(byte buffer[], int shift, int[] out) {
184         /*
185          * len += shift; for (int i = 0; shift < len; i++, shift += 4) { out[i]
186          * = ((int) (buffer[shift] & 0xff)) | (((int) (buffer[shift + 1] &
187          * 0xff)) << 8) | (((int) (buffer[shift + 2] & 0xff)) << 16) | (((int)
188          * buffer[shift + 3]) << 24); }
189          */
190 
191         // unrolled loop (original loop shown above)
192         out[0] = buffer[shift] & 0xff | (buffer[shift + 1] & 0xff) << 8 |
193                 (buffer[shift + 2] & 0xff) << 16 | buffer[shift + 3] << 24;
194         out[1] = buffer[shift + 4] & 0xff | (buffer[shift + 5] & 0xff) << 8 |
195                 (buffer[shift + 6] & 0xff) << 16 | buffer[shift + 7] << 24;
196         out[2] = buffer[shift + 8] & 0xff | (buffer[shift + 9] & 0xff) << 8 |
197                 (buffer[shift + 10] & 0xff) << 16 | buffer[shift + 11] << 24;
198         out[3] = buffer[shift + 12] & 0xff | (buffer[shift + 13] & 0xff) << 8 |
199                 (buffer[shift + 14] & 0xff) << 16 | buffer[shift + 15] << 24;
200         out[4] = buffer[shift + 16] & 0xff | (buffer[shift + 17] & 0xff) << 8 |
201                 (buffer[shift + 18] & 0xff) << 16 | buffer[shift + 19] << 24;
202         out[5] = buffer[shift + 20] & 0xff | (buffer[shift + 21] & 0xff) << 8 |
203                 (buffer[shift + 22] & 0xff) << 16 | buffer[shift + 23] << 24;
204         out[6] = buffer[shift + 24] & 0xff | (buffer[shift + 25] & 0xff) << 8 |
205                 (buffer[shift + 26] & 0xff) << 16 | buffer[shift + 27] << 24;
206         out[7] = buffer[shift + 28] & 0xff | (buffer[shift + 29] & 0xff) << 8 |
207                 (buffer[shift + 30] & 0xff) << 16 | buffer[shift + 31] << 24;
208         out[8] = buffer[shift + 32] & 0xff | (buffer[shift + 33] & 0xff) << 8 |
209                 (buffer[shift + 34] & 0xff) << 16 | buffer[shift + 35] << 24;
210         out[9] = buffer[shift + 36] & 0xff | (buffer[shift + 37] & 0xff) << 8 |
211                 (buffer[shift + 38] & 0xff) << 16 | buffer[shift + 39] << 24;
212         out[10] = buffer[shift + 40] & 0xff | (buffer[shift + 41] & 0xff) << 8 |
213                 (buffer[shift + 42] & 0xff) << 16 | buffer[shift + 43] << 24;
214         out[11] = buffer[shift + 44] & 0xff | (buffer[shift + 45] & 0xff) << 8 |
215                 (buffer[shift + 46] & 0xff) << 16 | buffer[shift + 47] << 24;
216         out[12] = buffer[shift + 48] & 0xff | (buffer[shift + 49] & 0xff) << 8 |
217                 (buffer[shift + 50] & 0xff) << 16 | buffer[shift + 51] << 24;
218         out[13] = buffer[shift + 52] & 0xff | (buffer[shift + 53] & 0xff) << 8 |
219                 (buffer[shift + 54] & 0xff) << 16 | buffer[shift + 55] << 24;
220         out[14] = buffer[shift + 56] & 0xff | (buffer[shift + 57] & 0xff) << 8 |
221                 (buffer[shift + 58] & 0xff) << 16 | buffer[shift + 59] << 24;
222         out[15] = buffer[shift + 60] & 0xff | (buffer[shift + 61] & 0xff) << 8 |
223                 (buffer[shift + 62] & 0xff) << 16 | buffer[shift + 63] << 24;
224     }
225 
226     private native void TransformJni(int[] stat, byte buffer[], int shift,
227             int length);
228 
229     private void Transform(MD5State stat, byte buffer[], int shift,
230             int[] decode_buf) {
231         int a = stat.state[0], b = stat.state[1], c = stat.state[2], d = stat.state[3], x[] = decode_buf;
232 
233         Decode(buffer, shift, decode_buf);
234 
235         /* Round 1 */
236         a += (b & c | ~b & d) + x[0] + 0xd76aa478; /* 1 */
237         a = (a << 7 | a >>> 25) + b;
238         d += (a & b | ~a & c) + x[1] + 0xe8c7b756; /* 2 */
239         d = (d << 12 | d >>> 20) + a;
240         c += (d & a | ~d & b) + x[2] + 0x242070db; /* 3 */
241         c = (c << 17 | c >>> 15) + d;
242         b += (c & d | ~c & a) + x[3] + 0xc1bdceee; /* 4 */
243         b = (b << 22 | b >>> 10) + c;
244 
245         a += (b & c | ~b & d) + x[4] + 0xf57c0faf; /* 5 */
246         a = (a << 7 | a >>> 25) + b;
247         d += (a & b | ~a & c) + x[5] + 0x4787c62a; /* 6 */
248         d = (d << 12 | d >>> 20) + a;
249         c += (d & a | ~d & b) + x[6] + 0xa8304613; /* 7 */
250         c = (c << 17 | c >>> 15) + d;
251         b += (c & d | ~c & a) + x[7] + 0xfd469501; /* 8 */
252         b = (b << 22 | b >>> 10) + c;
253 
254         a += (b & c | ~b & d) + x[8] + 0x698098d8; /* 9 */
255         a = (a << 7 | a >>> 25) + b;
256         d += (a & b | ~a & c) + x[9] + 0x8b44f7af; /* 10 */
257         d = (d << 12 | d >>> 20) + a;
258         c += (d & a | ~d & b) + x[10] + 0xffff5bb1; /* 11 */
259         c = (c << 17 | c >>> 15) + d;
260         b += (c & d | ~c & a) + x[11] + 0x895cd7be; /* 12 */
261         b = (b << 22 | b >>> 10) + c;
262 
263         a += (b & c | ~b & d) + x[12] + 0x6b901122; /* 13 */
264         a = (a << 7 | a >>> 25) + b;
265         d += (a & b | ~a & c) + x[13] + 0xfd987193; /* 14 */
266         d = (d << 12 | d >>> 20) + a;
267         c += (d & a | ~d & b) + x[14] + 0xa679438e; /* 15 */
268         c = (c << 17 | c >>> 15) + d;
269         b += (c & d | ~c & a) + x[15] + 0x49b40821; /* 16 */
270         b = (b << 22 | b >>> 10) + c;
271 
272         /* Round 2 */
273         a += (b & d | c & ~d) + x[1] + 0xf61e2562; /* 17 */
274         a = (a << 5 | a >>> 27) + b;
275         d += (a & c | b & ~c) + x[6] + 0xc040b340; /* 18 */
276         d = (d << 9 | d >>> 23) + a;
277         c += (d & b | a & ~b) + x[11] + 0x265e5a51; /* 19 */
278         c = (c << 14 | c >>> 18) + d;
279         b += (c & a | d & ~a) + x[0] + 0xe9b6c7aa; /* 20 */
280         b = (b << 20 | b >>> 12) + c;
281 
282         a += (b & d | c & ~d) + x[5] + 0xd62f105d; /* 21 */
283         a = (a << 5 | a >>> 27) + b;
284         d += (a & c | b & ~c) + x[10] + 0x02441453; /* 22 */
285         d = (d << 9 | d >>> 23) + a;
286         c += (d & b | a & ~b) + x[15] + 0xd8a1e681; /* 23 */
287         c = (c << 14 | c >>> 18) + d;
288         b += (c & a | d & ~a) + x[4] + 0xe7d3fbc8; /* 24 */
289         b = (b << 20 | b >>> 12) + c;
290 
291         a += (b & d | c & ~d) + x[9] + 0x21e1cde6; /* 25 */
292         a = (a << 5 | a >>> 27) + b;
293         d += (a & c | b & ~c) + x[14] + 0xc33707d6; /* 26 */
294         d = (d << 9 | d >>> 23) + a;
295         c += (d & b | a & ~b) + x[3] + 0xf4d50d87; /* 27 */
296         c = (c << 14 | c >>> 18) + d;
297         b += (c & a | d & ~a) + x[8] + 0x455a14ed; /* 28 */
298         b = (b << 20 | b >>> 12) + c;
299 
300         a += (b & d | c & ~d) + x[13] + 0xa9e3e905; /* 29 */
301         a = (a << 5 | a >>> 27) + b;
302         d += (a & c | b & ~c) + x[2] + 0xfcefa3f8; /* 30 */
303         d = (d << 9 | d >>> 23) + a;
304         c += (d & b | a & ~b) + x[7] + 0x676f02d9; /* 31 */
305         c = (c << 14 | c >>> 18) + d;
306         b += (c & a | d & ~a) + x[12] + 0x8d2a4c8a; /* 32 */
307         b = (b << 20 | b >>> 12) + c;
308 
309         /* Round 3 */
310         a += (b ^ c ^ d) + x[5] + 0xfffa3942; /* 33 */
311         a = (a << 4 | a >>> 28) + b;
312         d += (a ^ b ^ c) + x[8] + 0x8771f681; /* 34 */
313         d = (d << 11 | d >>> 21) + a;
314         c += (d ^ a ^ b) + x[11] + 0x6d9d6122; /* 35 */
315         c = (c << 16 | c >>> 16) + d;
316         b += (c ^ d ^ a) + x[14] + 0xfde5380c; /* 36 */
317         b = (b << 23 | b >>> 9) + c;
318 
319         a += (b ^ c ^ d) + x[1] + 0xa4beea44; /* 37 */
320         a = (a << 4 | a >>> 28) + b;
321         d += (a ^ b ^ c) + x[4] + 0x4bdecfa9; /* 38 */
322         d = (d << 11 | d >>> 21) + a;
323         c += (d ^ a ^ b) + x[7] + 0xf6bb4b60; /* 39 */
324         c = (c << 16 | c >>> 16) + d;
325         b += (c ^ d ^ a) + x[10] + 0xbebfbc70; /* 40 */
326         b = (b << 23 | b >>> 9) + c;
327 
328         a += (b ^ c ^ d) + x[13] + 0x289b7ec6; /* 41 */
329         a = (a << 4 | a >>> 28) + b;
330         d += (a ^ b ^ c) + x[0] + 0xeaa127fa; /* 42 */
331         d = (d << 11 | d >>> 21) + a;
332         c += (d ^ a ^ b) + x[3] + 0xd4ef3085; /* 43 */
333         c = (c << 16 | c >>> 16) + d;
334         b += (c ^ d ^ a) + x[6] + 0x04881d05; /* 44 */
335         b = (b << 23 | b >>> 9) + c;
336 
337         a += (b ^ c ^ d) + x[9] + 0xd9d4d039; /* 33 */
338         a = (a << 4 | a >>> 28) + b;
339         d += (a ^ b ^ c) + x[12] + 0xe6db99e5; /* 34 */
340         d = (d << 11 | d >>> 21) + a;
341         c += (d ^ a ^ b) + x[15] + 0x1fa27cf8; /* 35 */
342         c = (c << 16 | c >>> 16) + d;
343         b += (c ^ d ^ a) + x[2] + 0xc4ac5665; /* 36 */
344         b = (b << 23 | b >>> 9) + c;
345 
346         /* Round 4 */
347         a += (c ^ (b | ~d)) + x[0] + 0xf4292244; /* 49 */
348         a = (a << 6 | a >>> 26) + b;
349         d += (b ^ (a | ~c)) + x[7] + 0x432aff97; /* 50 */
350         d = (d << 10 | d >>> 22) + a;
351         c += (a ^ (d | ~b)) + x[14] + 0xab9423a7; /* 51 */
352         c = (c << 15 | c >>> 17) + d;
353         b += (d ^ (c | ~a)) + x[5] + 0xfc93a039; /* 52 */
354         b = (b << 21 | b >>> 11) + c;
355 
356         a += (c ^ (b | ~d)) + x[12] + 0x655b59c3; /* 53 */
357         a = (a << 6 | a >>> 26) + b;
358         d += (b ^ (a | ~c)) + x[3] + 0x8f0ccc92; /* 54 */
359         d = (d << 10 | d >>> 22) + a;
360         c += (a ^ (d | ~b)) + x[10] + 0xffeff47d; /* 55 */
361         c = (c << 15 | c >>> 17) + d;
362         b += (d ^ (c | ~a)) + x[1] + 0x85845dd1; /* 56 */
363         b = (b << 21 | b >>> 11) + c;
364 
365         a += (c ^ (b | ~d)) + x[8] + 0x6fa87e4f; /* 57 */
366         a = (a << 6 | a >>> 26) + b;
367         d += (b ^ (a | ~c)) + x[15] + 0xfe2ce6e0; /* 58 */
368         d = (d << 10 | d >>> 22) + a;
369         c += (a ^ (d | ~b)) + x[6] + 0xa3014314; /* 59 */
370         c = (c << 15 | c >>> 17) + d;
371         b += (d ^ (c | ~a)) + x[13] + 0x4e0811a1; /* 60 */
372         b = (b << 21 | b >>> 11) + c;
373 
374         a += (c ^ (b | ~d)) + x[4] + 0xf7537e82; /* 61 */
375         a = (a << 6 | a >>> 26) + b;
376         d += (b ^ (a | ~c)) + x[11] + 0xbd3af235; /* 62 */
377         d = (d << 10 | d >>> 22) + a;
378         c += (a ^ (d | ~b)) + x[2] + 0x2ad7d2bb; /* 63 */
379         c = (c << 15 | c >>> 17) + d;
380         b += (d ^ (c | ~a)) + x[9] + 0xeb86d391; /* 64 */
381         b = (b << 21 | b >>> 11) + c;
382 
383         stat.state[0] += a;
384         stat.state[1] += b;
385         stat.state[2] += c;
386         stat.state[3] += d;
387     }
388 
389     /**
390      * Updates hash with the bytebuffer given (using at maximum length bytes
391      * from that buffer)
392      *
393      * @param stat
394      *            Which state is updated
395      * @param buffer
396      *            Array of bytes to be hashed
397      * @param offset
398      *            Offset to buffer array
399      * @param length
400      *            Use at maximum `length' bytes (absolute maximum is
401      *            buffer.length)
402      */
403     public void Update(MD5State stat, byte buffer[], int offset, int length) {
404         int index, partlen, i, start;
405         finals = null;
406         int newlength = length;
407         /* Length can be told to be shorter, but not inter */
408         if (newlength + offset > buffer.length) {
409             newlength = buffer.length - offset;
410         }
411 
412         /* compute number of bytes mod 64 */
413 
414         index = (int) (stat.count & 0x3f);
415         stat.count += newlength;
416 
417         partlen = 64 - index;
418 
419         if (newlength >= partlen) {
420             if (native_lib_loaded) {
421 
422                 // update state (using native method) to reflect input
423 
424                 if (partlen == 64) {
425                     partlen = 0;
426                 } else {
427                     for (i = 0; i < partlen; i ++) {
428                         stat.buffer[i + index] = buffer[i + offset];
429                     }
430                     TransformJni(stat.state, stat.buffer, 0, 64);
431                 }
432                 i = partlen + ((newlength - partlen) / 64) * 64;
433                 
434                 // break into chunks to guard against stack overflow in JNI
435                 
436                 int transformLength = newlength - partlen;
437                 int transformOffset = partlen + offset;
438                 final int MAX_LENGTH = 65536; // prevent stack overflow in JNI
439                 while (true) {
440                     if (transformLength > MAX_LENGTH) {
441                         TransformJni(stat.state, buffer, transformOffset, MAX_LENGTH);
442                         transformLength -= MAX_LENGTH;
443                         transformOffset += MAX_LENGTH;
444                     } else {
445                         TransformJni(stat.state, buffer, transformOffset, transformLength);
446                         break;
447                     }
448                 }
449                 //Was
450                 /*TransformJni(stat.state, buffer, partlen + offset, newlength -
451                         partlen);
452                 i = partlen + (newlength - partlen) / 64 * 64;*/
453             } else {
454 
455                 // update state (using only Java) to reflect input
456 
457                 int[] decode_buf = new int[16];
458                 if (partlen == 64) {
459                     partlen = 0;
460                 } else {
461                     for (i = 0; i < partlen; i ++) {
462                         stat.buffer[i + index] = buffer[i + offset];
463                     }
464                     Transform(stat, stat.buffer, 0, decode_buf);
465                 }
466                 for (i = partlen; i + 63 < newlength; i += 64) {
467                     Transform(stat, buffer, i + offset, decode_buf);
468                 }
469             }
470             index = 0;
471         } else {
472             i = 0;
473         }
474 
475         /* buffer remaining input */
476         if (i < newlength) {
477             start = i;
478             for (; i < newlength; i ++) {
479                 stat.buffer[index + i - start] = buffer[i + offset];
480             }
481         }
482     }
483 
484     /*
485      * Update()s for other datatypes than byte[] also. Update(byte[], int) is
486      * only the main driver.
487      */
488 
489     /**
490      * Plain update, updates this object
491      *
492      * @param buffer
493      * @param offset
494      * @param length
495      */
496 
497     public void Update(byte buffer[], int offset, int length) {
498         Update(state, buffer, offset, length);
499     }
500 
501     /**
502      * Plain update, updates this object
503      *
504      * @param buffer
505      * @param length
506      */
507     public void Update(byte buffer[], int length) {
508         Update(state, buffer, 0, length);
509     }
510 
511     /**
512      * Updates hash with given array of bytes
513      *
514      * @param buffer
515      *            Array of bytes to use for updating the hash
516      */
517     public void Update(byte buffer[]) {
518         Update(buffer, 0, buffer.length);
519     }
520 
521     /**
522      * Updates hash with a single byte
523      *
524      * @param b
525      *            Single byte to update the hash
526      */
527     public void Update(byte b) {
528         byte buffer[] = new byte[1];
529         buffer[0] = b;
530 
531         Update(buffer, 1);
532     }
533 
534     /**
535      * Update buffer with given string. Note that because the version of the
536      * s.getBytes() method without parameters is used to convert the string to a
537      * byte array, the results of this method may be different on different
538      * platforms. The s.getBytes() method converts the string into a byte array
539      * using the current platform's default character set and may therefore have
540      * different results on platforms with different default character sets. If
541      * a version that works consistently across platforms with different default
542      * character sets is desired, use the overloaded version of the Update()
543      * method which takes a string and a character encoding.
544      *
545      * @param s
546      *            String to be update to hash (is used as s.getBytes())
547      */
548     public void Update(String s) {
549         byte chars[] = s.getBytes();
550         Update(chars, chars.length);
551     }
552 
553     /**
554      * Update buffer with given string using the given encoding. If the given
555      * encoding is null, the encoding "ISO8859_1" is used.
556      *
557      * @param s
558      *            String to be update to hash (is used as
559      *            s.getBytes(charset_name))
560      * @param charset_name
561      *            The character set to use to convert s to a byte array, or null
562      *            if the "ISO8859_1" character set is desired.
563      * @exception java.io.UnsupportedEncodingException
564      *                If the named charset is not supported.
565      */
566     public void Update(String s, String charset_name)
567             throws java.io.UnsupportedEncodingException {
568         String newcharset = charset_name;
569         if (newcharset == null) {
570             newcharset = "ISO8859_1";
571         }
572         byte chars[] = s.getBytes(newcharset);
573         Update(chars, chars.length);
574     }
575 
576     /**
577      * Update buffer with a single integer (only & 0xff part is used, as a byte)
578      *
579      * @param i
580      *            Integer value, which is then converted to byte as i & 0xff
581      */
582 
583     public void Update(int i) {
584         Update((byte) (i & 0xff));
585     }
586     /**
587      * Updates hash with given {@link ChannelBuffer} (from Netty)
588      *
589      * @param buffer
590      *            ChannelBuffer to use for updating the hash
591      *            and this buffer will not be changed
592      */
593     public void Update(ChannelBuffer buffer) {
594         byte[] bytes;
595         int start = 0;
596         int len = buffer.readableBytes();
597         if (buffer.hasArray()) {
598             start = buffer.arrayOffset();
599             bytes = buffer.array();
600             if (bytes.length > start+len) {
601                 byte[] temp = new byte[len];
602                 System.arraycopy(bytes, start, temp, 0, len);
603                 start = 0;
604                 bytes = temp;
605             }
606         } else {
607             bytes = new byte[len];
608             buffer.getBytes(start, bytes);
609         }
610         Update(state, bytes, start, len);
611     }
612 
613     private byte[] Encode(int input[], int len) {
614         int i, j;
615         byte out[];
616 
617         out = new byte[len];
618 
619         for (i = j = 0; j < len; i ++, j += 4) {
620             out[j] = (byte) (input[i] & 0xff);
621             out[j + 1] = (byte) (input[i] >>> 8 & 0xff);
622             out[j + 2] = (byte) (input[i] >>> 16 & 0xff);
623             out[j + 3] = (byte) (input[i] >>> 24 & 0xff);
624         }
625 
626         return out;
627     }
628 
629     /**
630      * Returns array of bytes (16 bytes) representing hash as of the current
631      * state of this object. Note: getting a hash does not invalidate the hash
632      * object, it only creates a copy of the real state which is finalized.
633      *
634      * @return Array of 16 bytes, the hash of all updated bytes
635      */
636     public synchronized byte[] Final() {
637         byte bits[];
638         int index, padlen;
639         MD5State fin;
640 
641         if (finals == null) {
642             fin = new MD5State(state);
643 
644             int[] count_ints = {
645                     (int) (fin.count << 3), (int) (fin.count >> 29) };
646             bits = Encode(count_ints, 8);
647 
648             index = (int) (fin.count & 0x3f);
649             padlen = index < 56? 56 - index : 120 - index;
650 
651             Update(fin, padding, 0, padlen);
652             Update(fin, bits, 0, 8);
653 
654             /* Update() sets finals to null */
655             finals = fin;
656         }
657 
658         return Encode(finals.state, 16);
659     }
660 
661     private static final char[] HEX_CHARS = {
662             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c',
663             'd', 'e', 'f', };
664 
665     /**
666      * Turns array of bytes into string representing each byte as unsigned hex
667      * number.
668      *
669      * @param hash
670      *            Array of bytes to convert to hex-string
671      * @return Generated hex string
672      */
673     public static final String asHex(byte hash[]) {
674         char buf[] = new char[hash.length * 2];
675         for (int i = 0, x = 0; i < hash.length; i ++) {
676             buf[x ++] = HEX_CHARS[hash[i] >>> 4 & 0xf];
677             buf[x ++] = HEX_CHARS[hash[i] & 0xf];
678         }
679         return new String(buf);
680     }
681 
682     /**
683      * Turns String into array of bytes representing each couple of unsigned hex
684      * number as one byte.
685      *
686      * @param buf
687      *            hex string
688      * @return Array of bytes converted from hex-string
689      */
690     public static final byte[] asByte(String buf) {
691         byte from[] = buf.getBytes();
692         byte hash[] = new byte[from.length / 2];
693         for (int i = 0, x = 0; i < hash.length; i ++) {
694             byte code1 = from[x ++];
695             byte code2 = from[x ++];
696             if (code1 >= HEX_CHARS[10]) {
697                 code1 -= HEX_CHARS[10] - 10;
698             } else {
699                 code1 -= HEX_CHARS[0];
700             }
701             if (code2 >= HEX_CHARS[10]) {
702                 code2 -= HEX_CHARS[10] - 10;
703             } else {
704                 code2 -= HEX_CHARS[0];
705             }
706             hash[i] = (byte) ((code1 << 4) + code2);
707         }
708         return hash;
709     }
710 
711     /**
712      * Returns 32-character hex representation of this objects hash
713      *
714      * @return String of this object's hash
715      */
716     public String asHex() {
717         return asHex(Final());
718     }
719 
720     /**
721      * Disable lib loading (True) or enable it (false)
722      *
723      * @param disallow_lib_loading
724      * @return True if the native Library is loaded, else False
725      */
726     public static synchronized final boolean initNativeLibrary(
727             boolean disallow_lib_loading) {
728         if (disallow_lib_loading) {
729             native_lib_init_pending = false;
730             return false;
731         }
732         return _initNativeLibrary();
733     }
734 
735     /**
736      * Initialize the Native Library from path
737      *
738      * @param libpath
739      * @return True if the native Library is loaded, else False
740      */
741     public static synchronized final boolean initNativeLibrary(String libpath) {
742         native_lib_path = libpath;
743         native_lib_init_pending = true;// reload!
744         return _initNativeLibrary();
745     }
746 
747     /**
748      * Internal Function to initialize if needed the Native Library support
749      *
750      * @return True if the native Library is loaded, else False
751      */
752     private static synchronized final boolean _initNativeLibrary() {
753         if (!native_lib_init_pending) {
754             return native_lib_loaded;
755         }
756         native_lib_loaded = _loadNativeLibrary();
757         // System.out.println("MD5 Binary loaded:"+native_lib_loaded);
758         native_lib_init_pending = false;
759         return native_lib_loaded;
760     }
761 
762     /**
763      * Try to load the Native Library
764      *
765      * @return True if the native Library is loaded, else False
766      */
767     private static synchronized final boolean _loadNativeLibrary() {
768         try {
769 
770             // don't try to load if the right property is set
771             String prop = System
772                     .getProperty("com.twmacinta.util.MD5.NO_NATIVE_LIB");
773             if (prop != null) {
774                 prop = prop.trim();
775                 if (prop.equalsIgnoreCase("true") || prop.equals("1")) {
776                     return false;
777                 }
778             }
779 
780             // the library to load can be specified through native_lib_path
781             File fstring;
782             prop = native_lib_path;
783             if (prop != null) {
784                 fstring = new File(prop);
785                 if (fstring.canRead()) {
786                     System.load(fstring.getAbsolutePath());
787                     return true;
788                 }
789             }
790             // the library to load can be specified as a property
791 
792             File f;
793             prop = System.getProperty("com.twmacinta.util.MD5.NATIVE_LIB_FILE");
794             if (prop != null) {
795                 f = new File(prop);
796                 if (f.canRead()) {
797                     System.load(f.getAbsolutePath());
798                     return true;
799                 }
800             }
801             // determine the operating system and architecture
802 
803             String os_name = System.getProperty("os.name");
804             String os_arch = System.getProperty("os.arch");
805             if (os_name == null || os_arch == null) {
806                 return false;
807             }
808             os_name = os_name.toLowerCase();
809             os_arch = os_arch.toLowerCase();
810             // define settings which are OS arch architecture independent
811 
812             File arch_lib_path = null;
813             String arch_libfile_suffix = null;
814 
815             // fill in settings for Linux on x86
816 
817             if (os_name.equals("linux") &&
818                     (os_arch.equals("x86") || os_arch.equals("i386") ||
819                             os_arch.equals("i486") || os_arch.equals("i586") || os_arch
820                             .equals("i686"))) {
821                 arch_lib_path = new File(new File(new File("lib"), "arch"),
822                         "linux_x86");
823                 arch_libfile_suffix = ".so";
824             }
825             
826             // fill in settings for Linux on amd64
827             
828             else if (os_name.equals("linux") &&
829                      os_arch.equals("amd64")) {
830                 arch_lib_path = new File(new File(new File("lib"), "arch"), "linux_amd64");
831                 arch_libfile_suffix = ".so"; 
832                 // fill in settings for Windows on x86
833 
834             } else if (os_name.startsWith("windows ") &&
835                     (os_arch.equals("x86") || os_arch.equals("i386") ||
836                             os_arch.equals("i486") || os_arch.equals("i586") || os_arch
837                             .equals("i686"))) {
838                 arch_lib_path = new File(new File(new File("lib"), "arch"),
839                         "win32_x86");
840                 arch_libfile_suffix = ".dll";
841             }
842             
843             // fill in settings for Windows on amd64
844             
845             else if (os_name.startsWith("windows ") &&
846                      os_arch.equals("amd64")) {
847                 arch_lib_path = new File(new File(new File("lib"), "arch"), "win_amd64");
848                 arch_libfile_suffix = ".dll"; 
849             
850                 // fill in settings Mac OS X on PPC
851 
852             } else if (os_name.startsWith("mac os x") && os_arch.equals("ppc")) {
853                 arch_lib_path = new File(new File(new File("lib"), "arch"),
854                         "darwin_ppc");
855                 arch_libfile_suffix = ".jnilib";
856 
857                 // default to .so files with no architecture specific
858                 // subdirectory
859             }
860             
861             // fill in settings for Mac OS X on x86
862             
863             else if (os_name.startsWith("mac os x") &&
864                      (os_arch.equals("x86") ||
865                       os_arch.equals("i386") ||
866                       os_arch.equals("i486") ||
867                       os_arch.equals("i586") ||
868                       os_arch.equals("i686"))) {
869                 arch_lib_path = new File(new File(new File("lib"), "arch"), "darwin_x86");
870                 arch_libfile_suffix = ".jnilib";
871             }
872             
873             // fill in settings for Mac OS X on x86_64
874             
875             else if (os_name.startsWith("mac os x") &&
876                      os_arch.equals("x86_64")) {
877                 arch_lib_path = new File(new File(new File("lib"), "arch"), "darwin_x86_64");
878                 arch_libfile_suffix = ".jnilib";
879             }
880             
881             // fill in settings for FreeBSD on x86
882             
883             else if (os_name.equals("freebsd") &&
884                 (os_arch.equals("x86") ||
885                  os_arch.equals("i386") ||
886                  os_arch.equals("i486") ||
887                  os_arch.equals("i586") ||
888                  os_arch.equals("i686"))) {
889                 arch_lib_path = new File(new File(new File("lib"), "arch"), "freebsd_x86");
890                 arch_libfile_suffix = ".so"; 
891             }
892             
893             // fill in settings for FreeBSD on amd64
894             
895             else if (os_name.equals("freebsd") &&
896                      os_arch.equals("amd64")) {
897                 arch_lib_path = new File(new File(new File("lib"), "arch"), "freebsd_amd64");
898                 arch_libfile_suffix = ".so"; 
899             
900             } else {
901                 arch_libfile_suffix = ".so";
902             }
903             // build the required filename
904 
905             String fname = "MD5" + arch_libfile_suffix;
906 
907             // try the architecture specific directory
908 
909             if (arch_lib_path != null) {
910                 f = new File(arch_lib_path, fname);
911                 if (f.canRead()) {
912                     System.load(f.getAbsolutePath());
913                     return true;
914                 }
915             }
916             // try the "lib" subdirectory
917 
918             f = new File(new File("lib"), fname);
919             if (f.canRead()) {
920                 System.load(f.getAbsolutePath());
921                 return true;
922             }
923             // try the working directory
924 
925             f = new File(fname);
926             if (f.canRead()) {
927                 System.load(f.getAbsolutePath());
928                 return true;
929             }
930 
931             // discard SecurityExceptions
932 
933         } catch (SecurityException e) {
934             System.err.println("Can't do native library: " + e.getMessage());
935         } catch (Exception e) {
936             System.err.println("Can't do native library: " + e.getMessage());
937         }
938 
939         // unable to load
940 
941         return false;
942     }
943 
944     /**
945      * Calculates and returns the hash of the contents of the given file.
946      *
947      * @param f
948      *            FileInterface to hash
949      * @return the hash from the given file
950      * @throws IOException
951      **/
952     public static byte[] getHash(File f) throws IOException {
953         InputStream close_me = null;
954         try {
955             long buf_size = f.length();
956             if (buf_size < 512) {
957                 buf_size = 512;
958             }
959             if (buf_size > 65536) {
960                 buf_size = 65536;
961             }
962             byte[] buf = new byte[(int) buf_size];
963             FileInputStream in = new FileInputStream(f);
964             close_me = in;
965             MD5 md5 = new MD5();
966             int read = 0;
967             while ((read = in.read(buf)) >= 0) {
968                 md5.Update(md5.state, buf, 0, read);
969             }
970             in.close();
971             in = null;
972             close_me = null;
973             buf = null;
974             buf = md5.Final();
975             return buf;
976         } catch (IOException e) {
977             if (close_me != null) {
978                 try {
979                     close_me.close();
980                 } catch (Exception e2) {
981                 }
982             }
983             throw e;
984         }
985     }
986 
987     /**
988      * Calculates and returns the hash of the contents of the given file using
989      * Nio file access.
990      *
991      * @param f
992      *            for the FileInterface
993      * @return the hash from the FileInterface with NIO access
994      * @throws IOException
995      **/
996     public static byte[] getHashNio(File f) throws IOException {
997         if (!f.exists()) {
998             throw new FileNotFoundException(f.toString());
999         }
1000         InputStream close_me = null;
1001         try {
1002             long buf_size = f.length();
1003             if (buf_size < 512) {
1004                 buf_size = 512;
1005             }
1006             if (buf_size > 65536) {
1007                 buf_size = 65536;
1008             }
1009             byte[] buf = new byte[(int) buf_size];
1010             FileInputStream in = new FileInputStream(f);
1011             close_me = in;
1012             FileChannel fileChannel = in.getChannel();
1013             ByteBuffer bb = ByteBuffer.wrap(buf);
1014             int read = 0;
1015             MD5 md5 = new MD5();
1016             read = fileChannel.read(bb);
1017             while (read > 0) {
1018                 md5.Update(md5.state, buf, 0, read);
1019                 bb.clear();
1020                 read = fileChannel.read(bb);
1021             }
1022             fileChannel.close();
1023             fileChannel = null;
1024             in = null;
1025             close_me = null;
1026             bb = null;
1027             buf = null;
1028             buf = md5.Final();
1029             md5 = null;
1030             return buf;
1031         } catch (IOException e) {
1032             if (close_me != null) {
1033                 try {
1034                     close_me.close();
1035                 } catch (Exception e2) {
1036                 }
1037             }
1038             throw e;
1039         }
1040     }
1041 
1042 
1043     /**
1044      * Calculates and returns the hash of the contents of the given stream.
1045      *
1046      * @param stream
1047      *            Stream to hash
1048      * @return the hash from the given stream
1049      * @throws IOException
1050      **/
1051     public static byte[] getHash(InputStream stream) throws IOException {
1052         try {
1053             long buf_size = 65536;
1054             byte[] buf = new byte[(int) buf_size];
1055             MD5 md5 = new MD5();
1056             int read = 0;
1057             while ((read = stream.read(buf)) >= 0) {
1058                 md5.Update(md5.state, buf, 0, read);
1059             }
1060             stream.close();
1061             buf = null;
1062             buf = md5.Final();
1063             return buf;
1064         } catch (IOException e) {
1065             if (stream != null) {
1066                 try {
1067                     stream.close();
1068                 } catch (Exception e2) {
1069                 }
1070             }
1071             throw e;
1072         }
1073     }
1074     
1075     /**
1076      * Test if both hashes are equal
1077      *
1078      * @param hash1
1079      * @param hash2
1080      * @return true iff the first 16 bytes of both hash1 and hash2 are equal;
1081      *         both hash1 and hash2 are null; or either hash array is less than
1082      *         16 bytes in length and their lengths and all of their bytes are
1083      *         equal.
1084      **/
1085     public static boolean hashesEqual(byte[] hash1, byte[] hash2) {
1086         return Arrays.equals(hash1, hash2);
1087     }
1088 
1089     private static byte[] salt = {'G','o','l','d','e','n','G','a','t','e'};
1090     /**
1091      * Crypt a password
1092      * @param pwd to crypt
1093      * @return the crypted password
1094      */
1095     public static final String passwdCrypt(String pwd) {
1096         MD5 md5 = new MD5();
1097         byte [] bpwd = pwd.getBytes();
1098         for (int i = 0; i < 16; i++) {
1099             md5.Update(md5.state, bpwd, 0, bpwd.length);
1100             md5.Update(md5.state, salt, 0, salt.length);
1101         }
1102         return md5.asHex();
1103     }
1104     /**
1105      * Crypt a password
1106      * @param bpwd to crypt
1107      * @return the crypted password
1108      */
1109     public static final byte[] passwdCrypt(byte[] bpwd) {
1110         MD5 md5 = new MD5();
1111         for (int i = 0; i < 16; i++) {
1112             md5.Update(md5.state, bpwd, 0, bpwd.length);
1113             md5.Update(md5.state, salt, 0, salt.length);
1114         }
1115         return md5.Final();
1116     }
1117     /**
1118      * 
1119      * @param pwd
1120      * @param cryptPwd
1121      * @return True if the pwd is comparable with the cryptPwd
1122      */
1123     public static final boolean equalPasswd(String pwd, String cryptPwd){
1124         String asHex = passwdCrypt(pwd);
1125         return cryptPwd.equals(asHex);
1126     }
1127     /**
1128      * 
1129      * @param pwd
1130      * @param cryptPwd
1131      * @return True if the pwd is comparable with the cryptPwd
1132      */
1133     public static final boolean equalPasswd(byte[] pwd, byte[] cryptPwd){
1134         byte [] bytes = passwdCrypt(pwd);
1135         return Arrays.equals(cryptPwd, bytes);
1136     }
1137     /**
1138      * Test function
1139      *
1140      * @param argv
1141      *            with 2 arguments as filename to hash and full path to the
1142      *            Native Library
1143      */
1144     public static void main(String argv[]) {
1145         long start = System.currentTimeMillis();
1146         if (argv.length < 1) {
1147             // Only passwdCrypt test
1148             boolean nativeLib = false;
1149             //nativeLib = initNativeLibrary("D:\\NEWJARS\\gglib\\win32\\MD5.dll");
1150             nativeLib = initNativeLibrary(true);
1151             if (!nativeLib) {
1152                 System.err.println("Native library cannot be load from " +
1153                         "D:\\NEWJARS\\gglib\\win32\\MD5.dll");
1154             } else {
1155                 System.out.println("Native library loaded from " + "D:\\NEWJARS\\gglib\\win32\\MD5.dll");
1156             }
1157             for (int i = 0; i < 1000000; i++) {
1158                 passwdCrypt("Ceci est mon password!");
1159             }
1160             System.err.println("Final passwd crypted in "+(System.currentTimeMillis() - start)+"ms is: "+passwdCrypt("Ceci est mon password!"));
1161             System.err
1162                     .println("Not enough argument: <full path to the filename to hash> [<full path to the native library>]");
1163             return;
1164         }
1165         boolean nativeLib = false;
1166         if (argv.length == 2) {
1167             nativeLib = initNativeLibrary(argv[1]);
1168             if (!nativeLib) {
1169                 System.err.println("Native library cannot be load from " +
1170                         argv[1]);
1171             } else {
1172                 System.out.println("Native library loaded from " + argv[1]);
1173             }
1174         }
1175         File file = new File(argv[0]);
1176         byte[] bmd5;
1177         try {
1178             // By recompiling using the first: NIO support, the second: standard
1179             // support
1180             bmd5 = getHashNio(file);
1181             // bmd5 = getHash(file);
1182         } catch (IOException e1) {
1183             bmd5 = null;
1184         }
1185         if (bmd5 != null) {
1186             if (nativeLib) {
1187                 System.out.println("FileInterface MD5 is " + asHex(bmd5) +
1188                         " using Native Library in " +
1189                         (System.currentTimeMillis() - start) + " ms");
1190             } else {
1191                 System.out.println("FileInterface MD5 is " + asHex(bmd5) +
1192                         " using Java version in " +
1193                         (System.currentTimeMillis() - start) + " ms");
1194             }
1195         } else {
1196             System.err.println("Cannot compute md5 for " + argv[1]);
1197         }
1198     }
1199 }