/****************************************************************************
 * 
 * Author: Justin Samuel <justin /at/ justinsamuel /dot/ com>
 * Date: 2008-04-25
 * License: GPL
 * 
 * DES implementation. Based off of the public domain DES code
 * in "Applied Cryptography" (Schneier). ECB mode is used.
 *  
 ****************************************************************************/

var EN0 = 0      /* MODE == encrypt */
var DE1 = 1      /* MODE == decrypt */

/* static unsigned short bytebit[8] */
var bytebit    = [
       0200, 0100, 040, 020, 010, 04, 02, 01 ];

/* static unsigned long bigbyte[24] */
var bigbyte = [
       0x800000,    0x400000,     0x200000,    0x100000,
       0x80000,     0x40000,      0x20000,     0x10000,
       0x8000,      0x4000,       0x2000,      0x1000,
       0x800,       0x400,        0x200,       0x100,
       0x80,        0x40,         0x20,        0x10,
       0x8,         0x4,          0x2,         0x1   ];

/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */

/* JS: ANSI X3.92-1981 is identical to FIPS 46-1, which is described here:
 * http://www.itl.nist.gov/fipspubs/fip46-2.htm */

/* JS: initial key permutation, which leaves out the parity bits 
 * (Schneier Table 12.2) */
/* static unsigned char pc1[56] */
var pc1 = [
       56, 48, 40, 32, 24, 16,  8,   0, 57, 49, 41, 33, 25, 17,
        9,  1, 58, 50, 42, 34, 26,  18, 10,  2, 59, 51, 43, 35,
       62, 54, 46, 38, 30, 22, 14,   6, 61, 53, 45, 37, 29, 21,
       13,  5, 60, 52, 44, 36, 28,  20, 12,  4, 27, 19, 11,  3 ];

/* JS: key bits shift per round (Schneier Table 12.3) */
/* static unsigned char totrot[16] */
var totrot = [
       1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 ];

/* JS: key compression permutation (a.k.a. permuted choice), 56 to 48 bits, 
 * before XOR (Schneier Table 12.4) */
/* static unsigned char pc2[48] */
var pc2 = [
       13, 16, 10, 23,  0,  4,       2, 27, 14,  5, 20,  9,
       22, 18, 11,  3, 25,  7,      15,  6, 26, 19, 12,  1,
       40, 51, 30, 36, 46, 54,      29, 39, 50, 44, 32, 47,
       43, 48, 38, 55, 33, 52,      45, 41, 49, 35, 28, 31 ];


/* JS: SP1 through SP8 are each combinations of an individual S-box
 * substitution (SP1 uses S-box 1) along with the P-box each is followed by,
 * which is the same each time (Schneier Tables 12.6 and 12.7) */
// static unsigned long SP1[64]
SP1 = [
       0x01010400, 0x00000000, 0x00010000, 0x01010404,
       0x01010004, 0x00010404, 0x00000004, 0x00010000,
       0x00000400, 0x01010400, 0x01010404, 0x00000400,
       0x01000404, 0x01010004, 0x01000000, 0x00000004,
       0x00000404, 0x01000400, 0x01000400, 0x00010400,
       0x00010400, 0x01010000, 0x01010000, 0x01000404,
       0x00010004, 0x01000004, 0x01000004, 0x00010004,
       0x00000000, 0x00000404, 0x00010404, 0x01000000,
       0x00010000, 0x01010404, 0x00000004, 0x01010000,
       0x01010400, 0x01000000, 0x01000000, 0x00000400,
       0x01010004, 0x00010000, 0x00010400, 0x01000004,
       0x00000400, 0x00000004, 0x01000404, 0x00010404,
       0x01010404, 0x00010004, 0x01010000, 0x01000404,
       0x01000004, 0x00000404, 0x00010404, 0x01010400,
       0x00000404, 0x01000400, 0x01000400, 0x00000000,
       0x00010004, 0x00010400, 0x00000000, 0x01010004 ];

