// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib;

import krb5.lib.asn1.*;
import java.util.Vector;

/**
 * Authenticator
 */
public class Authenticator {

	/**
	 * Version
	 */
	public int authenticator_vno;

	/**
	 * Realm
	 */
	public Realm crealm;

	/**
	 * Name
	 */
	public PrincipalName cname;

	/**
	 * Checksum
	 */
	public Checksum cksum; //optional

	/**
	 * Use checksum
	 */
	public int cusec;

	/**
	 * Time
	 */
	public KerberosTime ctime;

	/**
	 * Subject key
	 */
	public EncryptionKey subKey; //optional

	/**
	 * Sequence number
	 */
	public Integer seqNumber; //optional

	/**
	 * Authorization data
	 */
	public AuthorizationData authorizationData; //optional

	/**
	 * Class constructor
	 *
	 * @param new_crealm is of type Realm
	 * @param new_cname is of type PrincipalName
	 * @param new_cksum is of type Checksum
	 * @param new_cusec is of type int
	 * @param new_ctime is of type KerberosTime
	 * @param new_subKey is of type EncryptionKey
	 * @param new_seqNumber is of type Integer
	 * @param new_authorizationData is of type AuthorizationData
	 * @see krb5.lib.Realm
	 * @see krb5.lib.PrincipalName
	 * @see krb5.lib.Checksum
	 * @see krb5.lib.KerberosTime
	 * @see krb5.lib.EncryptionKey
	 * @see krb5.lib.AuthorizationData
	 */
	public Authenticator (
		Realm new_crealm,
		PrincipalName new_cname,
		Checksum new_cksum,
		int new_cusec,
		KerberosTime new_ctime,
		EncryptionKey new_subKey,
		Integer new_seqNumber,
		AuthorizationData new_authorizationData
	) {
		authenticator_vno = Krb5.AUTHNETICATOR_VNO;
		crealm = new_crealm;
		cname = new_cname;
		cksum = new_cksum;
		cusec = new_cusec;
		ctime = new_ctime;
		subKey = new_subKey;
		seqNumber = new_seqNumber;
		authorizationData = new_authorizationData;
	}

	/**
	 * This is the method description
	 *
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @exception RealmException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.
	 * @see krb5.lib.
	 * @see krb5.lib.
	 */
	public Authenticator(byte[] data)
		throws Asn1Exception, RealmException, KrbApErrException {
		this(new EncodeRef(data));
	}

	/**
	 * Class constructor
	 *
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @exception RealmException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.RealmException
	 * @see krb5.lib.KrbApErrException
	 */
	public Authenticator(EncodeRef ref)
		throws Asn1Exception, RealmException, KrbApErrException {
		if (decode.TagApp(ref) != Krb5.KRB_AUTHENTICATOR)
			//XXX may not be the correct error code for a tag
			//mismatch on an encrypted structure
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		EncodeRef subRef = decode.Sequence(ref.startOfData());

		if (decode.Tag(subRef) == 0) {
			authenticator_vno = decode.Integer(subRef.startOfData());
			if (authenticator_vno != Krb5.AUTHNETICATOR_VNO)
				throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION);
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 1) {
			crealm = new Realm(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 2) {
			cname = new PrincipalName(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 3) {
			cksum = new Checksum(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 4) {
			cusec = decode.Integer(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 5) {
			ctime = new KerberosTime(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 6) {
			subKey = new EncryptionKey(subRef.startOfData());
			subRef.next();
		}

		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 7) {
			seqNumber = new Integer(decode.Integer(subRef.startOfData()));
			subRef.next();
		}

		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 8) {
			authorizationData = new AuthorizationData(subRef.startOfData());
			subRef.next();
		}

		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.Asn1Exception
	 */
	public byte[] asn1Encode() throws Asn1Exception {
		Vector tempAuthenticator = new Vector();

		tempAuthenticator.addElement(
			encode.prependExplicitTag(0, encode.Integer(authenticator_vno)));
		tempAuthenticator.addElement(
			encode.prependExplicitTag(1, crealm.asn1Encode()));
		tempAuthenticator.addElement(
			encode.prependExplicitTag(2, cname.asn1Encode()));

		if (cksum != null)
			tempAuthenticator.addElement(
				encode.prependExplicitTag(3, cksum.asn1Encode()));

		tempAuthenticator.addElement(
			encode.prependExplicitTag(4, encode.Integer(cusec)));
		tempAuthenticator.addElement(
			encode.prependExplicitTag(5, ctime.asn1Encode()));

		if (subKey != null)
			tempAuthenticator.addElement(
				encode.prependExplicitTag(6, subKey.asn1Encode()));
		if (seqNumber != null)
			tempAuthenticator.addElement(
				encode.prependExplicitTag(7,
					encode.Integer(seqNumber.intValue())));
		if (authorizationData != null)
			tempAuthenticator.addElement(
				encode.prependExplicitTag(8,
					authorizationData.asn1Encode()));

		byte[][] temp_authenticator = new byte[tempAuthenticator.size()][];
		for (int i = 0; i < tempAuthenticator.size(); i++)
			temp_authenticator[i] = (byte[])(tempAuthenticator.elementAt(i));

		return encode.prependExplicitTag(asn1Class.APPLICATION,
			Krb5.KRB_AUTHENTICATOR,	encode.Sequence(temp_authenticator));
	}

	/**
	 * Checks for equality
	 *
	 * @return boolean is a result
	 * @param other is of type Authenticator
	 * @see krb5.lib.Authenticator
	 */
    public boolean replayEquals(Authenticator other) {
        return other.cname.equals(cname) && other.cname.equals(crealm) &&
            other.ctime.equals(ctime) && other.cusec == cusec;
    }
}