#include "enc_lib.h"

#define BUFLEN 8192

unsigned char* readFile(char * file,int *readLen);
unsigned char* addString(unsigned char *destString, int destLen, const unsigned char *addString, int addLen);

int do_encryption(char* input_file_path, char* output_file_path, int is_encrypt)
{
	BIO* errBIO = NULL;
	BIO* outBIO = NULL;
	BIO* encBIO = NULL;

	unsigned char salt[8] = {1, 2, 3, 4, 5, 6, 7, 8};
	unsigned char key[EVP_MAX_KEY_LENGTH];
	unsigned char iv[EVP_MAX_IV_LENGTH];
	char pass[]	= "skbitstr";

	const EVP_CIPHER* cipher = NULL;

	//	Initialize crypto library
	ERR_load_crypto_strings(); 
	OpenSSL_add_all_ciphers();

	cipher = EVP_get_cipherbyname(CIPHER);
	if (cipher == NULL)
	{
		printf("[ERROR] Failed to get cipher.\n");
		return -1;
	}

	//	Create error IO
	if ((errBIO = BIO_new(BIO_s_file())) != NULL)
	{
		BIO_set_fp(errBIO, stderr, BIO_NOCLOSE | BIO_FP_TEXT);
	}

	//	Create output BIO
	outBIO = BIO_new_file(output_file_path, "wb");
	if (!outBIO)
	{
		BIO_printf(errBIO, "[ERROR] Failed to create output BIO: [%s]", output_file_path);
		ERR_print_errors(errBIO);
		return -1;
	}

	//	Create cipher BIO
	encBIO = BIO_new(BIO_f_cipher());
	if (encBIO == NULL)
	{
		BIO_printf(errBIO, "[ERROR] Failed to create cipher BIO.");
		ERR_print_errors(errBIO);
		return -1;
	}

	//	Create encryption key and IV using password
	EVP_BytesToKey(cipher, EVP_md5(), salt, (unsigned char *)pass, strlen(pass), 1, key, iv);

	//	Cipher set: encrypt(1), decrypt(0) 
	BIO_set_cipher(encBIO, cipher, key, iv, is_encrypt);

	//	Read from input file
	{
		int len = 0;
		
		unsigned char* readBuffer = readFile(input_file_path, &len);
		if (readBuffer == NULL)
		{
			return -1;
		}

		encBIO = BIO_push(encBIO,outBIO);
		BIO_write(encBIO, (char *)readBuffer, len);
	}

	BIO_flush(encBIO);
	BIO_free(encBIO);

	return 0;
}

unsigned char* readFile(char* file, int* readLen)
{
	BIO*			fileBIO		= NULL;
	unsigned char*	retBuffer	= NULL;
	unsigned char*	buffer		= NULL;
	int				length		= 0;

	fileBIO = BIO_new_file(file, "rb");
	if (!fileBIO)
	{
		printf("[ERROR] Failed to open input file [%s]\n", file);
		return NULL;
	}
	
	buffer = (unsigned char*)malloc(BUFLEN + 1);
	*readLen = 0;

	while (1)
	{
		length = BIO_read(fileBIO, buffer, BUFLEN);
		if (length > 0)
		{
			buffer[length] = 0;
			retBuffer = addString(retBuffer,*readLen,buffer,length);
			*readLen = *readLen + length;
			BIO_seek(fileBIO, *readLen);
		}
		else
		{
			break;
		}
	}

	//	Free objects
	BIO_free(fileBIO);
	free(buffer);
	buffer = NULL;

	return retBuffer;
}

unsigned char *addString(unsigned char *destString, int destLen, const unsigned char *addString, int addLen)
{
	unsigned char* retString;
	int i;

	if ((destString == NULL) || (destLen == 0))
	{
		retString = (unsigned char*)malloc(sizeof(unsigned char) * (addLen + 1));
		for (i = 0; i < addLen; i++)
		{
			retString[i] = addString[i];
		}
		retString[i] = NULL;
	}
	else
	{
		retString = (unsigned char * )malloc(sizeof(unsigned char) * (destLen + addLen + 1));
		for (i = 0; i < destLen; i++)
		{
			retString[i] = destString[i];
		}
		
		for (i = 0; i < addLen; i++)
		{
			retString[i + destLen] = addString[i];
		}
		retString[i + destLen] = NULL;
	}
	
	free(destString);
	destString = NULL;

	return retString;
}

#if defined (CRYPTO_TEST)

int main(int argc, char* argv[])
{
	int is_encrypt = 0;
	int result = 0;

	if (argv[1][0] == 'e')
	{
		is_encrypt = 1;
	}
	else if (argv[1][0] == 'd')
	{
		is_encrypt = 0;	
	}
	else
	{
		printf("[ERROR] invalid option. Only e (for encryption) and d (for decryption) allowed.\n");
		return -1;
	}

	result = do_encryption(argv[2], argv[3], is_encrypt);
	if (result != 0)
	{
		printf("[ERROR] Failed to encrypt.\n");
	}
	else
	{
		printf("[INFO] Successfully done.\n");
	}

	return 0;
}

#endif /* CRYPTO_TEST */
