// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib.asn1;

import krb5.lib.Asn1Exception;
import krb5.lib.Krb5;
import java.util.Vector;

/**
 * A subset of ASN.1 DER decoding
 */
public class decode {

    //assumes number of unused bits is implicit
	/**
	 * Bit string
	 *
	 * @return byte[] is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] BitString(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.BIT_STRING)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		if (length < 2)
			return null;
		byte[] result = new byte[length - 1];
		System.arraycopy(data, index + tagLength + lengthLength + 1,
			result, 0, length - 1);
		return result;
	}

	/**
	 * Returns size of bit string
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int BitStringSize(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.BIT_STRING)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		return (((length - 1) * 8) + data[index + tagLength + lengthLength]);
	}

	/**
	 * Bit string
	 *
	 * @return byte[] is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] BitString(EncodeRef ref)
		throws Asn1Exception {
		return BitString(ref.data, ref.index);
	}

	/**
	 * Boolean
	 *
	 * @return boolean is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static boolean Boolean(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.BOOLEAN)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		if (lengthLength != 1)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int length = Length(data, index + tagLength);
		if (length != 1)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		byte result = data[index + tagLength + lengthLength];
		if (result != 0 && result != (byte)0xff)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		return (result == 0 ? false : true);
	}

	/**
	 * Boolean
	 *
	 * @return boolean is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static boolean Boolean(EncodeRef ref)
		throws Asn1Exception {
		return Boolean(ref.data, ref.index);
	}

	/**
	 * Generalized time
	 *
	 * @return String is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static String GeneralizedTime(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.GeneralizedTime)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		return new String(data, 0, index + tagLength + lengthLength, length);
	}

	/**
	 * Generalized time
	 *
	 * @return String is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static String GeneralizedTime(EncodeRef ref)
		throws Asn1Exception {
		return GeneralizedTime(ref.data, ref.index);
	}

	/**
	 * General string
	 *
	 * @return String is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static String GeneralString(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.GeneralString)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		return new String(data, 0, index + tagLength + lengthLength, length);
	}

	/**
	 * General string
	 *
	 * @return String is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static String GeneralString(EncodeRef ref)
		throws Asn1Exception {
		return GeneralString(ref.data, ref.index);
	}

	/**
	 * IA5 string
	 *
	 * @return String is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static String IA5String(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.IA5String)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		return new String(data, 0, index + tagLength + lengthLength, length);
	}

	/**
	 * IA5 string
	 *
	 * @return String is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static String IA5String(EncodeRef ref)
		throws Asn1Exception {
		return IA5String(ref.data, ref.index);
	}

	/**
	 * Integer
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int Integer(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.INTEGER)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		return Base256(data, index + tagLength + lengthLength, length);
	}

	/**
	 * Integer
	 *
	 * @return int is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static int Integer(EncodeRef ref)
		throws Asn1Exception {
		return Integer(ref.data, ref.index);
	}

	/**
	 * Null
	 *
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static void Null(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.NULL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		if (lengthLength != 1)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int length = Length(data, index + tagLength);
		if (length != 1)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int result = Base256(data, index + tagLength + lengthLength, length);
		if (result != 0)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
	}

	/**
	 * Null
	 *
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static void Null(EncodeRef ref)
		throws Asn1Exception {
		Null(ref.data, ref.index);
	}

	/**
	 * Numeric string
	 *
	 * @return String is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static String NumericString(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.NumericString)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		return new String(data, 0, index + tagLength + lengthLength, length);
	}

	/**
	 * Numeric string
	 *
	 * @return String is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static String NumericString(EncodeRef ref)
		throws Asn1Exception {
		return NumericString(ref.data, ref.index);
	}

	/**
	 * Object identifier
	 *
	 * @return int[] is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int[] ObjectIdentifier(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.OBJECT_IDENTIFIER)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		if (length < 1)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int temp_int1 = ((int)data[index + tagLength + lengthLength]) / 40;
		int temp_int2 = ((int)data[index + tagLength + lengthLength]) % 40;
		if (temp_int1 == 3) {
			temp_int1 = 2;
			temp_int2 += 40;
		}
		if (temp_int1 < 0 || temp_int1 > 2)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (temp_int2 < 0 ||
			(temp_int1 < 2 && temp_int2 > 39) ||
			(temp_int1 == 2 && temp_int2 > 47))
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		Vector temp = new Vector();
		temp.addElement(new Integer(temp_int1));
		temp.addElement(new Integer(temp_int2));
		int subIndex = index + tagLength + lengthLength + 1;
		while (subIndex < index + tagLength + lengthLength + length) {
			int subLength = Base128Length(data, subIndex);
			temp.addElement(new Integer(Base128Unsigned(data, subIndex,
				subLength)));
			subIndex += subLength;
		}
		int[] result = new int[temp.size()];
		for (int i = 0; i < temp.size(); i++)
			result[i] = ((Integer)temp.elementAt(i)).intValue();
		return result;
	}

	/**
	 * Object identifier
	 *
	 * @return int[] is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static int[] ObjectIdentifier(EncodeRef ref)
		throws Asn1Exception {
		return ObjectIdentifier(ref.data, ref.index);
	}

	/**
	 * Octet string
	 *
	 * @return byte[] is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] OctetString(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.OCTET_STRING)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		//XXX nope:
		//if (length < 1)
		//	return null;
		byte[] result = new byte[length];
		System.arraycopy(data, index + tagLength + lengthLength,
			result, 0, length);
		return result;
	}

	/**
	 * Octet string
	 *
	 * @return byte[] is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] OctetString(EncodeRef ref)
		throws Asn1Exception {
		return OctetString(ref.data, ref.index);
	}

	/**
	 * Printable string
	 *
	 * @return String is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static String PrintableString(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.PrintableString)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		return new String(data, 0, index + tagLength + lengthLength, length);
	}

	/**
	 * Printable string
	 *
	 * @return String is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static String PrintableString(EncodeRef ref)
		throws Asn1Exception {
		return PrintableString(ref.data, ref.index);
	}

	/**
	 * T61 string
	 *
	 * @return String is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static String T61String(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.T61String)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		return new String(data, 0, index + tagLength + lengthLength, length);
	}

	/**
	 * T61 string
	 *
	 * @return String is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static String T61String(EncodeRef ref)
		throws Asn1Exception {
		return T61String(ref.data, ref.index);
	}

	/**
	 * UTC time
	 *
	 * @return String is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static String UTCTime(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.PRIMITIVE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.UTCTime)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		int tagLength = TagLength(data, index);
		int lengthLength = LengthLength(data, index + tagLength);
		int length = Length(data, index + tagLength);
		return new String(data, 0, index + tagLength + lengthLength, length);
	}

	/**
	 * UTC time
	 *
	 * @return String is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static String UTCTime(EncodeRef ref)
		throws Asn1Exception {
		return UTCTime(ref.data, ref.index);
	}

	/**
	 * Sequence
	 *
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static void Sequence(byte[] data, int index)
		throws Asn1Exception {
		if (Class(data, index) != asn1Class.UNIVERSAL)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Method(data, index) != asn1Method.CONSTRUCTED)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		if (Tag(data, index) != asn1Type.SEQUENCE)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
	}

	//returns ref.startOfData().startOfSequence();
	/**
	 * Sequence
	 *
	 * @return EncodeRef is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static EncodeRef Sequence(EncodeRef ref)
		throws Asn1Exception {
		Sequence(ref.data, ref.index);
		return ref.startOfSequence();
	}

	/**
	 * Base256
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @param length is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int Base256(byte[] data, int index, int length)
		throws Asn1Exception {
		int result = 0;

		if (length > 4)
			throw new Asn1Exception(Krb5.ASN1_OVERFLOW);
		for (int i = 0; i < length; i++)
			result |= (data[index + i] & 0xff) << (8 * (length - (i + 1)));
		if (length > 0 && ((data[index] & 0x80) == 0x80))
			result |= 0xffffffff << (8 * length);
		return result;
	}

	/**
	 * Base256 as long
	 *
	 * @return long is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @param length is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static long Base256AsLong(byte[] data, int index,
		int length) throws Asn1Exception {
		long result = 0;

		if (length > 8)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		for (int i = 0; i < length; i++)
			result |= (data[index + i] & 0xff) << (8 * (length - (i + 1)));
		if (length > 0 && ((data[index] & 0x80) == 0x80))
			result |= 0xffffffffffffffffL << (8 * length);
		return result;
	}

	/**
	 * Base256 unsigned
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @param length is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int Base256Unsigned(byte[] data, int index,
		int length) throws Asn1Exception {
		int result = 0;

		if (length > 4)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		for (int i = 0; i < length; i++)
			result |= (data[index + i] & 0xff) << (8 * (length - (i + 1)));
		return result;
	}

	/**
	 * Base256 unsigned as long
	 *
	 * @return long is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @param length is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static long Base256UnsignedAsLong(byte[] data, int index,
		int length) throws Asn1Exception {
		long result = 0;

		if (length > 8)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		for (int i = 0; i < length; i++)
			result |= (data[index + i] & 0xff) << (8 * (length - (i + 1)));
		return result;
	}

	//XXX only handles length values under Integer.MAX_VALUE
	/**
	 * Length
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int LengthLength(byte[] data, int index)
		throws Asn1Exception {
		int result;
		int temp = data[index] & 0xff;

		if ((temp & 0x80) == 0x80)
			result = temp & 0x7f;
		else
			result = 0;
		return ++result;
	}

	/**
	 * Length
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int Length(byte[] data, int index)
		throws Asn1Exception {
		int result = 0;
		int length = LengthLength(data, index);

		if (length == 1)
			result = data[index] & 0x7f;
		else
			if (length < 6)
				result = Base256Unsigned(data, index + 1, length - 1);
			else
				throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		return result;
	}

	//XXX only handles values under Integer.MAX_VALUE
	/**
	 * Base128 unsigned
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @param length is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int Base128Unsigned(byte[] data, int index,
		int length) throws Asn1Exception {
		int result = 0;
		int temp;

		if (length > 5)
			throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		for (int i = 0; i < length; i++) {
			temp = data[index + i] & 0x7f;
			result |= temp << (7 * (length - (i + 1)));
		}
		return result;
	}

	/**
	 * Base128 length
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 */
	public static int Base128Length(byte[] data, int index) {
		int result = 0;

		while ((data[index + result] & 0x80) == 0x80)
			result++;
		return ++result;
	}

