// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib;

import krb5.lib.asn1.*;
import java.util.Vector;

/**
 * Kerberos Credential
 */
public class KRBCred {
    
    /**
	 * Version Number
	 * @see krb5.lib.Krb5
	 */    
	public int pvno;
	
    /**
	 * Message Type
	 * @see krb5.lib.Krb5
	 */		
	public int msgType;
	
	/**
	 * Array of tickets
	 * @see krb5.lib.Ticket
	 */	
	public Ticket[] tickets;
	
	/**
	 * Encrypted Data
	 * @see krb5.lib.EncryptedData
	 */		
	public EncryptedData encPart;


	/**
	 * Class constructor
	 *
	 * @param new_tickets is of type Ticket[]
	 * @param new_encPart is of type EncryptedData
	 * @see krb5.lib.Ticket
	 * @see krb5.lib.EncryptedData
	 */
	public KRBCred(Ticket[] new_tickets, EncryptedData new_encPart) {
		pvno = Krb5.PVNO;
		msgType = Krb5.KRB_CRED;
		tickets = new_tickets;
		encPart = new_encPart;
	}

	/**
	 * Class constructor
	 *
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public KRBCred(byte[] data) throws Asn1Exception,
		RealmException, KrbApErrException {
		this(new EncodeRef(data));
	}

	/**
	 * Class constructor
	 *
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public KRBCred(EncodeRef ref) throws Asn1Exception,
		RealmException, KrbApErrException {
		if (decode.TagApp(ref) != Krb5.KRB_CRED)
			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 != Krb5.KRB_CRED)
				//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 tempTickets = new Vector();
			EncodeRef subSubRef = decode.Sequence(subRef.startOfData());
			while (subSubRef.isMoreInSequence()) {
				tempTickets.addElement(new Ticket(subSubRef));
				subSubRef.next();
			}
			if (tempTickets.size() > 0) {
				tickets = new Ticket[tempTickets.size()];
				tempTickets.copyInto(tickets);
			}
			//XXX should catch empty ticket list with an error code
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);

		if (decode.Tag(subRef) == 3) {
			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);
	}

	/**
	 * Encodes the data in asn1
	 *
	 * @return byte[] is a result
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public byte[] asn1Encode() throws Asn1Exception {
		byte[][] temp_tickets = new byte[tickets.length][];
		for (int i = 0; i < tickets.length; i++)
			temp_tickets[i] = tickets[i].asn1Encode();
		byte[][] krb_cred = {
			encode.prependExplicitTag(0, encode.Integer(pvno)),
			encode.prependExplicitTag(1, encode.Integer(msgType)),
			encode.prependExplicitTag(2, encode.Sequence(temp_tickets)),
			encode.prependExplicitTag(3, encPart.asn1Encode())
		};
		return encode.prependExplicitTag(asn1Class.APPLICATION,
			msgType, encode.Sequence(krb_cred));
	}

}
