#include "test_common.h"

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

    rc = funcs->C_GetMechanismInfo(SlotId,
                                   CKM_GOSTR3411_12_256_HMAC, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_GOSTR3411_12_256_HMAC "
                "not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_GOSTR3411_12_256_HMAC "
                "test ==================\n");
        rc = test_gost3411_12_256_hmac(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_GOSTR3411_12_256_HMAC failed, rc = 0x%lx\n",
                    rc);
        }
        else
        {
            fprintf(stderr,
                    "CKM_GOSTR3411_12_256_HMAC 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_gost3411_12_256_hmac(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_BYTE                value[256];
    CK_ULONG               len;
    CK_MECHANISM           mechanism_desc =
    { CKM_GOSTR3411_12_256_HMAC, NULL, 0 };
    CK_MECHANISM_PTR       mechanism      = &mechanism_desc;
    CK_OBJECT_HANDLE       keyh           = CK_INVALID_HANDLE;
    static CK_OBJECT_CLASS oclass         = CKO_SECRET_KEY;
    static CK_KEY_TYPE     key_type       = CKK_GENERIC_SECRET;
    static CK_BBOOL        ltrue          = CK_TRUE;
    CK_ATTRIBUTE           key_template[] = {
        { CKA_VALUE,    NULL_PTR,                 0 },
        { CKA_CLASS,    &oclass,   sizeof(oclass)   },
        { CKA_KEY_TYPE, &key_type, sizeof(key_type) },
        { CKA_SIGN,     &ltrue,    sizeof(ltrue)    },
        { CKA_VERIFY,   &ltrue,    sizeof(ltrue)    }
    };
    CK_BYTE                *state    = NULL;
    CK_ULONG               state_len = 0;
/*
   Контрольный пример ТК 26

   1) HMAC_GOSTR3411_2012_256
   Ключ K:
   00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
   10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
   Данные T:
   01 26 bd b8 78 00 af 21 43 41 45 65 63 78 01 00
   Значение HMAC256(K,T):
   a1 aa 5f 7d e4 02 d7 b3 d3 23 f2 99 1c 8d 45 34
   01 31 37 01 0a 83 75 4f d0 af 6d 7c d4 92 2e d9
 */
    unsigned char K[] = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    };
    unsigned char T[] = {
        0x01, 0x26, 0xbd, 0xb8, 0x78, 0x00, 0xaf, 0x21,
        0x43, 0x41, 0x45, 0x65, 0x63, 0x78, 0x01, 0x00,
    };
    unsigned char HMAC256[] = {
        0xa1, 0xaa, 0x5f, 0x7d, 0xe4, 0x02, 0xd7, 0xb3,
        0xd3, 0x23, 0xf2, 0x99, 0x1c, 0x8d, 0x45, 0x34,
        0x01, 0x31, 0x37, 0x01, 0x0a, 0x83, 0x75, 0x4f,
        0xd0, 0xaf, 0x6d, 0x7c, 0xd4, 0x92, 0x2e, 0xd9,
    };

    printf("HMAC256 TC 26 test\n");
    key_template[0].pValue     = K;
    key_template[0].ulValueLen = sizeof(K);
    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;
    }

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

    if (len != sizeof(HMAC256))
    {
        fprintf(stderr, "Invalid length: %ld\n", len);
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    print_bytes(value, len);
    if (memcmp(value, HMAC256, len) != 0)
    {
        fprintf(stderr, "Invalid value\n");
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    printf("Sign OK\n");

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

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

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

    printf("Multipart test\n");
    key_template[0].pValue     = K;
    key_template[0].ulValueLen = sizeof(K);
    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;
    }

    mechanism->pParameter     = 0;
    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;
    }

    memset(value, 0, sizeof(value));
    len = sizeof(value);
    rc  = funcs->C_SignUpdate(sess, T, 7);
    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, T + 7, sizeof(T) - 7);
    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;
    }

    if (len != sizeof(HMAC256))
    {
        fprintf(stderr, "Invalid length: %ld\n", len);
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    print_bytes(value, len);
    if (memcmp(value, HMAC256, len) != 0)
    {
        fprintf(stderr, "Invalid value\n");
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    printf("Sign multipart OK\n");

    mechanism->pParameter     = 0;
    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;
    }

    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, T + 7, sizeof(T) - 7);
    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;
    }

    if (len != sizeof(HMAC256))
    {
        fprintf(stderr, "Invalid length: %ld\n", len);
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    print_bytes(value, len);
    if (memcmp(value, HMAC256, len) != 0)
    {
        fprintf(stderr, "Invalid value\n");
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    printf("Restored Sign multipart OK\n");

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

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

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

    printf("SUCCESS\n");
    return CKR_OK;
 end:
    if (keyh != CK_INVALID_HANDLE)
    {
        funcs->C_DestroyObject(sess, keyh);
    }

    return rc;
}

