#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_ULONG             j;
   CK_SLOT_INFO         SlotInfo; 
   CK_SLOT_INFO         SlotInfo2;
   CK_SESSION_HANDLE    hSessionSend, hSessionRecp;

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

   CK_BYTE	            pbPlainText[] = 
	   "This is plaintext for ecryption and decryption";
   CK_ULONG             ulPlainTextSize = sizeof(pbPlainText);   

   CK_BYTE_PTR          pbCipherText = NULL;
   CK_ULONG             ulCipherSize = 0;

   CK_BYTE_PTR          pbDecryptedText = NULL;
   CK_ULONG             ulDecryptedSize = 0; 

   /**************************************************************************/
   CK_BBOOL blTrue = CK_TRUE,
            blFalse = CK_FALSE;
   CK_ULONG ulKeyType_Gost2001 = CKK_GOSTR3410,
            ulKeyType_Gost28147 = CKK_GOST28147,
			ulKeyType_Gost3411	= CKK_GOSTR3411,
            ulClass_PubKey = CKO_PUBLIC_KEY,
            ulClass_PriKey = CKO_PRIVATE_KEY,
            ulClass_SecKey = CKO_SECRET_KEY,
			ulClass_Domain = CKO_DOMAIN_PARAMETERS;
	// PAR ECC A OID
	CK_BYTE gostR3410params[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 
	};
	// 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 
	};
	// template for GOST R 34.10-2001 public key
	CK_ATTRIBUTE caGOST_PublicKeyTemplate[] =
	{
		{CKA_TOKEN,           &blFalse,             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)},
    };
	// template for GOST R 34.10-2001 private key
    CK_ATTRIBUTE caGOST_PrivateKeyTemplate[] =
    {
       {CKA_TOKEN,           &blFalse,             sizeof(CK_BBOOL)},
       {CKA_PRIVATE,         &blFalse,             sizeof(CK_BBOOL)},
       {CKA_DERIVE,          &blTrue,             sizeof(CK_BBOOL)},
//	   {CKA_EXTRACTABLE,     &blTrue,			  sizeof(CK_BBOOL)},
    };
	// template for secret GOST key
	CK_ATTRIBUTE     caGOST_SecretKeyTemplate[] =
    {
		{CKA_CLASS,    &ulClass_SecKey,      sizeof(CK_ULONG)},
        {CKA_KEY_TYPE, &ulKeyType_Gost28147, sizeof(CK_ULONG)},
		{CKA_PRIVATE,  &blFalse,              sizeof(CK_BBOOL)},
		{CKA_ENCRYPT,  &blTrue,              sizeof(CK_BBOOL)},
		{CKA_DECRYPT,  &blTrue,              sizeof(CK_BBOOL)},
		{CKA_WRAP,	&blTrue,				sizeof(CK_BBOOL)},
		{CKA_EXTRACTABLE, &blTrue,			sizeof(CK_BBOOL)},
	    {CKA_GOST28147_PARAMS, gost28147params_A, sizeof(gost28147params_A)},
    };
	// template for derive key
	CK_ATTRIBUTE    caDeriveKey[] =
    {
		{CKA_CLASS,    &ulClass_SecKey,      sizeof(CK_ULONG)},
		{CKA_KEY_TYPE, &ulKeyType_Gost28147, sizeof(CK_ULONG)},
		{CKA_ENCRYPT,  &blTrue,              sizeof(CK_BBOOL)},
		{CKA_DECRYPT,  &blTrue,              sizeof(CK_BBOOL)},
		{CKA_WRAP, &blTrue,	sizeof(CK_BBOOL)},
		{CKA_UNWRAP, &blTrue,	sizeof(CK_BBOOL)},
		{CKA_GOST28147_PARAMS, gost28147params_A, sizeof(gost28147params_A) },
    };
	CK_ULONG   ulPubKeyCount = 
	    sizeof(caGOST_PublicKeyTemplate)/sizeof(CK_ATTRIBUTE),
	ulPriKeyCount = 
		sizeof(caGOST_PrivateKeyTemplate)/sizeof(CK_ATTRIBUTE),
    ulSecKeyCount = 
		sizeof(caGOST_SecretKeyTemplate)/sizeof(CK_ATTRIBUTE),
    ulDeriveKeyCount = 
		sizeof(caDeriveKey)/sizeof(CK_ATTRIBUTE);
	CK_OBJECT_HANDLE hKeyForWU   = 0, // key which will be wrapped and unwrapped
                    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
                    hUnwrappedSendKey = 0, // unwrapped key of the sender
                    hUnwrappedRecpKey = 0; // unwrapped key of the recipient

	CK_MECHANISM     cmKeyGenMechanism, // mechanism for key pair genaration
                    cmWrapMechanism,   // mechanism for key wrap/unwrap
                    cmDeriveMechanism, // mechanism for key derivation
                    cmCryptMechanism;  // mechanism for encrypt/decrypt

	CK_BYTE_PTR      pbWrappedKeySend;      // wrapped key

	CK_ULONG         ulWrappedKeyLen,   // length of wrapped key
                    i;

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

	CK_BYTE_PTR		pbImitValue = NULL;
	CK_ULONG			ulImitSize = 0;
	CK_KEY_TYPE		keyType = 0;
	CK_ATTRIBUTE 
		caSecretKeyParam = {CKA_GOST28147_PARAMS, pbSecretKeyParam, 0},
		// template for value of sender public key
		caSend_PubKeyValue = {CKA_VALUE, pbSend_PubKeyValue, 0},
        caSend_PriKeyValue = {CKA_VALUE, pbSend_PriKeyValue, 0},
        // template for value of recipient public key
        caRecp_PubKeyValue = {CKA_VALUE, pbRecp_PubKeyValue, 0},
        caRecp_PriKeyValue = {CKA_VALUE, pbRecp_PriKeyValue, 0},
		caSecret_Key_Value = {CKA_VALUE, pbSecret_Key_Value, 0},
		caKeyType = {CKA_KEY_TYPE, &keyType, sizeof(keyType)},
		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 RFC4357
	CK_BYTE UKM[8] = {0x28,0xaf,0xc5,0x50,0x9d,0x0c,0x74,0xb3};
	CK_ULONG ulUKMLen = 8;
	// parameters for wrapping mechanism
	CK_BYTE pWrapIV[] = {
		0x56, 0xfa, 0x7d, 0x54, 0xb2, 0x90, 0x2c, 0x41
	};
	CK_BYTE iv[] = {
		0x37, 0x2a, 0x7f, 0x00, 0x2c, 0xea, 0x7d, 0x39
	};

	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;

	printf("Starting CKM_GOST28147_KEY_WRAP 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: sender %d\n", SlotId);

	// 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_TRUE, 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_TRUE, pSlotList2, &ulSlotCount2);

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

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

	cmKeyGenMechanism.mechanism = CKM_GOST28147_KEY_GEN;
	cmKeyGenMechanism.pParameter = NULL; //gost28147params_A;
	cmKeyGenMechanism.ulParameterLen = 0; //sizeof(gost28147params_A);

	// generate secret key which will be wrapped and unwrapped
	printf(
		"Sender generate key for wrapping and unwrapping (hKeyForWU)\n");
	printf("   mechanism type: CKM_GOST28147_KEY_GEN\n");

	rvResult = Pkcs11FuncList->C_GenerateKey(hSessionSend,
         &cmKeyGenMechanism,
         caGOST_SecretKeyTemplate,
         ulSecKeyCount,
         &hKeyForWU);
	printf("   Sender generate key hKeyForWU "
		"for wrapping and unwrapping: result: 0x%x\n", 
		rvResult);
	if (rvResult != CKR_OK) return rvResult;

	printf("   key handle: %d\n", (unsigned long)hKeyForWU);
	{
		CK_BYTE value[32];
		CK_ATTRIBUTE attr_get[] = {
			{CKA_VALUE, NULL, 0},
		};
		attr_get[0].pValue = value;
		attr_get[0].ulValueLen = sizeof(value);
		rvResult = Pkcs11FuncList->C_GetAttributeValue(
			hSessionSend, hKeyForWU,
			attr_get, sizeof(attr_get)/sizeof(CK_ATTRIBUTE));
		printf("   key to wrap:\n");
		print_hex(value, attr_get[0].ulValueLen);
	}
	printf("   wrapping IV:\n");
	print_hex(pWrapIV, sizeof(pWrapIV));

	// 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(hSessionSend,
        &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(hSessionRecp,
        &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( hSessionSend,
        hSendPubKey,
        &caSend_PubKeyValue,
        1);
	if (rvResult == CKR_OK)
	{  
		caSend_PubKeyValue.pValue = 
			(CK_BYTE_PTR) malloc(caSend_PubKeyValue.ulValueLen);
		rvResult = Pkcs11FuncList->C_GetAttributeValue(hSessionSend,
            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: \n");
	print_hex(pbSend_PubKeyValue, caSend_PubKeyValue.ulValueLen);

	// get value of recipient public key
	printf("Get value of recipient public key\n");
	rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSessionRecp,
        hRecpPubKey,
        &caRecp_PubKeyValue,
        1);
	if (rvResult == CKR_OK)
	{  
		caRecp_PubKeyValue.pValue = 
			(CK_BYTE_PTR) malloc(caRecp_PubKeyValue.ulValueLen);
		rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSessionRecp,
			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: \n");
	pbRecp_PubKeyValue = (CK_BYTE_PTR) caRecp_PubKeyValue.pValue;
	print_hex(pbRecp_PubKeyValue, caRecp_PubKeyValue.ulValueLen);

	// 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 = UKM;
	DeriveParams->ulUKMLen = ulUKMLen;

	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");
	rvResult = Pkcs11FuncList->C_DeriveKey(hSessionSend,
        &cmDeriveMechanism,
        hSendPriKey,
        caDeriveKey,
        ulDeriveKeyCount,
        &hSendDH_Key);
	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);
	{
		CK_BYTE value[32];
		CK_ATTRIBUTE attr_get[] = {
			{CKA_VALUE, NULL, 0},
		};
		attr_get[0].pValue = value;
		attr_get[0].ulValueLen = sizeof(value);
		rvResult = Pkcs11FuncList->C_GetAttributeValue(hSessionSend, 
			hSendDH_Key,
			attr_get, sizeof(attr_get)/sizeof(CK_ATTRIBUTE));
		printf("   sender KEK:\n");
		print_hex(value, attr_get[0].ulValueLen);
	}

	// 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(hSessionRecp,
        &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);
	{
		CK_BYTE value[32];
		CK_ATTRIBUTE attr_get[] = {
			{CKA_VALUE, NULL, 0},
		};
		attr_get[0].pValue = value;
		attr_get[0].ulValueLen = sizeof(value);
		rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSessionRecp, hRecpDH_Key,
			attr_get, sizeof(attr_get)/sizeof(CK_ATTRIBUTE));
		printf("   recipient KEK:\n");
		print_hex(value, attr_get[0].ulValueLen);
	}

	// wrap key hKeyForWU with sender Diffi-Hellman key hSendDH_Key
	// Здесь используется UKM в качестве IV в соответствии с RFC 4357
	cmWrapMechanism.mechanism = CKM_GOST28147_KEY_WRAP;
	cmWrapMechanism.pParameter = UKM;
	cmWrapMechanism.ulParameterLen = ulUKMLen;

	printf("Wrap key hKeyForWU with sender Diffi-Hellman key\n");

	rvResult = Pkcs11FuncList->C_WrapKey(hSessionSend,
        &cmWrapMechanism,
        hSendDH_Key,
        hKeyForWU,
        NULL,
        &ulWrappedKeyLen);
	if (rvResult == CKR_OK)
	{  	   
		pbWrappedKeySend = (CK_BYTE_PTR) malloc(ulWrappedKeyLen);
		rvResult = Pkcs11FuncList->C_WrapKey(hSessionSend,
            &cmWrapMechanism,
            hSendDH_Key,
            hKeyForWU,
            pbWrappedKeySend,
            &ulWrappedKeyLen);
	}

	printf("   wrap result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	printf("   wrapped key:\n");
	print_hex(pbWrappedKeySend, ulWrappedKeyLen);

	// unwrap wrapped key with recipient Diffie-Hellman key
	printf("Unwrap key hUnwrappedRecpKey "
		"from pbWrappedKeySend with recipient Diffie-Hellman key\n");

	rvResult = Pkcs11FuncList2->C_UnwrapKey(hSessionRecp,
        &cmWrapMechanism,
        hRecpDH_Key,
        pbWrappedKeySend,
        ulWrappedKeyLen,
        caGOST_SecretKeyTemplate,
        ulSecKeyCount,
        &hUnwrappedRecpKey);
	printf("   unwrap key: result: 0x%x\n", rvResult);
	if (rvResult != CKR_OK) return rvResult;

	printf("   key handle (hUnwrappedRecpKey): %d\n", 
		(unsigned long)hUnwrappedRecpKey);
	{
		CK_BYTE value[32];
		CK_ATTRIBUTE attr_get[] = {
			{CKA_VALUE, NULL, 0},
		};
		attr_get[0].pValue = value;
		attr_get[0].ulValueLen = sizeof(value);
		rvResult = Pkcs11FuncList2->C_GetAttributeValue(
			hSessionRecp, hUnwrappedRecpKey,
			attr_get, sizeof(attr_get)/sizeof(CK_ATTRIBUTE));
		printf("   recipient unvrapped key:\n");
		print_hex(value, attr_get[0].ulValueLen);
	}

	printf("Sender encrypt with hKeyForWU:\n");
	cmCryptMechanism.mechanism = CKM_GOST28147;
	cmCryptMechanism.pParameter = iv;
	cmCryptMechanism.ulParameterLen = sizeof(iv);
	printf("   mechanism type: CKM_GOST28147\n");
	printf("   plain text:\n%s\n", pbPlainText);
	print_hex(pbPlainText, strlen(pbPlainText));

	rvResult = Pkcs11FuncList->C_EncryptInit(hSessionSend,
        &cmCryptMechanism,
        hKeyForWU);
	printf("   encrypt initialization result: 0x%x\n", rvResult);

	if (rvResult == CKR_OK)
	{  
		rvResult = Pkcs11FuncList->C_Encrypt(hSessionSend,
            pbPlainText,
            ulPlainTextSize,
            NULL,
            &ulCipherSize);


		if (rvResult == CKR_OK)
		{  
			printf("   cipher size: %d\n", ulCipherSize);

			pbCipherText = (CK_BYTE_PTR) 
				malloc(ulCipherSize * sizeof(CK_BYTE));
			rvResult = Pkcs11FuncList->C_Encrypt(hSessionSend,
				pbPlainText,
                ulPlainTextSize,
                pbCipherText,
                &ulCipherSize);
		}
	}
	else return rvResult;

	printf("   encrypt result: 0x%x\n", rvResult);

	if (rvResult != CKR_OK) return rvResult;

	printf("   cipher text: \n");
	print_hex(pbCipherText, ulCipherSize);

	printf("Recipient decrypt with hUnwrappedRecpKey:\n");
	rvResult = Pkcs11FuncList2->C_DecryptInit(hSessionRecp,
        &cmCryptMechanism,
        hUnwrappedRecpKey);
	printf("   decrypt initialization result: 0x%x\n", rvResult);

	if (rvResult == CKR_OK)
	{  
		rvResult = Pkcs11FuncList2->C_Decrypt(hSessionRecp,
            pbCipherText,
            ulCipherSize,
            NULL,
            &ulDecryptedSize);


		if (rvResult == CKR_OK)
		{  
			printf("   decrypted text size: %d\n", ulDecryptedSize);

			pbDecryptedText = (CK_BYTE_PTR) 
				malloc(ulDecryptedSize * sizeof(CK_BYTE));
			rvResult = Pkcs11FuncList2->C_Decrypt(hSessionRecp,
                pbCipherText,
                ulCipherSize,
                pbDecryptedText,
                &ulDecryptedSize);
		}
	}
	else return rvResult;

	printf("   decrypt result: 0x%x\n", rvResult);

	if (rvResult != CKR_OK) return rvResult;

	printf("   decrypted text: \n");
	print_hex(pbDecryptedText, ulDecryptedSize);

	if (ulDecryptedSize != ulPlainTextSize) {
		fprintf(stderr, "Invalid decrypted text size\n");
		return -1;
	}

	if (memcmp(pbDecryptedText, pbPlainText, ulPlainTextSize) != 0) {
		fprintf(stderr, "Invalid decrypted text\n");
		return -1;
	}
	printf("CKM_GOST28147_KEY_WRAP test SUCCESS\n");

	rvResult = 
		Pkcs11FuncList->C_DestroyObject(hSessionSend, hKeyForWU);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = 
		Pkcs11FuncList->C_DestroyObject(hSessionSend, hSendPubKey);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = 
		Pkcs11FuncList->C_DestroyObject(hSessionSend, hSendPriKey);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = 
		Pkcs11FuncList2->C_DestroyObject(hSessionRecp, hRecpPubKey);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = 
		Pkcs11FuncList2->C_DestroyObject(hSessionRecp, hRecpPriKey);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = 
		Pkcs11FuncList->C_DestroyObject(hSessionSend, hSendDH_Key);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = 
		Pkcs11FuncList2->C_DestroyObject(hSessionRecp, hRecpDH_Key);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = 
		Pkcs11FuncList2->C_DestroyObject(hSessionRecp, hUnwrappedRecpKey);
	printf("   C_DestroyObject result: 0x%x\n", rvResult);
	rvResult = Pkcs11FuncList->C_CloseSession(hSessionSend);
	printf("Sender close session result: 0x%x\n", rvResult);

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

	printf("SUCCESS\n");
	return 0;
}
