// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib;

import krb5.lib.asn1.*;
import java.util.Vector;

/**
 * KDC response
 */
public class KDCRep {

	/**
	 * Version
	 */
	public int pvno;

	/**
	 * Message type
	 */
	public int msgType;

	/**
	 * PA data
	 */
	public PAData[] pAData; //optional

	/**
	 * Realm
	 */
	public Realm crealm;

	/**
	 * Name
	 */
	public PrincipalName cname;

	/**
	 * Ticket
	 */
	public Ticket ticket;

	/**
	 * Encrypted part
	 */
	public EncryptedData encPart;

	/**
	 * Encrypted KDC response part
	 */
	public EncKDCRepPart encKDCRepPart; //not part of ASN.1 encoding

	/**
	 * Class constructor
	 *
	 * @param new_pAData is of type PAData[]
	 * @param new_crealm is of type Realm
	 * @param new_cname is of type PrincipalName
	 * @param new_ticket is of type Ticket
	 * @param new_encPart is of type EncryptedData
	 * @param req_type is of type int
	 * @see krb5.lib.PAData
	 * @see krb5.lib.Realm
	 * @see krb5.lib.PrincipalName
	 * @see krb5.lib.Ticket
	 * @see krb5.lib.EncryptedData
	 */
	public KDCRep(
		PAData[] new_pAData,
		Realm new_crealm,
		PrincipalName new_cname,
		Ticket new_ticket,
		EncryptedData new_encPart,
		int req_type
	) {
		pvno = Krb5.PVNO;
		msgType = req_type;
		pAData = new_pAData;
		crealm = new_crealm;
		cname = new_cname;
		ticket = new_ticket;
		encPart = new_encPart;
	}

	/**
	 * Class constructor
	 */
	public KDCRep() {
	}

	/**
	 * Class constructor
	 *
	 * @param data is of type byte[]
	 * @param req_type is of type int
	 * @exception Asn1Exception an exception
	 * @exception RealmException an exception
	 * @exception KrbApErrException an exception
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.Realm
	 * @see krb5.lib.KrbApErrExceptionException
	 */
	public KDCRep(byte[] data, int req_type) throws Asn1Exception,
		RealmException, KrbApErrException {
		this(new EncodeRef(data), req_type);
	}

	/**
	 * Class constructor
	 *
	 * @param ref is of type EncodeRef
	 * @param req_type is of type int
	 * @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 KDCRep(EncodeRef ref, int req_type) throws Asn1Exception,
		RealmException, KrbApErrException {
		if (decode.TagApp(ref) != req_type)
			//XXX check if ASN1_BAD_ID is correct return code
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		EncodeRef subRef = decode.Sequence(ref.startOfData());

		if (decode.Tag(subRef) == 0) {
			pvno = decode.Integer(subRef.startOfData());
			if (pvno != Krb5.PVNO)
				//XXX check if KRB_AP_ERR_BADVERSION is correct return code
				throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION);
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 1) {
			msgType = decode.Integer(subRef.startOfData());
			if (msgType != req_type)
				//XXX check if KRB_AP_ERR_MSG_TYPE is correct return code
				throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE);
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 2) {
			Vector tempPAData = new Vector();
			EncodeRef subSubRef = decode.Sequence(subRef);
			while (subSubRef.isMoreInSequence()) {
				tempPAData.addElement(new PAData(subSubRef));
				subSubRef.next();
			}
			if (tempPAData.size() > 0) {
				pAData = new PAData[tempPAData.size()];
				tempPAData.copyInto(pAData);
			}
			subRef.next();
		}

		if (decode.Tag(subRef) == 3) {
			crealm = new Realm(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 4) {
			cname = new PrincipalName(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 5) {
			ticket = new Ticket(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 6) {
			encPart = new EncryptedData(subRef.startOfData());
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (subRef.isMoreInSequence())
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
	}

	/**
	 * Decrypts with password
	 *
	 * @param password is of type String
	 * @exception Asn1Exception an exception
	 * @exception BitStringException an exception
	 * @exception KdcErrException an exception
	 * @exception KrbApErrException an exception
	 * @exception RealmException an exception
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.BitStringException
	 * @see krb5.lib.KdcErrException
	 * @see krb5.lib.KrbApErrException
	 * @see krb5.lib.RealmException
	 */
	public void decrypt(String password) throws Asn1Exception,
		BitStringException, KdcErrException, KrbApErrException,
		RealmException {
		encKDCRepPart = new EncKDCRepPart(encPart.decrypt(password),
			msgType);
	}

	/**
	 * Decrypts with a key
	 *
	 * @param key is of type EncryptionKey
	 * @exception Asn1Exception an exception
	 * @exception BitStringException an exception
	 * @exception KdcErrException an exception
	 * @exception KrbApErrException an exception
	 * @exception RealmException an exception
	 * @see krb5.lib.EncryptionKey
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.BitStringException
	 * @see krb5.lib.KdcErrException
	 * @see krb5.lib.KrbApErrException
	 * @see krb5.lib.RealmException
	 */
	public void decrypt(EncryptionKey key) throws Asn1Exception,
		BitStringException, KdcErrException, KrbApErrException,
		RealmException {
		encKDCRepPart = new EncKDCRepPart(encPart.decrypt(key),
			msgType);
	}

	/**
	 * Encodes the object in asn1
	 *
	 * @return byte[] is a result
	 * @exception Asn1Exception an exception
	 * @exception BitStringException an exception
	 * @see krb5.lib.Asn1Exception
	 * @see krb5.lib.BitStringException
	 */
	public byte[] asn1Encode() throws Asn1Exception,
		BitStringException {
		if (pAData != null && pAData.length > 0) {
			byte[][] pa_data = new byte[pAData.length][];
			for (int i = 0; i < pAData.length; i++) {
				pa_data[i] = pAData[i].asn1Encode();
			}
			byte[][] kdc_rep = {
				encode.prependExplicitTag(0, encode.Integer(pvno)),
				encode.prependExplicitTag(1, encode.Integer(msgType)),
				encode.prependExplicitTag(2, encode.Sequence(pa_data)),
				encode.prependExplicitTag(3, crealm.asn1Encode()),
				encode.prependExplicitTag(4, cname.asn1Encode()),
				encode.prependExplicitTag(5, ticket.asn1Encode()),
				encode.prependExplicitTag(6, encPart.asn1Encode())
			};
			return encode.prependExplicitTag(asn1Class.APPLICATION,
				msgType, encode.Sequence(kdc_rep));
		}
		else {
			byte[][] kdc_rep = {
				encode.prependExplicitTag(0, encode.Integer(pvno)),
				encode.prependExplicitTag(1, encode.Integer(msgType)),
				encode.prependExplicitTag(3, crealm.asn1Encode()),
				encode.prependExplicitTag(4, cname.asn1Encode()),
				encode.prependExplicitTag(5, ticket.asn1Encode()),
				encode.prependExplicitTag(6, encPart.asn1Encode())
			};
			return encode.prependExplicitTag(asn1Class.APPLICATION,
				msgType, encode.Sequence(kdc_rep));
		}
	}

}