/*
 * Copyright (C) 1997, by The Open Group, Cambridge, MA, USA.  All Rights
 * Reserved.
 * 
 * This software is being provided to you, the LICENSEE, by The Open Group
 * (T.O.G.) under the following license.  By obtaining, using and/or
 * copying this software, you agree that you have read, understood, and
 * will comply with these terms and conditions:  
 * 
 * Export of this software from the United States of America may
 * require a specific license from the United States Government.
 * It is the responsibility of any person or organization contemplating
 * export to obtain such a license before exporting.
 * 
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify and distribute 
 * this software and its documentation for any purpose and without fee or 
 * royalty is hereby granted, provided that you agree to comply with the 
 * following copyright notice and statements, including the disclaimer, and 
 * that the same appear on ALL copies of the software and documentation, 
 * including modifications that you make for internal use or for 
 * distribution:
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS", AND T.O.G. MAKES NO REPRESENTATIONS 
 * OR WARRANTIES, EXPRESS OR IMPLIED.  By way of example, but not 
 * limitation, T.O.G. MAKES NO REPRESENTATIONS OR WARRANTIES OF 
 * MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF 
 * THE LICENSED SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY 
 * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.   
 * 
 * The name of The Open Group, or T.O.G., or TOG may NOT be used in
 * advertising or publicity pertaining to distribution of the software.
 * Title to copyright in this software and any associated documentation
 * shall at all times remain with T.O.G., and USER agrees to preserve same.  
 */

package krb4.lib.crypto;

public class des {

	private static final byte[] initial_permutation = {
		58,	50, 42,	34,	26,	18,	10,	2,	60,	52,	44,	36,	28,	20,	12,	4,
		62,	54,	46,	38,	30,	22,	14,	6,	64,	56,	48,	40,	32,	24,	16,	8,
		57,	49,	41,	33,	25,	17,	9,	1,	59,	51,	43,	35,	27,	19,	11,	3,
		61,	53,	45,	37,	29,	21,	13,	5,	63,	55,	47,	39,	31,	23,	15,	7
	};
	
	private static final byte[] final_permutation = {
		40,	8,	48,	16,	56,	24,	64,	32,	39,	7,	47,	15,	55,	23,	63,	31,
		38,	6,	46,	14,	54,	22,	62,	30,	37,	5,	45,	13,	53,	21,	61,	29,
		36, 4,	44,	12,	52,	20,	60,	28,	35,	3,	43,	11,	51,	19,	59,	27,
		34,	2,	42,	10,	50,	18,	58,	26,	33,	1,	41,	9,	49,	17,	57,	25
	};

	private static final byte[] key_permutation = {
		57,	49,	41,	33,	25,	17,	9,	1,	58,	50,	42,	34,	26,	18,
		10,	2,	59,	51,	43,	35,	27,	19,	11,	3,	60,	52, 44, 36,
		63,	55,	47,	39,	31,	23,	15,	7,	62,	54,	46,	38,	30,	22,
		14,	6,	61,	53,	45,	37,	29,	21,	13,	5,	28,	20,	12,	4
	};

	private static final byte[] compression_permutation = {
		14,	17,	11,	24,	1,	5,	3,	28,	15,	6,	21,	10,
		23,	19,	12,	4,	26,	8,	16,	7,	27,	20,	13,	2,
		41,	52,	31,	37,	47,	55,	30,	40,	51,	45,	33,	48,
		44,	49,	39,	56,	34,	53,	46,	42,	50,	36,	29,	32
	};

	public static final byte[] expansion_permutation = {
		32,	1,	2,	3,	4,	5,	4,	5,	6,	7,	8,	9,
		8,	9,	10,	11,	12,	13,	12,	13,	14,	15,	16,	17,
		16,	17,	18,	19,	20,	21,	20,	21,	22,	23,	24,	25,
		24, 25,	26,	27,	28,	29,	28,	29,	30,	31,	32,	1
	};

	private static final byte[] p_box_permutation = {
		16,	7,	20,	21,	29,	12,	28,	17,	1,	15,	23,	26,	5,	18,	31,	10,
		2,	8,	24,	14,	32,	27,	3,	9,	19,	13,	30,	6,	22,	11,	4,	25
	};

