HR$DES$VERSION = "2005-01-19"
// =====================================================================================================================
// Copyright (c) 2005 Henk Reints (http://henk-reints.nl)
// ------------------------------------------------------
// Date: 18-JAN-2005
//
// This script defines and creates a DESobject Object named "DES"
// which provides methods for DES enciphering and deciphering.
// On initialisation it performs a self-test.
// If that fails an error message is issued and the methods are NOT exposed;
// if it succeeds then it remains quiet and you can use:
// single DES:
//	cipher = DES.encipher(block ,key)
//	block  = DES.decipher(cipher,key)
// triple DES with 2 keys:
//	cipher = DES.encipher(block ,key1,key2)
//	block  = DES.decipher(cipher,key1,key2)
// triple DES with 3 keys:
//	cipher = DES.encipher(block ,key1,key2,key3)
//	block  = DES.decipher(cipher,key1,key2,key3)
// all values (block, cipher, and keys) are 64-bit words, stored as two 32-bit words in an Array as [L,R]
// when interpreting a word as a series of bytes, then the 'big endian' convention should be used.
// See hr$binstring.js for explantion of 'big endian'.
// You can use hr$binstring.js to convert Strings to binary blocks for performing DES operations.
//
// Methods exposed:
//
//	encipher(block ,key1 [,key2 [,key3] ] )		see above
//	decipher(cipher,key1 [,key2 [,key3] ] )		see above
//
//	encipherChain(array,IV,key1 [,key2 [,key3] ] )	Cipher Block Chaining on a series of data blocks,
//	decipherChain(array,IV,key1 [,key2 [,key3] ] )	see http://csrc.nist.gov/publications/fips/fips81/fips81.htm
//							in both methods, the array is expected to be an even-length
//		array of 32-bit words, each successive pair of them forming a 64-bit block to be enciphered or
//		deciphered. Any message padding to make it an integer multiple of 64 bits must already have been done.
//
// 	StringToKey(password)		converts a password string to a binay DES key.
//					password can be either 7 characters or 8.
//		if it is 7 characters (which is 7 bytes = 56 bits), then it is converted to a key by putting all 56 bits
//		at their proper place in the DES key by inserting a zero bit after every 7th bit (that inserted bit will
//		thus become every 8th bit, which is ignored by DES).
//		if the original bits are:	12345678123456781234567812345678123456781234567812345678
//		then the result will be:	1234567_8123456_7812345_6781234_5678123_4567812_3456781_2345678_
//		where the underscores mark the unused 8th bits in the key.
//		  if the password is 8 characters long, then from each character the low 7 bits are used
//		and shifted one	place left to form the key, i.e.
//		if the original bits are	1234567812345678123456781234567812345678123456781234567812345678
//		then the result will be:	2345678_2345678_2345678_2345678_2345678_2345678_2345678_2345678_
//		in either case, conversion from string to bytes is done using the built-in fromCharCode method of the
//		String Object and for (Unicode) character codes greater than 255 only the low order byte is used.
//
//	BlockToString(block)	returns a String representation of a 64-bit block (defined as an Array of two 32-bit
//				words) as a normal JavaScript Array definition string with hexadecimally encoded
//				values, e.g. "[0x01234567,0x89abcdef]"
//
//	RandomBlock(isKey)	returns a random 64-bit block (using built-in Math.random)
//				if isKey is given and true, then every 8th bit will be zero.
//
// LEGAL STUFF:
//	o  Copyright (c) 2005 Henk Reints (http://henk-reints.nl)
//		this copyright is of course not for the DES algorithm itself - I did not invent it -
//		but only for this JavaScript implementation thereof;
//	o  This software is free to be used, as long as it is left unmodified, including this legal notice;
//	o  I SHALL NOT BE RESPONSIBLE FOR ANY NEGATIVE CONSEQUENCES OF THE USE OF THIS SOFTWARE;
//	o  I DO NOT GUARANTEE THE CORRECTNESS OF THIS SOFTWARE
//	   o  I have not yet found many sample data on the Internet for testing the algorithm,
//	      but I was able to reproduce the data given on "http://www.aci.net/kalliste/des.htm";
//	   o  The included selftest uses the JavaScript built-in Math.random method to generate a random datablock
//	      and random keys, on which it performs an encipher/decipher cycle for each mode. If deciphering produces
//	      output identical to the random block that was enciphered, the test passes.
//
// Reference:
//	FIPS PUB 46-3 = DATA ENCRYPTION STANDARD (DES)
//	http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf
//
// I have defined the tables for the DES algorithm compactly in pseudo-base64 notation, i.e. 1 character
// per byte, specifying values from 1 through 64, which spans the entire range of bit numbers used in DES.
// The S array is stored as single-digit hexadecimal strings. I have simplified the implementation of the S(B)
// functions. Originally, the S(B) functions are defined using 2-dimensional tables to be addressed in a strange way
// (the example below is S1):
//
//	Row| Column No.
//	No.|   0  1   2  3   4  5   6  7   8  9  10 11  12 13  14 15
//	---+--------------------------------------------------------
//	 0 |  14  4  13  1   2 15  11  8   3 10   6 12   5  9   0  7
//	 1 |   0 15   7  4  14  2  13  1  10  6  12 11   9  5   3  8
//	 2 |   4  1  14  8  13  6   2 11  15 12   9  7   3 10   5  0
//	 3 |  15 12   8  2   4  9   1  7   5 11   3 14  10  0   6 13
//
// To compute S(B), where B is a 6 bit number, you should address this table by using the leftmost + rightmost bits
// of B as row number and the inner 4 bits as colun number. For example (from http://www.aci.net/kalliste/des.htm),
// for input block B = 011011 the first bit is "0" and the last bit "1" giving 01 = 1 as the row number. The middle
// four bits are 1101 which is the binary equivalent of decimal 13, so the column number is 13. In row 1, column 13
// appears 5, which is binary 0101. Hence S1(011011) = 0101. 
//
//	I CONSIDER THIS A VERY STUPID AND UNNECESSARILY COMPLEX PROCEDURE!
//
// There are simply 64 possible values of B, from 0 to 63, and each B produces an S-value. Putting those S-values in
// order of the possible values of B yields the simple one-dimensional list S(0),S(1), ... ,S(62),S(63), which is:
//	14,0,4,15,13,7,1,4,2,14,15,2,11,13,8,1,3,10,10,6,6,12,12,11,5,9,9,5,0,3,7,8,
//	4,15,1,12,14,8,8,2,13,4,6,9,2,1,11,7,15,5,12,11,9,3,7,14,3,10,10,0,5,6,0,13;
// (although written on two lines, this should be read as a single list of numbers). Then S(B) is simply the
// (zero-based) B'th value from this list! Same example: B = 011011 is decimal 27, and the (ZERO-BASED!) 27th
// value in the list is 5 = binary 0101.
// =====================================================================================================================


