#include "test_common.h"

void usage(void);
CK_RV test_crypto();
int test_gost3411(CK_SESSION_HANDLE hSession);

CK_SLOT_ID SlotId;

CK_SLOT_ID_PTR SlotList = NULL;
CK_ULONG SlotCount = 0;

int main(int argc, char *argv[]) {
   CK_RV rc = 1;
   CK_FLAGS flags = 0;
   unsigned long slot_num = 0;  
   int i;

   printf("CKM_GOSTR3411 test\n");

   // parse the command line parameters
	for (i=1; i<argc; i++) {
		if (strcmp("-api", argv[i]) == 0) {
			i++;
			api_path = argv[i];
		} else if (strcmp("-user_pin", argv[i]) == 0) {
			i++;
			user_pin = argv[i];
		}
	}

   // load the PKCS11 library 
	rc = init(api_path, &funcs, NULL);
   if (rc != CKR_OK) {
      fprintf(stderr, "ERROR calling init, rc = %p.\n", (void *)rc);
      return rc;
   }
	rc = get_slot_list(funcs, 
                    &SlotList, 
                    &SlotCount);
	if (rc != CKR_OK) {
		printf("get_slot_list failed, rc = 0x%x\n", rc );
		return rc;
	}
	rc = find_slot_with_token(funcs, 
                           SlotList, 
                           SlotCount, 
                           &SlotId);
	if (rc != CKR_OK) {
		printf("find_slot_with_token failed, rc = 0x%x\n", rc );
		return rc;
	}
		// test the crypto functions 
		rc = test_crypto();
		if (rc != CKR_OK) {
			fprintf(stderr, "test_crypto failed.\n");
			return rc;
		}
//   }
   printf("CKM_GOSTR3411 test SUCCESS\n");
   return CKR_OK;
}


void usage(void)
{
   fprintf(stderr, "Usage: lissi [-c <slotId>]\n"
      "Default slotID is 0\n"
      "To get a list of slotIDs, call pkcs11_cfg -s\n");
   exit(-1);
}

CK_RV test_crypto() 
{
	CK_RV rc, ret;
	CK_SESSION_HANDLE hSession;
	CK_MECHANISM_INFO minfo;
	int i;

   // open a R/W cryptoki session, CKR_SERIAL_SESSION is a legacy bit we have to set
   rc = funcs->C_OpenSession(SlotId, CKF_RW_SESSION | CKF_SERIAL_SESSION, NULL_PTR,
         NULL_PTR, &hSession);
   if (rc != CKR_OK) {
      fprintf(stderr, "ERROR call to C_OpenSession failed, rc = %p\n", (void *)rc);
      goto out;
   }
  fprintf(stderr, "C_OpenSession success\n");
/*
   // log in as normal user 
   rc = funcs->C_Login(hSession, CKU_USER, user_pin, strlen(user_pin));
//   rc = funcs->C_Login(hSession, CKU_USER, "1234567890", 10);
   if (rc != CKR_OK) {
      fprintf(stderr, "ERROR call to C_Login failed, rc = %p\n", (void *)rc);
      goto out_close;
   }
  fprintf(stderr, "C_Login success\n");
*/
	rc = funcs->C_GetMechanismInfo(SlotId, CKM_GOSTR3411, &minfo);
	if (rc != CKR_OK) {
		fprintf(stderr, "\n======== Mechanism CKM_GOSTR3411 not supported ===========\n");
	} else {
//		for (i=0; i<40; i++) {
			fprintf(stderr, "\n==================== CKM_GOSTR3411 test ==================\n");
				rc = test_gost3411(hSession);
			if (rc != CKR_OK) {
				fprintf(stderr, "ERROR CKM_GOSTR3411 failed, rc = %p\n", (void *)rc);
			} else {
				fprintf(stderr, "CKM_GOSTR3411 test passed.\n");
			}
//		}
	}  
 
//out_close:
   if( (ret = funcs->C_CloseSession(hSession)) != CKR_OK ) {
      fprintf(stderr, "Error: C_CloseSession failed with %p\n", (void *)ret);
	  rc = ret;
   }
   else {
      fprintf(stderr, "C_CloseSession success\n");
   }

out:
   return rc;
}

