/*
 * @(#)MessageDigest.java	1.46 97/12/18
 * 
 * Copyright 1993-1997 Sun Microsystems, Inc. 901 San Antonio Road, 
 * Palo Alto, California, 94303, U.S.A.  All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 * 
 * CopyrightVersion 1.2
 * 
 */

package com.novell.java.security;

import java.util.*;
import java.lang.*;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.InputStream;
import java.io.ByteArrayInputStream;

/**
 * Provides applications with the functionality of a message digest
 * algorithm, such as MD5 or SHA. Message digests are secure one-way
 * hash functions that take arbitrary-sized data and output a 
 * fixed-length hash value.
 *
 * <p>A MessageDigest object starts out initialized. The data is 
 * processed through it using the update() methods. At any point
 * the reset() method can be called to reset the digest. Once all
 * the data has been updated, one of the digest() methods should
 * be called to complete the hash computation. The digest() method
 * can be called once for a given number of updates. After digest()
 * has been called, the MessageDigest object is reset to its
 * initialized state.
 *
 * <p>Implementations of the MessageDigest class can be used by
 * implementing the Cloneable interface. Such an implementation will
 * let client applications test cloneability using instanceof Cloneable
 * before cloning. Following is such an example:
 *
 * <pre>MessageDigest md = MessageDigest.getInstance("SHA");
 * if (md instanceof Cloneable) {
 *     md.update(toChapter1);
 *     MessageDigest tc1 = md.clone();
 *     byte[] toChapter1Digest = tc1.digest;
 *     md.update(toChapter2);
 *     ...etc.
 * } else {
 *     throw new DigestException("couldn't make digest of partial content");
 * }</pre>
 *
 * <p>If a given implementation is not cloneable, but the number of
 * digests is known in advance, it is still possible to compute
 * intermediate digests by instantiating several instances.
 *
 * <p>The MessageDigest class is abstract and extends from the
 * MessageDigestSpi class for historical reasons. Application
 * developers should only take notice of the methods defined in
 * this MessageDigest class, and ignore all the methods in the
 * superclass.
 *
 * @see DigestInputStream
 * @see DigestOutputStream
 * @see MessageDigestSpi
 *
 * @author Benjamin Renaud 
 * @version 1.46 98/01/14
 */

public abstract class MessageDigest extends MessageDigestSpi {

    /* Are we in debugging mode? */
    private static boolean debug = false;

    /* The digest bits, if any. */
    byte[] digestBits;

    private String algorithm;

    // The provider
    Provider provider;

    /**
     * Creates a message digest with the specified standard digest
     * algorithm name. See Appendix A in Sun's Java Cryptography
     * Architecture API Specification & Reference documentation 
     * for information about standard algorithm names.
     * 
     * @param algorithm The standard name of the digest algorithm. 
     */
    protected MessageDigest(String algorithm) {
	this.algorithm = algorithm;
    }

    /**
     * Generates a MessageDigest object that implements the specified
     * digest algorithm. If the default provider package contains a
     * MessageDigest subclass implementing the algorithm, an instance
     * of that subclass is returned. If the algorithm is not available
     * in the default package, other packages are searched. See Appendix
     * A in Sun's Java Cryptography Architecture API Specification &
     * Reference documentation for information about standard algorithm
     * names.
     *
     * @param algorithm The name of the algorithm requested. 
     *
     * @return A Message Digest object implementing the specified
     *         algorithm.
     *
     * @exception NoSuchAlgorithmException Thrown if the algorithm is
     * not available in the caller's environment.  
     */
    public static MessageDigest getInstance(String algorithm) 
    throws NoSuchAlgorithmException { 
/*	try {
	    Object[] objs = Security.getImpl(algorithm, "MessageDigest", null);
	    if (objs[0] instanceof MessageDigest) {
		MessageDigest md = (MessageDigest)objs[0];
		md.provider = (Provider)objs[1];
		return md;
	    } else {
		Delegate delegate = new Delegate((MessageDigestSpi)objs[0],
						 algorithm);
		delegate.provider = (Provider)objs[1];
		return delegate;
	    }
	} catch(NoSuchProviderException e) {
	    throw new NoSuchAlgorithmException(algorithm + " not found");
	}
*/
       return null;
    }