// static unsigned long SP2[64]
SP2 = [
       0x80108020, 0x80008000, 0x00008000, 0x00108020,
       0x00100000, 0x00000020, 0x80100020, 0x80008020,
       0x80000020, 0x80108020, 0x80108000, 0x80000000,
       0x80008000, 0x00100000, 0x00000020, 0x80100020,
       0x00108000, 0x00100020, 0x80008020, 0x00000000,
       0x80000000, 0x00008000, 0x00108020, 0x80100000,
       0x00100020, 0x80000020, 0x00000000, 0x00108000,
       0x00008020, 0x80108000, 0x80100000, 0x00008020,
       0x00000000, 0x00108020, 0x80100020, 0x00100000,
       0x80008020, 0x80100000, 0x80108000, 0x00008000,
       0x80100000, 0x80008000, 0x00000020, 0x80108020,
       0x00108020, 0x00000020, 0x00008000, 0x80000000,
       0x00008020, 0x80108000, 0x00100000, 0x80000020,
       0x00100020, 0x80008020, 0x80000020, 0x00100020,
       0x00108000, 0x00000000, 0x80008000, 0x00008020,
       0x80000000, 0x80100020, 0x80108020, 0x00108000 ];

// static unsigned long SP3[64]
SP3 = [
       0x00000208, 0x08020200, 0x00000000, 0x08020008,
       0x08000200, 0x00000000, 0x00020208, 0x08000200,
       0x00020008, 0x08000008, 0x08000008, 0x00020000,
       0x08020208, 0x00020008, 0x08020000, 0x00000208,
       0x08000000, 0x00000008, 0x08020200, 0x00000200,
       0x00020200, 0x08020000, 0x08020008, 0x00020208,
       0x08000208, 0x00020200, 0x00020000, 0x08000208,
       0x00000008, 0x08020208, 0x00000200, 0x08000000,
       0x08020200, 0x08000000, 0x00020008, 0x00000208,
       0x00020000, 0x08020200, 0x08000200, 0x00000000,
       0x00000200, 0x00020008, 0x08020208, 0x08000200,
       0x08000008, 0x00000200, 0x00000000, 0x08020008,
       0x08000208, 0x00020000, 0x08000000, 0x08020208,
       0x00000008, 0x00020208, 0x00020200, 0x08000008,
       0x08020000, 0x08000208, 0x00000208, 0x08020000,
       0x00020208, 0x00000008, 0x08020008, 0x00020200 ];

// static unsigned long SP4[64]       
SP4 = [
       0x00802001, 0x00002081, 0x00002081, 0x00000080,
       0x00802080, 0x00800081, 0x00800001, 0x00002001,
       0x00000000, 0x00802000, 0x00802000, 0x00802081,
       0x00000081, 0x00000000, 0x00800080, 0x00800001,
       0x00000001, 0x00002000, 0x00800000, 0x00802001,
       0x00000080, 0x00800000, 0x00002001, 0x00002080,
       0x00800081, 0x00000001, 0x00002080, 0x00800080,
       0x00002000, 0x00802080, 0x00802081, 0x00000081,
       0x00800080, 0x00800001, 0x00802000, 0x00802081,
       0x00000081, 0x00000000, 0x00000000, 0x00802000,
       0x00002080, 0x00800080, 0x00800081, 0x00000001,
       0x00802001, 0x00002081, 0x00002081, 0x00000080,
       0x00802081, 0x00000081, 0x00000001, 0x00002000,
       0x00800001, 0x00002001, 0x00802080, 0x00800081,
       0x00002001, 0x00002080, 0x00800000, 0x00802001,
       0x00000080, 0x00800000, 0x00002000, 0x00802080 ];

