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

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

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

	CK_BYTE key[] = {
		0xB2,0x4C,0x23,0x32,0xE5,0x97,0xAD,0x26,
		0x39,0xB9,0x1A,0xD4,0xDF,0x4E,0x40,0x61,
		0xAA,0x38,0xDD,0xFE,0x32,0xE9,0xD3,0xED,
		0x4A,0xC6,0xEE,0x08,0x57,0x5A,0x6A,0xAB
	};
	CK_BYTE data[64];
	CK_BYTE key2[] = {
		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
	};
//	CK_BYTE data2[2697];
	// CryptoPro gost28147 B Param Set
	static CK_BYTE oid1[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x02 
	};
	static CK_BYTE et1[] = { 0xf6, 0x6a, 0x39, 0xde };
	CK_ATTRIBUTE key_template[] = {
		{ CKA_VALUE, key, sizeof(key) },
		{ CKA_CLASS, &oclass, sizeof(oclass) },
		{ CKA_KEY_TYPE, &key_type, sizeof(key_type) },
		{ CKA_GOST28147PARAMS, oid1, sizeof(oid1) },
		{ CKA_SIGN, &ltrue, sizeof(CK_BBOOL) },
		{ CKA_VERIFY, &ltrue, sizeof(CK_BBOOL) },
	};
	CK_BYTE ivec[8] = {0};
	
	// S-Terra CSP plug-in test data
	CK_BYTE data5[] = {
	    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	    0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
	    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	    0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65
	};
	CK_BYTE key5[] = {
	    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	    0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
	    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	    0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
	};
	// CryptoPro gost28147 A Param Set (default)
	static CK_BYTE oid5[] = {
	    0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01
	};
	CK_BYTE ivec5[] = {
	    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
	};
	CK_ATTRIBUTE key_template5[] = {
		{ CKA_VALUE, key5, sizeof(key5) },
		{ CKA_CLASS, &oclass, sizeof(oclass) },
		{ CKA_KEY_TYPE, &key_type, sizeof(key_type) },
		{ CKA_GOST28147PARAMS, oid5, sizeof(oid5) },
		{ CKA_SIGN, &ltrue, sizeof(CK_BBOOL) },
		{ CKA_VERIFY, &ltrue, sizeof(CK_BBOOL) },
	};
	static CK_BYTE et5[] = {
//	    0x77, 0xb9, 0x86, 0xba		// with zero iv
	    0xa3, 0xcd, 0x56, 0x64		
	};

	CK_BYTE data_short[] = {
		0x12, 0x00, 0xae, 0x6d, 0xc5, 0x87, 0xb0, 0x3f,
	};
	static CK_BYTE et_short[] = { 0xa5, 0x66, 0x40, 0x04 };
	static CK_BYTE et_short_4[] = { 0xd2, 0xbc, 0xfb, 0xe7 };
	CK_BYTE *state = NULL;
	CK_ULONG state_len = 0;

	CK_BYTE key_to_wrap[] = {
		0x58, 0x4c, 0x3f, 0xce, 0x56, 0xb3, 0x96, 0xd0,
		0x8c, 0xf1, 0x5f, 0x60, 0xfc, 0x84, 0xaf, 0x6d,
		0xbf, 0x1e, 0x6c, 0x6d, 0xa7, 0x9a, 0xd7, 0x4f,
		0xca, 0x0e, 0x5c, 0x9d, 0x3d, 0xb2, 0xa2, 0xb9,
	};

	CK_BYTE wrapping_iv[] = {
		0x56, 0xfa, 0x7d, 0x54, 0xb2, 0x90, 0x2c, 0x41,
	};

	CK_BYTE wrapping_key[] = {
		0x3c, 0x34, 0x7c, 0xe7, 0xd5, 0x60, 0x83, 0x3a,
		0xb4, 0x8f, 0xd5, 0xb0, 0xec, 0x6d, 0xd7, 0x29,
		0x63, 0x9f, 0x64, 0x95, 0xb5, 0xdb, 0x3e, 0x12,
		0x9e, 0x77, 0x0f, 0xe3, 0x40, 0xce, 0xcd, 0x3a,
	};

	CK_BYTE wrapped_key[] = {
		0xdb, 0x46, 0x42, 0x7a, 0x12, 0x77, 0xd6, 0xdd,
		0x8a, 0xd6, 0x35, 0x2f, 0x46, 0x98, 0x09, 0x9b,
		0x60, 0xb3, 0x40, 0x58, 0x2b, 0xce, 0x1a, 0x99,
		0x72, 0xda, 0x6c, 0xa2, 0x4c, 0xf0, 0xc1, 0xc7,
	};

	CK_BYTE wrapped_mac[] = {
		0x36, 0xb3, 0x73, 0x20,
	};

	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, oid5, sizeof(oid5) },
		{ CKA_SIGN, &ltrue, sizeof(CK_BBOOL) },
		{ CKA_VERIFY, &ltrue, sizeof(CK_BBOOL) },
	};
	CK_CHAR str[4096];
