/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab tw=72: $Id: MD4.java,v 1.9 2003/03/30 15:18:46 rsdio Exp $ This version is derived from the version in GNU Crypto. MD4: The MD4 message digest algorithm. Copyright (C) 2002 The Free Software Foundation, Inc. Copyright (C) 2003 Casey Marshall <rsdio@metastatic.org> This file is a part of Jarsync. Jarsync is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Jarsync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jarsync; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Linking Jarsync statically or dynamically with other modules is making a combined work based on Jarsync. Thus, the terms and conditions of the GNU General Public License cover the whole combination. */ package io.milton.zsync; import java.security.DigestException; import java.security.MessageDigestSpi; /** * <p>An implementation of Ron Rivest's MD4 message digest algorithm. * MD4 was the precursor to the stronger MD5 * algorithm, and while not considered cryptograpically secure itself, * MD4 is in use in various applications. It is slightly faster than * MD5.</p> * * <p>This implementation is derived from the version of MD4 in <a * href="http://www.gnu.org/software/gnu-crypto/">GNU Crypto.</p> * * <p>References:</p> * * <ol> * <li>The <a href="http://www.ietf.org/rfc/rfc1320.txt">MD4</a> Message- * Digest Algorithm.<br> * R. Rivest.</li> * </ol> * * @version $Revision: 1.9 $ */ public class MD4 extends MessageDigestSpi implements Cloneable { // Constants and variables. // ----------------------------------------------------------------- /** * An MD4 message digest is always 128-bits long, or 16 bytes. */ public static final int DIGEST_LENGTH = 16; /** * The MD4 algorithm operates on 512-bit blocks, or 64 bytes. */ public static final int BLOCK_LENGTH = 64; protected static final int A = 0x67452301; protected static final int B = 0xefcdab89; protected static final int C = 0x98badcfe; protected static final int D = 0x10325476; /* The four chaining variables. */ protected int a, b, c, d; protected long count; protected final byte[] buffer; /** Word buffer for transforming. */ private final int[] X = new int[16]; // Constructors. // ----------------------------------------------------------------- /** * Trivial zero-argument constructor. */ public MD4() { buffer = new byte[BLOCK_LENGTH]; engineReset(); } /** * Private constructor for cloning. */ private MD4(MD4 that) { this(); this.a = that.a; this.b = that.b; this.c = that.c; this.d = that.d; this.count = that.count; System.arraycopy(that.buffer, 0, this.buffer, 0, BLOCK_LENGTH); } // java.lang.Cloneable interface implementation -------------------- public Object clone() { return new MD4(this); } // SPI instance methods. // ----------------------------------------------------------------- protected int engineGetDigestLength() { return DIGEST_LENGTH; } public void engineUpdate(byte b) { // compute number of bytes still unhashed; ie. present in buffer int i = (int)(count % BLOCK_LENGTH); count++; buffer[i] = b; if (i == (BLOCK_LENGTH - 1)) { transform(buffer, 0); } } protected void engineUpdate(byte[] b, int offset, int len) { int n = (int)(count % BLOCK_LENGTH); count += len; int partLen = BLOCK_LENGTH - n; int i = 0; if (len >= partLen) { System.arraycopy(b, offset, buffer, n, partLen); transform(buffer, 0); for (i = partLen; i + BLOCK_LENGTH - 1 < len; i+= BLOCK_LENGTH) { transform(b, offset + i); } n = 0; } if (i < len) { System.arraycopy(b, offset + i, buffer, n, len - i); } } /** * Pack the four chaining variables into a byte array. */ protected byte[] engineDigest() { byte[] tail = padBuffer(); engineUpdate(tail, 0, tail.length); byte[] digest = { (byte) a, (byte) (a >>> 8), (byte) (a >>> 16), (byte) (a >>> 24), (byte) b, (byte) (b >>> 8), (byte) (b >>> 16), (byte) (b >>> 24), (byte) c, (byte) (c >>> 8), (byte) (c >>> 16), (byte) (c >>> 24), (byte) d, (byte) (d >>> 8), (byte) (d >>> 16), (byte) (d >>> 24) }; engineReset(); return digest; } protected int engineDigest(byte[] out, int off, int len) throws DigestException { if (off < 0 || off + len >= out.length) { throw new DigestException(); } System.arraycopy(engineDigest(), 0, out, off, Math.min(len, DIGEST_LENGTH)); return Math.min(len, DIGEST_LENGTH); } /** Reset the four chaining variables. */ protected void engineReset() { a = A; b = B; c = C; d = D; count = 0; } /** * Pad the buffer by appending the byte 0x80, then as many zero bytes * to fill the buffer 8 bytes shy of being a multiple of 64 bytes, then * append the length of the buffer, in bits, before padding. */ protected byte[] padBuffer() { int n = (int) (count % BLOCK_LENGTH); int padding = (n < 56) ? (56 - n) : (120 - n); byte[] pad = new byte[padding + 8]; pad[0] = (byte) 0x80; long bits = count << 3; pad[padding++] = (byte) bits; pad[padding++] = (byte) (bits >>> 8); pad[padding++] = (byte) (bits >>> 16); pad[padding++] = (byte) (bits >>> 24); pad[padding++] = (byte) (bits >>> 32); pad[padding++] = (byte) (bits >>> 40); pad[padding++] = (byte) (bits >>> 48); pad[padding ] = (byte) (bits >>> 56); return pad; } /** Transform a 64-byte block. */ protected void transform(byte[] in, int offset) { int aa, bb, cc, dd; for (int i = 0, n = 0; i < 16; i++) { X[i] = (in[offset++] & 0xff) | (in[offset++] & 0xff) << 8 | (in[offset++] & 0xff) << 16 | (in[offset++] & 0xff) << 24; } aa = a; bb = b; cc = c; dd = d; // Round 1 a += ((b & c) | ((~b) & d)) + X[ 0]; a = a << 3 | a >>> (32 - 3); d += ((a & b) | ((~a) & c)) + X[ 1]; d = d << 7 | d >>> (32 - 7); c += ((d & a) | ((~d) & b)) + X[ 2]; c = c << 11 | c >>> (32 - 11); b += ((c & d) | ((~c) & a)) + X[ 3]; b = b << 19 | b >>> (32 - 19); a += ((b & c) | ((~b) & d)) + X[ 4]; a = a << 3 | a >>> (32 - 3); d += ((a & b) | ((~a) & c)) + X[ 5]; d = d << 7 | d >>> (32 - 7); c += ((d & a) | ((~d) & b)) + X[ 6]; c = c << 11 | c >>> (32 - 11); b += ((c & d) | ((~c) & a)) + X[ 7]; b = b << 19 | b >>> (32 - 19); a += ((b & c) | ((~b) & d)) + X[ 8]; a = a << 3 | a >>> (32 - 3); d += ((a & b) | ((~a) & c)) + X[ 9]; d = d << 7 | d >>> (32 - 7); c += ((d & a) | ((~d) & b)) + X[10]; c = c << 11 | c >>> (32 - 11); b += ((c & d) | ((~c) & a)) + X[11]; b = b << 19 | b >>> (32 - 19); a += ((b & c) | ((~b) & d)) + X[12]; a = a << 3 | a >>> (32 - 3); d += ((a & b) | ((~a) & c)) + X[13]; d = d << 7 | d >>> (32 - 7); c += ((d & a) | ((~d) & b)) + X[14]; c = c << 11 | c >>> (32 - 11); b += ((c & d) | ((~c) & a)) + X[15]; b = b << 19 | b >>> (32 - 19); // Round 2. a += ((b & (c | d)) | (c & d)) + X[ 0] + 0x5a827999; a = a << 3 | a >>> (32 - 3); d += ((a & (b | c)) | (b & c)) + X[ 4] + 0x5a827999; d = d << 5 | d >>> (32 - 5); c += ((d & (a | b)) | (a & b)) + X[ 8] + 0x5a827999; c = c << 9 | c >>> (32 - 9); b += ((c & (d | a)) | (d & a)) + X[12] + 0x5a827999; b = b << 13 | b >>> (32 - 13); a += ((b & (c | d)) | (c & d)) + X[ 1] + 0x5a827999; a = a << 3 | a >>> (32 - 3); d += ((a & (b | c)) | (b & c)) + X[ 5] + 0x5a827999; d = d << 5 | d >>> (32 - 5); c += ((d & (a | b)) | (a & b)) + X[ 9] + 0x5a827999; c = c << 9 | c >>> (32 - 9); b += ((c & (d | a)) | (d & a)) + X[13] + 0x5a827999; b = b << 13 | b >>> (32 - 13); a += ((b & (c | d)) | (c & d)) + X[ 2] + 0x5a827999; a = a << 3 | a >>> (32 - 3); d += ((a & (b | c)) | (b & c)) + X[ 6] + 0x5a827999; d = d << 5 | d >>> (32 - 5); c += ((d & (a | b)) | (a & b)) + X[10] + 0x5a827999; c = c << 9 | c >>> (32 - 9); b += ((c & (d | a)) | (d & a)) + X[14] + 0x5a827999; b = b << 13 | b >>> (32 - 13); a += ((b & (c | d)) | (c & d)) + X[ 3] + 0x5a827999; a = a << 3 | a >>> (32 - 3); d += ((a & (b | c)) | (b & c)) + X[ 7] + 0x5a827999; d = d << 5 | d >>> (32 - 5); c += ((d & (a | b)) | (a & b)) + X[11] + 0x5a827999; c = c << 9 | c >>> (32 - 9); b += ((c & (d | a)) | (d & a)) + X[15] + 0x5a827999; b = b << 13 | b >>> (32 - 13); // Round 3. a += (b ^ c ^ d) + X[ 0] + 0x6ed9eba1; a = a << 3 | a >>> (32 - 3); d += (a ^ b ^ c) + X[ 8] + 0x6ed9eba1; d = d << 9 | d >>> (32 - 9); c += (d ^ a ^ b) + X[ 4] + 0x6ed9eba1; c = c << 11 | c >>> (32 - 11); b += (c ^ d ^ a) + X[12] + 0x6ed9eba1; b = b << 15 | b >>> (32 - 15); a += (b ^ c ^ d) + X[ 2] + 0x6ed9eba1; a = a << 3 | a >>> (32 - 3); d += (a ^ b ^ c) + X[10] + 0x6ed9eba1; d = d << 9 | d >>> (32 - 9); c += (d ^ a ^ b) + X[ 6] + 0x6ed9eba1; c = c << 11 | c >>> (32 - 11); b += (c ^ d ^ a) + X[14] + 0x6ed9eba1; b = b << 15 | b >>> (32 - 15); a += (b ^ c ^ d) + X[ 1] + 0x6ed9eba1; a = a << 3 | a >>> (32 - 3); d += (a ^ b ^ c) + X[ 9] + 0x6ed9eba1; d = d << 9 | d >>> (32 - 9); c += (d ^ a ^ b) + X[ 5] + 0x6ed9eba1; c = c << 11 | c >>> (32 - 11); b += (c ^ d ^ a) + X[13] + 0x6ed9eba1; b = b << 15 | b >>> (32 - 15); a += (b ^ c ^ d) + X[ 3] + 0x6ed9eba1; a = a << 3 | a >>> (32 - 3); d += (a ^ b ^ c) + X[11] + 0x6ed9eba1; d = d << 9 | d >>> (32 - 9); c += (d ^ a ^ b) + X[ 7] + 0x6ed9eba1; c = c << 11 | c >>> (32 - 11); b += (c ^ d ^ a) + X[15] + 0x6ed9eba1; b = b << 15 | b >>> (32 - 15); a += aa; b += bb; c += cc; d += dd; } }