/********************************************************
* Programmed by: John Law                               *
* Date: July 12th 2012                                  *
* Purpose: Implementation of blowfish encryption        *
********************************************************/

#include <iostream>
#include <string.h>
#include <string>
#include "blowfish_consts.c"
#include <stdio.h>
using namespace std;

unsigned long long getNextBlock (int startingLocation, int blockSize, char source[]);
unsigned long long blowfishEncrypt (unsigned long long block, unsigned long *p, unsigned long *s0, unsigned long *s1, unsigned long *s2, unsigned long *s3, int reverse);
unsigned long long generateSubkeys(unsigned long *subkey, int sizeOfArray, unsigned long long zeroString, unsigned long *p, unsigned long *s0, unsigned long *s1, unsigned long *s2, unsigned long *s3);

int main (int argc, char *argv[])
{
	if (argc < 2)
	{
		cout << "Encryption key not provided. Please provide 16 hex characters as command argument." << endl;
		return (0);
	}

	//Get plaintext message
	char plaintextMessage[1000];
	cout << "Your message in plaintext: ";
	fgets(plaintextMessage, 1000, stdin);
	//remove the newlines
	int m = 0;
	for (m = 0; m < strlen(plaintextMessage); m++)
	{
		if ( plaintextMessage[m] == '\n' )
		{
			plaintextMessage[m] = '\0';
		}
	}
	
	//Split key into two 32 bit pieces
	unsigned long long fullKey = getNextBlock(0, 8, argv[1]);
	unsigned long keyLeft = fullKey >> 32;	
	unsigned long keyRight = fullKey & 0xFFFFFFFF;
	
	//initialize P
	int i = 0;
	for (i = 0; i < sizeof(parray)/sizeof(unsigned long); i++)
	{
		if ( i % 2 == 0 )
		{
			parray[i] = parray[i] ^ keyRight; 
		}
		else
		{
			parray[i] = parray[i] ^ keyLeft;
		}
	}
	
	//Generate the subkeys
	unsigned long long zeroString = 0;
	zeroString = generateSubkeys(parray, sizeof(parray)/sizeof(unsigned long), zeroString, parray, sbox0, sbox1, sbox2, sbox3);
	zeroString = generateSubkeys(sbox0, sizeof(sbox0)/sizeof(unsigned long), zeroString, parray, sbox0, sbox1, sbox2, sbox3);
	zeroString = generateSubkeys(sbox1, sizeof(sbox1)/sizeof(unsigned long), zeroString, parray, sbox0, sbox1, sbox2, sbox3);
	zeroString = generateSubkeys(sbox2, sizeof(sbox2)/sizeof(unsigned long), zeroString, parray, sbox0, sbox1, sbox2, sbox3);
	zeroString = generateSubkeys(sbox3, sizeof(sbox3)/sizeof(unsigned long), zeroString, parray, sbox0, sbox1, sbox2, sbox3);

	//Loop through each block in the message, encrypting and ouputing result to screen
	cout << endl << "Encrypted blocks: " << endl;
	int numOfBlocks = strlen(plaintextMessage) / 8 + 1;
	if (strlen(plaintextMessage) % 8 == 0)
	{
		numOfBlocks = strlen(plaintextMessage) / 8;
	}
	int location = 0;
	for (i = 0; i < numOfBlocks; i++)
	{
		cout << "Original block: " << hex << getNextBlock(location, 8, plaintextMessage) << " Encrypted block: ";
		unsigned long long encryptedBlock = blowfishEncrypt(getNextBlock(location, 8, plaintextMessage), parray, sbox0, sbox1, sbox2, sbox3, 0);
		cout << encryptedBlock << endl;
		location = location + 8;
	}
	return (0);
}

unsigned long long generateSubkeys(unsigned long *subkey, int sizeOfArray, unsigned long long zeroString, unsigned long *p, unsigned long *s0, unsigned long *s1, unsigned long *s2, unsigned long *s3)
{
	int i = 0;
	for (i = 0; i < sizeOfArray; i++)
	{
		zeroString = blowfishEncrypt(zeroString, p, s0, s1, s2, s3, 0);
		subkey[i] = zeroString;
		subkey[i++] = zeroString;
	}
	return zeroString;	
}

unsigned long long blowfishEncrypt (unsigned long long block, unsigned long *p, unsigned long *s0, unsigned long *s1, unsigned long *s2, unsigned long *s3, int reverse)
{
	unsigned long long encryptedBlock = 0;
	//Split 64 bit block into two 32 bit pieces
	unsigned long leftPiece = block >> 32;
	unsigned long swap = 0;
	unsigned long rightPiece = block & 0xFFFFFFFF;
	int start = 0, end = 16;

	if (reverse == 1)
	{
		start = 18;
		end = 0;
	}

	//Blowfish encryption rounds
	int i = 0;
	int a = 0, b = 0, c = 0, d = 0;
	//Decryption
	if (reverse == 1)
	{
		for (i = start; i > end; i--)
		{
			leftPiece = leftPiece ^ p[i];
			//split leftPiece into four bytes to feed its encryption function
			d = leftPiece & 0xFF;
 			c = (leftPiece >> 8) & 0xFF;
			b = (leftPiece >> 16) & 0xFF;
			a = leftPiece >> 24;
			rightPiece = ((s0[a] + s1[b] % 2^32) ^ (s2[c] + s3[d] % 2^32)) ^ rightPiece;
			swap = leftPiece;
			leftPiece = rightPiece;
			rightPiece = swap;
		}
		swap = leftPiece;
		leftPiece = rightPiece;
		rightPiece = swap;
		rightPiece = rightPiece ^ p[1];
		leftPiece = leftPiece ^ p[0];
	}
	//Encryption
	else
	{
		for (i = start; i < end; i++)
		{
			leftPiece = leftPiece ^ p[i];
			//split leftPiece into four bytes to feed its encryption function
			d = leftPiece & 0xFF;
			c = (leftPiece >> 8) & 0xFF;
			b = (leftPiece >> 16) & 0xFF;
			a = leftPiece >> 24;
			rightPiece = ((s0[a] + s1[b] % 2^32) ^ (s2[c] + s3[d] % 2^32)) ^ rightPiece;
			swap = leftPiece;
			leftPiece = rightPiece;
			rightPiece = swap;	
		}
		swap = leftPiece;
		leftPiece = rightPiece;
		rightPiece = swap;
		rightPiece = rightPiece ^ p[16];
		leftPiece = leftPiece ^ p[17];
	}
	
	//recombine the two 32 bit pieces
	encryptedBlock = (unsigned long long)leftPiece << 32 | rightPiece;
	return encryptedBlock;
}

unsigned long long getNextBlock (int startingLocation, int blockSize, char source[])
{
	int i = 0;
	unsigned long long block = 0;

	for (i = 0; i < blockSize; i++)
	{
		block <<= blockSize;
		block |= (startingLocation < strlen (source) ? source[startingLocation++] : '\0');
	}

	return block;
}