#include "test_common.h"

CK_RV test_crypto();
CK_RV test_gost3410_with_gost3411(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");

	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_GOSTR3410_WITH_GOSTR3411, &minfo);
	if (rc != CKR_OK) {
		fprintf(stderr, 
"\n======== Mechanism CKM_GOSTR3410_WITH_GOSTR3411 not supported ===========\n");
	} else {
		fprintf(stderr, 
"\n==================== CKM_GOSTR3410_WITH_GOSTR3411 test ==================\n");
		rc = test_gost3410_with_gost3411(hSession);
		if (rc != CKR_OK) {
			fprintf(stderr, 
				"ERROR, CKM_GOSTR3410_WITH_GOSTR3411 failed, rc = 0x%x\n", rc);
		} else {
			fprintf(stderr, "CKM_GOSTR3410_WITH_GOSTR3411 test passed.\n");
		}
	}
   
out_close:
	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_gost3410_with_gost3411(CK_SESSION_HANDLE sess)
{
	CK_RV rc;
	CK_BYTE value[256];
	CK_ULONG len;
	CK_OBJECT_HANDLE pub_key = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE priv_key = CK_INVALID_HANDLE;
   	CK_MECHANISM mechanism_desc = {CKM_GOSTR3410_WITH_GOSTR3411, NULL, 0};
   	CK_MECHANISM hash_mechanism_desc = {CKM_GOSTR3411, NULL, 0};
   	CK_MECHANISM mechanism_3410_desc = {CKM_GOSTR3410, NULL, 0};
   	CK_MECHANISM_PTR mechanism = &mechanism_desc;
   	CK_MECHANISM_PTR hash_mechanism = &hash_mechanism_desc;
   	CK_MECHANISM_PTR mechanism_3410 = &mechanism_3410_desc;
   	CK_MECHANISM mechanism_gen_desc = {CKM_GOSTR3410_KEY_PAIR_GEN, NULL, 0};
   	CK_MECHANISM_PTR mechanism_gen = &mechanism_gen_desc;

	static CK_BBOOL ltrue = CK_TRUE;
	static CK_BBOOL lfalse = CK_FALSE;

	static CK_BYTE gostR3410params[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 };
	static CK_BYTE gostR3411params[] = { 
		0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01 };

	static CK_BYTE data[] = {
	    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 data_hash[] = {
	    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
	};

	static CK_ATTRIBUTE pub_template[] = {
		{ CKA_VERIFY, &ltrue, sizeof(CK_BBOOL) },
		{ CKA_GOSTR3410PARAMS, gostR3410params, sizeof(gostR3410params) },
		{ CKA_GOSTR3411PARAMS, gostR3411params, sizeof(gostR3411params) },
	};

	static CK_ATTRIBUTE priv_template[] = {
		{ CKA_SIGN, &ltrue, sizeof(CK_BBOOL) },
	};

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

	hash_mechanism->pParameter = gostR3411params;
	hash_mechanism->ulParameterLen = sizeof(gostR3411params);
	rc = funcs->C_DigestInit(sess, hash_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(sess, data, sizeof(data), value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_Digest failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

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

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

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

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

    rc = funcs->C_DigestUpdate(sess, data + 39, sizeof(data) - 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(sess, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_DigestFinal failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}

	if (len != sizeof(data_hash)) {
		fprintf(stderr, 
			"%4d: Invalid result length: %d\n", __LINE__, len);
		return -1;
	}
	if (memcmp(value, data_hash, len) != 0) {
		fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
		return -2;
	}
	
	rc = funcs->C_GenerateKeyPair(sess, mechanism_gen,
		pub_template, sizeof(pub_template)/sizeof(CK_ATTRIBUTE),
		priv_template, sizeof(priv_template)/sizeof(CK_ATTRIBUTE),
		&pub_key, &priv_key);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_GenerateKeyPair failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_GenerateKeyPair OK\n");

	rc = funcs->C_SignInit(sess, mechanism_3410, priv_key);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_SignInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_SignInit CKM_GOSTR3410 OK\n");

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

	printf("SIGNATURE:\n");
	print_hex(value, len);

	rc = funcs->C_VerifyInit(sess, mechanism_3410, pub_key);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_VerifyInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_VerifyInit CKM_GOSTR3410 OK\n");
	rc = funcs->C_Verify(sess, 
		data_hash, sizeof(data_hash), value, len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_Verify failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_Verify OK\n");

	rc = funcs->C_VerifyInit(sess, mechanism, pub_key);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_VerifyInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_VerifyInit CKM_GOSTR3410_WITH_GOSTR3411 OK\n");

	rc = funcs->C_VerifyUpdate(sess, data, 39);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_VerifyUpdate failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	rc = funcs->C_VerifyUpdate(sess, data+39, sizeof(data)-39);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_VerifyUpdate failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	rc = funcs->C_VerifyFinal(sess, value, len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_VerifyFinal failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_VerifyFinal OK\n");

	rc = funcs->C_VerifyInit(sess, mechanism, pub_key);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_VerifyInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_VerifyInit CKM_GOSTR3410_WITH_GOSTR3411 OK\n");

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

	rc = funcs->C_SignInit(sess, mechanism, priv_key);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_SignInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_SignInit CKM_GOSTR3410_WITH_GOSTR3411 OK\n");

	rc = funcs->C_SignUpdate(sess, data, 39);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_SignUpdate failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	rc = funcs->C_SignUpdate(sess, data+39, sizeof(data)-39);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_SignUpdate failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	len = sizeof(value);
	rc = funcs->C_SignFinal(sess, value, &len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_SignFinal failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_SignFinal OK\n");

	printf("SIGNATURE:\n");
	print_hex(value, len);

	rc = funcs->C_VerifyInit(sess, mechanism_3410, pub_key);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_VerifyInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_VerifyInit CKM_GOSTR3410 OK\n");

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

	rc = funcs->C_SignInit(sess, mechanism, priv_key);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_SignInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_SignInit CKM_GOSTR3410_WITH_GOSTR3411 OK\n");

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

	printf("SIGNATURE:\n");
	print_hex(value, len);

	rc = funcs->C_VerifyInit(sess, mechanism_3410, pub_key);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_VerifyInit failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_VerifyInit CKM_GOSTR3410 OK\n");
	rc = funcs->C_Verify(sess, 
		data_hash, sizeof(data_hash), value, len);
	if (rc != CKR_OK) {
		fprintf(stderr, 
			"%4d: C_Verify failed, rc = 0x%x\n", __LINE__, rc);
		return rc;
	}
	fprintf(stderr, "C_Verify OK\n");

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

	return rc;
}