// static unsigned long SP5[64]       
SP5 = [
       0x00000100, 0x02080100, 0x02080000, 0x42000100,
       0x00080000, 0x00000100, 0x40000000, 0x02080000,
       0x40080100, 0x00080000, 0x02000100, 0x40080100,
       0x42000100, 0x42080000, 0x00080100, 0x40000000,
       0x02000000, 0x40080000, 0x40080000, 0x00000000,
       0x40000100, 0x42080100, 0x42080100, 0x02000100,
       0x42080000, 0x40000100, 0x00000000, 0x42000000,
       0x02080100, 0x02000000, 0x42000000, 0x00080100,
       0x00080000, 0x42000100, 0x00000100, 0x02000000,
       0x40000000, 0x02080000, 0x42000100, 0x40080100,
       0x02000100, 0x40000000, 0x42080000, 0x02080100,
       0x40080100, 0x00000100, 0x02000000, 0x42080000,
       0x42080100, 0x00080100, 0x42000000, 0x42080100,
       0x02080000, 0x00000000, 0x40080000, 0x42000000,
       0x00080100, 0x02000100, 0x40000100, 0x00080000,
       0x00000000, 0x40080000, 0x02080100, 0x40000100 ];

// static unsigned long SP6[64]
SP6 = [
       0x20000010, 0x20400000, 0x00004000, 0x20404010,
       0x20400000, 0x00000010, 0x20404010, 0x00400000,
       0x20004000, 0x00404010, 0x00400000, 0x20000010,
       0x00400010, 0x20004000, 0x20000000, 0x00004010,
       0x00000000, 0x00400010, 0x20004010, 0x00004000,
       0x00404000, 0x20004010, 0x00000010, 0x20400010,
       0x20400010, 0x00000000, 0x00404010, 0x20404000,
       0x00004010, 0x00404000, 0x20404000, 0x20000000,
       0x20004000, 0x00000010, 0x20400010, 0x00404000,
       0x20404010, 0x00400000, 0x00004010, 0x20000010,
       0x00400000, 0x20004000, 0x20000000, 0x00004010,
       0x20000010, 0x20404010, 0x00404000, 0x20400000,
       0x00404010, 0x20404000, 0x00000000, 0x20400010,
       0x00000010, 0x00004000, 0x20400000, 0x00404010,
       0x00004000, 0x00400010, 0x20004010, 0x00000000,
       0x20404000, 0x20000000, 0x00400010, 0x20004010 ];

// static unsigned long SP7[64]
SP7 = [
       0x00200000, 0x04200002, 0x04000802, 0x00000000,
       0x00000800, 0x04000802, 0x00200802, 0x04200800,
       0x04200802, 0x00200000, 0x00000000, 0x04000002,
       0x00000002, 0x04000000, 0x04200002, 0x00000802,
       0x04000800, 0x00200802, 0x00200002, 0x04000800,
       0x04000002, 0x04200000, 0x04200800, 0x00200002,
       0x04200000, 0x00000800, 0x00000802, 0x04200802,
       0x00200800, 0x00000002, 0x04000000, 0x00200800,
       0x04000000, 0x00200800, 0x00200000, 0x04000802,
       0x04000802, 0x04200002, 0x04200002, 0x00000002,
       0x00200002, 0x04000000, 0x04000800, 0x00200000,
       0x04200800, 0x00000802, 0x00200802, 0x04200800,
       0x00000802, 0x04000002, 0x04200802, 0x04200000,
       0x00200800, 0x00000000, 0x00000002, 0x04200802,
       0x00000000, 0x00200802, 0x04200000, 0x00000800,
       0x04000002, 0x04000800, 0x00000800, 0x00200002 ];

// static unsigned long SP8[64]
SP8 = [
       0x10001040, 0x00001000, 0x00040000, 0x10041040,
       0x10000000, 0x10001040, 0x00000040, 0x10000000,
       0x00040040, 0x10040000, 0x10041040, 0x00041000,
       0x10041000, 0x00041040, 0x00001000, 0x00000040,
       0x10040000, 0x10000040, 0x10001000, 0x00001040,
       0x00041000, 0x00040040, 0x10040040, 0x10041000,
       0x00001040, 0x00000000, 0x00000000, 0x10040040,
       0x10000040, 0x10001000, 0x00041040, 0x00040000,
       0x00041040, 0x00040000, 0x10041000, 0x00001000,
       0x00000040, 0x10040040, 0x00001000, 0x00041040,
       0x10001000, 0x00000040, 0x10000040, 0x10040000,
       0x10040040, 0x10000000, 0x00040000, 0x10001040,
       0x00000000, 0x10041040, 0x00040040, 0x10000040,
       0x10040000, 0x10001000, 0x10001040, 0x00000000,
       0x10041040, 0x00041000, 0x00041000, 0x00001040,
       0x00001040, 0x00040040, 0x10000000, 0x10041000 ];

              
