// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib.asn1;

import krb5.lib.Asn1Exception;
import krb5.lib.Krb5;

/**
 * A subset of ASN.1 DER encoding
 */
public class encode {

	/**
	 * Bit string
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @param length is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] BitString(int class_num, int tag,
		byte[] data, int length) throws Asn1Exception {
		byte[] result;
		byte[] temp = new byte[1 + data.length];
		int extra_bits = (data.length * 8) - length;

		if (extra_bits < 0 || extra_bits > 7) 
			throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
		temp[0] = (byte)extra_bits;
		for (int i = 0; i < data.length; i++)
			temp[i + 1] = data[i];
		result = prependTag(tag, prependLength(temp));
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * Bit string
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @param length is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] BitString(int tag, byte[] data, int length) 
		throws Asn1Exception {
		return BitString(asn1Class.UNIVERSAL, tag, data, length);
	}

	/**
	 * Bit string
	 *
	 * @return byte[] is a result
	 * @param data is of type byte[]
	 * @param length is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] BitString(byte[] data, int length) 
		throws Asn1Exception {
		return BitString(asn1Class.UNIVERSAL, asn1Type.BIT_STRING,
			data, length);
	}

    //assumes data is a multiple of 8 bits
	/**
	 * Bit string
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] BitString(int class_num, int tag,
		byte[] data) throws Asn1Exception {
		byte[] result;
		byte[] temp = new byte[1 + data.length];

		temp[0] = 0; //number of extra bits in last byte
		for (int i = 0; i < data.length; i++)
			temp[i + 1] = data[i];
		result = prependTag(tag, prependLength(temp));
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * Bit string
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] BitString(int tag, byte[] data) 
		throws Asn1Exception {
		return BitString(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * Bit string
	 *
	 * @return byte[] is a result
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] BitString(byte[] data) 
		throws Asn1Exception {
		return BitString(asn1Class.UNIVERSAL, asn1Type.BIT_STRING,
			data);
	}

	/**
	 * Boolean
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type boolean
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Boolean(int class_num, int tag,
		boolean data) throws Asn1Exception {
		byte[] temp = new byte[1];
		byte[] result;

		temp[0] = data ? 0 : (byte)0xff;
		result = prependTag(tag, temp);
		setTag(tag, result);
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * Boolean
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type boolean
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Boolean(int tag, boolean data)
		throws Asn1Exception {
		return Boolean(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * Boolean
	 *
	 * @return byte[] is a result
	 * @param data is of type boolean
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Boolean(boolean data)
		throws Asn1Exception {
		return Boolean(asn1Class.UNIVERSAL, asn1Type.BOOLEAN,
			data);
	}

	//XXX invalid GeneralizedTime implementation; need to limit chars
	//and check format; assumes valid ISO/IEC character encoding in the
	//low order octets of the String
	/**
	 * Generalized time
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] GeneralizedTime(int class_num, int tag,
		String data) throws Asn1Exception {
		return GeneralString(class_num, tag, data);
	}

	/**
	 * Generalized time
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] GeneralizedTime(int tag, String data) 
		throws Asn1Exception {
		return GeneralizedTime(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * General string
	 *
	 * @return byte[] is a result
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] GeneralizedTime(String data) 
		throws Asn1Exception {
		return GeneralString(asn1Class.UNIVERSAL,
			asn1Type.GeneralizedTime, data);
	}

	//assumes valid ISO/IEC character encoding in the low order octets
	//of the String
	/**
	 * Byte
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] GeneralString(int class_num, int tag,
		String data) throws Asn1Exception {
		byte[] temp = new byte[data.length()];
		byte[] result;

		for (int i = 0; i < data.length(); i++)
			if (data.charAt(i) > 255)
				throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
		data.getBytes(0, data.length(), temp, 0);
		result = prependTag(tag, prependLength(temp));
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * General string
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] GeneralString(int tag, String data) 
		throws Asn1Exception {
		return GeneralString(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * General string
	 *
	 * @return byte[] is a result
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] GeneralString(String data) 
		throws Asn1Exception {
		return GeneralString(asn1Class.UNIVERSAL,
			asn1Type.GeneralString, data);
	}

	//XXX invalid IA5String implementation; assumes valid ISO/IEC
	//character encoding in the low order octets of the String
	/**
	 * IA5 string
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] IA5String(int class_num, int tag,
		String data) throws Asn1Exception {
		return GeneralString(class_num, tag, data);
	}

	/**
	 * This is the method description
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] IA5String(int tag, String data) 
		throws Asn1Exception {
		return IA5String(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * IA5 string
	 *
	 * @return byte[] is a result
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] IA5String(String data) 
		throws Asn1Exception {
		return IA5String(asn1Class.UNIVERSAL, asn1Type.IA5String,
			data);
	}

	/**
	 * Integer
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Integer(int class_num, int tag, int data) 
		throws Asn1Exception {
		byte[] result;

		result = prependTag(tag, prependLength(Base256(data)));
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * Integer
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Integer(int tag, int data) 
		throws Asn1Exception {
		return Integer(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * Integer
	 *
	 * @return byte[] is a result
	 * @param data is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Integer(int data) 
		throws Asn1Exception {
		return Integer(asn1Class.UNIVERSAL, asn1Type.INTEGER, data);
	}

	/**
	 * Null
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Null(int class_num, int tag) 
		throws Asn1Exception {
		byte[] temp = new byte[1];
		byte[] result;

		temp[0] = 0;
		result = prependTag(tag, temp);
		setTag(tag, result);
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * Null
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Null(int tag) throws Asn1Exception {
		return Null(asn1Class.UNIVERSAL, tag);
	}

	/**
	 * Null
	 *
	 * @return byte[] is a result
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Null() throws Asn1Exception {
		return Null(asn1Class.UNIVERSAL, asn1Type.NULL);
	}

	/**
	 * Numeric string table
	 */
	public static String NumericStringTable = "0123456789 ";

