#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%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;
    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);
        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%lx\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%lx\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%lx\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;
    char              str[4096];
/*
    static CK_BYTE       oid_Z[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
    };
*/
/*
    static unsigned char kMasterKeyGost89[] =
    {
        0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
        0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
        0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
    };
*/
/*
    static unsigned char kGost89PlainText[] =
    {
        0x92, 0xde, 0xf0, 0x6b, 0x3c, 0x13, 0x0a, 0x59,
        0xdb, 0x54, 0xc7, 0x04, 0xf8, 0x18, 0x9d, 0x20,
        0x4a, 0x98, 0xfb, 0x2e, 0x67, 0xa8, 0x02, 0x4c,
        0x89, 0x12, 0x40, 0x9b, 0x17, 0xb5, 0x7e, 0x41,
    };
*/
/*
    static unsigned char key_Z[] =
    {
        0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
        0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x80,
        0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
        0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xd0
    };
*/
/*
    static unsigned char plaintext_Z[] =
    {
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
        0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8
    };
*/
    keyh = CK_INVALID_HANDLE;
    print_bytes_str(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%lx\n", rc);
        goto end;
    }

    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);
    memset(data, 0xd4, sizeof(data));
    print_bytes_str(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%lx\n", rc);
        goto end;
    }
    print_bytes_str(et1, sizeof(et1), str);
    fprintf(stderr, "et1 MAC:\n%s\n", str);
    print_bytes_str(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%lx\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%lx\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%lx\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%lx\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%lx\n", rc);
        goto end;
    }
    rc = funcs->C_SignUpdate(sess, data5, 5);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_SignUpdate failed: 0x%lx\n", rc);
        goto end;
    }

    rc = funcs->C_GetOperationState(sess, NULL, &state_len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetOperationState failed: 0x%lx\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%lx\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%lx\n", rc);
        goto end;
    }

    rc = funcs->C_SignUpdate(sess, data5 + 5, sizeof(data5) - 5);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_SignUpdate failed: 0x%lx\n", rc);
        goto end;
    }

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

    rc = funcs->C_SignUpdate(sess, data5 + 5, sizeof(data5) - 5);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_SignUpdate failed: 0x%lx\n", rc);
        goto end;
    }

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

    CHECK(value, len, et_short_4);

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

    return rc;
}
