#include "stdafx.h"
#include "SecurityV1.h"

//--------------------------------------------------------------------
//  

void ByteToStr(
     DWORD cb, 
     void* pv, 
     LPSTR sz)
//--------------------------------------------------------------------
// Parameters passed are:
//    pv is the array of BYTES to be converted.
//    cb is the number of BYTEs in the array.
//    sz is a pointer to the string to be returned.

{
//--------------------------------------------------------------------
//  Declare and initialize local variables.

BYTE* pb = (BYTE*) pv; // local pointer to a BYTE in the BYTE array
DWORD i;               // local loop counter
int b;                 // local variable

//--------------------------------------------------------------------
//  Begin processing loop.

for (i = 0; i<cb; i++)
{
   b = (*pb & 0xF0) >> 4;
   *sz++ = (b <= 9) ? b + '0' : (b - 10) + 'A';
   b = *pb & 0x0F;
   *sz++ = (b <= 9) ? b + '0' : (b - 10) + 'A';
   pb++;
}
*sz++ = 0;
}

const TCHAR * ByteToWideString(BYTE * bytes, int len){
	TCHAR * wstr;
	char * sz = new char[len*2+1];
	ByteToStr(len,bytes,sz);
	wstr = ConvertToWideString(sz);
	delete [] sz;
	return wstr;
}


SecurityV1::SecurityV1()
{
	hCryptProv = 0;
}

SecurityV1::~SecurityV1()
{
	if(hCryptProv)
		CryptReleaseContext(hCryptProv,0);	
}

int SecurityV1::Init(){
	//--------------------------------------------------------------------
	// Declare and initialize variables.

	std::wstring UserName = L"Superkalafragilistic";  // Name of the key container
	// to be used.
	//--------------------------------------------------------------------
	// Attempt to acquire a context and a key
	// container. The context will use the default CSP
	// for the RSA_FULL provider type. DwFlags is set to 0
	// to attempt to open an existing key container.

	if(CryptAcquireContext(
		&hCryptProv,               // Handle to the CSP
		UserName.data(),                  // Container name 
		NULL,                      // Use the default provider
		PROV_RSA_FULL,             // Provider type
		0))                        // Flag values
	{
	//	print(L"A crypto context with the %s key container \n", UserName.data());
	//	print(L"has been acquired.\n\n");
	} else	{ 
		//--------------------------------------------------------------------
		// An error occurred in acquiring the context. This could mean
		// that the key container requested does not exist. In this case,
		// the function can be called again to attempt to create a new key 
		// container. 
		if (GetLastError() == 0x80090016 )
		{
			if(CryptAcquireContext(
				&hCryptProv, 
				UserName.data(), 
				NULL, 
				PROV_RSA_FULL, 
				CRYPT_NEWKEYSET)) 
			{
				print(L"A new key container has been created.\n");
			} else {
				print(L"Could not create a new key container.\n");
				return 0;
			}
		
		} else {
			print(L"A cryptographic service handle could not be acquired.\n");
			return 0;
		}

	} // End of else
	//--------------------------------------------------------------------
	// A cryptographic context and a key container is available. Perform
	// any functions that require a Cryptographic provider handle.

	//--------------------------------------------------------------------
	// When the handle is no longer needed, it should be released.

	return 1;
}

const TCHAR * SecurityV1::Encrypt(const TCHAR * password, const TCHAR * text)
{
	if(hCryptProv==0) return NULL;

	// Derive an encryption key from the password 
	HCRYPTKEY hKey;
	HCRYPTHASH hHash;
	BYTE * outBuffer = NULL;
	const TCHAR * str = NULL;
	DWORD inLen, outLen;

	outLen = inLen = _tcslen(text)*sizeof(TCHAR);

	CryptCreateHash(
		hCryptProv, 
		CALG_MD5, 
		0, 
		0, 
		&hHash);
	// Hash the password string.

	CryptHashData(
		hHash, 
		(const BYTE*)password, 
		_tcslen(password)*sizeof(TCHAR), 
		0);

	// Create a session key based on the hash of the password.

	if(!CryptDeriveKey(
		hCryptProv, 
		CALG_RC2, 
		hHash, 
		CRYPT_EXPORTABLE, 
		&hKey))
	{
		print(L"SecurityV1::Encrypt: Unable to derive key.\n");
		PrintLastError("SecurityV1::Encrypt");
		return NULL;
	}

	CryptDestroyHash(hHash);

	// First call determines size of output buffer
	if(!CryptEncrypt(
		hKey, 
		0, 
		TRUE, 
		0, 
		NULL, 
		&outLen, 
		inLen 
		))
	{
		print(L"SecurityV1::Encrypt: unable to get encryption buffer length.\n");
		PrintLastError("SecurityV1::Encrypt");
		return NULL;
	}

	outBuffer = new BYTE[outLen+1];
	memcpy(outBuffer,text,inLen);

	// Second call encrypts
	if(!CryptEncrypt(
		hKey, 
		0, 
		TRUE, 
		0, 
		outBuffer, 
		&inLen, 
		outLen 
		))
	{
		print(L"SecurityV1::Encrypt: Unable to encrypt with key.\n");
		PrintLastError("SecurityV1::Encrypt");
		return NULL;
	}

	CryptDestroyKey(hKey);

	str = ByteToWideString(outBuffer,inLen);
	delete [] outBuffer;
	return str;
}


const TCHAR * SecurityV1::Decrypt(const TCHAR * password, const TCHAR * text)
{
	if(hCryptProv==0) return NULL;

	// Derive an encryption key from the password 
	HCRYPTKEY hKey;
	HCRYPTHASH hHash;
	static TCHAR * outBuffer = NULL;
	DWORD inLen, outLen;

	inLen = _tcslen(text)*sizeof(TCHAR);

	CryptCreateHash(
		hCryptProv, 
		CALG_MD5, 
		0, 
		0, 
		&hHash);
	// Hash the password string.

	CryptHashData(
		hHash, 
		(const BYTE*)password, 
		_tcslen(password)*sizeof(TCHAR), 
		0);

	// Create a session key based on the hash of the password.

	CryptDeriveKey(
		hCryptProv, 
		CALG_RC2, 
		hHash, 
		CRYPT_EXPORTABLE, 
		&hKey);

	CryptDestroyHash(hHash);

	outLen = inLen;
	if(outBuffer) delete [] outBuffer;
	outBuffer = new TCHAR[outLen+1];
	memcpy(outBuffer,text,inLen);

	CryptDecrypt(
		hKey, 
		0, 
		TRUE, 
		0, 
		(BYTE*)outBuffer, 
		&outLen 
		);

	CryptDestroyKey(hKey);

	outBuffer[outLen] = '\0';

	return outBuffer;
}