/* Generated by Together */
#include "stdafx.h"

#include <vector>
#include <utils/local_mutex.hpp>
#include <utils/prague/scoped_handle.hpp>

#include "../include/ActivationErrors.h"
#include "AuthTokenImp.h"

namespace OnlineActivation
{
///////////////////////////////////////////////////////////////////////////////
/**
 * Session key class for encrypting/decrypting.
 */
class SessionKey
{
	/**
	 * Wrapper function to release cryptographic provider
	 */
	static BOOL CryptReleaseProvider(HCRYPTPROV hProv)
	{
		return CryptReleaseContext(hProv, 0);
	}

	/**
	 * Cryptographic provider auto-handle.
	 */
	typedef util::scoped_handle<HCRYPTPROV, BOOL (*)(HCRYPTPROV), &SessionKey::CryptReleaseProvider, 1> hprovider_t;

	/**
	 * Key auto-handle.
	 */
	typedef util::scoped_handle<HCRYPTKEY, BOOL (WINAPI*)(HCRYPTKEY), &CryptDestroyKey, 1> hkey_t;

public:

	/**
	 * Returns an instance of session key.
	 */
	static HCRYPTKEY Instance();

private:

	/**
	 * Creates session key object.
	 */
	SessionKey();

	/**
	 * Cryptographic provider.
	 */
	hprovider_t m_provider;

	/**
	 * Session key.
	 */
	hkey_t m_key;

	/**
	 * Stores an instance of session key object.
	 */
	static std::auto_ptr<SessionKey> m_instance;

	/**
	 * Instance access guard.
	 */
	static sync::local_mutex m_guard;
};
///////////////////////////////////////////////////////////////////////////////
std::auto_ptr<SessionKey> SessionKey::m_instance;
sync::local_mutex SessionKey::m_guard;
///////////////////////////////////////////////////////////////////////////////
SessionKey::SessionKey()
{
	// acquire cryptographic provider
	if (!CryptAcquireContextA(&m_provider.get_ref(), 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
		throw SystemError(GetLastError());
	// generate session key
	if (!CryptGenKey(m_provider.get(), CALG_RC2, 0, &m_key.get_ref()))
		throw SystemError(GetLastError());
}
///////////////////////////////////////////////////////////////////////////////
HCRYPTKEY SessionKey::Instance()
{
	if (!m_instance.get())
	{
		sync::local_mutex::scoped_lock lock(m_guard);
		m_instance = std::auto_ptr<SessionKey>(new SessionKey());
	}
	return m_instance->m_key.get();
}
///////////////////////////////////////////////////////////////////////////////
void ProtectData(const secure_buffer& data, std::vector<unsigned char>& prot)
{
	DWORD dwBufferSize = data.size();
	if (!CryptEncrypt(SessionKey::Instance(), 0, TRUE, 0, 0, &dwBufferSize, data.size()))
		throw SystemError(GetLastError());

	std::vector<unsigned char> buf(dwBufferSize);
	// copy data to encrypt
	std::copy(data.begin(), data.end(), buf.begin());

	dwBufferSize = data.size();
	if (!CryptEncrypt(SessionKey::Instance(), 0, TRUE, 0, &buf[0], &dwBufferSize, buf.size()))
		throw SystemError(GetLastError());
	// set result
	prot.swap(buf);
}
///////////////////////////////////////////////////////////////////////////////
void UnprotectData(const std::vector<unsigned char>& prot, secure_buffer& data)
{
	// populate buffer with encrypted data
	secure_buffer buf(prot.begin(), prot.end());
	// set initial size
	DWORD dwBufferSize = buf.size();
	// decrypt data using session key
	if (!CryptDecrypt(SessionKey::Instance(), 0, TRUE, 0, &buf[0], &dwBufferSize))
		throw SystemError(GetLastError());
	// set actual data size
	buf.resize(dwBufferSize);
	// set result
	data.swap(buf);
}
///////////////////////////////////////////////////////////////////////////////
void AuthTokenImp::SetCredentials(const char* userName, const char* passWord)
{
	if (userName && userName[0])
	{
		secure_buffer buf
		(
			reinterpret_cast<const unsigned char*>(userName),
			reinterpret_cast<const unsigned char*>(userName) + strlen(userName)
		);
		ProtectData(buf, m_user);
	}
	else
		m_user.clear();

	if (passWord && passWord[0])
	{
		secure_buffer buf
		(
			reinterpret_cast<const unsigned char*>(passWord),
			reinterpret_cast<const unsigned char*>(passWord) + strlen(passWord)
		);
		ProtectData(buf, m_pass);
	}
	else
		m_pass.clear();
}
///////////////////////////////////////////////////////////////////////////////
bool AuthTokenImp::IsEmpty() const
{
	return m_user.empty() || m_pass.empty();
}
///////////////////////////////////////////////////////////////////////////////
void AuthTokenImp::GetUserName(secure_buffer& buf) const
{
	UnprotectData(m_user, buf);
}
///////////////////////////////////////////////////////////////////////////////
void AuthTokenImp::GetPassword(secure_buffer& buf) const
{
	UnprotectData(m_pass, buf);
}
///////////////////////////////////////////////////////////////////////////////
} // namespace OnlineActivation
