// This file is currently unlocked (change this line if you lock the file) // // $Log: MD4.java,v $ // Revision 1.2 1998/01/05 03:41:19 iang // Added references only. // // Revision 1.1.1.1 1997/11/03 22:36:56 hopwood // + Imported to CVS (tagged as 'start'). // // Revision 0.1.0.0 1997/07/14 R. Naffah // + original version // // $Endlog$ /* * Copyright (c) 1997 Systemics Ltd * on behalf of the Cryptix Development Team. All rights reserved. */ package jcifs.util; import java.security.MessageDigest; /** * Implements the MD4 message digest algorithm in Java. * <p> * <b>References:</b> * <ol> * <li> Ronald L. Rivest, * "<a href="http://www.roxen.com/rfc/rfc1320.html"> * The MD4 Message-Digest Algorithm</a>", * IETF RFC-1320 (informational). * </ol> * * <p><b>$Revision: 1.2 $</b> * @author Raif S. Naffah */ public class MD4 extends MessageDigest implements Cloneable { // MD4 specific object variables //........................................................................... /** * The size in bytes of the input block to the tranformation algorithm. */ private static final int BLOCK_LENGTH = 64; // = 512 / 8; /** * 4 32-bit words (interim result) */ private int[] context = new int[4]; /** * Number of bytes processed so far mod. 2 power of 64. */ private long count; /** * 512 bits input buffer = 16 x 32-bit words holds until reaches 512 bits. */ private byte[] buffer = new byte[BLOCK_LENGTH]; /** * 512 bits work buffer = 16 x 32-bit words */ private int[] X = new int[16]; // Constructors //........................................................................... public MD4 () { super("MD4"); engineReset(); } /** * This constructor is here to implement cloneability of this class. */ private MD4 (MD4 md) { this(); context = (int[])md.context.clone(); buffer = (byte[])md.buffer.clone(); count = md.count; } // Cloneable method implementation //........................................................................... /** * Returns a copy of this MD object. */ public Object clone() { return new MD4(this); } // JCE methods //........................................................................... /** * Resets this object disregarding any temporary data present at the * time of the invocation of this call. */ public void engineReset () { // initial values of MD4 i.e. A, B, C, D // as per rfc-1320; they are low-order byte first context[0] = 0x67452301; context[1] = 0xEFCDAB89; context[2] = 0x98BADCFE; context[3] = 0x10325476; count = 0L; for (int i = 0; i < BLOCK_LENGTH; i++) buffer[i] = 0; } /** * Continues an MD4 message digest using the input byte. */ public void engineUpdate (byte b) { // compute number of bytes still unhashed; ie. present in buffer int i = (int)(count % BLOCK_LENGTH); count++; // update number of bytes buffer[i] = b; if (i == BLOCK_LENGTH - 1) transform(buffer, 0); } /** * MD4 block update operation. * <p> * Continues an MD4 message digest operation, by filling the buffer, * transform(ing) data in 512-bit message block(s), updating the variables * context and count, and leaving (buffering) the remaining bytes in buffer * for the next update or finish. * * @param input input block * @param offset start of meaningful bytes in input * @param len count of bytes in input block to consider */ public void engineUpdate (byte[] input, int offset, int len) { // make sure we don't exceed input's allocated size/length if (offset < 0 || len < 0 || (long)offset + len > input.length) throw new ArrayIndexOutOfBoundsException(); // compute number of bytes still unhashed; ie. present in buffer int bufferNdx = (int)(count % BLOCK_LENGTH); count += len; // update number of bytes int partLen = BLOCK_LENGTH - bufferNdx; int i = 0; if (len >= partLen) { System.arraycopy(input, offset, buffer, bufferNdx, partLen); transform(buffer, 0); for (i = partLen; i + BLOCK_LENGTH - 1 < len; i+= BLOCK_LENGTH) transform(input, offset + i); bufferNdx = 0; } // buffer remaining input if (i < len) System.arraycopy(input, offset + i, buffer, bufferNdx, len - i); } /** * Completes the hash computation by performing final operations such * as padding. At the return of this engineDigest, the MD engine is * reset. * * @return the array of bytes for the resulting hash value. */ public byte[] engineDigest () { // pad output to 56 mod 64; as RFC1320 puts it: congruent to 448 mod 512 int bufferNdx = (int)(count % BLOCK_LENGTH); int padLen = (bufferNdx < 56) ? (56 - bufferNdx) : (120 - bufferNdx); // padding is alwas binary 1 followed by binary 0s byte[] tail = new byte[padLen + 8]; tail[0] = (byte)0x80; // append length before final transform: // save number of bits, casting the long to an array of 8 bytes // save low-order byte first. for (int i = 0; i < 8; i++) tail[padLen + i] = (byte)((count * 8) >>> (8 * i)); engineUpdate(tail, 0, tail.length); byte[] result = new byte[16]; // cast this MD4's context (array of 4 ints) into an array of 16 bytes. for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) result[i * 4 + j] = (byte)(context[i] >>> (8 * j)); // reset the engine engineReset(); return result; } // own methods //........................................................................... /** * MD4 basic transformation. * <p> * Transforms context based on 512 bits from input block starting * from the offset'th byte. * * @param block input sub-array. * @param offset starting position of sub-array. */ private void transform (byte[] block, int offset) { // encodes 64 bytes from input block into an array of 16 32-bit // entities. Use A as a temp var. for (int i = 0; i < 16; i++) X[i] = (block[offset++] & 0xFF) | (block[offset++] & 0xFF) << 8 | (block[offset++] & 0xFF) << 16 | (block[offset++] & 0xFF) << 24; int A = context[0]; int B = context[1]; int C = context[2]; int D = context[3]; A = FF(A, B, C, D, X[ 0], 3); D = FF(D, A, B, C, X[ 1], 7); C = FF(C, D, A, B, X[ 2], 11); B = FF(B, C, D, A, X[ 3], 19); A = FF(A, B, C, D, X[ 4], 3); D = FF(D, A, B, C, X[ 5], 7); C = FF(C, D, A, B, X[ 6], 11); B = FF(B, C, D, A, X[ 7], 19); A = FF(A, B, C, D, X[ 8], 3); D = FF(D, A, B, C, X[ 9], 7); C = FF(C, D, A, B, X[10], 11); B = FF(B, C, D, A, X[11], 19); A = FF(A, B, C, D, X[12], 3); D = FF(D, A, B, C, X[13], 7); C = FF(C, D, A, B, X[14], 11); B = FF(B, C, D, A, X[15], 19); A = GG(A, B, C, D, X[ 0], 3); D = GG(D, A, B, C, X[ 4], 5); C = GG(C, D, A, B, X[ 8], 9); B = GG(B, C, D, A, X[12], 13); A = GG(A, B, C, D, X[ 1], 3); D = GG(D, A, B, C, X[ 5], 5); C = GG(C, D, A, B, X[ 9], 9); B = GG(B, C, D, A, X[13], 13); A = GG(A, B, C, D, X[ 2], 3); D = GG(D, A, B, C, X[ 6], 5); C = GG(C, D, A, B, X[10], 9); B = GG(B, C, D, A, X[14], 13); A = GG(A, B, C, D, X[ 3], 3); D = GG(D, A, B, C, X[ 7], 5); C = GG(C, D, A, B, X[11], 9); B = GG(B, C, D, A, X[15], 13); A = HH(A, B, C, D, X[ 0], 3); D = HH(D, A, B, C, X[ 8], 9); C = HH(C, D, A, B, X[ 4], 11); B = HH(B, C, D, A, X[12], 15); A = HH(A, B, C, D, X[ 2], 3); D = HH(D, A, B, C, X[10], 9); C = HH(C, D, A, B, X[ 6], 11); B = HH(B, C, D, A, X[14], 15); A = HH(A, B, C, D, X[ 1], 3); D = HH(D, A, B, C, X[ 9], 9); C = HH(C, D, A, B, X[ 5], 11); B = HH(B, C, D, A, X[13], 15); A = HH(A, B, C, D, X[ 3], 3); D = HH(D, A, B, C, X[11], 9); C = HH(C, D, A, B, X[ 7], 11); B = HH(B, C, D, A, X[15], 15); context[0] += A; context[1] += B; context[2] += C; context[3] += D; } // The basic MD4 atomic functions. private int FF (int a, int b, int c, int d, int x, int s) { int t = a + ((b & c) | (~b & d)) + x; return t << s | t >>> (32 - s); } private int GG (int a, int b, int c, int d, int x, int s) { int t = a + ((b & (c | d)) | (c & d)) + x + 0x5A827999; return t << s | t >>> (32 - s); } private int HH (int a, int b, int c, int d, int x, int s) { int t = a + (b ^ c ^ d) + x + 0x6ED9EBA1; return t << s | t >>> (32 - s); } }