    /**
     * Generates a MessageDigest object implementing the specified
     * algorithm as supplied from the specified provider. The 
     * MessageDigest is generated if an algorithm is available from
     * the provider. See Appendix A in Sun's Java Cryptography
     * Architecture API Specification & Reference documentation 
     * for information about standard algorithm names.
     *
     * @param algorithm The name of the algorithm requested. 
     *
     * @param provider The name of the provider.
     *
     * @return A Message Digest object implementing the specified
     * algorithm.
     *
     * @exception NoSuchAlgorithmException Thrown if the algorithm
     * is not available in the package supplied by the requested
     * provider.
     *
     * @exception NoSuchProviderException Thrown if the provider is
     * not available in the environment. 
     * 
     * @see Provider 
     */
    public static MessageDigest getInstance(String algorithm, String provider)
    throws NoSuchAlgorithmException, NoSuchProviderException {
/*	Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider);
	if (objs[0] instanceof MessageDigest) {
	    MessageDigest md = (MessageDigest)objs[0];
	    md.provider = (Provider)objs[1];
	    return md;
	} else {
	    Delegate delegate = new Delegate((MessageDigestSpi)objs[0],
					     algorithm);
	    delegate.provider = (Provider)objs[1];
	    return delegate;
	}
*/
       return null;
    }

    /** 
     * Returns the Provider of this message digest object.
     * 
     * @return The Provider of this message digest object
     */
    public final Provider getProvider() {
	return this.provider;
    }

    /**
     * Updates the message digest using the specified byte.    
     * 
     * @param input The byte with which to update the digest.
     */
    public void update(byte input) {
	digestBits = null;
	engineUpdate(input);
    }

    /**
     * Updates the message digest using the specified array of bytes,
     * starting at the specified offset.
     * 
     * @param input The array of bytes.
     *
     * @param offset The offset in the array of bytes from which to start.
     *
     * @param len The number of bytes to use, starting at the offset.  
     */
    public void update(byte[] input, int offset, int len) {
	if (input == null) {
	    throw new IllegalArgumentException("No input buffer given");
	}
	if (input.length - offset < len) {
	    throw new IllegalArgumentException("Input buffer too short");
	}
	digestBits = null;
	engineUpdate(input, offset, len);
    }

    /**
     * Updates the message digest using the specified array of bytes.
     * 
     * @param input The array of bytes.
     */
    public void update(byte[] input) {
	digestBits = null;
	engineUpdate(input, 0, input.length);
    }

    /**
     * Completes the hash computation by performing final operations
     * such as padding. The digest is reset after this call is made.
     *
     * @return The array of bytes for the resulting hash value.  
     */
    public byte[] digest() {
	/* Resetting is the responsibility of implementors. */
	digestBits = engineDigest();
	return digestBits;
    }

    /**
     * Completes the hash computation by performing final operations
     * such as padding. The digest is reset after this call is made.
     *
     * @param buf The output buffer for the computed digest.
     *
     * @param offset The offset into the output buffer at which to
     *               begin storing the digest.
     *
     * @param len The number of bytes within the output buffer (buf)
     *            allotted for the digest.
     *
     * @return The length of the digest.
     * 
     * @exception DigestException Thrown if an error occurs.
     */
    public int digest(byte[] buf, int offset, int len) throws DigestException {
	if (buf == null) {
	    throw new IllegalArgumentException("No input buffer given");
	}
	if (buf.length - offset < len) {
	    throw new IllegalArgumentException("Input buffer too short");
	}
	int diglen = engineDigest(buf, offset, len);
	digestBits = new byte[diglen];
	System.arraycopy(buf, offset, digestBits, 0, diglen);
	return diglen;
    }

    /**
     * Performs a final update and digest computation on the digest
     * using the specified array of bytes. That is, this digest()
     * method first calls update() on the array, then calls this digest()
     * method.
     *
     * @param input The input to be updated before the digest is
     *              completed.
     *
     * @return The array of bytes for the resulting hash value.  
     */
    public byte[] digest(byte[] input) {
	update(input);
	return digest();
    }

    /**
     * Helper function that prints unsigned two character hex digits.
     */
    private static void hexDigit(PrintStream p, byte x) {
	char c = (char) ((x >> 4) & 0xf);
	if (c > 9) {
	    c = (char) ((c - 10) + 'a');
	} else {
	    c = (char) (c + '0');
	}
	p.write(c);

	c = (char) (x & 0xf);
	if (c > 9) {
	    c = (char)((c - 10) + 'a');
	} else {
	    c = (char)(c + '0');
	}
	p.write(c);
    }

    /**
     * Returns a string representation of this message digest object.
     * In general, the toString() method returns a string that 
     * textually represents the object. The result should be a 
     * concise but informative representation that is easy for a
     * person to read.
     *
     * @return A string representation of the message digest object.  
     */
    public String toString() {
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	PrintStream p = new PrintStream(baos);
	p.print(this.getClass().getName()+" Message Digest ");
	if (digestBits != null) {
	    p.print("<");
	    for (int i = 0; i < digestBits.length; i++)
 	        hexDigit(p, digestBits[i]);
	    p.print(">");
	} else {
	    p.print("<incomplete>");
	}
	p.println();
	return (baos.toString());
    }

