
// My (slow) implementation of DES

// Currently set for 3 rounds with no IP or inverse IP perms.
// The plaintext is 748502CD 38451097 and the key is 1a624c89 520dec46,
//    just like in the first line of Example 3.3 of Stinson (p. 95)

// Copyright 2005 by John Black
//
// Permission is granted to students of CSCI 7000 at CU to use, modify,
//   and freely take pieces of this code for use in homework.



#define NUMRNDS 3

// Set these to 1 to turn them on; 0 turns them off
#define APPLY_IP   0
#define APPLY_IPI  0
#define FINAL_REVERSE 0

main()
{
    int pt[2]={0x748502CD, 0x38451097};
    int ct[2];
    int key[2]={0x1a624c89, 0x520dec46};

    des_encrypt(pt, ct, key);
    printf("Ciphertext: %08x, %08x\n", ct[0], ct[1]);
}

// the expansion function E()
char exp[]={  0,
	     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};

// these permutations, pc1 and pc2, are used in key scheduling
static unsigned char pc1[] = { 0,
       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 };

static unsigned char pc2[] = { 0, 
       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 };

// The S-boxes (the heart of DES)
//
static unsigned char s[][64] = {
{ // S1
  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 },
{ // S2
  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 },
{ // S3
  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 },
{ // S4
   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 },
{ // S5
   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 },
{ // S6
  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 },
{ // S7
   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 },
{ // S8
  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 }
};

// the permutation P is applied after the S-boxes
static char p[] = { 0,
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 };

des_encrypt(int *pt, int *ct, int *key)
{
    char ip[65];
    char ipi[65];
    char pta[65], ptb[65];
    char cta[65];
    char ex[49], rk[49];
    char rval[33], mask[33], temp[33];
    int  i, j;
    int  x=0;
    int  y=8;
    int  round = 1;


    // set up ip and ip inverse
    for (i=1; i <= 64; i += 2)
    {
	ip[((x+4) << 3) + y] = i; ip[(x << 3) + y] = i+1;
	ipi[i] = ((x+4) << 3) + y; ipi[i+1] = (x << 3) + y;

	x++;
	if (i % 8 == 7) { y--; x=0; }
    }

    // to avoid horrendous bit masking we break everything into char arrays
    unpack(pt, pta);

    // apply ip to the plaintext pta to get ptb
    if (APPLY_IP)
    {
	for (i = 1; i <= 64; i++)
	    ptb[i] = pta[ip[i]];
	printf("After IP: "); dump(ptb, 64);
    }
    else
	for (i = 1; i <= 64; i++)
	    ptb[i] = pta[i];

    for (round=1; round <= NUMRNDS; round++)
    {
        printf("\n\n*** Round %d:\n", round);
	// apply the expansion function E() to get ex from ptb's right half
	for (i = 1; i <= 48; i++)
	    ex[i] = ptb[exp[i]+32];
	printf("Expansion: "); dump(ex, 48);

	// get key for this round
	getkey(key, rk, round);
	printf("Round Key: "); dump(rk, 48);
	
	// xor the round key rk into the exanded right half ex
	for (i = 1; i <= 48; i++)
	    ex[i] ^= rk[i];
	printf("XOR output: ");  dump(ex, 48);

	// and now apply the 8 S-boxes
	for (i = 1; i <= 8; i++)
	{
	    int sval;
	    int j;

	    j = (i-1)*6;
	    sval = s[i-1][(ex[j+1]*2+ex[j+6])*16 +
		    ex[j+2]*8 + ex[j+3]*4 + ex[j+4]*2 + ex[j+5]];
	    for (j=4; j >= 1; j--)
	    {
		rval[(i-1)*4 + j] = (sval & 1);
		sval >>= 1;
	    }
	}
	printf("S-Box output: "); dump(rval, 32);

	// finally apply the P permutation
	for (i = 1; i <= 32; i++)
	    mask[i] = rval[p[i]];
	printf("Mask Value: "); dump(mask, 32);

	// now we do the Feistel dance: move right side of ptb to
	// the left, then store mask xor that left side in the right
	for (i = 1; i <= 32; i++)
	    temp[i] = ptb[i];        // copy the left side
	for (i = 1; i <= 32; i++)
	    ptb[i] = ptb[i+32];      // move right half to left
	for (i = 1; i <= 32; i++)
	    ptb[i+32] = temp[i] ^ mask[i];
	printf("Round Output: "); dump(ptb, 64);
    }
    // Almost done; now apply IP^-1 to ptb (with halves reversed)
    if (FINAL_REVERSE)
    {
	for (i=1; i <= 32; i++)
	    temp[i] = ptb[i];      // copy the left half
	for (i = 1; i <= 32; i++)
	    ptb[i] = ptb[i+32];    // move right half to left
	for (i=1; i <= 32; i++)
	    ptb[i+32] = temp[i];   // put right half back 
    }
    
    if (APPLY_IPI)
	for (i=1; i <= 64; i++)
	    cta[i] = ptb[ipi[i]];
    else
	for (i=1; i <= 64; i++)
	    cta[i] = ptb[i];

    printf("\n\nCiphertext: "); dump(cta, 64);

    pack(ct, cta);
    
}

getkey(int *key, char *rk, int round)
{
    char t[65], s[57];
    int i;
    char p;
    int k;

    // we transfer k1 and k2 (the key) into the t array
    unpack(key, t);

    // now apply the pc1 permutation
    for (i=1; i <= 56; i++)
        s[i] = t[pc1[i]];

    // next circular shift each half once for i up to the round number
    // but double shift all except 1, 2, 9, and 16 
    for (k=1; k <= round; k++)
    {
	p = s[1]; for (i=1; i <=27; i++)  s[i] = s[i+1]; s[28] = p;
	p = s[29]; for (i=29; i <=55; i++)  s[i] = s[i+1]; s[56] = p;
	if (k == 1  ||  k == 2  ||  k == 9  ||  k == 16) continue;
	p = s[1]; for (i=1; i <=27; i++)  s[i] = s[i+1]; s[28] = p;
	p = s[29]; for (i=29; i <=55; i++)  s[i] = s[i+1]; s[56] = p;
    }

    // finally, apply pc2 to these 56 bits to produce the 48-bit round key
    for (i=1; i <= 48; i++)
        rk[i] = s[pc2[i]];

}

// this routine unpacks 64-bit values into a char array
unpack(int *pt, char *ca)
{
    int i;
    int a = pt[0];
    int b = pt[1];

    for (i=32; i >= 1; i--)
    {
	ca[i] = (a & 1);
	a >>= 1;
	ca[i+32] = (b & 1);
	b >>= 1;
    }

    //dump(ca, 64);
}

// this routine packs a 0-1 char array into two 32-bit ints
pack(int *ct, char *ca)
{
    int i;

    ct[0] = ct[1] = 0;

    for (i=1; i <= 32; i++)
    {
	ct[0] <<= 1;        ct[0] += ca[i];  
	ct[1] <<= 1;        ct[1] += ca[i+32];  
    }
}

// dump a 0-1 char array for debugging purposes
dump(char *ca, int len)
{
    int i;
    for (i=1; i <= len; i++)
    {
        printf("%d", ca[i]);
	if (i % 4 == 0) printf(" ");
    }
    printf("\n");
}