function DESobject()
{
	var B64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

	function decPseudoB64(x){for(var a=[],i=0;i<x.length;a[i]=B64chars.indexOf(x.charAt(i))+1,i++);return a}
	function decSingleHex(x){for(var a=[],i=0;i<x.length;a[i]=parseInt(x.charAt(i),16)       ,i++);return a}

// tables for obtaining a Key Schedule:
	var DES_PC1 = decPseudoB64("4wogYQIA5xphZRJB6yqiaSKC7zrj+2umeWOG91tldVNF80skcUMEbTLD")
	var DES_PC2 = decPseudoB64("NQKXAECbOFUJWSLDZHPGaTMBozeku2dnysgvrwm3h0tpxjcf")
	var DES_SHL = [1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]

// tables for the main DES algorithm:
	var DES_IP =	 decPseudoB64("5xphZRJB7zrjbTLD91tldVNF/3vnfXPH4wogYQIA6yqiaSKC80skcUME+2umeWOG")
	var DES_FP =	 decPseudoB64("nHvP3X/fmGuO2W+elFtN1V9dkEsM0U8cjDrLzT7biCqKyS6ahBpJxR5ZgAoIwQ4Y")
	var DES_E  =	 decPseudoB64("fABCDEDEFGHIHIJKLMLMNOPQPQRSTUTUVWXYXYZabcbcdefA")
	var DES_P  =	 decPseudoB64("PGTUcLbQAOWZEReJBHXNfaCISMdFVKDY")
	var DES_S  =	[decSingleHex("e04fd7142ef2bd813aa66ccb599503784f1ce882d46921b7f5cb937e3aa0560d")
			,decSingleHex("f31d84e76fb2384e9c7021dac6095ba50de87ab1a34fd4125b86c76c90352ef9")
			,decSingleHex("ad0790e96334f65a12d8c57ebc4b2f81d16a4d9086f93807b41f2ec35ba5e27c")
			,decSingleHex("7dd8eb35066f90a31427825cb1ca4ef9a36f9006cab17dd8f91435eb5c27824e")
			,decSingleHex("2ecb421c74a7bd6185503ffad309e8964b281cb7a1de728df69fc0596a3405e3")
			,decSingleHex("ca1fa4f2972c698506d13d4ee07b53b894e3f25c2985cf3a7b0e41a716d0b86d")
			,decSingleHex("4db02be7f40981da3ec3957c52af6816164bbdd8c1347ae7a9f5608f0e52932c")
			,decSingleHex("d12f8d486af3b714ac9536eb500ec97272b14e1794cae82d0f6ca9d0f335568b")
			]

// elementary bit-level stuff:
	function unsign(B)						// undo sign bit appearing after bitwise op's
	{	for (var X = [], i=0; i<B.length; X[i] = (B[i] < 0 ? B[i]+0x100000000 : B[i] ), i++);
		return X
	}
	function getbit(B,n)								// get value of a bit in a block
	{	var b = (n > 32 ? 1 : 0), s = (b ? 64-n : 32-n)
		return  ( B[b] & (1 << s) ) >>> s
	}
	function setbit(B,n,v)								// set value of a bit in a block
	{	var b = (n > 32 ? 1 : 0), s = (b ? 64-n : 32-n)
		if (v)	B[b] |=  (1 << s)
		else	B[b] &= ~(1 << s)
	}
	function rol28(W,n)				// rotate-left a 28-bit word,left-justified within a 32-bit word
	{	return (W << n) | (W >>> (28-n) ) & ~0xf
	}
	function xor64(A,B)				// xor 2 64-bit words, each stored as an array of 2 32-bit words
	{	return [ A[0] ^ B[0], A[1] ^ B[1] ]
	}

// modules for DES:
	function perm(B,P)				// get permutation or any other choice of bits from a block
	{	for (var B1 = [0,0], i = 0, n = 1; i < P.length; setbit(B1,n++,getbit(B,P[i++])) );
		return B1
	}
	function getKS(KEY)							// generate the key schedule from a key
	{	var CD = perm(KEY,DES_PC1), C = CD[0]&~0xf, D = ((CD[0]&0xf)<<28)|(CD[1]>>>4), n,i,K
		for(K=[],i=0;i<16;n=DES_SHL[i],C=rol28(C,n),D=rol28(D,n),K[i++]=perm([C|(D>>>28),D<<4],DES_PC2));
		return K
	}
	function f(R,K)										// the cipher function
	{	var E = perm([R,0],DES_E), X = xor64(E,K), L = X[0], R = X[1]
		var S1 = DES_S[0][   (L&0xfc000000) >>> 26  ] << 28
		var S2 = DES_S[1][   (L&0x03f00000) >>> 20  ] << 24
		var S3 = DES_S[2][   (L&0x000fc000) >>> 14  ] << 20
		var S4 = DES_S[3][   (L&0x00003f00) >>>  8  ] << 16
		var S5 = DES_S[4][   (L&0x000000fc) >>>  2  ] << 12
		var S6 = DES_S[5][ ( (L&0x00000003)  <<  4) | ( (R&0xf0000000) >>> 28) ] <<  8
		var S7 = DES_S[6][   (R&0x0fc00000) >>> 22  ] <<  4
		var S8 = DES_S[7][   (R&0x003f0000) >>> 16  ]
		var S  = S1 | S2 | S3 | S4 | S5 | S6 | S7 | S8
		return perm([S,0],DES_P)[0]
	}
	function des(M,KS1,KS2,KS3)								// the core algorithm
	{	var i,C,IP,L,R,X, n=arguments.length
			for(IP=perm(M,DES_IP),L=IP[0],R=IP[1],i=0;i<16;X=L^f(R,KS1[i++]),L=R,R=X); C=perm([R,L],DES_FP)
		if (n==3)
		{	for(IP=perm(C,DES_IP),L=IP[0],R=IP[1],i=0;i<16;X=L^f(R,KS2[i++]),L=R,R=X); C=perm([R,L],DES_FP)
			for(IP=perm(C,DES_IP),L=IP[0],R=IP[1],i=0;i<16;X=L^f(R,KS1[i++]),L=R,R=X); C=perm([R,L],DES_FP)
		} else if (n==4)
		{	for(IP=perm(C,DES_IP),L=IP[0],R=IP[1],i=0;i<16;X=L^f(R,KS2[i++]),L=R,R=X); C=perm([R,L],DES_FP)
			for(IP=perm(C,DES_IP),L=IP[0],R=IP[1],i=0;i<16;X=L^f(R,KS3[i++]),L=R,R=X); C=perm([R,L],DES_FP)
		}
		return C
	}

// DES methods to be exposed:
	function encipher(M,K1,K2,K3)
	{	var KS1, n = arguments.length
		return unsign	( n==2	? des(M,     getKS(K1))
				: n==3	? des(M, KS1=getKS(K1), getKS(K2).reverse(), KS1)
					: des(M,     getKS(K1), getKS(K2).reverse(), getKS(K3))
				)
	}
	function decipher(C,K1,K2,K3)
	{	var KS1, n = arguments.length
		return unsign	( n==2	? des(C,     getKS(K1).reverse())
				: n==3	? des(C, KS1=getKS(K1).reverse(), getKS(K2), KS1)
					: des(C,     getKS(K3).reverse(), getKS(K2), getKS(K1).reverse())
				)
	}

	// DES modes of operation - Cipher Block Chaining to encrypt a chain of plain text data blocks:
	// For the "Cipher Block Chaining" DES Operation Mode for encryption of long messages, I have not yet found any
	// standard for triple DES. I mean, should I use three different initialisation vectors for triple DES or just
	// one? Effectively, the IV is another sort of encryption key, although not as strong as the key itself. The
	// implementation below uses just one IV. M should be an even-length Array of 32-bit words, each successive pair
	// forming a 64-bit data block to be enciphered. Any padding of a message that is not an exact multiple of 64
	// bits should already have been done! C is the equivalent enciphered version of M, also as an even-length Array
	// of 32-bit words:

	function encipherChain(M,IV,K1,K2,K3)
	{	var KS1=getKS(K1), KS2,KS3,X, Y=IV.slice(), C=[], n=arguments.length, i=0
		if (n < 4)
		{	for(;i<M.length;X=M.slice(i,i+2),Y=des(xor64(X,Y),KS1),C[i++]=Y[0],C[i++]=Y[1]);
		} else
		{	KS2 = getKS(K2).reverse()
			if (n > 4)	KS3 = getKS(K3);
			else		KS3 = KS1;
			for(;i<M.length;X=M.slice(i,i+2),Y=des(xor64(X,Y),KS1,KS2,KS3),C[i++]=Y[0],C[i++]=Y[1]);
		}
		return unsign(C)
	}
	function decipherChain(C,IV,K1,K2,K3)
	{	var KS1=getKS(K1).reverse(), KS2,KS3,X, Y=IV.slice(), M=[], n=arguments.length, i=0
		if (n < 4)
		{	for(;i<C.length;X=Y,Y=C.slice(i,i+2),X=xor64(des(Y,KS1),X),M[i++]=X[0],M[i++]=X[1]);
		} else
		{	KS2 = getKS(K2)
			if (n > 4)	KS3 = getKS(K3).reverse();
			else		KS3 = KS1;
			for(;i<C.length;X=Y,Y=C.slice(i,i+2),X=xor64(des(Y,KS3,KS2,KS1),X),M[i++]=X[0],M[i++]=X[1]);
		}
		return unsign(M)
	}

// auxiliary methods to be exposed:
	function StringToKey(password)					// convert 7 or 8 char password to 64-bit key
	{	for (var B = [], i = 0; i < password.length; B[i] = password.charCodeAt(i)&0xff, i++);
		if (password.length == 7)
		{	var K1 =                           (B[0] & 0xfe)
			var K2 = ( (B[0] & 0x01) << 7) | ( (B[1] & 0xfc) >> 1)
			var K3 = ( (B[1] & 0x03) << 6) | ( (B[2] & 0xf8) >> 2)
			var K4 = ( (B[2] & 0x07) << 5) | ( (B[3] & 0xf0) >> 3)
			var K5 = ( (B[3] & 0x0f) << 4) | ( (B[4] & 0xe0) >> 4)
			var K6 = ( (B[4] & 0x1f) << 3) | ( (B[5] & 0xc0) >> 5)
			var K7 = ( (B[5] & 0x3f) << 2) | ( (B[6] & 0x80) >> 6)
			var K8 = ( (B[6] & 0x7f) << 1)
			var KL = (K1 << 24) | (K2 << 16) | (K3 << 8) | K4
			var KR = (K5 << 24) | (K6 << 16) | (K7 << 8) | K8
		} else if (password.length == 8)
		{	var KL = ((B[0]&0x7f) << 25) | ((B[1]&0x7f) << 17) | ((B[2]&0x7f) << 9) | ((B[3]&0x7f) << 1)
			var KR = ((B[4]&0x7f) << 25) | ((B[5]&0x7f) << 17) | ((B[6]&0x7f) << 9) | ((B[7]&0x7f) << 1)
		} else
		{	throw "Wrong password length in DESobject.StringToKey - should be 7 or 8"
		}
		return unsign ([KL,KR])
	}
	function BlockToString(B)				// returns textual Array definition of a 64-bit block
	{	return	"[0x" + ((B[0]&0xffffffff)+0x100000000).toString(16).slice(-8)
		+	",0x" + ((B[1]&0xffffffff)+0x100000000).toString(16).slice(-8)
		+	"]"
	}
	// next one was made for debugging, not exposed, currently not used, I leave it here for possible future use:
	function KStoStrings(KS)					// return array of hex strings of key schedule
	{	for (var i = 0, R = []; i < KS.length; i++)
		{	var KU = unsign(KS)
			R[i] = ("000000000000" + (KU[i][0]*0x10000 + (KU[i][1]>>>16)).toString(16)).slice(-12)
		}
		return R
	}

// methods to produce random stuff:
	function ranwrd()							// random 32-bit word - not exposed
	{	var W = Math.floor(Math.random()*0x100000000)&0xffffffff
		return (W < 0 ? W + 0x100000000 : W)
	}
	function RandomBlock(isKey)						// random 64-bit block - exposed
	{	var L = ranwrd(), R = ranwrd()
		if (isKey) {L &= 0xfefefefe, R &= 0xfefefefe}
		return unsign([L,R])
	}

// self-test stuff - not exposed:
	function isEqual(A,B)
	{	var M=0xffffffff
		return (A[0]&M) == (B[0]&M) && (A[1]&M) == (B[1]&M)
	}
	function check(M,C,K1,K2,K3)
	{	var n = arguments.length
		var P = (n==3 ? encipher(M,K1) : n==4 ? encipher(M,K1,K2) : encipher(M,K1,K2,K3) )
		var Q = (n==3 ? decipher(P,K1) : n==4 ? decipher(P,K1,K2) : decipher(P,K1,K2,K3) )
		var result = isEqual(Q,M)
		if (C.length > 0)
		{	result = result && isEqual(P,C)
			var R  = (n==3 ? decipher(C,K1) : n==4 ? decipher(C,K1,K2) : decipher(C,K1,K2,K3) )
			var S  = (n==3 ? encipher(R,K1) : n==4 ? encipher(R,K1,K2) : encipher(R,K1,K2,K3) )
			result = result && isEqual(R,M) && isEqual(S,C)
		}
		return result
	}
	function selftest()
	{	var e, x = []
		function ranblk(x) {return RandomBlock(x)}

			// first example  found in http://www.aci.net/kalliste/des.htm
			// other examples found in http://csrc.nist.gov/publications/fips/fips81/fips81.htm
		if(	!check([0x01234567,0x89abcdef],[0x85e81354,0x0f0ab405],[0x13345779,0x9bbcdff1])
		||	!check([0x4e6f7720,0x69732074],[0x3fa40e8a,0x984d4815],[0x01234567,0x89abcdef])
		||	!check([0x68652074,0x696d6520],[0x6a271787,0xab8883f9],[0x01234567,0x89abcdef])
		||	!check([0x666f7220,0x616c6c20],[0x893d51ec,0x4b563b53],[0x01234567,0x89abcdef])
		)							x.push("- predefined vectors")
		if (!check(ranblk(),[],ranblk(1)))			x.push("- random single DES")
		if (!check(ranblk(),[],ranblk(1),ranblk(1)))		x.push("- random triple DES (2 keys)")
		if (!check(ranblk(),[],ranblk(1),ranblk(1),ranblk(1)))	x.push("- random triple DES (3 keys)")

		var P  = [ranwrd(),ranwrd(),ranwrd(),ranwrd(),ranwrd(),ranwrd(),ranwrd(),ranwrd()]
		var IV = ranblk()
		var K1 = ranblk(1), Q1 = encipherChain(P,IV,K1      ), P1 = decipherChain(Q1,IV,K1      )
		var K2 = ranblk(1), Q2 = encipherChain(P,IV,K1,K2   ), P2 = decipherChain(Q2,IV,K1,K2   )
		var K3 = ranblk(1), Q3 = encipherChain(P,IV,K1,K2,K3), P3 = decipherChain(Q3,IV,K1,K2,K3)
		if (P1.toString() != P.toString()) x.push("- random single DES CBC")
		if (P2.toString() != P.toString()) x.push("- random triple DES CBC (2 keys)")
		if (P3.toString() != P.toString()) x.push("- random triple DES CBC (3 keys)")

		if (x.length > 0)
		{	x.unshift("Selftest" + (x.length > 1 ? "s" : "")
				+ " failed during creation of DESobject in hr$des.js:")
			try	{alert		(x.join("\n"))	}
			catch(e){WScript.Echo	(x.join("\n"))	}
			return false
		} else	return true
	}

// object initialisation:
	if (selftest())					// expose methods only if selftest succeeds
	{	this.encipher		= encipher
		this.decipher		= decipher
		this.encipherChain	= encipherChain
		this.decipherChain	= decipherChain
		this.StringToKey	= StringToKey
		this.BlockToString	= BlockToString
		this.RandomBlock	= RandomBlock
	}
}
var DES = new DESobject() // create a DES object, just 1 instance needed: only methods provided, no values stored

// =====================================================================================================================
// end of file hr$des.js, Copyright (c) 2005 Henk Reints (http://henk-reints.nl)

