#ifdef WIN32
#pragma warning(disable: 4996)
#endif
#include "test_common.h"

CK_RV test_crypto();
CK_RV test_magma_ecb_random_data(CK_SESSION_HANDLE sess);
CK_RV test_magma_cbc_random_data(CK_SESSION_HANDLE sess);
CK_RV test_magma_ctr_random_data(CK_SESSION_HANDLE sess);
CK_RV test_magma_ofb_random_data(CK_SESSION_HANDLE sess);
CK_RV test_magma_cfb_random_data(CK_SESSION_HANDLE sess);
CK_RV test_magma_mac_random_data(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%lx\n", rc);
        return rc;
    }
    rc = test_crypto();
    if (rc != CKR_OK)
    {
        fprintf(stderr, "test_crypto failed: 0x%lx\n", rc);
        return rc;
    }
    return CKR_OK;
}

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

    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%lx\n", rc);
        goto out;
    }
    fprintf(stderr, "C_OpenSession success\n");

	fprintf(stderr,
		"\n==================== CKM_MAGMA_ECB random data ==================\n");
	rc = test_magma_ecb_random_data(hSession);
	if (rc != CKR_OK)
	{
		fprintf(stderr,
			"ERROR CKM_MAGMA_ECB random data test failed, rc = 0x%lx\n", rc);
			return rc;
	}
	else
	{
		fprintf(stderr, "End of CKM_MAGMA_ECB random data\n");
	}

	fprintf(stderr,
		"\n==================== CKM_MAGMA_CBC random data ==================\n");
	rc = test_magma_cbc_random_data(hSession);
	if (rc != CKR_OK)
	{
		fprintf(stderr,
			"ERROR CKM_MAGMA_CBC random data test failed, rc = 0x%lx\n", rc);
			return rc;
	}
	else
	{
		fprintf(stderr, "End of CKM_MAGMA_CBC random data\n");
	}

	fprintf(stderr,
		"\n==================== CKM_MAGMA_CTR random data ==================\n");
	rc = test_magma_ctr_random_data(hSession);
	if (rc != CKR_OK)
	{
		fprintf(stderr,
			"ERROR CKM_MAGMA_CTR random data test failed, rc = 0x%lx\n", rc);
			return rc;
	}
	else
	{
		fprintf(stderr, "End of CKM_MAGMA_CTR random data\n");
	}

	fprintf(stderr,
		"\n==================== CKM_MAGMA_OFB random data ==================\n");
	rc = test_magma_ofb_random_data(hSession);
	if (rc != CKR_OK)
	{
		fprintf(stderr,
			"ERROR CKM_MAGMA_OFB random data test failed, rc = 0x%lx\n", rc);
			return rc;
	}
	else
	{
		fprintf(stderr, "End of CKM_MAGMA_OFB random data\n");
	}

	fprintf(stderr,
		"\n==================== CKM_MAGMA_CFB random data ==================\n");
	rc = test_magma_cfb_random_data(hSession);
	if (rc != CKR_OK)
	{
		fprintf(stderr,
			"ERROR CKM_MAGMA_CFB random data test failed, rc = 0x%lx\n", rc);
			return rc;
	}
	else
	{
		fprintf(stderr, "End of CKM_MAGMA_CFB random data\n");
	}

	fprintf(stderr,
		"\n==================== CKM_MAGMA_MAC random data ==================\n");
	rc = test_magma_mac_random_data(hSession);
	if (rc != CKR_OK)
	{
		fprintf(stderr,
			"ERROR CKM_MAGMA_MAC random data test failed, rc = 0x%lx\n", rc);
			return rc;
	}
	else
	{
		fprintf(stderr, "End of CKM_MAGMA_MAC random data\n");
	}

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

 out:
    return rc;
}

