/*
This file contains the declaration and implementation of two classes, 
class Package, and class Crypt.

Class Crypt is an implementation of a Fiestel cipher. It uses the helper class Package.
The user must also use class Package to use class Crypt.

As we can see, if we have a look at the constructor for class Crypt, 

	Crypt(int atype, Package akey, int numr);

it wants the type of crypting needed, that is, either

	Crypt::ENCRYPT;	or 
	Crypt::DECRYPT;

it wants a packages containing the key to use in either encryption or decryption.

and it wants to know how many rounds of enryption to use in the fiestel cipher.

now once we have created our instance of Crypt, we can encrypt, OR decrypt 
out text, a packet at a time, through the start member function.

The start function recieves a plain text packet, (or cipher text), and performs an operation on it, 
and passes the cipher text, or plain text respectively, back by reference through the same
packet argument.

have a look at the packet constructor to tell you how to construct a packet. 

For interests sake, the most important functions in this file, are the 

start, 
	and
f_func

	functions.

In studying of these two functions, you may see the main encryption algorithm in play.
DES is fiestel cipher, and might be coded in much the same way, except that this may
be a little inefficient because it is written for ease of coding and studying purposes, 
NOT speed.

*/



/*	changes since last version that worked well was turning real swaps to reference swaps
	This had the effect of decreasing to a small proportion the time taken to perform the operation
	Any change for the better is good, therefore, we keep it. */
/* recent mods include : 
			inclusion of copy constructor in Package
			
*/

#include "word.h"
#include "sxtoll.h"
#include <string.h>
#include "LinkedList.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#ifndef _CRYPT
#define _CRYPT



class Package{  // have a string constructor, and a HEX constructor

	public:
		
		static const int MAX_SIZE=8; // bytes


		Package(){
			for (int i=0;i<8;i++)
					eight_bytes[i]=0;
		}

		Package(const Package& other){
		
			for (int i=0;i<8;i++)
				eight_bytes[i]=other.eight_bytes[i];
		}
		
		
		int pack_hex(const char sixteen_hex[]){
			
			int i=0;
			int length = strlen(sixteen_hex);

			for (i=0;i<8;i++)
					eight_bytes[i]=0;

			if (length>16){
				return 0;
			}

			unsigned long long answer = sxtoll(sixteen_hex);
			unsigned char* store = (unsigned char*) &answer;

			for (i=0;i<8;i++){
				eight_bytes[i] = store[i];
			}
		return (answer>0);
		}



		int pack_str(const char up_to_eight_bytes[], int length){
			
			int i=0;

			for (i=0;i<8;i++)
				eight_bytes[i]=0;

			if (length>8){
				return 0;
			}

			for (i=0;i<length;i++){
				eight_bytes[i]=up_to_eight_bytes[i];
			}
		return 1;
		}


	private:

		char eight_bytes[8];

};



static const int SBOX[256] = {
 			99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 103,  43, 254, 215, 171, 118,
 			202, 130, 201, 125, 250,  89,  71, 240, 173, 212, 162, 175, 156, 164, 114, 192,
 			183, 253, 147,  38,  54,  63, 247, 204,  52, 165, 229, 241, 113, 216,  49,  21,
 			4, 199,  35, 195,  24, 150,   5, 154,   7,  18, 128, 226, 235,  39, 178, 117,
 			9, 131,  44,  26,  27, 110,  90, 160,  82,  59, 214, 179,  41, 227,  47, 132,
 			83, 209,   0, 237,  32, 252, 177,  91, 106, 203, 190,  57,  74,  76,  88, 207,
 			208, 239, 170, 251,  67,  77,  51, 133,  69, 249,   2, 127, 80, 60, 159, 168,
			 81, 163,  64, 143, 146, 157,  56, 245, 188, 182, 218,  33,  16, 255, 243, 210,
 			205,  12,  19, 236,  95, 151,  68,  23, 196, 167, 126,  61, 100, 93,  25, 115,
 			96, 129,  79, 220,  34,  42, 144, 136,  70, 238, 184,  20, 222,  94,  11, 219,
 			224,  50,  58,  10,  73,   6,  36,  92, 194, 211, 172,  98,145,149, 228, 121,
 			231, 200,  55, 109, 141, 213,  78, 169, 108,  86,244, 234, 101, 122, 174,   8,
 			186, 120,  37,  46,  28, 166, 180,198, 232, 221, 116,  31,  75, 189, 139, 138,
 			112,  62, 181, 102, 72,   3, 246,  14,  97,  53,  87, 185, 134, 193,  29, 158,
 			225, 248, 152,  17, 105, 217, 142, 148, 155,  30, 135, 233, 206,  85, 40, 223,
 			140, 161, 137,  13, 191, 230,  66, 104,  65, 153,  45,15, 176,  84, 187,  22
			};



class Crypt{

	public:
		static const int ENCRYPT=0;
		static const int DECRYPT=1;
	
		Crypt(int atype, Package akey, int numr);
		~Crypt();
		
		int start(Package* input);  //size of Packet is fixed at 8 bytes, and cipher is returned through this

	private:
		word f_func(word aword);
		word key_function(word kr);
		void next_key_round();
		void create_keys();

		int type;
		int nrounds;
		word w64;
		word key;	// this is the left, which is used as the 32 bit key
		word k64;	// this is the main 64 bit key which user enters
		word kright; 	// this is the other side which for round n, does not get used
				// but for n+1 will get used as 'key' after a swap
		LinkedList* list;
};


Crypt::Crypt(int atype, Package akey, int numr){
	
	type=atype;
	nrounds=numr; 
	
	// now organise 64 bit key
	
	k64.set((char*) &akey, 8);
	
	key.subword(k64,4,0);
	kright.subword(k64,4,1);
	key.vswap(kright);

	// create list of keys
	list = new LinkedList(type);
	create_keys();

}


Crypt::~Crypt(){
		delete list;
}



int Crypt::start(Package *input){

	word s32Left;
	word s32Right;

	w64.set((char*)input,Package::MAX_SIZE);  // put the packet in a word (what crypt uses)

			s32Left.subword(w64, 4,1);
			s32Right.subword(w64, 4, 0);

			// setup for encryption NOT decrypt 
			if (type==ENCRYPT){
				s32Left.rswap(s32Right);
			}
			else w64<<16;

			for(int i=0;i<nrounds;i++){	
				s32Left.rswap(s32Right);	
				s32Left^f_func(s32Right);
			}
			
			if ((nrounds%2)!=0){
				s32Left.vswap(s32Right);
			}

			if (type==ENCRYPT){		
				w64>>16;
			}
		
		w64.get_bytes((unsigned char*)input, Package::MAX_SIZE); 
		
	list->reset(); 
return 0;
}




// this is the encryption f function
word Crypt::f_func(word aword){

	word sub;
	word keysub;
	int value=0;
	char keyn[4]={0};
	
	list->iterate(keyn); // ask for a new key from schedule
	
	key.set(keyn, 4);
	
	for (int i=0;i<4;i++){
		sub.subword(aword, 1, i);
		keysub.subword(key, 1,i);
		value = SBOX[(int)sub.get_byte()];
		sub.insert_byte((unsigned char)value);
		sub^keysub;
	}
	
	aword>>2;
	
return aword;
}



word Crypt::key_function(word kr){

	~kr;  // complement
	kr<<2;
return kr;
}


void Crypt::create_keys(){

	for (int i =0;i<nrounds;i++){
		key.vswap(kright);
		key^key_function(kright);
		list->insert(key);
	}
}



#endif
