// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb5.lib;

import krb5.lib.asn1.*;
import krb5.lib.crypto.*;

/**
 * Defines encryption key
 */
public class EncryptionKey implements Cloneable {
	/**
	 * Key type
	 * @see krb5.lib.Krb5
	 */    
	public int keyType;
	
	/**
	 * Key value
	 */
	public byte[] keyValue;
	
	/**
	 * Encryption type
	 * @see krb5.lib.Krb5
	 */	
	public Integer _eType; //not part of ASN1 encoding; may be missing

	/**
	 * Version
	 * @see krb5.lib.Krb5
	 */
	public Integer kvno; //not part of ASN1 encoding; may be missing

    /**
	 * Class constructor
	 */
	private EncryptionKey() {
	}

	/**
	 * Returns encryption type
	 *
	 * @return int is a result
	 * @see krb5.lib.Krb5
	 */
	public synchronized int eType() {
		if (_eType != null)
			return _eType.intValue();
		return Config.ETYPE_DEFAULT;
	}

    /**
	 * Clones a copy of the object
	 *
	 * @return Object is a result
	 * @see java.lang.Object
	 */
	public synchronized Object clone() {
		EncryptionKey new_encryptionKey = new EncryptionKey();
		new_encryptionKey.keyType = keyType;
		if (keyValue != null) {
			new_encryptionKey.keyValue = new byte[keyValue.length];
			System.arraycopy(keyValue, 0, new_encryptionKey.keyValue,
				0, keyValue.length);
		}
		if (_eType != null) {
			new_encryptionKey._eType = new Integer(_eType.intValue());
		}
		if (kvno != null) {
			new_encryptionKey.kvno = new Integer(kvno.intValue());
		}
		return new_encryptionKey;
	}

    /**
	 * Class constructor specifying key type, key value, encryption type and version
	 *
	 * @param new_keyType is of type int
	 * @param new_keyValue is of type byte[]
	 * @param new__eType is of type Integer
	 * @param new_kvno is of type Integer
	 * @see krb5.lib.Krb5
	 */
	public EncryptionKey(
		int new_keyType,
		byte[] new_keyValue,
		Integer new__eType,
		Integer new_kvno
	) {
		keyType = new_keyType;
		keyValue = new_keyValue;
		_eType = new__eType;
		kvno = new_kvno;
	}

    /**
	 * Class constructor specifying key type, encryption type and version
	 *
	 * @param new_keyType is of type int
	 * @param new__eType is of type Integer
	 * @param new_kvno is of type Integer
	 * @see krb5.lib.Krb5
	 */
	public EncryptionKey(
		byte[] new_keyValue,
		Integer new__eType,
		Integer new_kvno
	) {
		keyType = Config.KEYTYPE_DEFAULT;
		keyValue = new_keyValue;
		_eType = new__eType;
		kvno = new_kvno;
	}

	/**
	 * Class constructor specifying the key value
	 *
	 * @param new_keyValue is of type byte[]
	 */
	public EncryptionKey(
		byte[] new_keyValue
	) {
		keyType = Config.KEYTYPE_DEFAULT;
		keyValue = new_keyValue;
	}

	/**
	 * Class constructor specifying password
	 *
	 * @param password is of type String
	 * @see java.lang.String
	 */
	public EncryptionKey(
		String password
	) {
		keyType = Config.KEYTYPE_DEFAULT;
		keyValue = des.string_to_key_bytes(password);
	}

	/**
	 * Class constructor specifying password and salt
	 *
	 * @param password is of type String
	 * @param salt is of type String
	 * @see java.lang.String
	 */
	public EncryptionKey(
		String password,
		String salt
	) {
		keyType = Config.KEYTYPE_DEFAULT;
		keyValue = des.string_to_key_bytes(password + salt);
	}

  	/**
	 * Class constructor specifying encoding reference
	 *
	 * @param ref is of type EncodeRef
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.asn1.EncodeRef
	 * @see krb5.lib.Asn1Exception
	 */  
	public EncryptionKey(EncodeRef ref) throws Asn1Exception {
		EncodeRef subRef = decode.Sequence(ref);
		if (decode.Tag(subRef) == 0) {
			keyType = decode.Integer(subRef.startOfData());
			//XXX need to validate that the type is recognized
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		if (decode.Tag(subRef) == 1) {
			keyValue = decode.OctetString(subRef.startOfData());
			//XXX need to validate keyValue length is right for keyType
			subRef.next();
		}
		else
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
		if (subRef.isMoreInSequence())
			throw new Asn1Exception(Krb5.ASN1_BAD_ID);
	}

    /**
	 * Returns asn1 encoding
	 *
	 * @return byte[] is a result
	 * @exception Asn1Exception an exception
	 * @see krb5.lib.Asn1Exception
	 */
	public synchronized byte[] asn1Encode() throws Asn1Exception {
		byte[][] encryption_key = {
			encode.prependExplicitTag(0, encode.Integer(keyType)),
			encode.prependExplicitTag(1, encode.OctetString(keyValue))
		};
		return encode.Sequence(encryption_key);
	}

    /**
	 * Cleans up 
	 */
	public synchronized void destroy() {
		if (keyValue != null)
			for (int i = 0; i < keyValue.length; i++)
				keyValue[i] = 0;
	}

}