/*
 void deskey(key, edf) 
 unsigned char *key; 
 short edf; {
 */
function des_roundkeys(key, edf) {

        /*
		register int i, j, l, m, n; 
		unsigned char pc1m[56], pcr[56]; 
		unsigned long kn[32];
		*/

       var pc1m = new Array(56);
       var pcr = new Array(56);
       var kn = new Array(32);
       
       for ( var j=0, l, m, loc_bytebit=bytebit, loc_pc1=pc1; j < 56; j++ ) {
              l = loc_pc1[j];
              m = l & 07;
              pc1m[j] = (key[l >>> 3] & loc_bytebit[m]) ? 1 : 0;
              }
       for( var i=0, m, n, cur_totrot, loc_totrot=totrot, loc_kn=kn, loc_DE1=DE1; i < 16; i++ ) {
       	      cur_totrot = loc_totrot[i];
              if( edf == loc_DE1 ) m = (15 - i) << 1;
              else m = i << 1;
              n = m + 1;
              loc_kn[m] = loc_kn[n] = 0;
              for( var j=0, l, loc_pcr=pcr; j < 28; j++ ) {
                     l = j + cur_totrot;
                     if( l < 28 ) loc_pcr[j] = pc1m[l];
                     else loc_pcr[j] = pc1m[l - 28];
                     }
              for( var j=28, l, loc_pcr=pcr; j < 56; j++ ) {
                  l = j + cur_totrot;
                  if( l < 56 ) loc_pcr[j] = pc1m[l];
                  else loc_pcr[j] = pc1m[l - 28];
                  }
              for( var j = 0, loc_pcr=pcr, loc_pc2=pc2, loc_bigbyte=bigbyte; j < 24; j++ ) {
                     if( loc_pcr[loc_pc2[j]] ) loc_kn[m] |= loc_bigbyte[j];
                     if( loc_pcr[loc_pc2[j+24]] ) loc_kn[n] |= loc_bigbyte[j];
                     }
              }
              
       return cookey(kn);
}

/*
 static void cookey(raw1)
 register unsigned long *raw1;
 {
 */
function cookey(raw)
{
	  /*
       register unsigned long *cook, *raw0;
       unsigned long dough[32];
       register int i;
      */ 
       var dough = new Array(32);

       for( var i=0, loc_dough=dough, loc_raw=raw, raw0, raw1, cook; i < 32; i+=2 ) {
              raw0   = loc_raw[i];
              raw1   = loc_raw[i+1]
              cook   = (raw0 & 0x00fc0000) << 6;
              cook  |= (raw0 & 0x00000fc0) << 10;
              cook  |= (raw1 & 0x00fc0000) >>> 10;
              cook  |= (raw1 & 0x00000fc0) >>> 6;
              loc_dough[i] = cook;
              cook   = (raw0 & 0x0003f000) << 12;
              cook  |= (raw0 & 0x0000003f) << 16;
              cook  |= (raw1 & 0x0003f000) >>> 4;
              cook  |= (raw1 & 0x0000003f);
              loc_dough[i+1] = cook;
        }

        return dough;
}

/*
static void scrunch(outof, into)
register unsigned char *outof;
register unsigned long *into;
*/
function scrunch (outof, into)
{
	   into[0]  = (outof[0] & 0xff) << 24;
       into[0] |= (outof[1] & 0xff) << 16;
       into[0] |= (outof[2] & 0xff) << 8;
       into[0] |= (outof[3] & 0xff);
       into[1]  = (outof[4] & 0xff) << 24;
       into[1] |= (outof[5] & 0xff) << 16;
       into[1] |= (outof[6] & 0xff) << 8;
       into[1] |= (outof[7] & 0xff);
       return;
}