	private static final byte[] rot_num = {
		1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
	};

	private static final long[] chunk_mask = {
		0xfc00000000000000L,
		0x03f0000000000000L,
		0x000fc00000000000L,
		0x00003f0000000000L,
		0x000000fc00000000L,
		0x00000003f0000000L,
		0x000000000fc00000L,
		0x00000000003f0000L
	};

	private static final long[] bad_keys = {
		0x0101010101010101L, 0xfefefefefefefefeL,
		0x1f1f1f1f1f1f1f1fL, 0xe0e0e0e0e0e0e0e0L,
		0x01fe01fe01fe01feL, 0xfe01fe01fe01fe01L,
		0x1fe01fe00ef10ef1L, 0xe01fe01ff10ef10eL,
		0x01e001e001f101f1L, 0xe001e001f101f101L,
		0x1ffe1ffe0efe0efeL, 0xfe1ffe1ffe0efe0eL,
		0x011f011f010e010eL, 0x1f011f010e010e01L,
		0xe0fee0fef1fef1feL, 0xfee0fee0fef1fef1L
	};

	private static final byte[] good_parity = {
 		1,	 1,   2,   2,   4,   4,   7,   7,
		8,   8,   11,  11,  13,  13,  14,  14, 
		16,  16,  19,  19,  21,  21,  22,  22,
		25,  25,  26,  26,  28,  28,  31,  31, 
		32,  32,  35,  35,  37,  37,  38,  38,
		41,  41,  42,  42,  44,  44,  47,  47, 
		49,  49,  50,  50,  52,  52,  55,  55,
		56,  56,  59,  59,  61,  61,  62,  62, 
		64,  64,  67,  67,  69,  69,  70,  70,
		73,  73,  74,  74,  76,  76,  79,  79, 
		81,  81,  82,  82,  84,  84,  87,  87,
		88,  88,  91,  91,  93,  93,  94,  94, 
		97,  97,  98,  98,  100, 100, 103, 103,
		104, 104, 107, 107, 109, 109, 110, 110, 
		112, 112, 115, 115, 117, 117, 118, 118,
		121, 121, 122, 122, 124, 124, 127, 127, 
		(byte)128, (byte)128, (byte)131, (byte)131,
		(byte)133, (byte)133, (byte)134, (byte)134,
		(byte)137, (byte)137, (byte)138, (byte)138,
		(byte)140, (byte)140, (byte)143, (byte)143,
		(byte)145, (byte)145, (byte)146, (byte)146,
		(byte)148, (byte)148, (byte)151, (byte)151,
		(byte)152, (byte)152, (byte)155, (byte)155,
		(byte)157, (byte)157, (byte)158, (byte)158,
		(byte)161, (byte)161, (byte)162, (byte)162,
		(byte)164, (byte)164, (byte)167, (byte)167,
		(byte)168, (byte)168, (byte)171, (byte)171,
		(byte)173, (byte)173, (byte)174, (byte)174,
		(byte)176, (byte)176, (byte)179, (byte)179,
		(byte)181, (byte)181, (byte)182, (byte)182,
		(byte)185, (byte)185, (byte)186, (byte)186,
		(byte)188, (byte)188, (byte)191, (byte)191,
		(byte)193, (byte)193, (byte)194, (byte)194,
		(byte)196, (byte)196, (byte)199, (byte)199,
		(byte)200, (byte)200, (byte)203, (byte)203,
		(byte)205, (byte)205, (byte)206, (byte)206,
		(byte)208, (byte)208, (byte)211, (byte)211,
		(byte)213, (byte)213, (byte)214, (byte)214,
		(byte)217, (byte)217, (byte)218, (byte)218,
		(byte)220, (byte)220, (byte)223, (byte)223,
		(byte)224, (byte)224, (byte)227, (byte)227,
		(byte)229, (byte)229, (byte)230, (byte)230,
		(byte)233, (byte)233, (byte)234, (byte)234,
		(byte)236, (byte)236, (byte)239, (byte)239,
		(byte)241, (byte)241, (byte)242, (byte)242,
		(byte)244, (byte)244, (byte)247, (byte)247,
		(byte)248, (byte)248, (byte)251, (byte)251,
		(byte)253, (byte)253, (byte)254, (byte)254
	};