	//XXX only handles tag values under Integer.MAX_VALUE
	/**
	 * Tag
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int Tag(byte[] data, int index)
		throws Asn1Exception {
		int result = 0;
		int temp = data[index] & 0x1f;

		if (temp < 0x1f)
			result = temp;
		else {
			int length = Base128Length(data, index + 1);
			if (length < 6)
				result = Base128Unsigned(data, index + 1, length);
			else
				throw new Asn1Exception(Krb5.ASN1_BAD_FORMAT);
		}
		return result;
	}

	/**
	 * Tag
	 *
	 * @return int is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int Tag(EncodeRef ref) throws Asn1Exception {
		if (ref.class_num != asn1Class.CONTEXT_SPECIFIC)
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		if (ref.method_num != asn1Method.CONSTRUCTED)
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		return ref.tag_num;
	}

	/**
	 * Tag appication
	 *
	 * @return int is a result
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */
	public static int TagApp(EncodeRef ref) throws Asn1Exception {
		if (ref.class_num != asn1Class.APPLICATION)
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		if (ref.method_num != asn1Method.CONSTRUCTED)
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		return ref.tag_num;
	}

	//XXX only handles tag values under Integer.MAX_VALUE
	/**
	 * Tag length
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static int TagLength(byte[] data, int index)
		throws Asn1Exception {
		int length = data[index] & 0x1f;

		if (length < 0x1f)
			return 1;
		return Base128Length(data, index + 1) + 1;
	}

	//XXX may be a bad name!
	/**
	 * Class
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 */
	public static int Class(byte[] data, int index) {
		return (data[index] & 0xc0) >>> 6;
	}

	/**
	 * Method
	 *
	 * @return int is a result
	 * @param data is of type byte[]
	 * @param index is of type int
	 */
	public static int Method(byte[] data, int index) {
		return (data[index] & 0x20) >>> 5;
	}

}