#include "test_common.h"

CK_RV test_crypto();
CK_RV test_gost28147_ecb(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_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 = 0x%x\n", rc);
		} else {
			fprintf(stderr, "CKM_GOST28147_ECB 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_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;

	static 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-ми.
    static 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 };

	static 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(value, len, 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;
	}

	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(value, len, 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(value, len, data);
	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject 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(value, len, 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(value, len, data);
	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject 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(value, len, 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(value, len, data);
	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject 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(value, len, 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(value, len, data);
	rc = funcs->C_DestroyObject(sess, keyh);
	if (rc != CKR_OK) {
		fprintf(stderr, "C_DestroyObject 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(value, len, 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(value, len, data);

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

	return rc;
}