	private static final byte [][] sbox = {
		{
			14,	4,	13,	1,	2,	15,	11,	8,	3,	10,	6,	12,	5,	9,	0,	7,
			0,	15,	7,	4,	14,	2,	13,	1,	10,	6,	12,	11,	9,	5,	3,	8,
			4,	1,	14,	8,	13,	6,	2,	11,	15,	12,	9,	7,	3,	10,	5,	0,
			15,	12,	8,	2,	4,	9,	1,	7,	5,	11,	3,	14,	10,	0,	6,	13
		},
		{
			15,	1,	8,	14,	6,	11,	3,	4,	9,	7,	2,	13,	12,	0,	5,	10,
			3,	13,	4,	7,	15,	2,	8,	14,	12,	0,	1,	10,	6,	9,	11,	5,
			0,	14,	7,	11,	10,	4,	13,	1,	5,	8,	12,	6,	9,	3,	2,	15,
			13,	8,	10,	1,	3,	15,	4,	2,	11,	6,	7,	12,	0,	5,	14,	9
		},
		{
			10,	0,	9,	14,	6,	3,	15,	5,	1,	13,	12,	7,	11,	4,	2,	8,
			13,	7,	0,	9,	3,	4,	6,	10,	2,	8,	5,	14,	12,	11,	15,	1,
			13,	6,	4,	9,	8,	15,	3,	0,	11,	1,	2,	12,	5,	10,	14,	7,
			1,	10,	13,	0,	6,	9,	8,	7,	4,	15,	14,	3,	11,	5,	2,	12
		},
		{
			7,	13,	14,	3,	0,	6,	9,	10,	1,	2,	8,	5,	11,	12,	4,	15,
			13,	8,	11,	5,	6,	15,	0,	3,	4,	7,	2,	12,	1,	10,	14,	9,
			10,	6,	9,	0,	12,	11,	7,	13,	15,	1,	3,	14,	5,	2,	8,	4,
			3,	15,	0,	6,	10,	1,	13,	8,	9,	4,	5,	11,	12,	7,	2,	14
		},
		{
			2,	12,	4,	1,	7,	10,	11,	6,	8,	5,	3,	15,	13,	0,	14,	9,
			14,	11,	2,	12,	4,	7,	13,	1,	5,	0,	15,	10,	3,	9,	8,	6,
			4,	2,	1,	11,	10,	13,	7,	8,	15,	9,	12,	5,	6,	3,	0,	14,
			11,	8,	12,	7,	1,	14,	2,	13,	6,	15,	0,	9,	10,	4,	5,	3
		},
		{
			12,	1,	10,	15,	9,	2,	6,	8,	0,	13,	3,	4,	14,	7,	5,	11,
			10,	15,	4,	2,	7,	12,	9,	5,	6,	1,	13,	14,	0,	11,	3,	8,
			9,	14,	15,	5,	2,	8,	12,	3,	7,	0,	4,	10,	1,	13,	11,	6,
			4,	3,	2,	12,	9,	5,	15,	10,	11,	14,	1,	7,	6,	0,	8,	13
		},
		{
			4,	11,	2,	14,	15,	0,	8,	13,	3,	12,	9,	7,	5,	10,	6,	1,
			13,	0,	11,	7,	4,	9,	1,	10,	14,	3,	5,	12,	2,	15,	8,	6,
			1,	4,	11,	13,	12,	3,	7,	14,	10,	15,	6,	8,	0,	5,	9,	2,
			6,	11,	13,	8,	1,	4,	10,	7,	9,	5,	0,	15,	14,	2,	3,	12
		},
		{
			13,	2,	8,	4,	6,	15,	11,	1,	10,	9,	3,	14,	5,	0,	12,	7,
			1,	15,	13,	8,	10,	3,	7,	4,	12,	5,	6,	11,	0,	14,	9,	2,
			7,	11,	4,	1,	9,	12,	14,	2,	0,	6,	10,	13,	15,	3,	5,	8,
			2,	1,	14,	7,	4,	10,	8,	13,	15,	12,	9,	0,	3,	5,	6,	11
		}
	};

