#include "test_common.h"

int main(int argc, char* argv[])
{	
	CK_RV rvResult;
#ifdef WIN32
	HMODULE hPkcsLib = NULL;
	HMODULE hPkcsLib2 = NULL;
#else
	void *hPkcsLib = NULL;
	void *hPkcsLib2 = NULL;
#endif
	CK_C_GetFunctionList pcGetFunctionList = 0;  
	CK_C_GetFunctionList pcGetFunctionList2 = 0;
	CK_FUNCTION_LIST_PTR Pkcs11FuncList = NULL;
	CK_FUNCTION_LIST_PTR Pkcs11FuncList2 = NULL;
	CK_SLOT_ID_PTR       pSlotList = NULL;
	CK_SLOT_ID_PTR       pSlotList2 = NULL;
	CK_SLOT_ID           SlotId;
	CK_SLOT_ID           SlotId2;
	CK_ULONG             ulSlotCount;
	CK_ULONG             ulSlotCount2;
	CK_SLOT_INFO         SlotInfo; 
	CK_SESSION_HANDLE    hSession;
	CK_SESSION_HANDLE    hSession2;

    CK_UTF8CHAR_PTR      pcUserPIN = (CK_UTF8CHAR_PTR)"01234567";   
	CK_ULONG             ulPinLength = 8;        // PIN length
    CK_UTF8CHAR_PTR      pcUserPIN2 = (CK_UTF8CHAR_PTR)"01234567";   
	CK_ULONG             ulPinLength2 = 8;        // PIN length

	CK_BBOOL blTrue = CK_TRUE,
            blFalse = CK_FALSE;
	CK_ULONG ulKeyType_Gost2001 = CKK_GOSTR3410,
            ulKeyType_Gost28147 = CKK_GOST28147,
            ulClass_PubKey = CKO_PUBLIC_KEY,
            ulClass_PriKey = CKO_PRIVATE_KEY,
            ulClass_SecKey = CKO_SECRET_KEY;
	// PAR ECC XchA OID
	CK_BYTE gostR3410params[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x24, 0x00 
	};
	// PAR HASH 1 OID
	CK_BYTE gostR3411params[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01 
	};
	// PAR CIPHER A OID
	CK_BYTE gost28147params_A[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01 
	};
	// PAR CIPHER B OID
	CK_BYTE gost28147params[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x02 
	};

	// ltemplate for GOST R 34.10-2001 public key
	CK_ATTRIBUTE caGOST_PublicKeyTemplate[] =
    {
		{CKA_TOKEN,           &blTrue,             sizeof(CK_BBOOL)},
		{CKA_PRIVATE,         &blFalse,            sizeof(CK_BBOOL)},
		{CKA_GOSTR3410_PARAMS, gostR3410params, sizeof(gostR3410params)},
		{CKA_GOSTR3411_PARAMS, gostR3411params,  sizeof(gostR3411params)},
		{CKA_GOST28147_PARAMS, gost28147params_A, sizeof(gost28147params_A)},
    };

	// ltemplate for GOST R 34.10-2001 private key
    CK_ATTRIBUTE caGOST_PrivateKeyTemplate[] =
    {
		{CKA_TOKEN,           &blTrue,             sizeof(CK_BBOOL)},
		{CKA_PRIVATE,         &blTrue,             sizeof(CK_BBOOL)},
		{CKA_DERIVE,			 &blTrue,			  sizeof(CK_BBOOL)},
    };
	// ltemplate for derive key
	CK_ATTRIBUTE    caDeriveKey[] =
	{
		{CKA_CLASS,			&ulClass_SecKey,	sizeof(CK_ULONG)},
		{CKA_KEY_TYPE,		&ulKeyType_Gost28147,	sizeof(CK_ULONG)},
		{CKA_TOKEN,			&blFalse,			sizeof(CK_BBOOL)},
		{CKA_SENSITIVE,		&blFalse,			sizeof(CK_BBOOL)},
		{CKA_EXTRACTABLE,	&blTrue,			sizeof(CK_BBOOL)},
		{CKA_ENCRYPT,		&blTrue,			sizeof(CK_BBOOL)},
		{CKA_DECRYPT,		&blTrue,			sizeof(CK_BBOOL)},
		{CKA_GOST28147PARAMS, gost28147params_A, sizeof(gost28147params_A)},
	};

	CK_ULONG         
        ulPubKeyCount = 
			sizeof(caGOST_PublicKeyTemplate)/sizeof(CK_ATTRIBUTE),
        ulPriKeyCount = 
			sizeof(caGOST_PrivateKeyTemplate)/sizeof(CK_ATTRIBUTE),
        ulDeriveKeyCount = 
			sizeof(caDeriveKey)/sizeof(CK_ATTRIBUTE);

	CK_OBJECT_HANDLE 
                    hSendPubKey = 0, // handle to public key of sender
                    hSendPriKey = 0, // handle to private key of sender
                    hRecpPubKey = 0, // handle to public key of recipient
                    hRecpPriKey = 0, // handle to private key of recipient
                    hSendDH_Key = 0, // Diffy-Hellman key of the sender
                    hRecpDH_Key = 0; // Diffy-Hellman key of the recipient
	CK_MECHANISM     
                    cmKeyGenMechanism, // mechanism for key pair genaration
                    cmDeriveMechanism; // mechanism for key derivation


	CK_ULONG         
                    i;

	CK_BYTE_PTR      
                    pbSecretKeyParam = NULL,
                    pbSend_PubKeyValue = NULL,
                    pbRecp_PubKeyValue = NULL,
                    pbSend_PriKeyValue = NULL,
                    pbRecp_PriKeyValue = NULL,
					pbSendDH_Key_Value = NULL,
					pbRecpDH_Key_Value = NULL;

	CK_ATTRIBUTE 
                    caSecretKeyParam = 
						{CKA_GOST28147PARAMS, pbSecretKeyParam, 0},
                    caSend_PubKeyValue = {CKA_VALUE, pbSend_PubKeyValue, 0},
                    caSend_PriKeyValue = {CKA_VALUE, pbSend_PriKeyValue, 0},
                    caRecp_PubKeyValue = {CKA_VALUE, pbRecp_PubKeyValue, 0},
                    caRecp_PriKeyValue = {CKA_VALUE, pbRecp_PriKeyValue, 0},
					SendDH_Key_Value = {CKA_VALUE, pbSendDH_Key_Value, 0},
					RecpDH_Key_Value = {CKA_VALUE, pbRecpDH_Key_Value, 0};
                    
	// parameters for derivation mechanism
	CK_GOSTR3410_DERIVE_PARAMS_PTR DeriveParams;
	// UKM must be non-zero by RFC 4357
	CK_ULONG ulUKMLen = 8;
    CK_BYTE fixed_ukm[8] = {
        0x9D, 0x23, 0x98, 0xC0, 0x12, 0x31, 0x2A, 0x8E
    };
	char         *TextBlock = 
"This text block will be encrypted and the cipher text will be decrypted.";
	CK_ULONG     ulDataSize = 0;          // size of text data

	CK_BYTE_PTR  pbCipherText = NULL;     // encrypted data
	CK_ULONG     ulCipherSize = 0;        // size of encrypted data
	CK_BYTE_PTR  pbDecryptedText = NULL;  // decrypted data
	CK_ULONG     ulDecryptedDataSize = 0; // size of decrypted data
	CK_CHAR *api_path = PKCS11_API_PATH;
	CK_UTF8CHAR *user_pin = "01234567";
	CK_ULONG slot_num = 0;
	CK_CHAR *api_path2 = PKCS11_API_PATH;
	CK_UTF8CHAR *user_pin2 = "01234567";
	CK_ULONG slot_num2 = 0;
	SYSTEMTIME          t1, t2;
	CK_ULONG            diff/*, min_time, max_time, avg_time*/;

	printf("Starting CKM_GOSTR3410_KEY_DERIVE test\n");

	for (i=1; i<(CK_ULONG)argc; i++) {
		if (strcmp("-api", argv[i]) == 0) {
			++i;
			api_path = argv[i];
		} else if (strcmp("-slot", argv[i]) == 0) {
			++i;
			slot_num = atoi(argv[i]);
		} else if (strcmp("-user_pin", argv[i]) == 0) {
			++i;
			user_pin = argv[i];
		}
	}

	api_path2 = api_path;
	user_pin2 = user_pin;
	slot_num2 = slot_num;
  
#ifdef WIN32
	hPkcsLib = LoadLibrary(api_path);
#else   
	hPkcsLib = dlopen(api_path, RTLD_NOW);
#endif   
	if ( hPkcsLib == NULL ) {
		printf(
			"Can't load PKCS#11 API library. "
			"Check API library path.\n"); 
#ifndef WIN32
		printf("dlerror: %s\n", dlerror());
#endif 	  
		return -1;
	}
#ifdef WIN32
	pcGetFunctionList = 
		(CK_C_GetFunctionList)GetProcAddress(
			hPkcsLib,"C_GetFunctionList");
#else
	pcGetFunctionList = 
		(CK_C_GetFunctionList)dlsym(
			hPkcsLib,"C_GetFunctionList");
#endif   

	// get PKCS #11 function list
	rvResult = pcGetFunctionList(&Pkcs11FuncList);
	 printf("Load PKCS #11 function list result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	// initialize Cryptoki
	rvResult = Pkcs11FuncList->C_Initialize(NULL);
	printf("Initialize Cryptoki result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;
   
	// get slot list
	rvResult = Pkcs11FuncList->C_GetSlotList(CK_FALSE, NULL, &ulSlotCount);
	printf("Get slot list result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	if (ulSlotCount > 0)
	{   // allocate memory for slot list
		pSlotList = (CK_SLOT_ID_PTR) malloc(
			ulSlotCount * sizeof(CK_SLOT_ID));

		rvResult = 
			Pkcs11FuncList->C_GetSlotList(
			CK_FALSE, pSlotList, &ulSlotCount);

		if (rvResult != CKR_OK) return rvResult;
		printf("Slot count: %d\n", ulSlotCount);
	}
	else return -3;
   
	// get information about sender and recipient slots.
	for(i=0; i<ulSlotCount; ++i)
	{  
		rvResult = Pkcs11FuncList->C_GetSlotInfo(pSlotList[i], &SlotInfo);
		if (rvResult == CKR_OK)
		{  // if a token is present in this slot
			if ((SlotInfo.flags & CKF_TOKEN_PRESENT) == CKF_TOKEN_PRESENT)
			{   
				SlotId = pSlotList[i];
				break;
			}
		}
	}
	if (i>=ulSlotCount) {
		printf("No slots with token present\n");
		return -3;
	}

	printf("Slot ID: %d\n", SlotId);

	// open session for slot with ID = SlotId[0]
	rvResult = Pkcs11FuncList->C_OpenSession(SlotId,
		(CKF_SERIAL_SESSION | CKF_RW_SESSION),
		NULL,
		0,
		&hSession);
	printf("Open session result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	// user login
	rvResult = Pkcs11FuncList->C_Login(hSession,
		CKU_USER,
		user_pin,
		strlen(user_pin));
	printf("Login result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;
	// load library PKCS #11
#ifdef WIN32
	hPkcsLib2 = LoadLibrary(api_path2);
#else   
	hPkcsLib2 = dlopen(api_path2, RTLD_NOW);
#endif   
	if ( hPkcsLib2 == NULL ) {
		printf("Can't load PKCS#11 API library. "
			"Check API library path.\n"); 
#ifndef WIN32
		printf("dlerror: %s\n", dlerror());
#endif 	  
		return FALSE;
	}
#ifdef WIN32
	pcGetFunctionList2 = 
		(CK_C_GetFunctionList)GetProcAddress(
			hPkcsLib2,"C_GetFunctionList");
#else
	pcGetFunctionList2 = 
		(CK_C_GetFunctionList)dlsym(
			hPkcsLib2,"C_GetFunctionList");
#endif   

	// get PKCS #11 function list
	rvResult = pcGetFunctionList2(&Pkcs11FuncList2);
	printf("Load PKCS #11 function list result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	// initialize Cryptoki
	rvResult = Pkcs11FuncList2->C_Initialize(NULL);
	printf("Initialize Cryptoki result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK && 
		rvResult != CKR_CRYPTOKI_ALREADY_INITIALIZED) return rvResult;
   
	// get slot list
	rvResult = Pkcs11FuncList2->C_GetSlotList(CK_FALSE, NULL, &ulSlotCount2);
	printf("Get slot list result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	if (ulSlotCount2 > 0)
	{   // allocate memory for slot list
		pSlotList2 = 
			(CK_SLOT_ID_PTR) malloc(
				ulSlotCount2 * sizeof(CK_SLOT_ID));

		rvResult = 
			Pkcs11FuncList2->C_GetSlotList(CK_FALSE, 
				pSlotList2, &ulSlotCount2);

		if (rvResult != CKR_OK) return rvResult;
		printf("Slot count: %d\n", ulSlotCount2);
	}
	else return -3;
   
	// get information about sender and recipient slots.
	for(i=0; i<ulSlotCount2; ++i)
	{  
		rvResult = Pkcs11FuncList2->C_GetSlotInfo(
			pSlotList2[i], &SlotInfo);
		if (rvResult == CKR_OK)
		{  // if a token is present in this slot
			if ((SlotInfo.flags & CKF_TOKEN_PRESENT) == CKF_TOKEN_PRESENT)
			{   
				SlotId2 = pSlotList2[i];
				break;
			}
		}
	}
	if (i>=ulSlotCount2) {
		printf("No slots with token present\n");
		return -3;
	}

	printf("Slot ID 2: %d\n", SlotId2);
	{
		CK_TOKEN_INFO tinfo;
		rvResult = Pkcs11FuncList2->C_GetTokenInfo(pSlotList2[i], &tinfo);
		if (rvResult != CKR_OK) {
			printf("Can't get token info\n");
			return -4;
		}
	}

	// open session for slot with ID = SlotId2[0]
	rvResult = Pkcs11FuncList2->C_OpenSession(SlotId2,
		(CKF_SERIAL_SESSION | CKF_RW_SESSION),
		NULL,
		0,
		&hSession2);
	printf("Open session result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	// User login may return CKR_USER_ALREADY_LOGGED_IN
	// if the same token used for recipient.
	rvResult = Pkcs11FuncList2->C_Login(hSession2,
		CKU_USER,
		user_pin2,
		strlen(user_pin2));
	printf("Login result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK && 
		rvResult != CKR_USER_ALREADY_LOGGED_IN) return rvResult;

	// generate sender key pair with GOST R 34.10-2001
	printf("Generate key pair of sender\n");
	printf("   mechanism type: CKM_GOSTR3410_KEY_PAIR_GEN\n");

	cmKeyGenMechanism.mechanism = CKM_GOSTR3410_KEY_PAIR_GEN;
	cmKeyGenMechanism.pParameter = NULL;
	cmKeyGenMechanism.ulParameterLen = 0;
	rvResult = Pkcs11FuncList->C_GenerateKeyPair(hSession,
		&cmKeyGenMechanism,
		caGOST_PublicKeyTemplate,
		ulPubKeyCount,
		caGOST_PrivateKeyTemplate,
		ulPriKeyCount,
		&hSendPubKey,
		&hSendPriKey);
	printf("   generate sender key pair: result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	printf("   sender public key handle: %d\n", 
		(unsigned long)hSendPubKey);
	printf("   sender private key handle: %d\n", 
		(unsigned long)hSendPriKey);

	// generate recipient key pair with GOST R 34.10-2001
	printf("Generate key pair of recipient\n");
	printf("   mechanism type: CKM_GR3410_KEY_PAIR_GEN\n");

	rvResult = Pkcs11FuncList2->C_GenerateKeyPair(hSession2,
		&cmKeyGenMechanism,
		caGOST_PublicKeyTemplate,
		ulPubKeyCount,
		caGOST_PrivateKeyTemplate,
		ulPriKeyCount,
		&hRecpPubKey,
		&hRecpPriKey);
	printf("   generate recipient key pair: result: 0x%x\n", 
		rvResult);
	if (rvResult != CKR_OK) return rvResult;

	printf("   recipient public key handle: %d\n", 
		(unsigned long)hRecpPubKey);
	printf("   recipient private key handle: %d\n", 
		(unsigned long)hRecpPriKey);

	// get value of sender public key
	printf("Get value of sender public key\n");

	rvResult = Pkcs11FuncList->C_GetAttributeValue(hSession,
		hSendPubKey,
		&caSend_PubKeyValue,
		1);
	if (rvResult == CKR_OK)
	{  
		caSend_PubKeyValue.pValue = 
			(CK_BYTE_PTR) malloc(caSend_PubKeyValue.ulValueLen);
		rvResult = Pkcs11FuncList->C_GetAttributeValue(hSession,
			hSendPubKey,
			&caSend_PubKeyValue,
            1);
	}
	printf("   Get sender public key value: result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	pbSend_PubKeyValue = (CK_BYTE_PTR) caSend_PubKeyValue.pValue;
	printf("   Sender public key value: ");

	for(i=0; i<caSend_PubKeyValue.ulValueLen; ++i)
		printf("%x ", pbSend_PubKeyValue[i]);
	printf("\n");

	// get value of recipient public key
	printf("Get value of recipient public key\n");
	rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSession2,
		hRecpPubKey,
		&caRecp_PubKeyValue,
		1);
	if (rvResult == CKR_OK)
	{  
		caRecp_PubKeyValue.pValue = 
			(CK_BYTE_PTR) malloc(caRecp_PubKeyValue.ulValueLen);     
		rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSession2,
			hRecpPubKey,
			&caRecp_PubKeyValue,
			1);
	}

	printf("   Get recipient public key value: result: 0x%x\n", 
		rvResult);
	if (rvResult != CKR_OK) return rvResult;

	printf("   Recipient public key value: ");
	pbRecp_PubKeyValue = (CK_BYTE_PTR) caRecp_PubKeyValue.pValue;

	for(i=0; i<caRecp_PubKeyValue.ulValueLen; ++i)
		printf("%x ", pbRecp_PubKeyValue[i]);
	printf("\n");

	// fill parameters for derivation mechanism
	DeriveParams = (CK_GOSTR3410_DERIVE_PARAMS_PTR) 
		malloc(sizeof(CK_GOSTR3410_DERIVE_PARAMS));
	DeriveParams->kdf = CKD_CPDIVERSIFY_KDF; 
	DeriveParams->pPublicData = (CK_BYTE_PTR) caRecp_PubKeyValue.pValue;
	DeriveParams->ulPublicDataLen = caRecp_PubKeyValue.ulValueLen;
	DeriveParams->pUKM = fixed_ukm;
	DeriveParams->ulUKMLen = sizeof(fixed_ukm);

	cmDeriveMechanism.mechanism = CKM_GOSTR3410_DERIVE;
	cmDeriveMechanism.pParameter = DeriveParams;
	cmDeriveMechanism.ulParameterLen = sizeof(CK_GOSTR3410_DERIVE_PARAMS);

	printf("Derive Diffie-Hellman key\n");
	printf("   derivation mechanism: CKM_GOSTR3410_DERIVE\n");

	// derive Diffi-Hellman key of sender
	printf("   derive Diffie-Hellman key for sender\n");
    GetSystemTime(&t1);
	rvResult = Pkcs11FuncList->C_DeriveKey(hSession,
       &cmDeriveMechanism,
       hSendPriKey,
       caDeriveKey,
       ulDeriveKeyCount,
       &hSendDH_Key);
    GetSystemTime(&t2);
    diff = process_time(t1, t2);
	fprintf(stderr, "C_DeriveKey time:  %ld msec\n", diff );
	printf("   derive key result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	printf("   sender's Diffie-Hellman key handle: %d\n", 
		(unsigned long)hSendDH_Key);

	// get value of sender derived key
	printf("Get value of sender derived key\n");
	rvResult = Pkcs11FuncList->C_GetAttributeValue(hSession,
		hSendDH_Key,
		&SendDH_Key_Value,
		1);
	if (rvResult == CKR_OK)
	{  
		SendDH_Key_Value.pValue = 
			(CK_BYTE_PTR) malloc(SendDH_Key_Value.ulValueLen);
		rvResult = Pkcs11FuncList->C_GetAttributeValue(hSession,
            hSendDH_Key,
            &SendDH_Key_Value,
            1);
	}

	printf("   Get sender derived key value: result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	pbSendDH_Key_Value = (CK_BYTE_PTR) SendDH_Key_Value.pValue;

	printf("   Sender derived key value: ");

	for(i=0; i<SendDH_Key_Value.ulValueLen; ++i)
		printf("%x ", pbSendDH_Key_Value[i]);
	printf("\n");

	// derive Diffi-Hellman key of recipient
	printf("   derive Diffie-Hellman key for recipient\n");
	DeriveParams->pPublicData = (CK_BYTE_PTR) caSend_PubKeyValue.pValue;
	DeriveParams->ulPublicDataLen = caSend_PubKeyValue.ulValueLen;
   
	rvResult = Pkcs11FuncList2->C_DeriveKey(hSession2,
		&cmDeriveMechanism,
		hRecpPriKey,
		caDeriveKey,
		ulDeriveKeyCount,
		&hRecpDH_Key);
	printf("   derive key result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	printf("   recipient's Diffie-Hellman key handle: %d\n", 
		(unsigned long)hRecpDH_Key);

	// get value of recipient derived key
	printf("Get value of recipient derived key\n");
	rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSession2,
		hRecpDH_Key,
		&RecpDH_Key_Value,
		1);
	if (rvResult == CKR_OK)
	{  
		RecpDH_Key_Value.pValue = 
			(CK_BYTE_PTR) malloc(RecpDH_Key_Value.ulValueLen);
		rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSession2,
			hRecpDH_Key,
			&RecpDH_Key_Value,
			1);
	}

	printf("   Get recipient derived key value: result: 0x%x\n",
		rvResult);
	if (rvResult != CKR_OK) return rvResult;

	pbRecpDH_Key_Value = (CK_BYTE_PTR) RecpDH_Key_Value.pValue;

	printf("   Recipient derived key value: ");

	for(i=0; i<RecpDH_Key_Value.ulValueLen; ++i)
		printf("%x ", pbRecpDH_Key_Value[i]);
	printf("\n");

	// Sender and recipient DH key values must be equal
	if (RecpDH_Key_Value.ulValueLen != SendDH_Key_Value.ulValueLen) {
		printf("Sender and recipient DH keys are of different length\n");
		return -1;
	}
	for(i=0; i<RecpDH_Key_Value.ulValueLen; ++i) {
		if (pbRecpDH_Key_Value[i] != pbSendDH_Key_Value[i]) {
            printf(
			"Sender and recipient KEK keys "
			"are different at position %d\n", i);
            return -1;
		}
	}
	printf("Sender and recipient KEK keys are equal\n");
	printf("CKM_GOSTR3410_KEY_DERIVE test SUCCESS\n");
	rvResult = Pkcs11FuncList->C_DestroyObject(hSession, hSendPubKey);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = Pkcs11FuncList->C_DestroyObject(hSession, hSendPriKey);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);

	rvResult = Pkcs11FuncList2->C_DestroyObject(hSession2, hRecpPubKey);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = Pkcs11FuncList2->C_DestroyObject(hSession2, hRecpPriKey);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);

	rvResult = Pkcs11FuncList->C_DestroyObject(hSession, hSendDH_Key);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);

	rvResult = Pkcs11FuncList2->C_DestroyObject(hSession2, hRecpDH_Key);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = Pkcs11FuncList->C_Logout(hSession);
	printf("Sender logout result: 0x%x\n", rvResult);

	// User logout may return CKR_USER_NOT_LOGGED_IN
	// if the same token used for recipient.
	rvResult = Pkcs11FuncList2->C_Logout(hSession2);
	printf("Recipient logout result: 0x%x\n", rvResult);
	// Close session
	rvResult = Pkcs11FuncList->C_CloseSession(hSession);
	printf("Sender close session result: 0x%x\n", rvResult);

	rvResult = Pkcs11FuncList2->C_CloseSession(hSession2);
	printf("Recipient close session result: 0x%x\n", rvResult);

	return 0;
}