/*
	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);
		goto end;
	}

	mechanism->pParameter = wrapping_iv;
	mechanism->ulParameterLen = sizeof(wrapping_iv);
	rc = funcs->C_SignInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SignInit failed: 0x%x\n", rc);
		goto end;
	}

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

	if (len != sizeof(wrapped_mac)) {
		fprintf(stderr, "Invalid MAC size: %d\n", len);
		rc = -1;
		goto end;
	}
	if (memcmp(value, wrapped_mac, len) != 0) {
		fprintf(stderr, "Invalid MAC\n", len);
		rc = -2;
		goto end;
	}

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

	keyh = CK_INVALID_HANDLE;
	print_bytes(key, sizeof(key), str);
	fprintf(stderr, "key:\n%s\n", str);

	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);
		goto end;
	}

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

	len = sizeof(value);
	memset(data, 0xd4, sizeof(data));
	print_bytes(data, sizeof(data), str);
	fprintf(stderr, "data:\n%s\n", str);
	rc = funcs->C_Sign(sess, data, sizeof(data), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Sign failed: 0x%x\n", rc);
		goto end;
	}
	print_bytes(et1, sizeof(et1), str);
	fprintf(stderr, "et1 MAC:\n%s\n", str);
	print_bytes(value, len, str);
	fprintf(stderr, "value MAC:\n%s\n", str);

	if (len != sizeof(et1)) {
		fprintf(stderr, "Invalid MAC size: %d\n", len);
		rc = -1;
		goto end;
	}
	if (memcmp(value, et1, len) != 0) {
		fprintf(stderr, "Invalid MAC\n", len);
		rc = -2;
		goto end;
	}

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

	keyh = CK_INVALID_HANDLE;

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

	mechanism->pParameter = ivec5;
	mechanism->ulParameterLen = sizeof(ivec5);

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

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

	if (len != sizeof(et5)) {
		fprintf(stderr, "Invalid MAC size: %d\n", len);
		rc = -3;
		goto end;
	}
	if (memcmp(value, et5, len) != 0) {
		fprintf(stderr, "Invalid MAC\n", len);
		rc = -4;
		goto end;
	}

	len = sizeof(value);
	rc = funcs->C_SignInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SignInit failed: 0x%x\n", rc);
		goto end;
	}
	rc = funcs->C_SignUpdate(sess, data5, 5);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SignUpdate failed: 0x%x\n", rc);
		goto end;
	}

	rc = funcs->C_GetOperationState(sess, NULL, &state_len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_GetOperationState failed: 0x%x\n", rc);
		goto end;
	}
	state = (CK_BYTE *)malloc(state_len);
	rc = funcs->C_GetOperationState(sess, state, &state_len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_GetOperationState failed: 0x%x\n", rc);
		goto end;
	}
	rc = funcs->C_SetOperationState(sess, state, state_len,
		CK_INVALID_HANDLE, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SetOperationState failed: 0x%x\n", rc);
		goto end;
	}

	rc = funcs->C_SignUpdate(sess, data5+5, sizeof(data5)-5);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SignUpdate failed: 0x%x\n", rc);
		goto end;
	}
/*
	rc = funcs->C_GetOperationState(sess, NULL, &state_len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_GetOperationState failed: 0x%x\n", rc);
		goto end;
	}
	state = (CK_BYTE *)malloc(state_len);
	rc = funcs->C_GetOperationState(sess, state, &state_len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_GetOperationState failed: 0x%x\n", rc);
		goto end;
	}
*/
	rc = funcs->C_SignFinal(sess, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SignFinal failed: 0x%x\n", rc);
		goto end;
	}

	if (len != sizeof(et5)) {
		fprintf(stderr, "Invalid MAC size: %d\n", len);
		rc = -5;
		goto end;
	}
	if (memcmp(value, et5, len) != 0) {
		fprintf(stderr, "Invalid MAC\n", len);
		rc = -6;
		goto end;
	}
/*
	rc = funcs->C_SignInit(sess, mechanism, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SignInit failed: 0x%x\n", rc);
		goto end;
	}
*/

	rc = funcs->C_SetOperationState(sess, state, state_len,
		CK_INVALID_HANDLE, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SetOperationState failed: 0x%x\n", rc);
		goto end;
	}

	rc = funcs->C_SignUpdate(sess, data5+5, sizeof(data5)-5);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SignUpdate failed: 0x%x\n", rc);
		goto end;
	}

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

	if (len != sizeof(et5)) {
		fprintf(stderr, "Invalid MAC size: %d\n", len);
		rc = -5;
		goto end;
	}
	if (memcmp(value, et5, len) != 0) {
		fprintf(stderr, "Invalid MAC\n", len);
		rc = -6;
		goto end;
	}
	free(state);
	state = NULL;

	mechanism->pParameter = NULL;
	mechanism->ulParameterLen = 0;

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

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

	if (len != sizeof(et_short)) {
		fprintf(stderr, "Invalid MAC size: %d\n", len);
		rc = -7;
		goto end;
	}
	if (memcmp(value, et_short, len) != 0) {
		fprintf(stderr, "Invalid MAC\n", len);
		rc = -8;
		goto end;
	}

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

	len = sizeof(value);
	rc = funcs->C_Sign(sess, 
		data_short, sizeof(data_short)-4, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_Sign failed: 0x%x\n", rc);
		goto end;
	}

	if (len != sizeof(et_short_4)) {
		fprintf(stderr, "Invalid MAC size: %d\n", len);
		rc = -9;
		goto end;
	}
	if (memcmp(value, et_short_4, len) != 0) {
		fprintf(stderr, "Invalid MAC\n", len);
		rc = -10;
		goto end;
	}

end:
	if (keyh != CK_INVALID_HANDLE) {
		funcs->C_DestroyObject(sess, keyh);
	}

	return rc;
}