	public static final long permute_des(byte[] p, long x) {
		long y = 0;
		for (int i = 0; i < p.length; i++) {
		//	y |= ((x & (0x8000000000000000L >>> (p[i] - 1))) << (p[i] - 1)) >>> i;
			if ((x & (0x8000000000000000L >>> (p[i] - 1))) != 0)
				y |= 0x8000000000000000L >>> i;
		}
		return y;
	}

	public static final boolean check_parity(long key) {
		byte[] octet = long2octet(key);
		return check_parity(octet);
	}

	public static final boolean check_parity(byte[] key) {
		for (int i=0; i < 8; i++) {
            	if (key[i] != good_parity[key[i] & 0xff])
				return false;
		}
		return true;
	}

	public static final byte[] set_parity(byte[] key) {
		for (int i=0; i < 8; i++) {
			key[i] = good_parity[key[i] & 0xff];
		}
		return key;
	}

	public static final long set_parity(long key) {
		return octet2long(set_parity(long2octet(key)));
	}

	public static final boolean bad_key(long key) {
		for (int i = 0; i < bad_keys.length; i++) {
			if (bad_keys[i] == key) {
				return true;
			}
		}
		return false;
	}

	public static final boolean bad_key(byte[] key) {
		return bad_key(octet2long(key));
	}

	public static boolean des_set_key(long key, long[] key_sched) {
		long x = permute_des(key_permutation, key);
		for (int i = 0; i < 16; i++) {
			if (rot_num[i] == 1)
				x = (x & 0x7ffffff7ffffff00L) << 1 |
					(x & 0x8000000800000000L) >>> 27;
			else
				x = (x & 0x3ffffff3ffffff00L) << 2 |
					(x & 0xc000000c00000000L) >>> 26;
			key_sched[i] = permute_des(compression_permutation, x);
		}
		return check_parity(key);
	}

	public static boolean des_set_key(byte[] key, long[] key_sched) {
		return des_set_key(octet2long(key), key_sched);
	}

	public static long mangle(long R, long key, byte[] permutation) {
		long output = 0;
		int chunk, nibble;
		long expanded_R = permute_des(permutation, R);
		/*
		for (int i = 0; i < 8; i++) {
			chunk = (int)(((expanded_R & chunk_mask[i]) ^
				(key & chunk_mask[i])) >>> ((6 * (7 - i)) + 16));
			
			chunk = ((chunk & 0x00000001) << 4) | (chunk & 0x00000020) |
				((chunk & 0x0000001e) >>> 1); 
			
			//chunk -> nibble
			nibble = (int)sbox[i][chunk];

			output |= ((long)nibble) << (((7 - i) * 4) + 32);
		}
		*/

		//round 0
		chunk = (int)(((expanded_R & chunk_mask[0]) ^
			(key & chunk_mask[0])) >>> 58);
		chunk = ((chunk & 0x00000001) << 4) | (chunk & 0x00000020) |
			((chunk & 0x0000001e) >>> 1); 
		output |= (((long)sbox[0][chunk]) << 60);

		//round 1
		chunk = (int)(((expanded_R & chunk_mask[1]) ^
			(key & chunk_mask[1])) >>> 52);
		chunk = ((chunk & 0x00000001) << 4) | (chunk & 0x00000020) |
			((chunk & 0x0000001e) >>> 1); 
		output |= (((long)sbox[1][chunk]) << 56);

		//round 2
		chunk = (int)(((expanded_R & chunk_mask[2]) ^
			(key & chunk_mask[2])) >>> 46);
		chunk = ((chunk & 0x00000001) << 4) | (chunk & 0x00000020) |
			((chunk & 0x0000001e) >>> 1); 
		output |= (((long)sbox[2][chunk]) << 52);

		//round 3
		chunk = (int)(((expanded_R & chunk_mask[3]) ^
			(key & chunk_mask[3])) >>> 40);
		chunk = ((chunk & 0x00000001) << 4) | (chunk & 0x00000020) |
			((chunk & 0x0000001e) >>> 1); 
		output |= (((long)sbox[3][chunk]) << 48);

		//round 4
		chunk = (int)(((expanded_R & chunk_mask[4]) ^
			(key & chunk_mask[4])) >>> 34);
		chunk = ((chunk & 0x00000001) << 4) | (chunk & 0x00000020) |
			((chunk & 0x0000001e) >>> 1); 
		output |= (((long)sbox[4][chunk]) << 44);

		//round 5
		chunk = (int)(((expanded_R & chunk_mask[5]) ^
			(key & chunk_mask[5])) >>> 28);
		chunk = ((chunk & 0x00000001) << 4) | (chunk & 0x00000020) |
			((chunk & 0x0000001e) >>> 1); 
		output |= (((long)sbox[5][chunk]) << 40);

		//round 6
		chunk = (int)(((expanded_R & chunk_mask[6]) ^
			(key & chunk_mask[6])) >>> 22);
		chunk = ((chunk & 0x00000001) << 4) | (chunk & 0x00000020) |
			((chunk & 0x0000001e) >>> 1); 
		output |= (((long)sbox[6][chunk]) << 36);

		//round 7
		chunk = (int)(((expanded_R & chunk_mask[7]) ^
			(key & chunk_mask[7])) >>> 16);
		chunk = ((chunk & 0x00000001) << 4) | (chunk & 0x00000020) |
			((chunk & 0x0000001e) >>> 1); 
		output |= (((long)sbox[7][chunk]) << 32);

		return permute_des(p_box_permutation, output);
	}

