/*--
	kcsTest.h

	Test csp.
	
	Include:
		list
		remove
		add
--*/


#include "global.h"


/*	Mapping from key spec(Algorithm ID) to string	*/
struct KeySpec
{
  DWORD dwSpec;
  const char* name;
};

static const KeySpec KEYS[] =
{
  { AT_SIGNATURE,   "AT_SIGNATURE"   },
  { AT_KEYEXCHANGE, "AT_KEYEXCHANGE" }
};


/*	Mapping from KP_PERMISSION to string	*/
struct Permission
{
  DWORD mask;
  const char* name;
};

static const Permission PERMS[] =
{
  { CRYPT_ENCRYPT   , "E" },
  { CRYPT_DECRYPT   , "D" },
  { CRYPT_EXPORT    , "Ex" },
  { CRYPT_READ      , "R" },
  { CRYPT_WRITE     , "W" },
  { CRYPT_MAC       , "M" },
  { CRYPT_EXPORT_KEY, "Exk" },
  { CRYPT_IMPORT_KEY, "I" }
};


int kcsTest(ULONG ulActive)
{
	HCRYPTPROV hProv;
	int rv = CSP_OK;

	if (ulActive == ACTIVE_LIST)
    {      
		//Acquire a "VERIFYCONTEXT" handle to the UniToken CSP, 
		//since we don't need a keypair in order to list the key containers.
		if (CryptAcquireContext(&hProv, NULL, CSP_NAME, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
		{
			//Enumerate the key containers(KCs) on the UniToken CSP. 
			DWORD flags = CRYPT_FIRST;
			BYTE name[MAX_PATH + 1];

			for(DWORD len = sizeof(name); CryptGetProvParam(hProv, PP_ENUMCONTAINERS, name, &len, flags); len = sizeof(name))
			{
				//remove the CRYPT_FIRST flag, since only the first call to CryptGetProvParam should use it
				flags = 0;
				//Display the KC name
				printf("KC: %s ", name);
				
				HCRYPTPROV hKeyProv = 0;
				HCRYPTKEY hKey      = 0;
				//Acquire a handle to the key container found
				if (CryptAcquireContext(&hKeyProv, (char*)name, CSP_NAME, PROV_RSA_FULL, 0))
				{
					// Loop over all possible key specs
					for (unsigned i = 0; i < 2; ++i)
					{
						//Try to get a handle to the key pair(depends on key spec - Signature or Exchange keys)
						if (CryptGetUserKey(hKeyProv, KEYS[i].dwSpec, &hKey))
						{
							// Get key permissions
							BYTE buf[4];
							DWORD bufLen = sizeof(buf);
							if (CryptGetKeyParam(hKey, KP_PERMISSIONS, buf, &bufLen, 0))
							{
								//Display key permissions
								DWORD* pFlags = reinterpret_cast<DWORD*>(buf);
								printf("key:%s perms:", KEYS[i].name);

								for (unsigned j = 0; j < 8; ++j)
									if ((*pFlags & PERMS[j].mask) != 0)
										printf("%s.", PERMS[j].name);
								
								printf("  ");
							}
							//Destroy the handle to the key pair
							CryptDestroyKey(hKey);
						}
					}
					//Destroy the handle to the key container
					CryptReleaseContext(hKeyProv, 0);
				}
				
				printf("\n");
			}

			//After CryptEnumProviders returns false, the error code should be ERROR_NO_MORE_ITEMS - indicating the enumeration succeeded, finding all key containers(otherwise an error has occurred)
			DWORD e = GetLastError();
			if (e != ERROR_NO_MORE_ITEMS)
			{
				printf("ERR: CryptGetProvParam error code = 0X%x\n", e);
				rv = CSP_FAILED;
			}
			//Release the "VERIFYCONTEXT"
			CryptReleaseContext(hProv, 0);
		}
		else
		{
			//
			CryptReleaseContext(hProv, 0);
			printf("Cannot acquire CRYPTOAPI context.\n");
		}
    }
    else if (ulActive == ACTIVE_ADD)
    {
		//Adding a new key container(name specified in CONTAINER)
		// First check if it already exists
		if (!CryptAcquireContext(&hProv, CONTAINER, CSP_NAME, PROV_RSA_FULL, 0))
		{
			// It doesn't, try to create
			if (!CryptAcquireContext(&hProv, CONTAINER, CSP_NAME, PROV_RSA_FULL, CRYPT_NEWKEYSET))
			{
				printf("\tErr: CryptAcquireContext() ERR CODE=0x%0x\n", GetLastError());
				rv = CSP_FAILED;
			}
			else
			{ 
				printf("...OK!\n");
			}
		}
		else
		{
			//Can't create - key container already exists
			::CryptReleaseContext(hProv, 0);
			printf("%s  already exists.\n", CONTAINER);
		}
    }
    else if (ulActive == ACTIVE_REMOVE)
    {
		//Removing a key container(name specified in CONTAINER)
		// First, make sure key container exists
		if (CryptAcquireContext(&hProv, CONTAINER, CSP_NAME, PROV_RSA_FULL, 0))
		{
			//It exists, release handle, then delete (Using CRYPT_DELETEKEYSET)
			CryptReleaseContext(hProv, 0);
			if (!CryptAcquireContext(&hProv, CONTAINER, CSP_NAME, PROV_RSA_FULL, CRYPT_DELETEKEYSET))
			{
				printf("\tErr: CryptAcquireContext() ERR CODE=0x%0x\n", GetLastError());
				return CSP_FAILED;
			}
			else
			{ 
				printf("...OK!\n");
			}
		}
		else
		{
			//Key container doesn't exists - can't delete.
			printf("%s  does not exist.\n", CONTAINER);
		}
    }
  
	return rv;
}