	/**
	 * Numeric string
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] NumericString(int class_num, int tag,
		String data) throws Asn1Exception {
		byte[] temp = new byte[data.length()];
		byte[] result;

		for (int i = 0; i < data.length(); i++)
			if (NumericStringTable.indexOf(data.charAt(i)) == -1)
				throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
		data.getBytes(0, data.length(), temp, 0);
		result = prependTag(tag, prependLength(temp));
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * Numeric string
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] NumericString(int tag, String data) 
		throws Asn1Exception {
		return NumericString(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * Numeric string
	 *
	 * @return byte[] is a result
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] NumericString(String data) 
		throws Asn1Exception {
		return NumericString(asn1Class.UNIVERSAL,
			asn1Type.NumericString, data);
	}

	/**
	 * Append bytes
	 *
	 * @return byte[] is a result
	 * @param data1 is of type byte[]
	 * @param data2 is of type byte[]
	 */
	public static byte[] appendBytes(byte[] data1, byte[] data2) {
		byte[] result = new byte[data1.length + data2.length];

		for (int i = 0; i < data1.length; i++)
			result[i] = data1[i];
		for (int i = 0; i < data2.length; i++)
			result[i + data1.length] = data2[i];
		return result;
	}

	/**
	 * Object identifier
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type int[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] ObjectIdentifier(int class_num, int tag,
		int[] data) throws Asn1Exception {
		byte[] temp = new byte[1];
		byte[] result;

		if (data.length < 2) //must have at least two components
			throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
		for (int i = 0; i < data.length; i++)
			if (data[i] < 0) //all must be non-negative
				throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
		if (data[0] > 2) //first component must be between 0 and 2
			throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
		if (data[0] < 2 && data[1] > 39) //must be between 0 and 39
			throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
		if (data[0] == 2 && data[1] > 47) //must be between 0 and 47
			throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
		temp[0] = (byte)((40 * data[0]) + data[1]);
		for (int i = 2; i < data.length; i++)
			temp = appendBytes(temp, Base128(data[i]));
		result = prependTag(tag, prependLength(temp));
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * Object identifier
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type int[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] ObjectIdentifier(int tag, int[] data)
		throws Asn1Exception {
		return ObjectIdentifier(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * Object identifier
	 *
	 * @return byte[] is a result
	 * @param data is of type int[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] ObjectIdentifier(int[] data)
		throws Asn1Exception {
		return ObjectIdentifier(asn1Class.UNIVERSAL,
			asn1Type.OBJECT_IDENTIFIER, data);
	}

	/**
	 * Octet string
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] OctetString(int class_num, int tag,
		byte[] data) throws Asn1Exception {
		byte[] result;

		result = prependTag(tag, prependLength(data));
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * Octet string
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] OctetString(int tag, byte[] data) 
		throws Asn1Exception {
		return OctetString(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * Octet string
	 *
	 * @return byte[] is a result
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] OctetString(byte[] data) 
		throws Asn1Exception {
		return OctetString(asn1Class.UNIVERSAL,
			asn1Type.OCTET_STRING, data);
	}

	/**
	 * Printable string table
	 */
	public static String PrintableStringTable =
		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?";