	public static long des_encrypt(long data, long[] key_sched,
		boolean encrypt, byte[] permutation) {
		long input, output;
		long L, R, L1 = 0, R1 = 0;
		input = permute_des(initial_permutation, data);
		L = (input & 0xffffffff00000000L);
		R = (input & 0x00000000ffffffffL) << 32;
		/*
		for (int i = 0; i < 16; i++) {
			if (encrypt)
				R1 = L ^ mangle(R, key_sched[i], permutation);
			else
				R1 = L ^ mangle(R, key_sched[15 - i], permutation);
			L = R;
			R = R1;
		}*/
		if (encrypt) {
			R1 = L ^ mangle(R, key_sched[ 0], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 1], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 2], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 3], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 4], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 5], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 6], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 7], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 8], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 9], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[10], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[11], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[12], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[13], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[14], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[15], permutation); L = R; R = R1;
		}
		else {
			R1 = L ^ mangle(R, key_sched[15], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[14], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[13], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[12], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[11], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[10], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 9], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 8], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 7], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 6], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 5], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 4], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 3], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 2], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 1], permutation); L = R; R = R1;
			R1 = L ^ mangle(R, key_sched[ 0], permutation); L = R; R = R1;
		}
		output = R | (L >>> 32);
		output = permute_des(final_permutation, output);
		return output;
	}

	public static long des_encrypt(long data, long[] key_sched, boolean encrypt) {
		return des_encrypt(data, key_sched, encrypt, expansion_permutation);
	}

	public static long des_encrypt(byte[] data, long[] key_sched, boolean encrypt) {
		return des_encrypt(octet2long(data), key_sched, encrypt);
	}

	public static long octet2long(byte[] input) {
		//return octet2long(input, 0);
		return ((((long)input[0]) & 0xffL) << 56) |
			   ((((long)input[1]) & 0xffL) << 48) |
			   ((((long)input[2]) & 0xffL) << 40) |
			   ((((long)input[3]) & 0xffL) << 32) |
			   ((((long)input[4]) & 0xffL) << 24) |
			   ((((long)input[5]) & 0xffL) << 16) |
			   ((((long)input[6]) & 0xffL) << 8) |
			   (((long)input[7]) & 0xffL);
	}

	public static long octet2long(byte[] input, int offset) {
		//for (int i = 0; i < 8; i++) {
		//	if (i + offset < input.length) {
		//		result |= (((long)input[i + offset]) & 0xffL) << ((7 - i) * 8);
		//	}
		//}
		return ((((long)(  offset < input.length ? input[offset] : 0)) & 0xffL) << 56) |
			   ((((long)(++offset < input.length ? input[offset] : 0)) & 0xffL) << 48) |
			   ((((long)(++offset < input.length ? input[offset] : 0)) & 0xffL) << 40) |
			   ((((long)(++offset < input.length ? input[offset] : 0)) & 0xffL) << 32) |
			   ((((long)(++offset < input.length ? input[offset] : 0)) & 0xffL) << 24) |
			   ((((long)(++offset < input.length ? input[offset] : 0)) & 0xffL) << 16) |
			   ((((long)(++offset < input.length ? input[offset] : 0)) & 0xffL) << 8) |
			    (((long)(++offset < input.length ? input[offset] : 0)) & 0xffL);
	}

	public static byte[] long2octet(long input) {
		byte[] output = new byte[8];
		//for (int i = 0; i < 8; i++) {
		//	output[i] =	(byte)((input >>> ((7 - i) * 8)) & 0xffL);
		//}
		output[0] = (byte)((input >>> 56) & 0xffL);
		output[1] = (byte)((input >>> 48) & 0xffL);
		output[2] = (byte)((input >>> 40) & 0xffL);
		output[3] = (byte)((input >>> 32) & 0xffL);
		output[4] = (byte)((input >>> 24) & 0xffL);
		output[5] = (byte)((input >>> 16) & 0xffL);
		output[6] = (byte)((input >>>  8) & 0xffL);
		output[7] = (byte)(input & 0xffL);
		return output;
	}

	public static void long2octet(long input, byte[] output) {
		long2octet(input, output, 0);
	}

	public static void long2octet(long input, byte[] output, int offset) {
		//for (int i = 0; i < 8; i++) {
		//	if (i + offset < output.length) {
		//		output[i + offset] =
		//			(byte)((input >>> ((7 - i) * 8)) & 0xffL);
		//		output[i + offset] = temp[i];
		//	}
		//}
		byte[] temp = new byte[8];
		if (offset < output.length)
			output[offset] = (byte)((input >>> 56) & 0xffL);
		if (++offset < output.length)
			output[offset] = (byte)((input >>> 48) & 0xffL);
		if (++offset < output.length)
			output[offset] = (byte)((input >>> 40) & 0xffL);
		if (++offset < output.length)
			output[offset] = (byte)((input >>> 32) & 0xffL);
		if (++offset < output.length)
			output[offset] = (byte)((input >>> 24) & 0xffL);
		if (++offset < output.length)
			output[offset] = (byte)((input >>> 16) & 0xffL);
		if (++offset < output.length)
			output[offset] = (byte)((input >>> 8) & 0xffL);
		if (++offset < output.length)
			output[offset] = (byte)(input & 0xffL);
	}

	public static byte[] string2byte(String str) {
		byte[] result = new byte[str.length()];
		str.getBytes(0, str.length(), result, 0);
		return result;
	}

	public static void ecb_encrypt(
		byte[] input,
		byte[] output,
		long[] schedule,
		boolean encrypt) {
		long2octet(des_encrypt(octet2long(input), schedule, encrypt), output);
	}

	public static void ecb_encrypt(
		byte[] input,
		int input_offset,
		byte[] output,
		int output_offset,
		long[] schedule,
		boolean encrypt) {
		long2octet(des_encrypt(octet2long(input, input_offset), schedule,
			encrypt), output, output_offset);
	}

	public static void cbc_encrypt(
		byte[] input,
		byte[] output,
		long[] schedule,
		long ivec,
		boolean encrypt) {
		cbc_encrypt(input, 0, output, 0, input.length, schedule, ivec, encrypt);
	}

	public static void cbc_encrypt(
		byte[] input,
		byte[] output,
		long[] schedule,
		byte[] ivec,
		boolean encrypt) {
		cbc_encrypt(input, 0, output, 0, input.length, schedule, octet2long(ivec), encrypt);
	}

	public static void cbc_encrypt(
		byte[] input,
		int input_offset,
		byte[] output,
		int output_offset,
		int	length,
		long[] schedule,
		byte[] ivec,
		boolean encrypt) {
		cbc_encrypt(input, input_offset, output, output_offset, length, schedule, octet2long(ivec), encrypt);
	}
	
	public static void cbc_encrypt(
		byte[] input,
		int input_offset,
		byte[] output,
		int output_offset,
		int	length,
		long[] schedule,
		long ivec,
		boolean encrypt) {
		long m;
		long c = ivec;
		for (int i = 0; i < (length / 8) + (length % 8 == 0 ? 0 : 1); i++) {
			m = octet2long(input, (i * 8) + input_offset);
			if (encrypt) {
				c = des_encrypt(m ^ c, schedule, encrypt);
				long2octet(c, output, (i * 8) + output_offset);
			}
			else {
				c = c ^ des_encrypt(m, schedule, encrypt);
				long2octet(c, output, (i * 8) + output_offset);
				c = m;
			}
		}
	}
	
	public static void pcbc_encrypt(
		byte[] input,
		byte[] output,
		long[] schedule,
		long ivec,
		boolean encrypt) {
		pcbc_encrypt(input, 0, output, 0, input.length, schedule, ivec, encrypt);
	}

	public static void pcbc_encrypt(
		byte[] input,
		byte[] output,
		long[] schedule,
		byte[] ivec,
		boolean encrypt) {
		pcbc_encrypt(input, 0, output, 0, input.length, schedule, octet2long(ivec), encrypt);
	}

	public static void pcbc_encrypt(
		byte[] input,
		int input_offset,
		byte[] output,
		int output_offset,
		int	length,
		long[] schedule,
		byte[] ivec,
		boolean encrypt) {
		pcbc_encrypt(input, input_offset, output, output_offset, length, schedule, octet2long(ivec), encrypt);
	}
	
	public static void pcbc_encrypt(
		byte[] input,
		int input_offset,
		byte[] output,
		int output_offset,
		int	length,
		long[] schedule,
		long ivec,
		boolean encrypt) {
		long o = 0;
		long m;
		long c = ivec;
		for (int i = 0; i < (length / 8) + (length % 8 == 0 ? 0 : 1); i++) {
			m = octet2long(input, (i * 8) + input_offset);
			if (encrypt) {
				c = des_encrypt(m ^ c ^ o, schedule, encrypt);
				o = m;
				long2octet(c, output, (i * 8) + output_offset);
			}
			else {
				c = c ^ des_encrypt(m, schedule, encrypt) ^ o;
				long2octet(c, output, (i * 8) + output_offset);
				o = c;
				c = m;
			}
		}
	}

	public static long cbc_cksum(
		byte[] input,
		long[] schedule,
		long ivec) {
		return cbc_cksum(input, 0, input.length, schedule, ivec);
	}

	public static long cbc_cksum(
		byte[] input,
		long[] schedule,
		byte[] ivec) {
		return cbc_cksum(input, 0, input.length, schedule, ivec);
	}

	public static long cbc_cksum(
		byte[] input,
		int input_offset,
		int	length,
		long[] schedule,
		long ivec) {
		long m;
		long c = ivec;
		for (int i = 0; i < (length / 8) + (length % 8 == 0 ? 0 : 1); i++) {
			m = octet2long(input, (i * 8) + input_offset);
			c = des_encrypt(m ^ c, schedule, true);
		}
		return c;
	}

	public static long cbc_cksum(
		byte[] input,
		int input_offset,
		int	length,
		long[] schedule,
		byte[] ivec) {
		return cbc_cksum(input, input_offset, length, schedule, octet2long(ivec));
	}
	
	public static long string_to_key(String str) {
		byte[] bytes = string2byte(str);
		long[] key_sched = new long[16];
		long key = 0;
		long octet = 0;
		long octet1 = 0;
		long octet2 = 0;
		
		int length = (bytes.length / 8) + (bytes.length % 8  == 0 ? 0 : 1);

		for (int i = 0; i < length; i++) {
			octet = octet2long(bytes, i * 8) & 0x7f7f7f7f7f7f7f7fL;
			if (i % 2 == 1) {
				octet1 = 0;
				for (int j = 0; j < 64; j++) {
					octet1 |= ((octet & (1L << j)) >>> j) << (63 - j);
				}
				octet = octet1 >>> 1;
			}
			key ^= (octet << 1);
		}

		key = set_parity(key);
	    if (bad_key(key))
			key ^= 0xf0L;
	    des_set_key(key, key_sched);
		key = cbc_cksum(bytes, key_sched, key);

		key = set_parity(key);
	    if (bad_key(key))
			key ^= 0xf0L;
		
		return key;
	}

	public static byte[] string_to_key_bytes(String str) {
		return long2octet(string_to_key(str));
	}

}


