#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      pcDefaultSOPIN = (CK_UTF8CHAR_PTR)"87654321";   
    CK_UTF8CHAR_PTR      pcSOPIN = (CK_UTF8CHAR_PTR)"76543210";   
    CK_UTF8CHAR_PTR      pcDefaultUserPIN = (CK_UTF8CHAR_PTR)"12345678";   
    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},
                    // ltemplate for value of sender public key
                    caSend_PubKeyValue = {CKA_VALUE, pbSend_PubKeyValue, 0},
                    caSend_PriKeyValue = {CKA_VALUE, pbSend_PriKeyValue, 0},
                    // ltemplate 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;
//#define SYSTEMTIME  struct timeb
//#define GetSystemTime(x)   ftime(x)
//SYSTEMTIME          t1, t2;
//CK_ULONG            diff, min_time, max_time, avg_time;

  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;
  
   // load library PKCS #11
#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 and slot libraries paths. "
		  "Check if Slot Manager started.\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 and slot libraries paths. "
		  "Check if Slot Manager started.\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);

//   for (i=0; i<10; i++) {

   // 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;
/*
   // user login
   rvResult = Pkcs11FuncList->C_Login(hSessionSend,
                                      CKU_USER,
                                      user_pin,
                                      strlen(user_pin));
   printf("Sender login 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;
/*
        // user login
        rvResult = Pkcs11FuncList2->C_Login(hSessionRecp,
                                      CKU_USER,
                                      user_pin2,
                                      strlen(user_pin2));
        printf("Recipient login result: 0x%x\n", rvResult);
        if (rvResult != CKR_OK && 
			rvResult != CKR_USER_ALREADY_LOGGED_IN) 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);

//   avg_time = 0;
//   max_time = 0;
//   min_time = 0xFFFFFFFF;
//   for (i=0; i < 10000; i++) {
//      GetSystemTime(&t1);
      rvResult = Pkcs11FuncList->C_WrapKey(hSessionSend,
                                        &cmWrapMechanism,
                                        hSendDH_Key,
                                        hKeyForWU,
                                        pbWrappedKeySend,
                                        &ulWrappedKeyLen);
//      GetSystemTime(&t2);
//      diff = process_time(t1, t2);
//      avg_time += diff;
//      if (diff < min_time)
//         min_time = diff;
//      if (diff > max_time)
//         max_time = diff;
//   }
//   printf("10000 GOST 28147-89 Wrap operations:  %ld \n", avg_time );
//   printf("Minimum:                        %ld \n", min_time );
//   printf("Maximum:                        %ld \n", max_time );
//   printf("\n");
   }

   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_Logout(hSessionSend);
   printf("Sender logout result: 0x%x\n", rvResult);
   rvResult = Pkcs11FuncList2->C_Logout(hSessionRecp);
   printf("Recipient logout result: 0x%x\n", rvResult);
*/
   // Close session
   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;
}