	/**
	 * Printable string
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] PrintableString(int class_num, int tag,
		String data) throws Asn1Exception {
		byte[] temp = new byte[data.length()];
		byte[] result;

		for (int i = 0; i < data.length(); i++)
			if (PrintableStringTable.indexOf(data.charAt(i)) == -1)
				throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
		data.getBytes(0, data.length(), temp, 0);
		result = prependTag(tag, prependLength(temp));
		setClass(class_num, result);
		setMethod(asn1Method.PRIMITIVE, result);
		return result;
	}

	/**
	 * Printable string
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] PrintableString(int tag, String data) 
		throws Asn1Exception {
		return PrintableString(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * Printable string
	 *
	 * @return byte[] is a result
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] PrintableString(String data) 
		throws Asn1Exception {
		return PrintableString(asn1Class.UNIVERSAL,
			asn1Type.PrintableString, data);
	}

	/**
	 * Sequence
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type byte[][]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Sequence(int class_num, int tag,
		byte[][] data) throws Asn1Exception {
		byte[] result = null;
		if (data != null &&  data.length > 0) {
			byte[] temp = data[0];
			for (int i = 1; i < data.length; i++)
				temp = appendBytes(temp, data[i]);
			result = prependTag(tag, prependLength(temp));
		}
		else {
			byte[] temp = new byte[0];
			result = prependTag(tag, prependLength(temp));
		}
		setClass(class_num, result);
		setMethod(asn1Method.CONSTRUCTED, result);
		return result;
	}

	/**
	 * Sequence
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type byte[][]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Sequence(int tag, byte[][] data) 
		throws Asn1Exception {
		return Sequence(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * Sequence
	 *
	 * @return byte[] is a result
	 * @param data is of type byte[][]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Sequence(byte[][] data) 
		throws Asn1Exception {
		return Sequence(asn1Class.UNIVERSAL,
			asn1Type.SEQUENCE, data);
	}

	/**
	 * Sequence
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Sequence(int tag) throws Asn1Exception {
		return Sequence(asn1Class.UNIVERSAL, tag, null);
	}

	/**
	 * Sequence
	 *
	 * @return byte[] is a result
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Sequence() throws Asn1Exception {
		return Sequence(asn1Class.UNIVERSAL, asn1Type.SEQUENCE, null);
	}

	//XXX invalid T61String implementation; assumes valid ISO/IEC
	//character encoding in the low order octets of the String
	/**
	 * T61 string
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] T61String(int class_num, int tag,
		String data) throws Asn1Exception {
		return GeneralString(class_num, tag, data);
	}

	/**
	 * T61 string
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] T61String(int tag, String data) 
		throws Asn1Exception {
		return T61String(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * T61 string
	 *
	 * @return byte[] is a result
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] T61String(String data) 
		throws Asn1Exception {
		return T61String(asn1Class.UNIVERSAL,
			asn1Type.T61String, data);
	}
	
	//XXX invalid UTCTime implementation; assumes valid ISO/IEC
	//character encoding in the low order octets of the String
	/**
	 * UTC time
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] UTCTime(int class_num, int tag,
		String data) throws Asn1Exception {
		return GeneralString(class_num, tag, data);
	}

	/**
	 * UTC time
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] UTCTime(int tag, String data) 
		throws Asn1Exception {
		return UTCTime(asn1Class.UNIVERSAL, tag, data);
	}

	/**
	 * UTC time
	 *
	 * @return byte[] is a result
	 * @param data is of type String
	 * @exception Asn1Exception an exception
	 */
	public static byte[] UTCTime(String data) 
		throws Asn1Exception {
		return GeneralString(asn1Class.UNIVERSAL,
			asn1Type.UTCTime, data);
	}

