// $Id: DigestEngine.java 81 2007-02-25 12:45:17Z tp $
package fr.cryptohash;
/**
* <p>This class is a template which can be used to implement hash
* functions. It takes care of some of the API, and also provides an
* internal data buffer whose length is equal to the hash function
* internal block length.</p>
*
* <p>Classes which use this template MUST provide a working
* <code>getBlockLength()</code> method even before initialization.
* The <code>getDigestLength()</code> should also be operational
* from the beginning, but it is acceptable that it returns 0 while
* the <code>doInit</code> method has not been called yet.</p>
*
* <pre>
* ==========================(LICENSE BEGIN)============================
*
* Copyright (c) 2007 Projet RNRT SAPHIR
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* ===========================(LICENSE END)=============================
* </pre>
*
* @version $Revision: 81 $
* @author Thomas Pornin <thomas.pornin@cryptolog.com>
*/
public abstract class DigestEngine implements Digest {
/**
* Reset the hash algorithm state.
*/
protected abstract void engineReset();
/**
* Process one block of data.
*
* @param data the data block
*/
protected abstract void processBlock(byte[] data);
/**
* Perform the final padding and store the result in the
* provided buffer. This method shall call <code>flush()</code>
* and then <code>update()</code> with the appropriate padding
* data in order to get the full input data.
*
* @param buf the output buffer
* @param off the output offset
*/
protected abstract void doPadding(byte[] buf, int off);
/**
* This function is called at object creation time; the
* implementation should use it to perform initialization tasks.
* After this method is called, the implementation should be ready
* to process data or meaningfully honour calls such as
* <code>getDigestLength()</code>.
*/
protected abstract void doInit();
private int digestLen, blockLen, inputLen;
private byte[] inputBuf, outputBuf;
private long blockCount;
/**
* Instantiate the engine.
*/
public DigestEngine()
{
doInit();
digestLen = getDigestLength();
blockLen = getBlockLength();
inputBuf = new byte[blockLen];
outputBuf = new byte[digestLen];
inputLen = 0;
blockCount = 0;
}
private void adjustDigestLen()
{
if (digestLen == 0) {
digestLen = getDigestLength();
outputBuf = new byte[digestLen];
}
}
/** @see Digest */
public byte[] digest()
{
adjustDigestLen();
byte[] result = new byte[digestLen];
digest(result, 0, digestLen);
return result;
}
/** @see Digest */
public byte[] digest(byte[] input)
{
update(input, 0, input.length);
return digest();
}
/** @see Digest */
public int digest(byte[] buf, int offset, int len)
{
adjustDigestLen();
if (len >= digestLen) {
doPadding(buf, offset);
reset();
return digestLen;
} else {
doPadding(outputBuf, 0);
System.arraycopy(outputBuf, 0, buf, offset, len);
reset();
return len;
}
}
/** @see Digest */
public void reset()
{
engineReset();
inputLen = 0;
blockCount = 0;
}
/** @see Digest */
public void update(byte input)
{
inputBuf[inputLen ++] = (byte)input;
if (inputLen == blockLen) {
processBlock(inputBuf);
blockCount ++;
inputLen = 0;
}
}
/** @see Digest */
public void update(byte[] input)
{
update(input, 0, input.length);
}
/** @see Digest */
public void update(byte[] input, int offset, int len)
{
while (len > 0) {
int copyLen = blockLen - inputLen;
if (copyLen > len)
copyLen = len;
System.arraycopy(input, offset, inputBuf, inputLen,
copyLen);
offset += copyLen;
inputLen += copyLen;
len -= copyLen;
if (inputLen == blockLen) {
processBlock(inputBuf);
blockCount ++;
inputLen = 0;
}
}
}
/**
* Flush internal buffers, so that less than a block of data
* may at most be upheld.
*
* @return the number of bytes still unprocessed after the flush
*/
protected int flush()
{
return inputLen;
}
/**
* Get the "block count": this is the number of times the
* <code>processBlock()</code> method has been invoked for the
* current hash operation.
*
* @return the block count
*/
protected long getBlockCount()
{
return blockCount;
}
/**
* This function copies the internal buffering state to some
* other instance of a class extending <code>DigestEngine</code>.
* It returns a reference to the copy. This method is intended
* to be called by the implementation of the <code>copy()</code>
* method.
*
* @param dest the copy
* @return the value <code>dest</code>
*/
protected Digest copyState(DigestEngine dest)
{
dest.inputLen = inputLen;
dest.blockCount = blockCount;
System.arraycopy(inputBuf, 0, dest.inputBuf, 0,
inputBuf.length);
adjustDigestLen();
dest.adjustDigestLen();
System.arraycopy(outputBuf, 0, dest.outputBuf, 0,
outputBuf.length);
return dest;
}
}