    /**
     * Compares two digests for equality using a simple byte compare.
     * 
     * @param digesta One of the digests to compare.
     * 
     * @param digestb The other digest to compare.    
     *
     * @return A boolean set to TRUE if the digests are equal,
     *         otherwise FALSE.
     */
    public static boolean isEqual(byte digesta[], byte digestb[]) {
	if (digesta.length != digestb.length)
	    return false;

	for (int i = 0; i < digesta.length; i++) {
	    if (digesta[i] != digestb[i]) {
		return false;
	    }
	}
	return true;
    }

    /**
     * Resets the digest for further use.
     */
    public void reset() {
	engineReset();
	digestBits = null;
    }

    /** 
     * Returns a string that identifies the algorithm, independent of
     * implementation details. The name should be a standard
     * Java Security name, such as SHA, MD5, and so forth. See
     * Appendix A in Sun's Java Cryptography Architecture API
     * Specification & Reference documentation for information
     * about standard algorithm names.
     */
    public final String getAlgorithm() {
	return this.algorithm;
    }

    /** 
     * Returns the length of the digest in bytes. Zero (0) is returned
     * if this operation is not supported by the provider and the
     * implementation is not cloneable.
     *
     * @return The digest length in bytes, or 0 if this operation is not
     *         supported by the provider and the implementation is not
     *         cloneable.
     * 
     * @since JDK1.2
     */
    public final int getDigestLength() {
	int digestLen = engineGetDigestLength();
	if (digestLen == 0) {
	    try {
		MessageDigest md = (MessageDigest)clone();
		byte[] digest = md.digest();
		return digest.length;
	    } catch (CloneNotSupportedException e) {
		return digestLen;
	    }
	}
	return digestLen;
    }

    /**    
     * Creates a new message digest object of the same class as
     * this message digest object. It then initializes each of the
     * new object's fields by assigning it the same value as the
     * corresponding field in this object. The clone() method will
     * only clone a message digest object if the implementation is
     * Cloneable. A class indicates that its instances can be
     * cloned by declaring that it implements the Cloneable interface.
     * 
     * @return a clone if the implementation is cloneable.
     *
     * @exception CloneNotSupportedException Thrown if the clone()
     * method is called on an implementation that does not support
     * Cloneable.
     */
    public Object clone() throws CloneNotSupportedException {
	if (this instanceof Cloneable) {
	    return super.clone();
	} else {
	    throw new CloneNotSupportedException();
	}
    }
  
    private void debug(String statement) {
	if (debug) {
	    System.err.println(statement);
	}
    }




    /*
     * The following class allows providers to extend from MessageDigestSpi
     * rather than from MessageDigest. It represents a MessageDigest with an
     * encapsulated, provider-supplied SPI object (of type MessageDigestSpi).
     * If the provider implementation is an instance of MessageDigestSpi,
     * the getInstance() methods above return an instance of this class, with
     * the SPI object encapsulated.
     *
     * Note: All SPI methods from the original MessageDigest class have been
     * moved up the hierarchy into a new class (MessageDigestSpi), which has
     * been interposed in the hierarchy between the API (MessageDigest)
     * and its original parent (Object).
     */

    static class Delegate extends MessageDigest {

	// The provider implementation (delegate)
	private MessageDigestSpi digestSpi;

	// constructor
	public Delegate(MessageDigestSpi digestSpi, String algorithm) {
	    super(algorithm);
	    this.digestSpi = digestSpi;
	}

	/*
	 * Returns a clone if the delegate is cloneable.    
	 * 
	 * @return a clone if the delegate is cloneable.
	 *
	 * @exception CloneNotSupportedException if this is called on a
	 * delegate that does not support <code>Cloneable</code>.
	 */
	public Object clone() throws CloneNotSupportedException {
	    if (digestSpi instanceof Cloneable) {
		MessageDigestSpi digestSpiClone =
		    (MessageDigestSpi)digestSpi.clone();
		Delegate that = new Delegate(digestSpiClone, algorithm);
		that.provider = provider;
		that.digestBits = digestBits;
		return that;
	    } else {
		throw new CloneNotSupportedException();
	    }
	}

	protected int engineGetDigestLength() {
	    return digestSpi.engineGetDigestLength();
	}

	protected void engineUpdate(byte input) {
	    digestSpi.engineUpdate(input);
	}

	protected void engineUpdate(byte[] input, int offset, int len) {
	    digestSpi.engineUpdate(input, offset, len);
	}

	protected byte[] engineDigest() {
	    return digestSpi.engineDigest();
	}

	protected int engineDigest(byte[] buf, int offset, int len)
	    throws DigestException {
		return digestSpi.engineDigest(buf, offset, len);
	}

	protected void engineReset() {
	    digestSpi.engineReset();
	}
    }
}