	//strips leading zero bytes or redundant 0xff bytes
	/**
	 * Base 256
	 *
	 * @return byte[] is a result
	 * @param x is of type int
	 */
	public static byte[] Base256(int x) {
		byte[] result;
		byte[] temp = new byte[4];
		int size = 4;
		boolean past_leading_redundancy = false;

        temp[3] = (byte) (x & 0x000000ff); 
        temp[2] = (byte)((x & 0x0000ff00) >>> 8); 
        temp[1] = (byte)((x & 0x00ff0000) >>> 16); 
        temp[0] = (byte)((x & 0xff000000) >>> 24);
		for (int i = 0; i < 3; i++) {
			if (temp[i] != 0 && temp[i] != (byte)0xff)
				past_leading_redundancy = true;
			else {
				if (temp[i] == 0) {
					if ((temp[i + 1] & (byte)0x80) != 0)
						past_leading_redundancy = true;
				}
				else {
					if ((temp[i + 1] & (byte)0x80) != (byte)0x80)
						past_leading_redundancy = true;
				}
			}
			if (!past_leading_redundancy)
				size--;
		}
		result = new byte[size];
		for (int i = 0; i < size; i++)
			result[i] = temp[i + (4 - size)];
		return result;
	}

	//strips leading zero bytes or redundant 0xff bytes
	/**
	 * Base 256
	 *
	 * @return byte[] is a result
	 * @param x is of type long
	 */
	public static byte[] Base256(long x) {
		byte[] result;
		byte[] temp = new byte[8];
		int size = 8;
		boolean past_leading_redundancy = false;

        temp[7] = (byte) (x & 0x00000000000000ffL); 
        temp[6] = (byte)((x & 0x000000000000ff00L) >>> 8); 
        temp[5] = (byte)((x & 0x0000000000ff0000L) >>> 16); 
        temp[4] = (byte)((x & 0x00000000ff000000L) >>> 24);
        temp[3] = (byte)((x & 0x000000ff00000000L) >>> 32); 
        temp[2] = (byte)((x & 0x0000ff0000000000L) >>> 40); 
        temp[1] = (byte)((x & 0x00ff000000000000L) >>> 48); 
        temp[0] = (byte)((x & 0xff00000000000000L) >>> 56);
		for (int i = 0; i < 7; i++) {
			if (temp[i] != 0 && temp[i] != (byte)0xff)
				past_leading_redundancy = true;
			else {
				if (temp[i] == 0) {
					if ((temp[i + 1] & (byte)0x80) != 0)
						past_leading_redundancy = true;
				}
				else {
					if ((temp[i + 1] & (byte)0x80) != (byte)0x80)
						past_leading_redundancy = true;
				}
			}
			if (!past_leading_redundancy)
				size--;
		}
		result = new byte[size];
		for (int i = 0; i < size; i++)
			result[i] = temp[i + (8 - size)];
		return result;
	}

	//strips leading zero bytes
	/**
	 * Base 256 unsigned
	 *
	 * @return byte[] is a result
	 * @param x is of type int
	 */
	public static byte[] Base256Unsigned(int x) {
		byte[] result;
		byte[] temp = new byte[4];
		int size = 4;

        temp[3] = (byte) (x & 0x000000ff); 
        temp[2] = (byte)((x & 0x0000ff00) >>> 8); 
        temp[1] = (byte)((x & 0x00ff0000) >>> 16); 
        temp[0] = (byte)((x & 0xff000000) >>> 24);
		for (int i = 0; i < 3; i++) {
			if (temp[i] != 0)
				break;
			size--;
		}
		result = new byte[size];
		for (int i = 0; i < size; i++)
			result[i] = temp[i + (4 - size)];
		return result;
	}

