#include "test_common.h"

CK_RV test_crypto();
CK_RV test_kuznyechik_acpkm_omac(CK_SESSION_HANDLE sess);
CK_RV test_magma_acpkm_omac(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;
    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%lx\n", rc);
        return rc;
    }
    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%lx\n", rc);
                goto out_close;
        }
        fprintf(stderr, "C_Login success\n");
 */
    rc = funcs->C_GetMechanismInfo(SlotId, CKM_GOST_CIPHER_ACPKM_OMAC, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_GOST_CIPHER_ACPKM_OMAC not supported ===========\n");
		goto out;
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_GOST_CIPHER_ACPKM_OMAC test ==================\n");
        rc = test_kuznyechik_acpkm_omac(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR test_kuznyechik_acpkm_omac failed, rc = 0x%lx\n", rc);
			goto out;
        }
        else
        {
            fprintf(stderr, "test_kuznyechik_acpkm_omac passed.\n");
        }
        rc = test_magma_acpkm_omac(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR test_magma_acpkm_omac failed, rc = 0x%lx\n", rc);
			goto out;
        }
        else
        {
            fprintf(stderr, "test_magma_acpkm_omac passed.\n");
        }
    }

 out:
	funcs->C_CloseSession(hSession);
    return rc;
}


CK_RV test_kuznyechik_acpkm_omac(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_BYTE                value[128];
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh           = CK_INVALID_HANDLE;
	CK_TC26_ACPKM_OMAC_PARAMS params;
    CK_MECHANISM           mech_desc = { CKM_GOST_CIPHER_ACPKM_OMAC, NULL, 0 };
    CK_MECHANISM_PTR       mech      = &mech_desc;

// Контрольные примеры ТК 26 для Кузнечика
static size_t s = 16;
static size_t N = 32;	// Размер секции в байтах
static size_t T = 96;	// Количество байтов для смены ключа K*

static unsigned char TC26_K[] = {
	0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 
	0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 
	0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
};
static unsigned char TC26_M_1_5[] = {
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 
	0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 
};
static unsigned char TC26_macM_1_5[] = {
	0xB5, 0x36, 0x7F, 0x47, 0xB6, 0x2B, 0x99, 0x5E, 
	0xEB, 0x2A, 0x64, 0x8C, 0x58, 0x43, 0x14, 0x5E,
};
static unsigned char TC26_M_5[] = {
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 
	0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 
	0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 
	0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, 
	0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 
	0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, 0x11, 
	0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 
	0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, 0x11, 0x22,
};
static unsigned char TC26_macM_5[] = {
	//0xDD, 0xE2, 0xC0, 0xCE, 0xFE, 0xCF, 0x5D, 0xAB, 
	//0x39, 0xA0, 0x84, 0xE6, 0xFB, 0x1D, 0x8E, 0x28,
	0xfb, 0xb8, 0xdc, 0xee, 0x45, 0xbe, 0xa6, 0x7c,
	0x35, 0xf5, 0x8c, 0x57, 0x00, 0x89, 0x8e, 0x5d,
};

    static CK_OBJECT_CLASS oclass   = CKO_SECRET_KEY;
    static CK_KEY_TYPE     key_type = CKK_KUZNYECHIK;
    static CK_BBOOL        ltrue    = CK_TRUE;
//    static CK_BBOOL        lfalse   = CK_FALSE;
    static CK_ATTRIBUTE    key_template[] = {
        { CKA_VALUE,           TC26_K,			sizeof(TC26_K)		},
        { CKA_CLASS,           &oclass,         sizeof(oclass)      },
        { CKA_KEY_TYPE,        &key_type,       sizeof(key_type)    },
        { CKA_SIGN,            &ltrue,          sizeof(CK_BBOOL)    },
        { CKA_VERIFY,          &ltrue,          sizeof(CK_BBOOL)    },
    };
    CK_BYTE                *state    = NULL;
    CK_ULONG               state_len = 0;
    char                str[4096];

    keyh = CK_INVALID_HANDLE;

    print_bytes_str(TC26_M_1_5, sizeof(TC26_M_1_5), str);
    fprintf(stderr, "TC26_M_1_5:\n%s\n", str);

    print_bytes_str(TC26_K, sizeof(TC26_K), 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%lx\n", rc);
        goto end;
    }

	params.ulSectionLen = (CK_ULONG)N;
	params.ulKeyChangeSectionLen = (CK_ULONG)T;
	params.ulOmacLen = (CK_ULONG)s;
	mech->pParameter = &params;
	mech->ulParameterLen = sizeof(params);

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

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

    CHECK(value, len, TC26_macM_1_5);

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

    len = sizeof(value);
    rc  = funcs->C_Verify(sess, TC26_M_1_5, sizeof(TC26_M_1_5),
                          TC26_macM_1_5, sizeof(TC26_macM_1_5));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Verify failed: 0x%lx\n", rc);
        goto end;
    }
    fprintf(stderr, "C_Verify OK\n");

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

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

    CHECK(value, len, TC26_macM_5);

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

    len = sizeof(value);
    rc  = funcs->C_Verify(sess, TC26_M_5, sizeof(TC26_M_5),
                          TC26_macM_5, sizeof(TC26_macM_5));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Verify failed: 0x%lx\n", rc);
        goto end;
    }
    fprintf(stderr, "C_Verify OK\n");

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

    return rc;
}


