#include "test_common.h"

CK_RV test_crypto();
CK_RV test_gost28147_mac(CK_SESSION_HANDLE sess);

int main(int argc, char *argv[]) {
	CK_RV rc = CKR_OK;

	rc = test_main(argc, argv);
	if (rc != CKR_OK) {
		fprintf(stderr, "test_main failed: 0x%x\n", rc);
		return rc;
	}
	rc = test_crypto();
	if (rc != CKR_OK) {
		fprintf(stderr, "test_crypto failed: 0x%x\n", rc);
		return rc;
	}
	return CKR_OK;
}

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

	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 = 0x%x\n", 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 = 0x%x\n", 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 = 0x%x\n", rc);
		} else {
			fprintf(stderr, "CKM_GOST28147_MAC test passed.\n");
		}
	}

	if ( (ret = funcs->C_CloseSession(hSession)) != CKR_OK ) {
		fprintf(stderr, "Error: C_CloseSession failed with 0x%x\n", ret);
		rc = ret;
	}
	else {
		fprintf(stderr, "C_CloseSession success\n");
	}

out:
	return rc;
}

CK_RV 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;

	static 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
	};
	static CK_BYTE data[64];
	static 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
	};
	static 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 };
	static 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) },
	};
	static CK_BYTE ivec[8] = {0};
	
	// S-Terra CSP plug-in test data
	static 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
	};
	static 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
	};
	static CK_BYTE ivec5[] = {
	    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
	};
	static 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		
	};

	static 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];

	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);

	CHECK(value, len, et1);

	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;
	}

	CHECK(value, len, et5);

	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_SignFinal(sess, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_SignFinal failed: 0x%x\n", rc);
		goto end;
	}

	CHECK(value, len, et5);

	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;
	}

	CHECK(value, len, et5);

	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;
	}

	CHECK(value, len, et_short);

	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;
	}

	CHECK(value, len, et_short_4);

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

	return rc;
}
