package com.subgraph.orchid.crypto; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import com.subgraph.orchid.TorException; import com.subgraph.orchid.data.HexDigest; /** * This class wraps the default cryptographic message digest algorithm * used in Tor (SHA-1). */ public class TorMessageDigest { public static final int TOR_DIGEST_SIZE = 20; public static final int TOR_DIGEST256_SIZE = 32; private static final String TOR_DIGEST_ALGORITHM = "SHA-1"; private static final String TOR_DIGEST256_ALGORITHM = "SHA-256"; private final MessageDigest digestInstance; private final boolean isDigest256; public TorMessageDigest(boolean isDigest256) { digestInstance = createDigestInstance(isDigest256); this.isDigest256 = isDigest256; } public TorMessageDigest() { this(false); } private MessageDigest createDigestInstance(boolean isDigest256) { try { final String algorithm = (isDigest256) ? TOR_DIGEST256_ALGORITHM : TOR_DIGEST_ALGORITHM; return MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { throw new TorException(e); } } /** * Return <tt>true</tt> if this is a 256 bit digest instance. * * @return <tt>true</tt> if this is a 256 bit digest instance. */ public boolean isDigest256() { return isDigest256; } /** * Return the digest value of all data processed up until this point. * @return The digest value as an array of <code>TOR_DIGEST_SIZE<code> or <code>TOR_DIGEST256_SIZE</code> bytes. */ public byte[] getDigestBytes() { try { // Make a clone because #digest() will reset the MessageDigest instance // and we want to be able to use this class for running digests on circuits final MessageDigest clone = (MessageDigest) digestInstance.clone(); return clone.digest(); } catch (CloneNotSupportedException e) { throw new TorException(e); } } /** * Return what the digest for the current running hash would be IF we * added <code>data</code>, but don't really add the data to the digest * calculation. */ public byte[] peekDigest(byte[] data, int offset, int length) { try { final MessageDigest clone = (MessageDigest) digestInstance.clone(); clone.update(data, offset, length); return clone.digest(); } catch (CloneNotSupportedException e) { throw new TorException(e); } } /** * Calculate the digest value of all data processed up until this point and convert * the digest into a <code>HexDigest</code> object. * @return A new <code>HexDigest</code> object representing the current digest value. * @see HexDigest */ public HexDigest getHexDigest() { return HexDigest.createFromDigestBytes(getDigestBytes()); } /** * Add the entire contents of the byte array <code>input</code> to the current digest calculation. * * @param input An array of input bytes to process. */ public void update(byte[] input) { digestInstance.update(input); } /** * Add <code>length</code> bytes of the contents of the byte array <code>input</code> beginning at * <code>offset</code> into the array to the current digest calculation. * * @param input An array of input bytes to process. * @param offset The offset into the <code>input</code> array to begin processing. * @param length A count of how many bytes of the <code>input</code> array to process. */ public void update(byte[] input, int offset, int length) { digestInstance.update(input, offset, length); } /** * Convert the String <code>input</code> into an array of bytes using the ISO-8859-1 encoding * and add these bytes to the current digest calculation. * * @param input A string to process. */ public void update(String input) { try { digestInstance.update(input.getBytes("ISO-8859-1")); } catch (UnsupportedEncodingException e) { throw new TorException(e); } } }