CK_RV test_magma_ecb_random_data(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc = { CKM_MAGMA_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       oid_Z[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
    };
    CK_MECHANISM           mechanism_keygen_desc = { CKM_GOST28147_KEY_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_keygen      = &mechanism_keygen_desc;
    CK_ATTRIBUTE key_template[] = {
        { CKA_CLASS,           &oclass,			sizeof(oclass)   },
        { CKA_KEY_TYPE,        &key_type,		sizeof(key_type) },
		{ CKA_GOST28147_PARAMS, oid_Z,			sizeof(oid_Z)	 },
        { CKA_ENCRYPT,         &ltrue,			sizeof(CK_BBOOL) },
        { CKA_DECRYPT,         &ltrue,			sizeof(CK_BBOOL) }
    };
	CK_ULONG	data_len_max = 128;
	CK_ULONG	data_len_min = 8;
	CK_ULONG	data_len_count = 16;
	CK_ULONG	data_len = data_len_max;
    CK_BYTE     *data = NULL;
	CK_BYTE		keyval[32];
	CK_ATTRIBUTE attr;
    CK_BYTE     *value = NULL;
    CK_BYTE     *value2 = NULL;
	int rounds = 5;
	int i;
	FILE *f = fopen("magma_ecb_data.h", "w");
	char str[8192];

	fprintf(f, "//==== Magma ECB test data ====================\n");
	for (i = 0; i < rounds; i++) {
		rc = funcs->C_GenerateKey(sess, mechanism_keygen,
			key_template, sizeof(key_template) / sizeof(CK_ATTRIBUTE), &keyh);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateKey failed: 0x%lx\n", rc);
			return rc;
		}
		attr.type       = CKA_VALUE;
		attr.pValue     = keyval;
		attr.ulValueLen = sizeof(keyval);
		rc              = funcs->C_GetAttributeValue(sess, keyh, &attr, 1);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char key_%d[32] = {\n", i+1);
		print_bytes_str(keyval, sizeof(keyval), str);
		fprintf(f, "%s};\n", str);

		data_len_count = get_random_size(sess, 1, data_len_max/16);
		if (data_len < data_len_min || data_len > data_len_max) {
			fprintf(stderr, "get_random_size failed\n");
			rc = CKR_FUNCTION_FAILED;
			goto end;
		}
		data_len = 16 * data_len_count;
		data = (CK_BYTE *)malloc(data_len);
		if (!data) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		value = (CK_BYTE *)malloc(data_len);
		if (!value) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		rc = funcs->C_GenerateRandom(sess, data, data_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char plain_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(data, data_len, str);
		fprintf(f, "%s};\n", str);

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

		len = data_len;
		rc  = funcs->C_EncryptUpdate(sess, data, data_len, value, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptUpdate failed: 0x%lx\n", rc);
			goto end;
		}
		// Finalize operations in first session
		len = data_len - data_len;
		rc  = funcs->C_EncryptFinal(sess, value + data_len, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptFinal failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char encr_ecb_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(value, data_len, str);
		fprintf(f, "%s};\n", str);

		if (keyh != CK_INVALID_HANDLE)
		{
			funcs->C_DestroyObject(sess, keyh);
			keyh = CK_INVALID_HANDLE;
		}
		if (data) {
			free(data);
			data = NULL;
		}
		if (value) {
			free(value);
			value = NULL;
		}
		fprintf(f, "//=================================================\n");
	}

    printf("SUCCESS\n");
    rc = CKR_OK;
 end:
    if (keyh != CK_INVALID_HANDLE)
    {
        funcs->C_DestroyObject(sess, keyh);
    }
	if (data) free(data);
	if (value) free(value);
	if (f) fclose(f);

    return rc;
}

CK_RV test_magma_cbc_random_data(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc = { CKM_MAGMA_CBC, 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       oid_Z[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
    };
    CK_MECHANISM           mechanism_keygen_desc = { CKM_GOST28147_KEY_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_keygen      = &mechanism_keygen_desc;
    CK_ATTRIBUTE key_template[] = {
        { CKA_CLASS,           &oclass,			sizeof(oclass)   },
        { CKA_KEY_TYPE,        &key_type,		sizeof(key_type) },
		{ CKA_GOST28147_PARAMS, oid_Z,			sizeof(oid_Z)	 },
        { CKA_ENCRYPT,         &ltrue,			sizeof(CK_BBOOL) },
        { CKA_DECRYPT,         &ltrue,			sizeof(CK_BBOOL) }
    };
	CK_ULONG	data_len_max = 128;
	CK_ULONG	data_len_min = 8;
	CK_ULONG	data_len_count = 16;
	CK_ULONG	data_len = data_len_max;
    CK_BYTE     *data = NULL;
	CK_BYTE		keyval[32];
    CK_BYTE		iv[16 * 8];
	CK_ULONG	iv_count = 1;
	CK_ULONG	iv_len = iv_count * 8;
	CK_ATTRIBUTE attr;
    CK_BYTE     *value = NULL;
    CK_BYTE     *value2 = NULL;
	int rounds = 5;
	int i;
	FILE *f = fopen("magma_cbc_data.h", "w");
	char str[8192];

	fprintf(f, "//==== Magma CBC test data ====================\n");
	for (i = 0; i < rounds; i++) {
		rc = funcs->C_GenerateKey(sess, mechanism_keygen,
			key_template, sizeof(key_template) / sizeof(CK_ATTRIBUTE), &keyh);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateKey failed: 0x%lx\n", rc);
			return rc;
		}
		attr.type       = CKA_VALUE;
		attr.pValue     = keyval;
		attr.ulValueLen = sizeof(keyval);
		rc              = funcs->C_GetAttributeValue(sess, keyh, &attr, 1);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char key_%d[32] = {\n", i+1);
		print_bytes_str(keyval, sizeof(keyval), str);
		fprintf(f, "%s};\n", str);

		iv_count = get_random_size(sess, 1, 16);
		if (iv_count < 1 || iv_count > 16) {
			fprintf(stderr, "get_random_size failed\n");
			rc = CKR_FUNCTION_FAILED;
			goto end;
		}
		iv_len = iv_count * 8;
		rc = funcs->C_GenerateRandom(sess, iv, iv_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char iv_%d[%d] = {\n", i+1, iv_len);
		print_bytes_str(iv, iv_len, str);
		fprintf(f, "%s};\n", str);

		data_len_count = get_random_size(sess, 1, data_len_max/16);
		if (data_len < data_len_min || data_len > data_len_max) {
			fprintf(stderr, "get_random_size failed\n");
			rc = CKR_FUNCTION_FAILED;
			goto end;
		}
		data_len = 16 * data_len_count;
		data = (CK_BYTE *)malloc(data_len);
		if (!data) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		value = (CK_BYTE *)malloc(data_len);
		if (!value) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		rc = funcs->C_GenerateRandom(sess, data, data_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char plain_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(data, data_len, str);
		fprintf(f, "%s};\n", str);

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

		len = data_len;
		rc  = funcs->C_EncryptUpdate(sess, data, data_len, value, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptUpdate failed: 0x%lx\n", rc);
			goto end;
		}
		// Finalize operations in first session
		len = data_len - data_len;
		rc  = funcs->C_EncryptFinal(sess, value + data_len, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptFinal failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char encr_cbc_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(value, data_len, str);
		fprintf(f, "%s};\n", str);

		if (keyh != CK_INVALID_HANDLE)
		{
			funcs->C_DestroyObject(sess, keyh);
			keyh = CK_INVALID_HANDLE;
		}
		if (data) {
			free(data);
			data = NULL;
		}
		if (value) {
			free(value);
			value = NULL;
		}
		fprintf(f, "//=================================================\n");
	}

    printf("SUCCESS\n");
    rc = CKR_OK;
 end:
    if (keyh != CK_INVALID_HANDLE)
    {
        funcs->C_DestroyObject(sess, keyh);
    }
	if (data) free(data);
	if (value) free(value);
	if (f) fclose(f);

    return rc;
}

CK_RV test_magma_ctr_random_data(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc = { CKM_MAGMA_CTR, 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       oid_Z[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
    };
    CK_MECHANISM           mechanism_keygen_desc = { CKM_GOST28147_KEY_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_keygen      = &mechanism_keygen_desc;
    CK_ATTRIBUTE key_template[] = {
        { CKA_CLASS,           &oclass,			sizeof(oclass)   },
        { CKA_KEY_TYPE,        &key_type,		sizeof(key_type) },
		{ CKA_GOST28147_PARAMS, oid_Z,			sizeof(oid_Z)	 },
        { CKA_ENCRYPT,         &ltrue,			sizeof(CK_BBOOL) },
        { CKA_DECRYPT,         &ltrue,			sizeof(CK_BBOOL) }
    };
	CK_ULONG	data_len_max = 128;
	CK_ULONG	data_len_min = 3;
	CK_ULONG	data_len = data_len_max;
    CK_BYTE     *data = NULL;
    CK_BYTE		iv[4];
	CK_ULONG	iv_len = 4;
	CK_BYTE		keyval[32];
	CK_ATTRIBUTE attr;
    CK_BYTE     *value = NULL;
    CK_BYTE     *value2 = NULL;
	int rounds = 5;
	int i;
	FILE *f = fopen("magma_ctr_data.h", "w");
	char str[8192];

	fprintf(f, "//==== Magma CTR test data ====================\n");
	for (i = 0; i < rounds; i++) {
		rc = funcs->C_GenerateKey(sess, mechanism_keygen,
			key_template, sizeof(key_template) / sizeof(CK_ATTRIBUTE), &keyh);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateKey failed: 0x%lx\n", rc);
			return rc;
		}
		attr.type       = CKA_VALUE;
		attr.pValue     = keyval;
		attr.ulValueLen = sizeof(keyval);
		rc              = funcs->C_GetAttributeValue(sess, keyh, &attr, 1);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char key_%d[32] = {\n", i+1);
		print_bytes_str(keyval, sizeof(keyval), str);
		fprintf(f, "%s};\n", str);

		rc = funcs->C_GenerateRandom(sess, iv, iv_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char iv_%d[%d] = {\n", i+1, iv_len);
		print_bytes_str(iv, iv_len, str);
		fprintf(f, "%s};\n", str);

		data_len = get_random_size(sess, data_len_min, data_len_max);
		if (data_len < data_len_min || data_len > data_len_max) {
			fprintf(stderr, "get_random_size failed\n");
			rc = CKR_FUNCTION_FAILED;
			goto end;
		}
		data = (CK_BYTE *)malloc(data_len);
		if (!data) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		value = (CK_BYTE *)malloc(data_len);
		if (!value) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		rc = funcs->C_GenerateRandom(sess, data, data_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char plain_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(data, data_len, str);
		fprintf(f, "%s};\n", str);

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

		len = data_len;
		rc  = funcs->C_EncryptUpdate(sess, data, data_len, value, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptUpdate failed: 0x%lx\n", rc);
			goto end;
		}
		// Finalize operations in first session
		len = data_len - data_len;
		rc  = funcs->C_EncryptFinal(sess, value + data_len, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptFinal failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char encr_ctr_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(value, data_len, str);
		fprintf(f, "%s};\n", str);

		if (keyh != CK_INVALID_HANDLE)
		{
			funcs->C_DestroyObject(sess, keyh);
			keyh = CK_INVALID_HANDLE;
		}
		if (data) {
			free(data);
			data = NULL;
		}
		if (value) {
			free(value);
			value = NULL;
		}
		fprintf(f, "//=================================================\n");
	}

    printf("SUCCESS\n");
    rc = CKR_OK;
 end:
    if (keyh != CK_INVALID_HANDLE)
    {
        funcs->C_DestroyObject(sess, keyh);
    }
	if (data) free(data);
	if (value) free(value);
	if (f) fclose(f);

    return rc;
}

CK_RV test_magma_ofb_random_data(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc = { CKM_MAGMA_OFB, 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       oid_Z[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
    };
    CK_MECHANISM           mechanism_keygen_desc = { CKM_GOST28147_KEY_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_keygen      = &mechanism_keygen_desc;
    CK_ATTRIBUTE key_template[] = {
        { CKA_CLASS,           &oclass,			sizeof(oclass)   },
        { CKA_KEY_TYPE,        &key_type,		sizeof(key_type) },
		{ CKA_GOST28147_PARAMS, oid_Z,			sizeof(oid_Z)	 },
        { CKA_ENCRYPT,         &ltrue,			sizeof(CK_BBOOL) },
        { CKA_DECRYPT,         &ltrue,			sizeof(CK_BBOOL) }
    };
	CK_ULONG	data_len_max = 128;
	CK_ULONG	data_len_min = 3;
	CK_ULONG	data_len = data_len_max;
    CK_BYTE     *data = NULL;
    CK_BYTE		iv[16 * 8];
	CK_ULONG	iv_count = 1;
	CK_ULONG	iv_len = iv_count * 8;
	CK_BYTE		keyval[32];
	CK_ATTRIBUTE attr;
    CK_BYTE     *value = NULL;
    CK_BYTE     *value2 = NULL;
	int rounds = 5;
	int i;
	FILE *f = fopen("magma_ofb_data.h", "w");
	char str[8192];

	fprintf(f, "//==== Magma OFB test data ====================\n");
	for (i = 0; i < rounds; i++) {
		rc = funcs->C_GenerateKey(sess, mechanism_keygen,
			key_template, sizeof(key_template) / sizeof(CK_ATTRIBUTE), &keyh);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateKey failed: 0x%lx\n", rc);
			return rc;
		}
		attr.type       = CKA_VALUE;
		attr.pValue     = keyval;
		attr.ulValueLen = sizeof(keyval);
		rc              = funcs->C_GetAttributeValue(sess, keyh, &attr, 1);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char key_%d[32] = {\n", i+1);
		print_bytes_str(keyval, sizeof(keyval), str);
		fprintf(f, "%s};\n", str);

		iv_count = get_random_size(sess, 1, 16);
		if (iv_count < 1 || iv_count > 16) {
			fprintf(stderr, "get_random_size failed\n");
			rc = CKR_FUNCTION_FAILED;
			goto end;
		}
		iv_len = iv_count * 8;
		rc = funcs->C_GenerateRandom(sess, iv, iv_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char iv_%d[%d] = {\n", i+1, iv_len);
		print_bytes_str(iv, iv_len, str);
		fprintf(f, "%s};\n", str);

		data_len = get_random_size(sess, data_len_min, data_len_max);
		if (data_len < data_len_min || data_len > data_len_max) {
			fprintf(stderr, "get_random_size failed\n");
			rc = CKR_FUNCTION_FAILED;
			goto end;
		}
		data = (CK_BYTE *)malloc(data_len);
		if (!data) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		value = (CK_BYTE *)malloc(data_len);
		if (!value) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		rc = funcs->C_GenerateRandom(sess, data, data_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char plain_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(data, data_len, str);
		fprintf(f, "%s};\n", str);

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

		len = data_len;
		rc  = funcs->C_EncryptUpdate(sess, data, data_len, value, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptUpdate failed: 0x%lx\n", rc);
			goto end;
		}
		// Finalize operations in first session
		len = data_len - data_len;
		rc  = funcs->C_EncryptFinal(sess, value + data_len, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptFinal failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char encr_ofb_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(value, data_len, str);
		fprintf(f, "%s};\n", str);

		if (keyh != CK_INVALID_HANDLE)
		{
			funcs->C_DestroyObject(sess, keyh);
			keyh = CK_INVALID_HANDLE;
		}
		if (data) {
			free(data);
			data = NULL;
		}
		if (value) {
			free(value);
			value = NULL;
		}
		fprintf(f, "//=================================================\n");
	}

    printf("SUCCESS\n");
    rc = CKR_OK;
 end:
    if (keyh != CK_INVALID_HANDLE)
    {
        funcs->C_DestroyObject(sess, keyh);
    }
	if (data) free(data);
	if (value) free(value);
	if (f) fclose(f);

    return rc;
}

CK_RV test_magma_cfb_random_data(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc = { CKM_MAGMA_CFB, 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       oid_Z[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
    };
    CK_MECHANISM           mechanism_keygen_desc = { CKM_GOST28147_KEY_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_keygen      = &mechanism_keygen_desc;
    CK_ATTRIBUTE key_template[] = {
        { CKA_CLASS,           &oclass,			sizeof(oclass)   },
        { CKA_KEY_TYPE,        &key_type,		sizeof(key_type) },
		{ CKA_GOST28147_PARAMS, oid_Z,			sizeof(oid_Z)	 },
        { CKA_ENCRYPT,         &ltrue,			sizeof(CK_BBOOL) },
        { CKA_DECRYPT,         &ltrue,			sizeof(CK_BBOOL) }
    };
	CK_ULONG	data_len_max = 128;
	CK_ULONG	data_len_min = 3;
	CK_ULONG	data_len = data_len_max;
    CK_BYTE     *data = NULL;
    CK_BYTE		iv[16 * 8];
	CK_ULONG	iv_count = 1;
	CK_ULONG	iv_len = iv_count * 8;
	CK_BYTE		keyval[32];
	CK_ATTRIBUTE attr;
    CK_BYTE     *value = NULL;
    CK_BYTE     *value2 = NULL;
	int rounds = 5;
	int i;
	FILE *f = fopen("magma_cfb_data.h", "w");
	char str[8192];

	fprintf(f, "//==== Magma CFB test data ====================\n");
	for (i = 0; i < rounds; i++) {
		rc = funcs->C_GenerateKey(sess, mechanism_keygen,
			key_template, sizeof(key_template) / sizeof(CK_ATTRIBUTE), &keyh);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateKey failed: 0x%lx\n", rc);
			return rc;
		}
		attr.type       = CKA_VALUE;
		attr.pValue     = keyval;
		attr.ulValueLen = sizeof(keyval);
		rc              = funcs->C_GetAttributeValue(sess, keyh, &attr, 1);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char key_%d[32] = {\n", i+1);
		print_bytes_str(keyval, sizeof(keyval), str);
		fprintf(f, "%s};\n", str);

		iv_count = get_random_size(sess, 1, 16);
		if (iv_count < 1 || iv_count > 16) {
			fprintf(stderr, "get_random_size failed\n");
			rc = CKR_FUNCTION_FAILED;
			goto end;
		}
		iv_len = iv_count * 8;
		rc = funcs->C_GenerateRandom(sess, iv, iv_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char iv_%d[%d] = {\n", i+1, iv_len);
		print_bytes_str(iv, iv_len, str);
		fprintf(f, "%s};\n", str);

		data_len = get_random_size(sess, data_len_min, data_len_max);
		if (data_len < data_len_min || data_len > data_len_max) {
			fprintf(stderr, "get_random_size failed\n");
			rc = CKR_FUNCTION_FAILED;
			goto end;
		}
		data = (CK_BYTE *)malloc(data_len);
		if (!data) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		value = (CK_BYTE *)malloc(data_len);
		if (!value) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		rc = funcs->C_GenerateRandom(sess, data, data_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char plain_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(data, data_len, str);
		fprintf(f, "%s};\n", str);

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

		len = data_len;
		rc  = funcs->C_EncryptUpdate(sess, data, data_len, value, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptUpdate failed: 0x%lx\n", rc);
			goto end;
		}
		// Finalize operations in first session
		len = data_len - data_len;
		rc  = funcs->C_EncryptFinal(sess, value + data_len, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_EncryptFinal failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char encr_cfb_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(value, data_len, str);
		fprintf(f, "%s};\n", str);

		if (keyh != CK_INVALID_HANDLE)
		{
			funcs->C_DestroyObject(sess, keyh);
			keyh = CK_INVALID_HANDLE;
		}
		if (data) {
			free(data);
			data = NULL;
		}
		if (value) {
			free(value);
			value = NULL;
		}
		fprintf(f, "//=================================================\n");
	}

    printf("SUCCESS\n");
    rc = CKR_OK;
 end:
    if (keyh != CK_INVALID_HANDLE)
    {
        funcs->C_DestroyObject(sess, keyh);
    }
	if (data) free(data);
	if (value) free(value);
	if (f) fclose(f);

    return rc;
}

CK_RV test_magma_mac_random_data(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_BYTE                value[8];
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh           = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc = { CKM_MAGMA_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_BYTE       oid_Z[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
    };
    static CK_BBOOL        ltrue            = CK_TRUE;
//    static CK_BBOOL        lfalse           = CK_FALSE;
    CK_MECHANISM           mechanism_keygen_desc = { CKM_GOST28147_KEY_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_keygen      = &mechanism_keygen_desc;
    CK_ATTRIBUTE key_template[] = {
        { CKA_CLASS,           &oclass,			sizeof(oclass)   },
        { CKA_KEY_TYPE,        &key_type,		sizeof(key_type) },
		{ CKA_GOST28147_PARAMS, oid_Z,			sizeof(oid_Z)	 },
        { CKA_ENCRYPT,         &ltrue,			sizeof(CK_BBOOL) },
        { CKA_DECRYPT,         &ltrue,			sizeof(CK_BBOOL) }
    };
	CK_BYTE		keyval[32];
	CK_ULONG	data_len_max = 128;
	CK_ULONG	data_len_min = 3;
	CK_ULONG	data_len = data_len_max;
    CK_BYTE     *data = NULL;
	CK_ATTRIBUTE attr;
	int rounds = 5;
	int i;
	FILE *f = fopen("magma_mac_data.h", "w");
	char str[8192];

	fprintf(f, "//==== Magma MAC test data ====================\n");
	for (i = 0; i < rounds; i++) {
		rc = funcs->C_GenerateKey(sess, mechanism_keygen,
			key_template, sizeof(key_template) / sizeof(CK_ATTRIBUTE), &keyh);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateKey failed: 0x%lx\n", rc);
			return rc;
		}
		attr.type       = CKA_VALUE;
		attr.pValue     = keyval;
		attr.ulValueLen = sizeof(keyval);
		rc              = funcs->C_GetAttributeValue(sess, keyh, &attr, 1);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char key_%d[32] = {\n", i+1);
		print_bytes_str(keyval, sizeof(keyval), str);
		fprintf(f, "%s};\n", str);

		data_len = get_random_size(sess, data_len_min, data_len_max);
		if (data_len < data_len_min || data_len > data_len_max) {
			fprintf(stderr, "get_random_size failed\n");
			rc = CKR_FUNCTION_FAILED;
			goto end;
		}
		data = (CK_BYTE *)malloc(data_len);
		if (!data) {
			fprintf(stderr, "malloc(%ld) failed\n", data_len);
			rc = CKR_HOST_MEMORY;
			goto end;
		}
		rc = funcs->C_GenerateRandom(sess, data, data_len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
			goto end;
		}
		fprintf(f, "const unsigned char plain_text_%d[%d] = {\n", i+1, data_len);		
		print_bytes_str(data, data_len, str);
		fprintf(f, "%s};\n", str);

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

		len = sizeof(value);
		rc  = funcs->C_Sign(sess, data, data_len, value, &len);
		if (rc != CKR_OK)
		{
			fprintf(stderr, "C_Sign failed: 0x%lx\n", rc);
			goto end;
		}
		print_bytes_str(value, len, str);
		fprintf(f, "const unsigned char mac_%d[%d] = {\n%s};\n", i+1, len, str);
		if (keyh != CK_INVALID_HANDLE)
		{
			funcs->C_DestroyObject(sess, keyh);
			keyh = CK_INVALID_HANDLE;
		}
		if (data) {
			free(data);
			data = NULL;
		}
		fprintf(f, "//=================================================\n");
	}

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

    return rc;
}