	//strips leading zero bytes
	/**
	 * Base 256 unsiged
	 *
	 * @return byte[] is a result
	 * @param x is of type long
	 */
	public static byte[] Base256Unsigned(long x) {
		byte[] result;
		byte[] temp = new byte[8];
		int size = 8;

        temp[7] = (byte) (x & 0x00000000000000ffL); 
        temp[6] = (byte)((x & 0x000000000000ff00L) >>> 8); 
        temp[5] = (byte)((x & 0x0000000000ff0000L) >>> 16); 
        temp[4] = (byte)((x & 0x00000000ff000000L) >>> 24);
        temp[3] = (byte)((x & 0x000000ff00000000L) >>> 32); 
        temp[2] = (byte)((x & 0x0000ff0000000000L) >>> 40); 
        temp[1] = (byte)((x & 0x00ff000000000000L) >>> 48); 
        temp[0] = (byte)((x & 0xff00000000000000L) >>> 56);
		for (int i = 0; i < 7; i++) {
			if (temp[i] != 0)
				break;
			size--;
		}
		result = new byte[size];
		for (int i = 0; i < size; i++)
			result[i] = temp[i + (8 - size)];
		return result;
	}

	/**
	 * Prepend length
	 *
	 * @return byte[] is a result
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] prependLength(byte[] data) 
		throws Asn1Exception {
		byte[] result;

		if (data == null || data.length == 0) {
			result = new byte[1];
			result[0] = 0;
		}
		else if (data.length < 128) {
			result = new byte[1 + data.length];
			result[0] = (byte)data.length;
			for (int i = 0; i < data.length; i++)
				result[i + 1] = data[i];
		}
		else {
			byte[] temp = Base256Unsigned(data.length);
			if (temp.length > 127) //cannot represent lengths greater
				                   //than 2^1008 - 1
				throw new Asn1Exception(Krb5.ASN1_OVERFLOW);
			result = new byte[1 + temp.length + data.length];
			result[0] = (byte)(0x80 | temp.length);
			for (int i = 0; i < temp.length; i++)
				result[i + 1] = temp[i];
			for (int i = 0; i < data.length; i++)
				result[i + 1 + temp.length] = data[i];
		}
		return result;
	}

	/**
	 * Prepend length
	 *
	 * @return byte[] is a result
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] prependLength() throws Asn1Exception {
		return prependLength(null);
	}

	/**
	 * Set class
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] setClass(int class_num, byte[] data) 
		throws Asn1Exception {
		if (class_num < 0) //only positive values
			throw new Asn1Exception(Krb5.ASN1_BAD_CLASS);
		if (class_num > 3) //only zero, one, two, or three
			throw new Asn1Exception(Krb5.ASN1_BAD_CLASS);
		data[0] = (byte)(((class_num << 6) & 0xc0) | (data[0] & 0x3f));
		return data;
	}

	/**
	 * Set method
	 *
	 * @return byte[] is a result
	 * @param method_num is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] setMethod(int method_num, byte[] data) 
		throws Asn1Exception {
		if (method_num < 0) //only positive values
			throw new Asn1Exception(Krb5.ASN1_BAD_TYPE);
		if (method_num > 1) //only zero or one
			throw new Asn1Exception(Krb5.ASN1_BAD_TYPE);
		data[0] = (byte)((data[0] & 0xdf) | ((method_num & 1) << 5));
		return data;
	}

	//only converts positive values
	//sets high bit of each byte except last
	//strips leading zero bytes
	/**
	 * Base 128
	 *
	 * @return byte[] is a result
	 * @param x is of type int
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Base128(int x) throws Asn1Exception {
		byte[] result;
		byte[] temp = new byte[5];
		int size = 5;
		boolean past_leading_zeros = false;
		
		if (x < 0) //only positive values
			throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
        temp[4] = (byte) (x & 0x0000007f); 
        temp[3] = (byte)((x & 0x00003f80) >>> 7); 
        temp[2] = (byte)((x & 0x001fc000) >>> 14); 
        temp[1] = (byte)((x & 0x0fe00000) >>> 21); 
        temp[0] = (byte)((x & 0xf0000000) >>> 28);
		for (int i = 0; i < 4; i++) {
			if (temp[i] != 0)
				past_leading_zeros = true;
			else {
				if (!past_leading_zeros)
					size--;
			}
			temp[i] = (byte)(temp[i] | 0x80);
		}
		result = new byte[size];
		for (int i = 0; i < size; i++)
			result[i] = temp[i + (5 - size)];
		return result;
	}

	//only converts positive values
	//sets high bit of each byte except last
	//strips leading zero bytes
	/**
	 * Base 128
	 *
	 * @return byte[] is a result
	 * @param x is of type long
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] Base128(long x) 
		throws Asn1Exception {
		byte[] result;
		byte[] temp = new byte[9];
		int size = 9;
		boolean past_leading_zeros = false;
		
		if (x < 0) //only positive values
			throw new Asn1Exception(Krb5.ASN1_CANNOT_ENCODE);
        temp[8] = (byte) (x & 0x000000000000007fL); 
        temp[7] = (byte)((x & 0x0000000000003f80L) >>> 7); 
        temp[6] = (byte)((x & 0x00000000001fc000L) >>> 14); 
        temp[5] = (byte)((x & 0x000000000fe00000L) >>> 21); 
        temp[4] = (byte)((x & 0x00000007f0000000L) >>> 28);
        temp[3] = (byte)((x & 0x000003f800000000L) >>> 35); 
        temp[2] = (byte)((x & 0x0001fc0000000000L) >>> 42); 
        temp[1] = (byte)((x & 0x00fe000000000000L) >>> 49); 
        temp[0] = (byte)((x & 0x7f00000000000000L) >>> 56);
		for (int i = 0; i < 8; i++) {
			if (temp[i] != 0)
				past_leading_zeros = true;
			else {
				if (!past_leading_zeros)
					size--;
			}
			temp[i] = (byte)(temp[i] | 0x80);
		}
		result = new byte[size];
		for (int i = 0; i < size; i++)
			result[i] = temp[i + (9 - size)];
		return result;
	}

	/**
	 * Prepend tag
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] prependTag(int tag, byte[] data) 
		throws Asn1Exception {
		byte[] result;
		if (tag < 0)  //cannot be negative
			throw new Asn1Exception(Krb5.ASN1_BAD_TAG);
		if (tag < 31) {
			result = new byte[1 + data.length];
			result[0] = (byte)(tag & 0x1f);
			for (int i = 0; i < data.length; i++)
				result[i + 1] = data[i];
		}
		else {
			byte[] temp = Base128(tag);
			result = new byte[1 + temp.length + data.length];
			result[0] = (byte)0x1f;
			for (int i = 0; i < temp.length; i++)
				result[i + 1] = temp[i];
			for (int i = 0; i < data.length; i++)
				result[i + 1 + temp.length] = data[i];
		}
		return result;
	}

	/**
	 * Prepend explicit tag
	 *
	 * @return byte[] is a result
	 * @param class_num is of type int
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] prependExplicitTag(int class_num, int tag,
		byte[] data) throws Asn1Exception {
		byte[] result = prependTag(tag, prependLength(data));
		setClass(class_num, result);
		setMethod(asn1Method.CONSTRUCTED, result);
		return result;
	}

	/**
	 * Prepend explicit tag
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] prependExplicitTag(int tag, byte[] data)
		throws Asn1Exception {
		return prependExplicitTag(asn1Class.CONTEXT_SPECIFIC, tag, data);
	}

	//only sets short form tags
	/**
	 * Set tag
	 *
	 * @return byte[] is a result
	 * @param tag is of type int
	 * @param data is of type byte[]
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public static byte[] setTag(int tag, byte[] data) 
		throws Asn1Exception {
		if (tag < 0) //cannot be negative
			throw new Asn1Exception(Krb5.ASN1_BAD_TAG);
		if (((data[0] & 0x1f) == 0x1f) && (tag < 31)) //long to short
			                                          //not allowed
			throw new Asn1Exception(Krb5.ASN1_BAD_TAG);
		if (((data[0] & 0x1f) != 0x1f) && (tag > 30)) //short to long
			                                          //not allowed
			throw new Asn1Exception(Krb5.ASN1_BAD_TAG);
		if (tag > 30) //long form not allowed
			throw new Asn1Exception(Krb5.ASN1_BAD_TAG);
		data[0] = (byte)((data[0] & 0xe0) | (tag & 0x1f));
		return data;
	}

}