int test_gost3411(CK_SESSION_HANDLE hSession)
{
	CK_RV rc;
	CK_BYTE value[256];
	CK_BYTE *state;
	CK_ULONG len, state_len;
	CK_OBJECT_HANDLE paramh = CK_INVALID_HANDLE;
   	CK_MECHANISM mechanism_desc = {CKM_GOSTR3411, NULL, 0};
   	CK_MECHANISM_PTR mechanism = &mechanism_desc;

	static CK_KEY_TYPE key_type = CKK_GOSTR3411;
	// CryptoPro gost3411 Test Param Set
	static CK_BYTE oid[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x00 
	};
	static CK_BYTE data1[] = 
		"Suppose the original message has length = 50 bytes";
	static CK_BYTE et1[] = {
		0x47,0x1a,0xba,0x57,0xa6,0x0a,0x77,0x0d,
		0x3a,0x76,0x13,0x06,0x35,0xc1,0xfb,0xea,
		0x4e,0xf1,0x4d,0xe5,0x1f,0x78,0xb4,0xae,
		0x57,0xdd,0x89,0x3b,0x62,0xf5,0x52,0x08
	};
	static CK_BYTE data2[] = {
		0xc3,0x73,0x0c,0x5c,0xbc,0xca,0xcf,0x91,
		0x5a,0xc2,0x92,0x67,0x6f,0x21,0xe8,0xbd,
		0x4e,0xf7,0x53,0x31,0xd9,0x40,0x5e,0x5f,
		0x1a,0x61,0xdc,0x31,0x30,0xa6,0x50,0x11
	};
	static CK_BYTE et2[] = {
		0x38,0x65,0x45,0xc7,0x71,0x4f,0x6d,0x0b,
		0xf1,0x27,0xad,0x57,0xca,0xc0,0x92,0x0a,
		0xa3,0xd8,0x84,0x62,0xfa,0x8a,0x0e,0x1d,
		0x30,0xd5,0xcd,0x5a,0x85,0x84,0xaf,0xf1
	};

	static CK_BYTE data3[] = {
		0x30,0x82,0x03,0x9c,0xa0,0x03,0x02,0x01,
		0x02,0x02,0x10,0x3a,0xc3,0xb8,0xac,0xec,
		0xfb,0xd7,0xae,0x28,0xb5,0x92,0x9f,0xd2,
		0xec,0x4c,0xf3,0x30,0x08,0x06,0x06,0x2a,
		0x85,0x03,0x02,0x02,0x03,0x30,0x81,0xec,
		0x31,0x19,0x30,0x17,0x06,0x09,0x2a,0x86,
		0x48,0x86,0xf7,0x0d,0x01,0x09,0x01,0x16,
		0x0a,0x67,0x64,0x75,0x63,0x40,0x63,0x61,
		0x2e,0x72,0x75,0x31,0x0b,0x30,0x09,0x06,
		0x03,0x55,0x04,0x06,0x13,0x02,0x52,0x55,
		0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,
		0x07,0x0c,0x0c,0xd0,0x9c,0xd0,0xbe,0xd1,
		0x81,0xd0,0xba,0xd0,0xb2,0xd0,0xb0,0x31,
		0x54,0x30,0x52,0x06,0x03,0x55,0x04,0x0a,
		0x0c,0x4b,0xd0,0x93,0xd0,0xbb,0xd0,0xb0,
		0xd0,0xb2,0xd0,0xbd,0xd1,0x8b,0xd0,0xb9,
		0x20,0xd0,0x94,0xd0,0xbe,0xd0,0xb2,0xd0,
		0xb5,0xd1,0x80,0xd0,0xb5,0xd0,0xbd,0xd0,
		0xbd,0xd1,0x8b,0xd0,0xb9,0x20,0xd0,0xa3,
		0xd0,0xb4,0xd0,0xbe,0xd1,0x81,0xd1,0x82,
		0xd0,0xbe,0xd0,0xb2,0xd0,0xb5,0xd1,0x80,
		0xd1,0x8f,0xd1,0x8e,0xd1,0x89,0xd0,0xb8,
		0xd0,0xb9,0x20,0xd0,0xa6,0xd0,0xb5,0xd0,
		0xbd,0xd1,0x82,0xd1,0x80,0x31,0x2c,0x30,
		0x2a,0x06,0x03,0x55,0x04,0x0b,0x0c,0x23,
		0xd0,0xa6,0xd0,0xb5,0xd0,0xbd,0xd1,0x82,
		0xd1,0x80,0x20,0xd0,0xa1,0xd0,0xb5,0xd1,
		0x80,0xd1,0x82,0xd0,0xb8,0xd1,0x84,0xd0,
		0xb8,0xd0,0xba,0xd0,0xb0,0xd1,0x86,0xd0,
		0xb8,0xd0,0xb8,0x31,0x27,0x30,0x25,0x06,
		0x03,0x55,0x04,0x03,0x0c,0x1e,0xd0,0x93,
		0xd0,0x94,0xd0,0xa3,0xd0,0xa6,0x20,0xd0,
		0xa1,0xd1,0x82,0xd0,0xb0,0xd0,0xbd,0xd0,
		0xb4,0xd0,0xb0,0xd1,0x80,0xd1,0x82,0x20,
		0xd0,0xa3,0xd0,0xa6,0x30,0x1e,0x17,0x0d,
		0x30,0x34,0x30,0x31,0x30,0x39,0x31,0x32,
		0x33,0x33,0x32,0x39,0x5a,0x17,0x0d,0x31,
		0x34,0x30,0x31,0x30,0x36,0x31,0x32,0x33,
		0x33,0x32,0x39,0x5a,0x30,0x81,0xec,0x31,
		0x19,0x30,0x17,0x06,0x09,0x2a,0x86,0x48,
		0x86,0xf7,0x0d,0x01,0x09,0x01,0x16,0x0a,
		0x67,0x64,0x75,0x63,0x40,0x63,0x61,0x2e,
		0x72,0x75,0x31,0x0b,0x30,0x09,0x06,0x03,
		0x55,0x04,0x06,0x13,0x02,0x52,0x55,0x31,
		0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x07,
		0x0c,0x0c,0xd0,0x9c,0xd0,0xbe,0xd1,0x81,
		0xd0,0xba,0xd0,0xb2,0xd0,0xb0,0x31,0x54,
		0x30,0x52,0x06,0x03,0x55,0x04,0x0a,0x0c,
		0x4b,0xd0,0x93,0xd0,0xbb,0xd0,0xb0,0xd0,
		0xb2,0xd0,0xbd,0xd1,0x8b,0xd0,0xb9,0x20,
		0xd0,0x94,0xd0,0xbe,0xd0,0xb2,0xd0,0xb5,
		0xd1,0x80,0xd0,0xb5,0xd0,0xbd,0xd0,0xbd,
		0xd1,0x8b,0xd0,0xb9,0x20,0xd0,0xa3,0xd0,
		0xb4,0xd0,0xbe,0xd1,0x81,0xd1,0x82,0xd0,
		0xbe,0xd0,0xb2,0xd0,0xb5,0xd1,0x80,0xd1,
		0x8f,0xd1,0x8e,0xd1,0x89,0xd0,0xb8,0xd0,
		0xb9,0x20,0xd0,0xa6,0xd0,0xb5,0xd0,0xbd,
		0xd1,0x82,0xd1,0x80,0x31,0x2c,0x30,0x2a,
		0x06,0x03,0x55,0x04,0x0b,0x0c,0x23,0xd0,
		0xa6,0xd0,0xb5,0xd0,0xbd,0xd1,0x82,0xd1,
		0x80,0x20,0xd0,0xa1,0xd0,0xb5,0xd1,0x80,
		0xd1,0x82,0xd0,0xb8,0xd1,0x84,0xd0,0xb8,
		0xd0,0xba,0xd0,0xb0,0xd1,0x86,0xd0,0xb8,
		0xd0,0xb8,0x31,0x27,0x30,0x25,0x06,0x03,
		0x55,0x04,0x03,0x0c,0x1e,0xd0,0x93,0xd0,
		0x94,0xd0,0xa3,0xd0,0xa6,0x20,0xd0,0xa1,
		0xd1,0x82,0xd0,0xb0,0xd0,0xbd,0xd0,0xb4,
		0xd0,0xb0,0xd1,0x80,0xd1,0x82,0x20,0xd0,
		0xa3,0xd0,0xa6,0x30,0x63,0x30,0x1c,0x06,
		0x06,0x2a,0x85,0x03,0x02,0x02,0x13,0x30,
		0x12,0x06,0x07,0x2a,0x85,0x03,0x02,0x02,
		0x23,0x01,0x06,0x07,0x2a,0x85,0x03,0x02,
		0x02,0x1e,0x01,0x03,0x43,0x00,0x04,0x40,
		0x50,0xab,0x7f,0xc4,0xcc,0x3d,0xd0,0xe2,
		0xdd,0x86,0xda,0x19,0x6b,0x14,0x8c,0x78,
		0xd9,0xca,0x58,0x67,0x62,0xf3,0xb7,0xba,
		0x7b,0x2a,0xda,0xc1,0x9c,0x3f,0x87,0xeb,
		0xf1,0xdc,0xaf,0x35,0xad,0x2d,0xe1,0xca,
		0xed,0xc1,0x8b,0x82,0xde,0xa0,0x8b,0x95,
		0xdd,0xa2,0xac,0x46,0x6a,0x8e,0xce,0x5d,
		0x5a,0x16,0xba,0x03,0x29,0x72,0x38,0x27,
		0xa3,0x82,0x01,0x14,0x30,0x82,0x01,0x10,
		0x30,0x5a,0x06,0x03,0x55,0x1d,0x11,0x01,
		0x01,0xff,0x04,0x50,0x30,0x4e,0x81,0x0b,
		0x67,0x64,0x75,0x63,0x63,0x40,0x75,0x63,
		0x2e,0x72,0x75,0xa4,0x3f,0x30,0x3d,0x31,
		0x3b,0x30,0x39,0x06,0x03,0x55,0x04,0x03,
		0x0c,0x32,0xd0,0x9f,0xd0,0xb5,0xd1,0x80,
		0xd0,0xb2,0xd0,0xbe,0xd0,0xb5,0x20,0xd0,
		0xa3,0xd0,0xbf,0xd0,0xbe,0xd0,0xbb,0xd0,
		0xbd,0xd0,0xbe,0xd0,0xbc,0xd0,0xbe,0xd1,
		0x87,0xd0,0xb5,0xd0,0xbd,0xd0,0xbd,0xd0,
		0xbe,0xd0,0xb5,0x20,0xd0,0x9b,0xd0,0xb8,
		0xd1,0x86,0xd0,0xbe,0x30,0x0f,0x06,0x03,
		0x55,0x1d,0x0f,0x01,0x01,0xff,0x04,0x05,
		0x03,0x03,0x07,0xc6,0x00,0x30,0x0f,0x06,
		0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,
		0x05,0x30,0x03,0x01,0x01,0xff,0x30,0x1d,
		0x06,0x03,0x55,0x1d,0x0e,0x04,0x16,0x04,
		0x14,0xb1,0x6e,0x0e,0xa4,0x40,0xbc,0xf0,
		0xd9,0xb6,0xf7,0xef,0xfa,0xf0,0x3d,0xa1,
		0x0c,0xd2,0x8f,0xf1,0xb6,0x30,0x71,0x06,
		0x03,0x55,0x1d,0x1f,0x04,0x6a,0x30,0x68,
		0x30,0x66,0xa0,0x64,0xa0,0x62,0x86,0x60,
		0x6c,0x64,0x61,0x70,0x3a,0x2f,0x2f,0x31,
		0x39,0x32,0x2e,0x31,0x36,0x38,0x2e,0x36,
		0x38,0x2e,0x37,0x30,0x2f,0x6f,0x3d,0x72,
		0x6f,0x6f,0x74,0x2c,0x63,0x3d,0x72,0x75,
		0x3f,0x63,0x65,0x72,0x74,0x69,0x66,0x69,
		0x63,0x61,0x74,0x65,0x52,0x65,0x76,0x6f,
		0x63,0x61,0x74,0x69,0x6f,0x6e,0x4c,0x69,
		0x73,0x74,0x3f,0x62,0x61,0x73,0x65,0x3f,
		0x6f,0x62,0x6a,0x65,0x63,0x74,0x63,0x6c,
		0x61,0x73,0x73,0x3d,0x63,0x52,0x4c,0x44,
		0x69,0x73,0x74,0x72,0x69,0x62,0x75,0x74,
		0x69,0x6f,0x6e,0x50,0x6f,0x69,0x6e,0x74
	};
	static CK_BYTE et3[] = {
		0xee,0x8c,0xd9,0x40,0x23,0xdd,0x9a,0xf8,
		0x17,0xdd,0xe8,0x1f,0x02,0x25,0x5b,0xb3,
		0xaa,0x6b,0xd3,0x21,0x09,0x41,0x95,0x7e,
		0xea,0x7e,0x0e,0x3c,0x35,0x9f,0x04,0x5a
	};
	// CryptoPro gostR3411 A Param Set
	static CK_BYTE oid_default[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01 
	};
	// S-Terra plugin test data
	static CK_BYTE data6[] = {
	    0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
	    0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
	    0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
	    0x67, 0x68, 0x69, 0x6A, 0x68, 0x69, 0x6A, 0x6B,
	    0x69, 0x6A, 0x6B, 0x6C, 0x6A, 0x6B, 0x6C, 0x6D,
	    0x6B, 0x6C, 0x6D, 0x6E, 0x6C, 0x6D, 0x6E, 0x6F,
	    0x6D, 0x6E, 0x6F, 0x70, 0x6E, 0x6F, 0x70, 0x71,
	    0x0A
	};
	static CK_BYTE et6[] = {
	    0xc8, 0x77, 0xc2, 0xd1, 0x7f, 0xd3, 0x99, 0x2e,
	    0x7a, 0x97, 0xc5, 0x67, 0x07, 0xdf, 0x57, 0xc0,
	    0x5b, 0xdc, 0xf2, 0x17, 0x34, 0x6a, 0x69, 0x2f,
	    0x6b, 0x9a, 0xad, 0xc4, 0x47, 0xa8, 0x2f, 0xd2
	};
	
//#define SYSTEMTIME  struct timeb
//#define GetSystemTime(x)   ftime(x)
//SYSTEMTIME          t1, t2;
//CK_ULONG            diff, min_time, max_time, avg_time;
//====================================================	
	memset(value,0,sizeof(value));

	mechanism->pParameter = oid;
	mechanism->ulParameterLen = sizeof(oid);
//   avg_time = 0;
//   max_time = 0;
//   min_time = 0xFFFFFFFF;
//   for (i=0; i < 1000; i++) {
//      GetSystemTime(&t1);
	rc = funcs->C_DigestInit(hSession, mechanism);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_Digest(hSession, data1, strlen(data1), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_Digest failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
//      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("1000 GOST R34.11-94 operations:  %ld \n", avg_time );
//   printf("Minimum:                        %ld \n", min_time );
//   printf("Maximum:                        %ld \n", max_time );
//   printf("\n");

	if (len != sizeof(et1)) {
		fprintf(stderr, 
			"%4d: Invalid result length: %d\n", __LINE__, len);
		return -1;
	}
	if (memcmp(value, et1, len) != 0) {
		fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
		return -2;
	}

	fprintf(stderr, "One step digest...\n");
//====================================================	
	// Check S-Terra plugin data
	memset(value,0,sizeof(value));

	mechanism->pParameter = oid_default;
	mechanism->ulParameterLen = sizeof(oid_default);
	rc = funcs->C_DigestInit(hSession, mechanism);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_Digest(hSession, data6, sizeof(data6), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_Digest failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	if (len != sizeof(et6)) {
		fprintf(stderr, 
			"%4d: Invalid result length: %d\n", __LINE__, len);
		return -1;
	}
	if (memcmp(value, et6, len) != 0) {
		fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
		return -2;
	}
	fprintf(stderr, "OK\n");
//====================================================	
	memset(value,0,sizeof(value));

	fprintf(stderr, "Yet another one step digest...\n");
	mechanism->pParameter = oid_default;
	mechanism->ulParameterLen = sizeof(oid_default);
	rc = funcs->C_DigestInit(hSession, mechanism);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_Digest(hSession, data2, sizeof(data2), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_Digest failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	if (len != sizeof(et2)) {
		fprintf(stderr, 
			"%4d: Invalid result length: %d\n", __LINE__, len);
		return -1;
	}
	if (memcmp(value, et2, len) != 0) {
		fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
		return -2;
	}
	fprintf(stderr, "OK\n");

//====================================================	
	memset(value,0,sizeof(value));

	fprintf(stderr, "The same digest with NULL mechanism parameter...\n");
	mechanism->pParameter = NULL_PTR;
	mechanism->ulParameterLen = 0;
	rc = funcs->C_DigestInit(hSession, mechanism);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_Digest(hSession, data2, sizeof(data2), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_Digest failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	if (len != sizeof(et2)) {
		fprintf(stderr, 
			"%4d: Invalid result length: %d\n", __LINE__, len);
		return -1;
	}
	if (memcmp(value, et2, len) != 0) {
		fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
		return -2;
	}
	fprintf(stderr, "OK\n");

//====================================================	
	memset(value,0,sizeof(value));

	fprintf(stderr, "Get operation state...\n");
	rc = funcs->C_DigestInit(hSession, mechanism);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	rc = funcs->C_DigestUpdate(hSession, data3, 39);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestUpdate failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	// Save intermediate cryptograhic state.
	state_len = 0;
	rc = funcs->C_GetOperationState(hSession, NULL, &state_len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_GetOperationState failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	state = malloc(state_len);
	rc = funcs->C_GetOperationState(hSession, state, &state_len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_GetOperationState failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
/*
{
	CK_ULONG i;
	printf("\n %%%%%% CRYPTOGRAPHIC STATE: ");
	for (i = 0; i < state_len; i++) printf(" %02x", state[i]);
	printf(" %%%%%%\n\n");
}
*/	
	rc = funcs->C_DigestUpdate(hSession, data3 + 39, sizeof(data3) - 39);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestUpdate failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_DigestFinal(hSession, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestFinal failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	if (len != sizeof(et3)) {
		fprintf(stderr, "%4d: Invalid result length: %d\n", __LINE__, len);
		return -1;
	}
	if (memcmp(value, et3, len) != 0) {
		fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
		return -2;
	}
	fprintf(stderr, "OK\n");
	
//====================================================	
	fprintf(stderr, "Set operation state...\n");
	// Restore saved cryptographic state.
	rc = funcs->C_SetOperationState(hSession, state, state_len, 
		CK_INVALID_HANDLE, CK_INVALID_HANDLE);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_SetOperationState failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	rc = funcs->C_DigestUpdate(hSession, data3 + 39, sizeof(data3) - 39);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestUpdate failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_DigestFinal(hSession, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestFinal failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	if (len != sizeof(et3)) {
		fprintf(stderr, 
			"%4d: Invalid result length: %d\n", __LINE__, len);
		return -1;
	}
	if (memcmp(value, et3, len) != 0) {
		fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
		return -2;
	}
	fprintf(stderr, "OK\n");
	
	free(state);
	printf("SUCCESS\n");

	return rc;	
}



