// Copyright 1997 The Open Group Research Institute.  All rights reserved.
/**
 * Checksum
 */

package krb5.lib;

import krb5.lib.asn1.*;
import krb5.lib.crypto.*;

public class Checksum {

	/**
	 * Checksum type
	 */  
	public int cksumType;

	/**
	 * Checksum in array of bytes
	 */
	public byte[] checksum;

	/**
	 * Class constructor specifying type and array of data
	 *
	 * @param new_cksumType is of type int
	 * @param data is of type byte[]
	 * @exception KdcErrException an exception
	 * @see krb5.lib.KdcErrException
	 */
	public Checksum(int new_cksumType, byte[] data)
		throws KdcErrException {
		cksumType = new_cksumType;
		CksumType cksumEngine = CksumType.getInstance(cksumType);
		checksum = cksumEngine.calculateChecksum(data, data.length);
	}

	/**
	 * Class constructor specifying type, array of data, and the encryption key
	 *
	 * @param new_cksumType is of type int
	 * @param data is of type byte[]
	 * @param key is of type EncryptionKey
	 * @exception KdcErrException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.EncryptionKey
	 * @see krb5.lib.KdcErrException
	 * @see krb5.lib.KrbApErrException
	 */	
	public Checksum(int new_cksumType, byte[] data, EncryptionKey key)
		throws KdcErrException, KrbApErrException {
		cksumType = new_cksumType;
		CksumType cksumEngine = CksumType.getInstance(cksumType);
		if (!cksumEngine.isSafe())
			throw new KrbApErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM);
		checksum = cksumEngine.calculateKeyedChecksum(data, data.length, key.keyValue);
	}
	
	/**
	 * Verifies the checksum value.
	 *
	 * @return boolean is a result
	 * @param data is of type byte[]
	 * @param key is of type EncryptionKey
	 * @exception KdcErrException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.EncryptionKey
	 * @see krb5.lib.KdcErrException
	 * @see krb5.lib.KrbApErrException
	 */	
	public boolean verifyKeyedChecksum(byte[] data, EncryptionKey key)
		throws KdcErrException, KrbApErrException {
		CksumType cksumEngine = CksumType.getInstance(cksumType);
		if (!cksumEngine.isSafe())
			throw new KrbApErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM);
		return cksumEngine.verifyKeyedChecksum(data, data.length, key.keyValue,
			checksum);
	}
	
	/**
	 * Class constructor specifying an array of data
	 *
	 * @param data is of type byte[]
	 * @exception KdcErrException an exception
	 * @see krb5.lib.KdcErrException
	 */	
	public Checksum(byte[] data) throws KdcErrException {
		this(Config.CKSUMTYPE_DEFAULT, data);
	}

	/**
	 * Checks to see whether a given checksum value is the same
	 *
	 * @return boolean is a result
	 * @param cksum is of type Checksum
	 * @exception KdcErrException an exception
	 * @see krb5.lib.Checksum
	 * @see krb5.lib.KdcErrException
	 */		
	public boolean isEqual(Checksum cksum) throws KdcErrException {
		if (cksumType != cksum.cksumType)
			return false;
		CksumType cksumEngine = CksumType.getInstance(cksumType);
		return cksumEngine.isChecksumEqual(checksum, cksum.checksum);
	}
	
	/**
	 * Class constructor specifying the encoding reference
	 *
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Ans1Exception
	 */	
	public Checksum(EncodeRef ref) throws Asn1Exception {
		EncodeRef subRef = decode.Sequence(ref);
		if (decode.Tag(subRef) == 0) {
			cksumType = decode.Integer(subRef.startOfData());
			//XXX need to validate that the type is recognized
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		if (decode.Tag(subRef) == 1) {
			checksum = decode.OctetString(subRef.startOfData());
			//XXX need to validate checksum length is right for cksumType
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		if (subRef.isMoreInSequence())
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
	}

	/**
	 * Encodes the object in asn1
	 *
	 * @return byte[] is a result
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Ans1Exception
	 */
	public byte[] asn1Encode() throws Asn1Exception {
		byte[][] check_sum = {
			encode.prependExplicitTag(0, encode.Integer(cksumType)),
			encode.prependExplicitTag(1, encode.OctetString(checksum))
		};
		return encode.Sequence(check_sum);
	}

}
