// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib;

import krb5.lib.asn1.*;
import java.util.Vector;

/**
 * Encrypted KDC response part
 */
public class EncKDCRepPart {

	/**
	 * Key
	 */
	public EncryptionKey key;

	/**
	 * Last request
	 */
	public LastReq lastReq;

	/**
	 * Nounce
	 */
	public int nonce;

	/**
	 * Key expiration time
	 */
	public KerberosTime keyExpiration; //optional

	/**
	 * Ticket flags
	 */
	public TicketFlags flags;

	/**
	 * Authentication time
	 */
	public KerberosTime authtime;

	/**
	 * Start time
	 */
	public KerberosTime starttime; //optional

	/**
	 * End time
	 */
	public KerberosTime endtime;

	/**
	 * Renew till
	 */
	public KerberosTime renewTill; //optional

	/**
	 * Realm
	 */
	public Realm srealm;

	/**
	 * Name
	 */
	public PrincipalName sname;

	/**
	 * Address
	 */
	public HostAddresses caddr; //optional

	/**
	 * Message type
	 */
	public int msgType; //not included in sequence

	/**
	 * Class constructor
	 *
	 * @param new_key is of type EncryptionKey
	 * @param new_lastReq is of type LastReq
	 * @param new_nonce is of type int
	 * @param new_keyExpiration is of type KerberosTime
	 * @param new_flags is of type TicketFlags
	 * @param new_authtime is of type KerberosTime
	 * @param new_starttime is of type KerberosTime
	 * @param new_endtime is of type KerberosTime
	 * @param new_renewTill is of type KerberosTime
	 * @param new_srealm is of type Realm
	 * @param new_sname is of type PrincipalName
	 * @param new_caddr is of type HostAddresses
	 * @param new_msgType is of type int
	 * @see krb5.lib.EncryptionKey
	 * @see krb5.lib.LastReq
	 * @see krb5.lib.KerberosTime
	 * @see krb5.lib.TicketFlags
	 * @see krb5.lib.Realm
	 * @see krb5.lib.PrincipalName
	 * @see krb5.lib.HostAddresses
	 */
	public EncKDCRepPart(
		EncryptionKey new_key,
		LastReq new_lastReq,
		int new_nonce,
		KerberosTime new_keyExpiration,
		TicketFlags new_flags,
		KerberosTime new_authtime,
		KerberosTime new_starttime,
		KerberosTime new_endtime,
		KerberosTime new_renewTill,
		Realm new_srealm,
		PrincipalName new_sname,
		HostAddresses new_caddr,
		int new_msgType
	) {
		key = new_key;
		lastReq = new_lastReq;
		nonce = new_nonce;
		keyExpiration = new_keyExpiration;
		flags = new_flags;
		authtime = new_authtime;
		starttime = new_starttime;
		endtime = new_endtime;
		renewTill = new_renewTill;
		srealm = new_srealm;
		sname = new_sname;
		caddr = new_caddr;
		msgType = new_msgType;
	}

	/**
	 * Class constructor
	 */
	public EncKDCRepPart() {
	}

	/**
	 * Class constructor
	 *
	 * @param data is of type byte[]
	 * @param rep_type is of type int
	 * @exception Asn1Exception an exception
	 * @exception BitStringException an exception
	 * @exception RealmException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.BitStringException
	 * @see krb5.lib.RealmException
	 * @see krb5.lib.KrbApErrException
	 */
	public EncKDCRepPart(byte[] data, int rep_type)
		throws Asn1Exception, BitStringException, RealmException,
		KrbApErrException {
		this(new EncodeRef(data), rep_type);
	}

	/**
	 * Class constructor
	 *
	 * @param ref is of type EncodeRef
	 * @param rep_type is of type int
	 * @exception Asn1Exception an exception
	 * @exception BitStringException an exception
	 * @exception RealmException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.BitStringException
	 * @see krb5.lib.RealmException
	 * @see krb5.lib.KrbApErrException
	 */
	public EncKDCRepPart(EncodeRef ref, int rep_type)
		throws Asn1Exception, BitStringException, RealmException,
		KrbApErrException {
		//if (decode.TagApp(ref) != rep_type)
		//XXX implementations return the incorrect tag value, so
		//we don't use the above line; instead we use the following
		msgType = decode.TagApp(ref);
		if (msgType != Krb5.KRB_ENC_AS_REP_PART &&
			msgType != Krb5.KRB_ENC_TGS_REP_PART)
			//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) {
			key = new EncryptionKey(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 1) {
			lastReq = new LastReq(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 2) {
			nonce = decode.Integer(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 3) {
			keyExpiration = new KerberosTime(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 4) {
			flags = new TicketFlags(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 5) {
			authtime = new KerberosTime(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 6) {
			starttime = new KerberosTime(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 7) {
			endtime = new KerberosTime(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 8) {
			renewTill = new KerberosTime(subRef.startOfData());
			subRef.next();
		}

		if (decode.Tag(subRef) == 9) {
			srealm = new Realm(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 10) {
			sname = new PrincipalName(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (subRef.isMoreInSequence() && decode.Tag(subRef) == 11) {
			caddr = new HostAddresses(subRef.startOfData());
			subRef.next();
		}

		if (subRef.isMoreInSequence())
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
	}

	/**
	 * Encodes the object in asn1
	 *
	 * @return byte[] is a result
	 * @param rep_type is of type int
	 * @exception Asn1Exception an exception
	 * @exception BitStringException an exception
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.BitStringException
	 */
	public byte[] asn1Encode(int rep_type) throws Asn1Exception,
		BitStringException {
		Vector tempEncKDCRepPart = new Vector();

		tempEncKDCRepPart.addElement(
			encode.prependExplicitTag(0, key.asn1Encode()));
		tempEncKDCRepPart.addElement(
			encode.prependExplicitTag(1, lastReq.asn1Encode()));
		tempEncKDCRepPart.addElement(
			encode.prependExplicitTag(2, encode.Integer(nonce)));

		if (keyExpiration != null)
			tempEncKDCRepPart.addElement(
				encode.prependExplicitTag(3, keyExpiration.asn1Encode()));

		tempEncKDCRepPart.addElement(
			encode.prependExplicitTag(4, flags.asn1Encode()));
		tempEncKDCRepPart.addElement(
			encode.prependExplicitTag(5, authtime.asn1Encode()));

		if (starttime != null)
			tempEncKDCRepPart.addElement(
				encode.prependExplicitTag(6, starttime.asn1Encode()));

		tempEncKDCRepPart.addElement(
			encode.prependExplicitTag(7, endtime.asn1Encode()));

		if (renewTill != null)
			tempEncKDCRepPart.addElement(
				encode.prependExplicitTag(8, renewTill.asn1Encode()));

		tempEncKDCRepPart.addElement(
			encode.prependExplicitTag(9, srealm.asn1Encode()));
		tempEncKDCRepPart.addElement(
			encode.prependExplicitTag(10, sname.asn1Encode()));

		if (caddr != null)
			tempEncKDCRepPart.addElement(
				encode.prependExplicitTag(11, caddr.asn1Encode()));

		byte[][] enc_kdc_rep_part = new byte[tempEncKDCRepPart.size()][];
		for (int i = 0; i < tempEncKDCRepPart.size(); i++)
			enc_kdc_rep_part[i] = (byte[])(tempEncKDCRepPart.elementAt(i));

		//XXX should use the rep_type to build the encoding
		//but other implementations do not; it is ignored and
		//the cached msgType is used instead
		return encode.prependExplicitTag(asn1Class.APPLICATION,
			msgType, encode.Sequence(enc_kdc_rep_part));
	}

}