/*
 * Copyright (C) 1997 by The Open Group, Cambridge, MA, USA.  All Rights
 * Reserved.
 *
 * Portions of this code:
 * Copyright (C) 1990 by Regents of The University of Michigan.
 * Copyright (C) 1985, 1986, 1987, 1988, 1989 by
 * the Massachusetts Institute of Technology.
 *
 * The following notes have been adapted from one of the original
 * C source files.  Thanks go to Marcus Watts <mdw@umich.edu> for
 * the original notes. 
 */

/*
 * 		Some Notes on String to Key Functions.
 *
 * In Kerberos 4, string_to_key functions are a mess.
 *
 * There are, variously:
 * (1) the MIT version.
 * (2) the CMU 8-byte version.
 * (3) the improved Transarc hybrid
 *
 * The MIT version does not put the realm into the function,
 * which results in the same key in every realm, which means
 * compromising one realm may weaken any other realms if there
 * are any users who have entries in both realms (because they
 * may well have selected the same password in all realms.)
 *  This is named "string_to_key" here (in the superclass).
 *
 * The CMU version puts the realm into the function, but
 * does not work with passwords > 8 characters.  The CMU
 * version also calls the unix function "crypt" which
 * renders this code less portable.
 *  This is named "Andrew_StringToKey" here.
 *
 * The Transarc version uses the CMU version for <=8 characters,
 * and an "improved" version (which doesn't call unix "crypt")
 * for passwords over 8 characters
 *  This is named "ka_StringToKey" here.
 */

package krb4.lib.crypto;

public class altdes extends des {

	private static byte[] StringToKey(String str, String cell) {   

		byte[] password = string2byte(str + cell);
		long[] schedule = new long[16];
		byte[] ivec = string2byte("kerberos");
		byte[] temp_key = string2byte("kerberos");
		temp_key = set_parity(temp_key);
		des_set_key(temp_key, schedule);
		ivec = long2octet(cbc_cksum(password, schedule, ivec));

		System.arraycopy(ivec, 0, temp_key, 0, 8);
		temp_key = set_parity(temp_key);
		des_set_key(temp_key, schedule);
		byte[] key = long2octet(cbc_cksum(password, schedule, ivec));

		key = set_parity(key);
		return key;
	}

	public static byte[] ka_StringToKey(String str, String realm) {   

		String cell = realm.toLowerCase();
		if (str.length() > 8)
			return StringToKey(str, cell);
		else
			return Andrew_StringToKey(str, cell);
	}


	public static byte[] Andrew_StringToKey(String str, String cell)
	{
		byte[] password = new byte[8];	// crypt is limited to 8 chars anyway
		int passlen;
		int i;

		cell.getBytes(0, (cell.length() < 8 ? cell.length() : 8), password, 0);
		passlen = str.length();
		if (passlen > 8) passlen = 8;
		for (i = 0; i < passlen; i++)
			password[i] = (byte)(((byte)str.charAt(i)) ^ ((byte)cell.charAt(i)));

		for (i = 0; i < 8; i++)
			if (password[i] == (byte)'\0') password[i] = (byte)'X';

		// crypt only considers the first 8 characters of password but for some
		// reason returns eleven characters of result (plus the two salt chars).
		String crypt_str = crypt(new String(password, 0), "#~");
		byte[] key = new byte[8];
		crypt_str.getBytes(2, 10, key, 0);

		// parity is inserted into the LSB so leftshift each byte up one bit.  This
		// allows ascii characters with a zero MSB to retain as much significance
		// as possible. 
		int temp;
		for (i = 0; i < 8; i++) {
			temp = (int)key[i];
			key[i] = (byte)(temp << 1);
		}

		key = set_parity(key);
		return key;
	}

	public static String crypt(String pw, String salt)
	{
		byte[] iobuf = new byte[13];
		byte c, temp;
		int i, j;

		byte[] block = new byte[8];
		pw.getBytes(0, 8, block, 0);
		for (i = 0; i < 8; i++)
			block[i] = (byte)(block[i] << 1);
		long[] schedule = new long[16];
		des_set_key(block, schedule);
				
		byte[] permutation = new byte[expansion_permutation.length];
		System.arraycopy(expansion_permutation, 0, permutation, 0,
			expansion_permutation.length);
		
		for (i = 0; i < 2; i++) {
			c = (byte)salt.charAt(i);
			iobuf[i] = c;
			if (c > 'Z') c -= 6;
			if (c > '9') c -= 7;
			c -= '.';

			for (j = 0; j < 6; j++) {
				if (((c>>>j) & 01) != 0) {
					temp = permutation[6*i+j];
					permutation[6*i+j] = permutation[6*i+j+24];
					permutation[6*i+j+24] = temp;
				}
			}
		}

		block = new byte[8];
		for(i=0; i<25; i++)
			block = long2octet(des_encrypt(octet2long(block), schedule, true,
				permutation));

		for(i = 0; i < 11; i++) {
			c = 0;
			for(j = 0; j < 6; j++) {
				c <<= 1;
				int k = 6 * i + j;
				if (k < 64)
					c |= ((block[k / 8] >>> (7 - (k % 8))) & 01);
			}
			c += '.';
			if (c > '9') c += 7;
			if (c > 'Z') c += 6;
			iobuf[i+2] = c;
		}
		if (iobuf[1] == 0)
			iobuf[1] = iobuf[0];

		return new String(iobuf, 0);
	}

}