/*
static void unscrun(outof, into)
register unsigned long *outof;
register unsigned char *into;
*/
function unscrun(outof, into)
{
       into[0] = (outof[0] >>> 24) & 0xff;
       into[1] = (outof[0] >>> 16) & 0xff;
       into[2] = (outof[0] >>>  8) & 0xff;
       into[3] =  outof[0]         & 0xff;
       into[4] = (outof[1] >>> 24) & 0xff;
       into[5] = (outof[1] >>> 16) & 0xff;
       into[6] = (outof[1] >>>  8) & 0xff;
       into[7] =  outof[1]         & 0xff;
       return;
}

/* JS: do the real encryption/decryption work */
/*
static void desfunc(block, keys)
register unsigned long *block, *keys;
*/
function desfunc(block, keys)
{
	/*
       register unsigned long fval, work, right, leftt;
       register int round;
    */
	   var work, right, leftt;
	
       leftt = block[0];
       right = block[1];
       work = ((leftt >>> 4) ^ right) & 0x0f0f0f0f;
       right ^= work;
       leftt ^= (work << 4);
       work = ((leftt >>> 16) ^ right) & 0x0000ffff;
       right ^= work;
       leftt ^= (work << 16);
       work = ((right >>> 2) ^ leftt) & 0x33333333;
       leftt ^= work;
       right ^= (work << 2);
       work = ((right >>> 8) ^ leftt) & 0x00ff00ff;
       leftt ^= work;
       right ^= (work << 8);
       right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff;
       work = (leftt ^ right) & 0xaaaaaaaa;
       leftt ^= work;
       right ^= work;
       leftt = ((leftt << 1) | ((leftt >>> 31) & 1)) & 0xffffffff;

       /* JS: perform the 16 rounds of DES -- each iteration does
        *     two rounds*/
       for( var round = 0, fval, work,
            loc_SP1=SP1, loc_SP2=SP2, loc_SP3=SP3, loc_SP4=SP4, 
            loc_SP5=SP5, loc_SP6=SP6, loc_SP7=SP7, loc_SP8=SP8
            ; round < 8; round++ ) {
              work  = (right << 28) | (right >>> 4);
              work ^= keys[4*round];
              fval  = loc_SP7[ work         & 0x3f];
              fval |= loc_SP5[(work >>>  8) & 0x3f];
              fval |= loc_SP3[(work >>> 16) & 0x3f];
              fval |= loc_SP1[(work >>> 24) & 0x3f];
              work  = right ^ keys[4*round+1];
              fval |= loc_SP8[ work         & 0x3f];
              fval |= loc_SP6[(work >>>  8) & 0x3f];
              fval |= loc_SP4[(work >>> 16) & 0x3f];
              fval |= loc_SP2[(work >>> 24) & 0x3f];
              leftt ^= fval;
              work  = (leftt << 28) | (leftt >>> 4);
              work ^= keys[4*round+2];
              fval  = loc_SP7[ work         & 0x3f];
              fval |= loc_SP5[(work >>>  8) & 0x3f];
              fval |= loc_SP3[(work >>> 16) & 0x3f];
              fval |= loc_SP1[(work >>> 24) & 0x3f];
              work  = leftt ^ keys[4*round+3];
              fval |= loc_SP8[ work         & 0x3f];
              fval |= loc_SP6[(work >>>  8) & 0x3f];
              fval |= loc_SP4[(work >>> 16) & 0x3f];
              fval |= loc_SP2[(work >>> 24) & 0x3f];
              right ^= fval;
       }

       right = (right << 31) | (right >>> 1);
       work = (leftt ^ right) & 0xaaaaaaaa;
       leftt ^= work;
       right ^= work;
       leftt = (leftt << 31) | (leftt >>> 1);
       work = ((leftt >>> 8) ^ right) & 0x00ff00ff;
       right ^= work;
       leftt ^= (work << 8);
       work = ((leftt >>> 2) ^ right) & 0x33333333;
       right ^= work;
       leftt ^= (work << 2);
       work = ((right >>> 16) ^ leftt) & 0x0000ffff;
       leftt ^= work;
       right ^= (work << 16);
       work = ((right >>> 4) ^ leftt) & 0x0f0f0f0f;
       leftt ^= work;
       right ^= (work << 4);
       block[0] = right;
       block[1] = leftt;
       return;
}


