#include "cryptopp.h"

Crypto::Crypto()
{
}

Crypto::~Crypto()
{
}

static CryptoPP::OFB_Mode<CryptoPP::AES>::Encryption s_globalRNG;

CryptoPP::Exception *lastException;


CryptoPP::Exception *GetLastException()
{
	return lastException;
}

CryptoPP::RandomNumberGenerator& GlobalRNG()
{
	return s_globalRNG;
}

void Crypto::Test(const String &message)
{
	PromptOK(message + " MD5 = " + HashMD5(message) + " | SHA = " + HashSHA(message));
	
	String key("ruiGonçalves-Sup€r.Strong~Passord");
	String enc = EncryptString(message, key);
	String dec = DecryptString(enc, key);
	
	if (message.Compare(dec) == 0)
		PromptOK("Encription Test Pass");
	else 
		PromptOK("Encription Test Did Not Pass");

	GenerateRSAKey("Priv.txt", "Publ.txt");
	String rsaencoded = RSAEncryptString("Publ.txt", message);
	String rsadecoded = RSADecryptString("Priv.txt", rsaencoded);
	
	if (message.Compare(rsadecoded) == 0)
		PromptOK("RSA Encription Test Pass");
	else 
		PromptOK("RSA Encription Test Did Not Pass");
}
	
String Crypto::HashMD5(const String &message)
{
	CryptoPP::Weak::MD5 hash;
	byte digest[ CryptoPP::Weak::MD5::DIGESTSIZE ];
	
	hash.CalculateDigest( digest, message, message.GetLength() );
	
	CryptoPP::HexEncoder encoder;
	std::string output;
	encoder.Attach( new CryptoPP::StringSink( output ) );
	encoder.Put( digest, sizeof(digest) );
	encoder.MessageEnd();
			
	return output;
}

String Crypto::HashSHA(const String &message)
{
	CryptoPP::SHA hash;
	byte digest[ CryptoPP::SHA::DIGESTSIZE ];
	
	hash.CalculateDigest( digest, message, message.GetLength() );
	
	CryptoPP::HexEncoder encoder;
	std::string output;
	encoder.Attach( new CryptoPP::StringSink( output ) );
	encoder.Put( digest, sizeof(digest) );
	encoder.MessageEnd();
			
	return output;
}

String Crypto::EncryptString(const String &instr, const String &passPhrase)
{
	std::string outstr;

	CryptoPP::DefaultEncryptorWithMAC encryptor(passPhrase, new CryptoPP::HexEncoder(new CryptoPP::StringSink(outstr)));
	encryptor.Put((byte *)instr.Begin(), instr.GetLength());
	encryptor.MessageEnd();

	return outstr;
}

String Crypto::DecryptString(const String &instr, const String &passPhrase)
{
	std::string outstr;

	CryptoPP::HexDecoder decryptor(new CryptoPP::DefaultDecryptorWithMAC(passPhrase, new CryptoPP::StringSink(outstr)));
	decryptor.Put((byte *)instr.Begin(), instr.GetLength());
	decryptor.MessageEnd();

	return outstr;
}

void Crypto::GenerateRSAKey(const String &privFilename, const String &pubFilename, UINT keyLength)
{
	CryptoPP::AutoSeededRandomPool randPool;
	
	CryptoPP::RSAES_OAEP_SHA_Decryptor priv(randPool, keyLength);
	CryptoPP::HexEncoder privFile(new CryptoPP::FileSink(privFilename.Begin()));
	priv.DEREncode(privFile);
	privFile.MessageEnd();

	CryptoPP::RSAES_OAEP_SHA_Encryptor pub(priv);
	CryptoPP::HexEncoder pubFile(new CryptoPP::FileSink(pubFilename.Begin()));
	pub.DEREncode(pubFile);
	pubFile.MessageEnd();
}


String Crypto::RSAEncryptString(const String &pubFilename, const String &message)
{	
	
	try{
        //CryptoPP::StringSource pubStr(message.Begin(), true, new CryptoPP::HexDecoder);
		CryptoPP::FileSource pubStr(pubFilename.Begin(), true, new CryptoPP::HexDecoder);

        CryptoPP::RSAES_OAEP_SHA_Encryptor pub(pubStr); 
        CryptoPP::AutoSeededX917RNG <CryptoPP::DES_EDE3> rng;

        std::string result;

        CryptoPP::StringSource(message.Begin(), true,
                new CryptoPP::PK_EncryptorFilter(rng, pub,
	    new CryptoPP::HexEncoder(
	                new CryptoPP::StringSink(result))));
	
	    return result;
	}
	catch(CryptoPP::Exception cppex)
	{
		lastException = &cppex;
	    return NULL;
	}
}

String Crypto::RSADecryptString(const String &privFilename, const String &ciphertext)
{
	try
	{
	    //CryptoPP::StringSource privStr(priString, true,new CryptoPP::HexDecoder);
	    CryptoPP::FileSource privStr(privFilename.Begin(), true,new CryptoPP::HexDecoder);
	
	    CryptoPP::RSAES_OAEP_SHA_Decryptor priv(privStr);
	    CryptoPP::AutoSeededX917RNG<CryptoPP::DES_EDE3> rng;
	
	    std::string result;
	
	    CryptoPP::StringSource(ciphertext.Begin(), true,
	            new CryptoPP::HexDecoder(
	            new CryptoPP::PK_DecryptorFilter(rng, priv,
	    		new CryptoPP::StringSink(result))));
	
	    return result;
	}
	catch(CryptoPP::Exception cppex)
	{
		lastException = &cppex;
	    return NULL;
	}
}

