#include "test_common.h"
#ifdef WIN32
#include "getopt.h"
#else 
extern char *optarg;
#endif

void usage(void);
CK_RV test_crypto();
int test_gost28147_ecb(CK_SESSION_HANDLE sess);

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;

   // 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;
	}
	for (i=0; i<20; i++) {
		// test the crypto functions 
		rc = test_crypto();
		if (rc != CKR_OK) {
			fprintf(stderr, "ERROR call to lissi failed.\n");
			return rc;
		}
	}
	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;

   // 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));
   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_GOST28147_ECB, &minfo);
	if (rc != CKR_OK) {
		fprintf(stderr, "\n======== Mechanism CKM_GOST28147_ECB not supported ===========\n");
	} else {
		fprintf(stderr, "\n==================== CKM_GOST28147_ECB test ==================\n");
		rc = test_gost28147_ecb(hSession);
		if (rc != CKR_OK) {
			fprintf(stderr, "ERROR CKM_GOST28147_ECB failed, rc = %p\n", (void *)rc);
		} else {
			fprintf(stderr, "CKM_GOST28147_ECB 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_gost28147_ecb(CK_SESSION_HANDLE sess)
{
    int rc = 0;
	CK_BYTE value[256];
	CK_ULONG len;
	CK_OBJECT_HANDLE keyh = CK_INVALID_HANDLE;
   	CK_MECHANISM mechanism_desc = {CKM_GOST28147_ECB, NULL, 0};
   	CK_MECHANISM_PTR mechanism = &mechanism_desc;

	static CK_BBOOL ltrue = CK_TRUE;
	static CK_BBOOL lfalse = CK_FALSE;
	static CK_OBJECT_CLASS oclass = CKO_SECRET_KEY;
	static CK_KEY_TYPE key_type = CKK_GOST28147;

	CK_BYTE key[] = {
			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};
	// Данные для шифрования в блочных режимах должны поступать на вход
	// порциями, длина которых кратна размеру блока, т.е. 8-ми.
    CK_BYTE data[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    };

	static CK_BYTE oid1[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x00 };
	static CK_BYTE et1[] = { 0xf4,0xe5,0x5c,0xf1,0xf0,0xb5,0xd6,0x29,0xf4,0xe5,0x5c,0xf1,0xf0,0xb5,0xd6,0x29 };

	static CK_BYTE oid2[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01 };
	static CK_BYTE et2[] = { 0xd8,0xea,0xd4,0xbd,0xb6,0xb5,0x43,0x26,0xd8,0xea,0xd4,0xbd,0xb6,0xb5,0x43,0x26 };

	static CK_BYTE oid3[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x02 };
	static CK_BYTE et3[] = { 0x7b,0x18,0xc6,0x12,0x5d,0xfd,0xe0,0x80,0x7b,0x18,0xc6,0x12,0x5d,0xfd,0xe0,0x80 };

	static CK_BYTE oid4[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x03 };
	static CK_BYTE et4[] = { 0x3d,0xac,0xa3,0x01,0x9c,0xee,0x92,0x14,0x3d,0xac,0xa3,0x01,0x9c,0xee,0x92,0x14 };

	static CK_BYTE oid5[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x04 };
	static CK_BYTE et5[] = { 0x6b,0x63,0x55,0xec,0x8c,0x78,0x62,0x46,0x6b,0x63,0x55,0xec,0x8c,0x78,0x62,0x46 };
	//HZ
	static CK_BYTE oid6[] = { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x02, 0x0c, 0x01, 0x05, 0x01 };
	static CK_BYTE et6[] = { 0x76,0xd9,0x44,0xa5,0xee,0x1c,0x3a,0xe1,0x76,0xd9,0x44,0xa5,0xee,0x1c,0x3a,0xe1 };

	CK_ATTRIBUTE key_template[] = {
		{ CKA_CLASS, &oclass, sizeof(oclass) },
		{ CKA_KEY_TYPE, &key_type, sizeof(key_type) },
		{ CKA_GOST28147PARAMS, oid1, sizeof(oid1) },
		{ CKA_ENCRYPT, &ltrue, sizeof(CK_BBOOL) },
		{ CKA_DECRYPT, &ltrue, sizeof(CK_BBOOL) },
		{ CKA_VALUE, key, sizeof(key) }
	};
	CK_BYTE dummy[8];
	CK_ULONG dummy_len = 0;
	CK_BYTE key_to_wrap[] = {
		0xc2, 0x8f, 0xf3, 0xe4, 0x3a, 0xe2, 0x72, 0xd2,
		0x41, 0x10, 0xff, 0x88, 0x3b, 0x5c, 0x16, 0x2b,
		0x51, 0xd9, 0x4d, 0x4d, 0xd5, 0x1f, 0x67, 0x0a,
		0x58, 0x95, 0x40, 0xf2, 0x54, 0x97, 0x4d, 0x61,
	};

	CK_BYTE wrapping_key[] = {
		0x28, 0xff, 0x2f, 0xbd, 0x65, 0x1e, 0x31, 0x25,
		0xef, 0xa6, 0x4c, 0xdd, 0x91, 0x78, 0xd1, 0x89,
		0x55, 0x96, 0xe0, 0x3c, 0x7f, 0x9d, 0xe5, 0xe9,
		0xa5, 0x9d, 0x5a, 0x9a, 0x2b, 0x1c, 0x6f, 0x6c,
	};

	CK_BYTE wrapped_key[] = {
		0xb6, 0xa6, 0xf7, 0x09, 0x52, 0x09, 0xea, 0xfd,
		0x78, 0xa3, 0x4d, 0xbd, 0x0c, 0xbd, 0x35, 0xc2,
		0x5f, 0xee, 0x6c, 0xf0, 0x17, 0xa2, 0xd8, 0x02,
		0x5b, 0x36, 0xf5, 0xb3, 0xd8, 0x0a, 0xca, 0x87,
	};

	CK_ATTRIBUTE wrapping_key_template[] = {
		{ CKA_VALUE, wrapping_key, sizeof(wrapping_key) },
		{ CKA_CLASS, &oclass, sizeof(oclass) },
		{ CKA_KEY_TYPE, &key_type, sizeof(key_type) },
		{ CKA_GOST28147PARAMS, oid2, sizeof(oid2) },
		{ CKA_SIGN, &ltrue, sizeof(CK_BBOOL) },
		{ CKA_VERIFY, &ltrue, sizeof(CK_BBOOL) },
	};

	rc = funcs->C_CreateObject(sess, wrapping_key_template, sizeof(wrapping_key_template)/sizeof(CK_ATTRIBUTE), &keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_CreateObject failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_EncryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptInit failed: 0x%x\n", rc);
		return rc;
	}

	// Данные для шифрования в блочных режимах должны поступать на вход
	// порциями, длина которых кратна размеру блока, т.е. 8-ми.
	len = sizeof(value);
	rc = funcs->C_EncryptUpdate(sess, key_to_wrap, sizeof(key_to_wrap), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptUpdate failed: 0x%x\n", rc);
		return rc;
	}
	rc = funcs->C_EncryptFinal(sess, dummy, &dummy_len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptFinal failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(wrapped_key);
	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_CreateObject(sess, key_template, sizeof(key_template)/sizeof(CK_ATTRIBUTE), &keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_CreateObject failed: 0x%x\n", rc);
		return rc;
	}
/*
	attr.type = CKA_GOST28147PARAMS; attr.pValue = oid1; attr.ulValueLen = sizeof(oid1);
	rc = funcs->C_SetAttributeValue(sess, keyh, &attr, 1);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SetAttributeValue failed: 0x%x\n", rc);
		return rc;
	}
*/
	rc = funcs->C_EncryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptInit failed: 0x%x\n", rc);
		return rc;
	}

	// Данные для шифрования в блочных режимах должны поступать на вход
	// порциями, длина которых кратна размеру блока, т.е. 8-ми.
	len = sizeof(value);
	rc = funcs->C_EncryptUpdate(sess, data, sizeof(data), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptUpdate failed: 0x%x\n", rc);
		return rc;
	}
	rc = funcs->C_EncryptFinal(sess, dummy, &dummy_len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptFinal failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(et1);

	rc = funcs->C_DecryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DecryptInit failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_Decrypt(sess, value, len, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Decrypt failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(data);
	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject failed: 0x%x\n", rc);
		return rc;
	}
/*
	attr.type = CKA_GOST28147PARAMS; attr.pValue = oid2; attr.ulValueLen = sizeof(oid2);
	rc = funcs->C_SetAttributeValue(sess, keyh, &attr, 1);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SetAttributeValue failed: 0x%x\n", rc);
		return rc;
	}
*/
	key_template[2].pValue = oid2;
	key_template[2].ulValueLen = sizeof(oid2);
	rc = funcs->C_CreateObject(sess, key_template, sizeof(key_template)/sizeof(CK_ATTRIBUTE), &keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_CreateObject failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_EncryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptInit failed: 0x%x\n", rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_Encrypt(sess, data, sizeof(data), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Encrypt failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(et2);

	rc = funcs->C_DecryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DecryptInit failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_Decrypt(sess, value, len, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Decrypt failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(data);
	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject failed: 0x%x\n", rc);
		return rc;
	}
/*
	attr.type = CKA_GOST28147PARAMS; attr.pValue = oid3; attr.ulValueLen = sizeof(oid3);
	rc = funcs->C_SetAttributeValue(sess, keyh, &attr, 1);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SetAttributeValue failed: 0x%x\n", rc);
		return rc;
	}
*/
	key_template[2].pValue = oid3;
	key_template[2].ulValueLen = sizeof(oid3);
	rc = funcs->C_CreateObject(sess, key_template, sizeof(key_template)/sizeof(CK_ATTRIBUTE), &keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_CreateObject failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_EncryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptInit failed: 0x%x\n", rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_Encrypt(sess, data, sizeof(data), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Encrypt failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(et3);

	rc = funcs->C_DecryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DecryptInit failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_Decrypt(sess, value, len, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Decrypt failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(data);
	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject failed: 0x%x\n", rc);
		return rc;
	}
/*
	attr.type = CKA_GOST28147PARAMS; attr.pValue = oid4; attr.ulValueLen = sizeof(oid4);
	rc = funcs->C_SetAttributeValue(sess, keyh, &attr, 1);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SetAttributeValue failed: 0x%x\n", rc);
		return rc;
	}
*/
	key_template[2].pValue = oid4;
	key_template[2].ulValueLen = sizeof(oid4);
	rc = funcs->C_CreateObject(sess, key_template, sizeof(key_template)/sizeof(CK_ATTRIBUTE), &keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_CreateObject failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_EncryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptInit failed: 0x%x\n", rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_Encrypt(sess, data, sizeof(data), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Encrypt failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(et4);

	rc = funcs->C_DecryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DecryptInit failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_Decrypt(sess, value, len, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Decrypt failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(data);
	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject failed: 0x%x\n", rc);
		return rc;
	}
/*
	attr.type = CKA_GOST28147PARAMS; attr.pValue = oid5; attr.ulValueLen = sizeof(oid5);
	rc = funcs->C_SetAttributeValue(sess, keyh, &attr, 1);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SetAttributeValue failed: 0x%x\n", rc);
		return rc;
	}
*/
	key_template[2].pValue = oid5;
	key_template[2].ulValueLen = sizeof(oid5);
	rc = funcs->C_CreateObject(sess, key_template, sizeof(key_template)/sizeof(CK_ATTRIBUTE), &keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_CreateObject failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_EncryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_EncryptInit failed: 0x%x\n", rc);
		return rc;
	}

	len = sizeof(value);
	rc = funcs->C_Encrypt(sess, data, sizeof(data), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Encrypt failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(et5);

	rc = funcs->C_DecryptInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DecryptInit failed: 0x%x\n", rc);
		return rc;
	}

	rc = funcs->C_Decrypt(sess, value, len, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Decrypt failed: 0x%x\n", rc);
		return rc;
	}

	CHECK(data);

	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject failed: 0x%x\n", rc);
		return rc;
	}

	return rc;
}