/*********************************************************************
 * 
 * Direct usage utility functions.
 *  
 *********************************************************************/

/*
 * Ensures the parity bits of the key are set properly. This isn't
 * required for encryption/decrytion by des_encrypt() or des_decrypt(),
 * but it can be useful for dealing with other tools that do require
 * the parity bits to be set correctly.
 */
function des_setparity(key) {
	for (var i = 0; i < 8; i++) {
		var a = key[i];
		parity_bit = (((a>>7)&1) + ((a>>6)&1) + ((a>>5)&1) + ((a>>4)&1)
		           + ((a>>3)&1) + ((a>>2)&1) + ((a>>1)&1) + 1) % 2;
		key[i] = parity_bit ? key[i] | 1 : key[i] & 0xfe;
	}
}

/*********************************************************************
 * 
 * Direct usage functions for des encryption and decryption of
 * blocks of data.
 *  
 *********************************************************************/


/* Encrypt several blocks in ECB mode.  Caller is responsible for
   short blocks. */
/* 
void des_enc(des_ctx *dc, unsigned char *data, int blocks)
*/
function des_encrypt(key, data, blocks)
{
 	/*
    unsigned long work[2];
    int i;
    unsigned char *cp;
    cp = data;
    */
    var roundkeys = des_roundkeys(key, EN0);
    var work = new Array(2);
    var current;

    for (var i = 0; i < blocks; i++){
    	current = data.slice(i*8, i*8+8);
        scrunch(current, work);
        desfunc(work, roundkeys);
        unscrun(work, current);
        for (var j = 0; j < 8; j++) {
            data[i*8+j] = current[j];
        }
    }
        
}

/*
void des_dec(des_ctx *dc, unsigned char *data, int blocks)
*/
function des_decrypt(key, data, blocks) {
	/*
        unsigned long work[2];
        int i;
        unsigned char *cp;
    */
	var roundkeys = des_roundkeys(key, DE1);
	var work = new Array(2);
	var current;
	
    for (var i = 0; i < blocks; i++){
        current = data.slice(i*8, i*8+8);
        scrunch(current, work);
        desfunc(work, roundkeys);
        unscrun(work, current);
        for (var j = 0; j < 8; j++) {
            data[i*8+j] = current[j];
        }
    }
        
}


/* Validation sets:
 *
 * Single-length key, single-length plaintext -
 * Key    : 0123 4567 89ab cdef
 * Plain  : 0123 4567 89ab cde7
 * Cipher : c957 4425 6a5e d31d
 *
 **********************************************************************/

/*
 * These tests use the same data the the tests from the Schneier/public domain
 * des.c which was used as a basis for des.js.
 * 
 * This relies on methods from descrack_ui.js and a div with id 
 * "desCrackOutput". Included here to keep this together with the core des 
 * code, so that des.js stays similar to des.c.
 */
function test() {

    setOutputDiv(document.getElementById('desCrackOutput'));
        
    /*
    unsigned long data[10];
    unsigned char *cp,key[8] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
    unsigned char x[8] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xe7};
    */
        
    var data = [];
    var key = [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef];
    var x = [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xe7];
    var i;

    output("<h2>Unencrypted</h2>");
    printHexArray(x);
    
    des_encrypt(key, x, 1);
    output("<h2>Encrypted</h2>");
    printHexArray(x);
    
    des_decrypt(key, x, 1);
    output("<h2>Decrypted</h2>");
    printHexArray(x);
    
    for (i = 0; i < 10; i++) {
        data[4*i] = i;
        data[4*i+1] = 0;
        data[4*i+2] = 0;
        data[4*i+3] = 0;
    }
    
    // Note that byte order on which des.c is tested will affect the display
    // when using it. As this is just showing single bytes in the same order
    // as in the array, no interpretation as ints, it will be in the order
    // of the array elements.
    des_encrypt(key, data, 5);
    output("<h2>Encrypted</h2>");
    printHexArray(data);
    
    des_decrypt(key, data, 5);
    output("<h2>Decrypted</h2>");
    printHexArray(data);
    
}