CK_RV test_magma_acpkm_omac(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_BYTE                value[128];
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh           = CK_INVALID_HANDLE;
	CK_TC26_ACPKM_OMAC_PARAMS params;
    CK_MECHANISM           mech_desc = { CKM_GOST_CIPHER_ACPKM_OMAC, NULL, 0 };
    CK_MECHANISM_PTR       mech      = &mech_desc;

// Контрольные примеры ТК 26 для Магмы
static size_t s = 8;
static size_t N = 16;	// Размер секции в байтах
static size_t T = 80;	// Количество байтов для смены базового ключа

static unsigned char TC26_K[] = {
	0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 
	0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 
	0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
};
// Полтора блока
static unsigned char TC26_M_1_5[] = {
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 
	0xFF, 0xEE, 0xDD, 0xCC,
};
static unsigned char TC26_macM_1_5[]= {
	0xA0, 0x54, 0x0E, 0x37, 0x30, 0xAC, 0xBC, 0xF3,
};
// Пять блоков
static unsigned char TC26_M_5[] = {
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 
	0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 
	0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
};
static unsigned char TC26_macM_5[]= {
	//0x52, 0x6B, 0xF7, 0xF5, 0x3A, 0x41, 0x97, 0xDE,
	0x34, 0x00, 0x8d, 0xad, 0x54, 0x96, 0xbb, 0x8e,
};

    static CK_OBJECT_CLASS oclass   = CKO_SECRET_KEY;
//    static CK_KEY_TYPE     key_type = CKK_GOST28147;	// Тоже работает
    static CK_KEY_TYPE     key_type = CKK_MAGMA;
    static CK_BBOOL        ltrue    = CK_TRUE;
//    static CK_BBOOL        lfalse   = CK_FALSE;
//    static CK_BYTE         oid_Z[]  = {
//        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
//    };
    static CK_ATTRIBUTE    key_template[] = {
        { CKA_VALUE,           TC26_K,			sizeof(TC26_K)		},
        { CKA_CLASS,           &oclass,         sizeof(oclass)      },
        { CKA_KEY_TYPE,        &key_type,       sizeof(key_type)    },
//        { CKA_GOST28147PARAMS, oid_Z,           sizeof(oid_Z)       },
        { CKA_SIGN,            &ltrue,          sizeof(CK_BBOOL)    },
        { CKA_VERIFY,          &ltrue,          sizeof(CK_BBOOL)    },
    };
    CK_BYTE                *state    = NULL;
    CK_ULONG               state_len = 0;
    char                str[4096];

    keyh = CK_INVALID_HANDLE;

    print_bytes_str(TC26_M_1_5, sizeof(TC26_M_1_5), str);
    fprintf(stderr, "TC26_M_1_5:\n%s\n", str);

    print_bytes_str(TC26_K, sizeof(TC26_K), 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%lx\n", rc);
        goto end;
    }

	params.ulSectionLen = (CK_ULONG)N;
	params.ulKeyChangeSectionLen = (CK_ULONG)T;
	params.ulOmacLen = (CK_ULONG)s;
	mech->pParameter = &params;
	mech->ulParameterLen = sizeof(params);

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

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

    CHECK(value, len, TC26_macM_1_5);

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

    len = sizeof(value);
    rc  = funcs->C_Verify(sess, TC26_M_1_5, sizeof(TC26_M_1_5),
                          TC26_macM_1_5, sizeof(TC26_macM_1_5));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Verify failed: 0x%lx\n", rc);
        goto end;
    }
    fprintf(stderr, "C_Verify OK\n");

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

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

    CHECK(value, len, TC26_macM_5);

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

    len = sizeof(value);
    rc  = funcs->C_Verify(sess, TC26_M_5, sizeof(TC26_M_5),
                          TC26_macM_5, sizeof(TC26_macM_5));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Verify failed: 0x%lx\n", rc);
        goto end;
    }
    fprintf(stderr, "C_Verify OK\n");

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

    return rc;
}
