/*--
	GOST28147.cpp

	2015-10-15

	Test pkcs Generate GOST28147 Key and encryption/decryption
--*/


#include "global.h"

CK_FUNCTION_LIST_PTR fl = 0;

CK_OBJECT_HANDLE find_object(CK_SESSION_HANDLE hSess, 
							 CK_ATTRIBUTE * t, 
							 CK_ULONG size);

int main(int argc, char* argv[])
{
	CK_RV rv;
	CK_ULONG slot_count = 100;
	CK_SLOT_ID slots[100];
	CK_SESSION_HANDLE session;
	CK_BBOOL         bFalse = FALSE;
	CK_BBOOL         bTrue = TRUE;
	CK_KEY_TYPE      key_type  = CKK_GOST28147;
	CK_ULONG i = 0;
	/* setup key attributes, specific name so we can find it 
	 * later, GOST28147 key type, saved on the UniMate/UniToken and private to only the
	 * users of the UniMate/UniToken
	 */
	CK_OBJECT_CLASS class_key = CKO_SECRET_KEY;
	CK_BYTE IdGen = '0';
	CK_BYTE IdImport = '1';
	CK_ULONG keyValueLen = 32;
	CK_BYTE lpKeyValue[] = {
		0xCC, 0x63, 0x98, 0x02, 0xD9, 0x81, 0xAC, 0xB4,
		0x34, 0x60, 0x85, 0x0A, 0xCF, 0xF6, 0xCD, 0x54,
		0xCC, 0x63, 0x98, 0x02, 0xD9, 0x81, 0xAC, 0xB4,
		0x34, 0x60, 0x85, 0x0A, 0xCF, 0xF6, 0xCD, 0x54};

	CK_BYTE lpData[] = {
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
	};
	CK_ULONG dataLen = sizeof(lpData);

	CK_BYTE en_data[1024] = {0};
	CK_ULONG en_len = 1024;
	CK_BYTE de_data[1024] = {0};
	CK_ULONG de_len = 1024;

	CK_BYTE lpIV[8] = {
		0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
	};
	CK_ULONG ivLen = sizeof(lpIV);

	CK_ATTRIBUTE 
		GOST28147_A[] =	{
		{CKA_ID,		&IdGen,			sizeof(CK_BYTE)},
		{CKA_CLASS,		&class_key,		sizeof(class_key)},
		{CKA_KEY_TYPE,	&key_type,		sizeof(CK_KEY_TYPE)},
		{CKA_PRIVATE,	&bTrue,			sizeof(bTrue)},
		{CKA_TOKEN,		&bTrue,			sizeof(bTrue)},
		{CKA_ENCRYPT,	&bTrue,			sizeof(bTrue)},
		{CKA_DECRYPT,	&bTrue,			sizeof(bTrue)},
		{CKA_SENSITIVE,	&bFalse,		sizeof(bFalse)},
		{CKA_VALUE_LEN,	&keyValueLen,	sizeof(keyValueLen)}
	};	

	CK_ATTRIBUTE GOST28147_A_Import[] =	{
		{CKA_ID,		&IdImport,		sizeof(CK_BYTE)},
		{CKA_CLASS,		&class_key,		sizeof(class_key)},
		{CKA_KEY_TYPE,	&key_type,		sizeof(CK_KEY_TYPE)},
		{CKA_PRIVATE,	&bTrue,			sizeof(bTrue)},
		{CKA_TOKEN,		&bTrue,			sizeof(bTrue)},
		{CKA_ENCRYPT,	&bTrue,			sizeof(bTrue)},
		{CKA_DECRYPT,	&bTrue,			sizeof(bTrue)},
		{CKA_SENSITIVE,	&bFalse,		sizeof(bFalse)},
		{CKA_VALUE,		lpKeyValue,		sizeof(lpKeyValue)},
		{CKA_VALUE_LEN,	&keyValueLen,	sizeof(keyValueLen)}
	};

	/* setup mechanism to generate GOST28147 key 
	 */
	CK_MECHANISM mechanism = {0};
	CK_OBJECT_HANDLE hKeyGen = NULL;
	CK_OBJECT_HANDLE hKeyImp = NULL;
	
	printf("Generate Gost28147 Key Sample:\n");
	
	fl = InitFunctionList();
	if (fl == 0)
		return leave("Can't get function list. \n");
	
	/* Initialize the PKCS #11 library
	 */
	if (PKCS_OK != fl->C_Initialize (0))
		return leave("C_Initialize failed...");                       
	
	/* Get list of slots containing UniMate/UniTokens
	 */
	if (PKCS_OK != fl->C_GetSlotList (TRUE, slots, &slot_count))
		return leave("C_GetSlotList failed...");

	/* One or more UniMate/UniToken must be inserted
	 */
	if (slot_count < 1)
		return leave("No UniMate/UniToken is available.\n");
	
	printf ("UniMate/UniToken Found.\n");
	/* open a read/write session on the first UniMate/UniToken found
	 */
	if (PKCS_OK != fl->C_OpenSession(slots[0], 
		(CKF_SERIAL_SESSION | CKF_RW_SESSION), 0, 0, &session)
		)
		return leave("C_OpenSession failed.\n");
	
	/* login as user so we can use the public and private
	 * keys on the UniToken
	 */
	if (PKCS_OK != fl->C_Login(session, CKU_USER, USER_PIN, 8))
		return leave("C_Login failed.\n");
	
	/* Try to find the two keys using the find_object utility function if exist,
	 * or generate new key.
	*/
	mechanism.mechanism = CKM_GOST28147_KEY_GEN;
	hKeyGen = find_object(session, GOST28147_A, 3);
	printf("Generate Gost28147 Key:\n");
	if (!hKeyGen)
	{
		printf ("Wait For Generating Gost28147 Key Now ... \n");
		rv = fl->C_GenerateKey(session, &mechanism, GOST28147_A, 9, &hKeyGen);
		if (rv != PKCS_OK)
		{
			fl->C_Logout(session);
			fl->C_CloseSession(session);
			return leave("C_GenerateKey failed.  Please initialize UniMate/InitToken first.");
		}

		printf ("Generating Gost28147 Key Succeed.\n");
	}
	else
	{
		printf("The Gost28147 Key ID %c is already exists.\n", IdGen);	
	}
	printf("\n");

	// Import Gost28147 Key
	hKeyImp = find_object(session, GOST28147_A_Import, 3);
	printf("Import Gost28147 Key:\n");
	if (!hKeyImp)
	{
		printf ("Wait For Import Gost28147 Key Now ... \n");
		rv = fl->C_CreateObject(session, GOST28147_A_Import, 10, &hKeyImp);
		if (rv != PKCS_OK)
		{
			fl->C_Logout(session);
			fl->C_CloseSession(session);
			return leave("C_CreateObject failed.  Please run InitToken first.");
		}
		
		printf ("Import Gost28147 Key Succeed.\n");
	}
	else
	{
		printf("The Gost28147 Key ID %c is already exists.\n", IdImport);	
	}
	printf("\n");


	// Gost28147-ECB Encrypt
	printf("Gost28147-ECB Encrypt\n");
	printf("Date to encrypt:\n");
	for (i=0; i<dataLen; i++)
	{
		printf("%02x", lpData[i]);
	}
	printf("\n");

	mechanism.mechanism = CKM_GOST28147_ECB;
	rv = fl->C_EncryptInit(session, &mechanism, hKeyGen);
	if(rv != CKR_OK)
	{
		printf("C_EncryptInit failed.\n");
		goto log_out;
	}	

	rv = fl->C_Encrypt(session, lpData, dataLen, NULL_PTR, &en_len);
	if(rv != CKR_OK)
	{
		printf("C_Encrypt failed.\n");
		goto log_out;
	}

	rv = fl->C_Encrypt(session, lpData, dataLen, en_data, &en_len);
	if(rv != CKR_OK)
	{
		printf("C_Encrypt failed.\n");
		goto log_out;
	}
	
	printf("\n");
	printf("Encrypt Result:\n");
	for (i=0; i<en_len; i++)
	{
		printf("%02x", en_data[i]);
	}
	printf("\n\n");
	
	// Gost28147-ECB Decrypt
	printf("Gost28147-ECB Decrypt\n");
	rv = fl->C_DecryptInit(session, &mechanism, hKeyGen);
	if(rv != CKR_OK)
	{	
		printf("C_DecryptInit failed.\n");
		goto log_out;
	}
	
	rv = fl->C_Decrypt(session, en_data, en_len, NULL_PTR, &de_len);
	if(rv != CKR_OK)
	{
		printf("C_Decrypt failed.\n");
		goto log_out;
	}

	rv = fl->C_Decrypt(session, en_data, en_len, de_data, &de_len);
	if(rv != CKR_OK)
	{
		printf("C_Decrypt failed.\n");
		goto log_out;
	}
	
	printf("Decrypt Result:\n");
	for (i=0; i<de_len; i++)
	{
		printf("%02x", de_data[i]);
	}
	printf("\n\n");

	// Gost28147-CFB Encrypt
	printf("Gost28147-CFB Encrypt\n");
	printf("Date to encrypt:\n");
	for (i=0; i<dataLen; i++)
	{
		printf("%02x", lpData[i]);
	}
	printf("\n");
	printf("IV:\n");
	for (i=0; i<ivLen; i++)
	{
		printf("%02x", lpIV[i]);
	}
	printf("\n");
	
	mechanism.mechanism = CKM_GOST28147_CFB;
	mechanism.pParameter = lpIV;
	mechanism.ulParameterLen = ivLen;
	rv = fl->C_EncryptInit(session, &mechanism, hKeyImp);
	if(rv != CKR_OK)
	{
		printf("C_EncryptInit failed.\n");
		goto log_out;
	}	
	
	memset(en_data, 0, 1024);
	rv = fl->C_Encrypt(session, lpData, dataLen, NULL_PTR, &en_len);
	if(rv != CKR_OK)
	{
		printf("C_Encrypt failed.\n");
		goto log_out;
	}
	
	rv = fl->C_Encrypt(session, lpData, dataLen, en_data, &en_len);
	if(rv != CKR_OK)
	{
		printf("C_Encrypt failed.\n");
		goto log_out;
	}
	
	printf("\n");
	printf("Encrypt Result:\n");
	for (i=0; i<en_len; i++)
	{
		printf("%02x", en_data[i]);
	}
	printf("\n\n");
	
	// Gost28147-CFB Decrypt
	printf("Gost28147-CFB Decrypt\n");
	rv = fl->C_DecryptInit(session, &mechanism, hKeyImp);
	if(rv != CKR_OK)
	{	
		printf("C_DecryptInit failed.\n");
		goto log_out;
	}
	
	rv = fl->C_Decrypt(session, en_data, en_len, NULL_PTR, &de_len);
	if(rv != CKR_OK)
	{
		printf("C_Decrypt failed.\n");
		goto log_out;
	}
	
	memset(de_data, 0, 1024);
	rv = fl->C_Decrypt(session, en_data, en_len, de_data, &de_len);
	if(rv != CKR_OK)
	{
		printf("C_Decrypt failed.\n");
		goto log_out;
	}
	
	printf("Decrypt Result:\n");
	for (i=0; i<de_len; i++)
	{
		printf("%02x", de_data[i]);
	}
	printf("\n\n");
	
log_out:
	/* logout of the UniMate/UniToken
	 */
	if (PKCS_OK != fl->C_Logout (session))
		return leave("C_Logout failed.\n");
    
	/* close open session
	 */
	if (PKCS_OK != fl->C_CloseSession (session))
		return leave("C_CloseSession failed.\n");
	
	
	/* close PKCS #11 library
	 */
	if (PKCS_OK != fl->C_Finalize(0))
		return leave("C_Finalize failed.\n");
    Finalize();
	
	printf ("Press Enter to exit...");
	getchar();

	return PKCS_OK;
}


/*
	Forward declaration of utility function
		(To find an object handle, with a certain set of attributes)
*/
CK_OBJECT_HANDLE 
find_object(CK_SESSION_HANDLE hSess, CK_ATTRIBUTE * t, CK_ULONG size)
{
	CK_OBJECT_HANDLE list[1];
	CK_ULONG found = 0;
	
	// initialize the search for any object with attribute 't'
	if (CKR_OK != fl->C_FindObjectsInit (hSess, t, size))
	{
		leave("C_FindObjectsInit failed.\n");
		return 0;
	}
	
	// find the objects which match, we ask for only a single match
	// since we know there is only one on the UniMate/UniToken(InitToken creates a single public/private key pair)
	if (CKR_OK != fl->C_FindObjects(hSess, list, 1, &found))
	{
		leave("C_FindObjects failed.\n");
		return 0;
	}
	
	// cleanup the search
	//
	if (CKR_OK != fl->C_FindObjectsFinal(hSess))
	{
		leave ("C_FindObjectsFinal failed.\n");
		return 0;
	}
	
	// either return NULL if nothing matches or return the object handle
	// of the matching object
	//
	if (found < 1)
		return 0;
	
	return (list[0]);
}

