#include "test_common.h"

CK_RV test_crypto();
CK_RV test_tls_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_TLS_MAC, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_TLS_MAC not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_TLS_MAC test ==================\n");
        rc = test_tls_mac(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_TLS_MAC failed, rc = 0x%lx\n", rc);
        }
        else
        {
            fprintf(stderr, "CKM_TLS_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;
}

// Механизм CKM_TLS_MAC позволяет подписывать мастер-ключом
// различные данные в реализации протокола TLS.
// Обычно размер подписи (MAC) равен 12-ти байтам.
CK_RV test_tls_mac(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    static CK_BYTE         value[1024];
    CK_MECHANISM           mechanism_desc = { CKM_TLS_MAC, 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,                     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_ULONG               len = 0;
    // GOST R 3411-94 (client)
    // Master secret (48 bytes)
    static CK_BYTE master_secret_94[] = {
        0x76, 0x22, 0x41, 0x3d, 0x5a, 0xb6, 0x7f, 0x5b,
        0x86, 0x2e, 0x75, 0x97, 0xe8, 0xf9, 0x31, 0xe4,
        0x2f, 0x13, 0x13, 0xd5, 0x61, 0xed, 0xab, 0xed,
        0xa5, 0x4b, 0xd8, 0x5d, 0x60, 0x0a, 0xec, 0x99,
        0x45, 0xfe, 0x39, 0xa1, 0xb7, 0x62, 0x0d, 0x66,
        0x3c, 0xdd, 0xb3, 0x71, 0x90, 0x0f, 0xc3, 0xdd
    };
    // Механизм CKM_TLS_MAC позволяет подписывать различные данные,
    // в зависимости от потребностей на разных этапах протокола.
    // seed (64 bytes) = client_random (32 bytes) + server_random (32 bytes)
    CK_BYTE seed_94[] = {
        0x87, 0x5a, 0x38, 0x94, 0xa4, 0xf9, 0x3c, 0x30,
        0x65, 0xfc, 0x66, 0xbe, 0xf2, 0xc0, 0x09, 0xb9,
        0xc3, 0x26, 0x3e, 0x16, 0xc7, 0x28, 0x14, 0x27,
        0x98, 0x26, 0xe4, 0xd5, 0x30, 0x7f, 0x9a, 0x2d,
        0x87, 0x0e, 0x1d, 0x34, 0xaf, 0x28, 0x1c, 0x60,
        0xae, 0x8d, 0x26, 0xe4, 0xd5, 0x30, 0x7f, 0x9a,
        0x2d, 0x87, 0x0e, 0x1d, 0x34, 0xaf, 0x28, 0x1c,
        0x60, 0xae, 0x8d, 0x21, 0xee, 0x7d, 0x52, 0x44
    };
    // Resulting TLS PRF value (12 bytes)
    CK_BYTE tls_mac_94[] = {
        0x2a, 0x6b, 0x4c, 0x14, 0x61, 0xb1, 0x39, 0x2d,
        0x18, 0xdf, 0x8a, 0x8d,
    };
    // GOST R 3411-2012-256 (client)
    // Master Secret:
    CK_BYTE master_secret_256[] = {
        0x34, 0xBF, 0x0B, 0x11, 0x3E, 0x5E, 0xA3, 0x5A,
        0x1D, 0x4A, 0x2B, 0xD9, 0x05, 0x1B, 0xC1, 0xF0,
        0xE0, 0x26, 0x43, 0x3B, 0x4E, 0xBD, 0xD8, 0xEF,
        0x28, 0x4F, 0x83, 0x03, 0xED, 0x0E, 0x0A, 0x01,
        0x51, 0xE1, 0xA6, 0x41, 0xC7, 0xE1, 0xF0, 0xC6,
        0x20, 0x34, 0x39, 0xAA, 0x6F, 0xD3, 0xC7, 0x2F,
    };
    // Механизм CKM_TLS_MAC позволяет подписывать различные данные,
    // в зависимости от потребностей на разных этапах протокола.
    // seed - final digest GOST R 3411-2012-256 (32 bytes):
    CK_BYTE seed_256[] = {
        0x8E, 0xCF, 0x2C, 0x12, 0xC4, 0x77, 0x55, 0xB4,
        0x37, 0xA6, 0x2B, 0xFC, 0x22, 0x43, 0x09, 0x94,
        0x5D, 0x0A, 0xA1, 0xDA, 0xD3, 0xC8, 0x79, 0x7B,
        0x6F, 0x93, 0xAE, 0x9C, 0xC0, 0xB2, 0x7C, 0xCE,
    };
    // tls final finish mac:
    CK_BYTE tls_mac_256[] = {
        0xBE, 0x39, 0x21, 0xB7, 0x64, 0xF5, 0x71, 0xE7,
        0x3F, 0x1C, 0xC4, 0xF3,
    };

// CK_TLS_MAC_PARAMS
// The fields of the structure have the following meanings:
// prfMechanism			the hash mechanism used in the TLS12 PRF construct
//                      or CKM_TLS_PRF to use with the TLS1.0 and 1.1 PRF construct.
// ulMacLength			the length of the MAC tag required or offered.
//						Always 12 octets in TLS 1.0 and 1.1.
//						Generally 12 octets, but may be negotiated
//						to a longer value in TLS1.2.
// ulServerOrClient		1 to use the label "server finished", 2 to use the label
//                      "client finished". All other values are invalid.
/*
   typedef struct CK_TLS_MAC_PARAMS {
        CK_MECHANISM_TYPE prfMechanism;
        CK_ULONG ulMacLength;
        CK_ULONG ulServerOrClient;
   } CK_TLS_MAC_PARAMS;
 */
    CK_TLS_MAC_PARAMS params = {
        CKM_TLS_GOST_PRF,
        12,
        0               // client
    };

    printf("TLS PRF 3411-94 test\n");
    key_template[0].pValue     = master_secret_94;
    key_template[0].ulValueLen = sizeof(master_secret_94);
    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);
        return rc;
    }
    // Стандарт 2.40 запрещает использовать
    // для генерации TLS PRF функцию C_DeriveKey.
    // Результат CKM_TLS_MAC рассматривается теперь
    // в качестве подписи, поэтому нужно использовать
    // функции C_SignInit и C_Sign.
    params.prfHashMechanism   = CKM_TLS_GOST_PRF;
    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_SignInit(sess, mechanism,
                                                  keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_SignInit failed: 0x%lx\n", rc);
        return rc;
    }

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

    printf("TLS_PRF_MAC 3411-94 Signature:\n");
    print_bytes(value, len);
    CHECK(value, len, tls_mac_94);

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

    rc = funcs->C_Verify(sess, seed_94, sizeof(seed_94), value, len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Verify failed: 0x%lx\n", rc);
        return rc;
    }
    printf("TLS_PRF_MAC 3411-94 Signature Verify OK\n");

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

    printf("TLS PRF 3411-2012-256 test\n");
    key_template[0].pValue     = master_secret_256;
    key_template[0].ulValueLen = sizeof(master_secret_256);
    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);
        return rc;
    }

    params.prfHashMechanism   = CKM_TLS_GOST_PRF_2012_256;
    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_SignInit(sess, mechanism,
                                                  keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_SignInit failed: 0x%lx\n", rc);
        return rc;
    }

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

    printf("TLS_PRF_MAC 3411-2012-256 Signature:\n");
    print_bytes(value, len);
    CHECK(value, len, tls_mac_256);

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

    rc = funcs->C_Verify(sess, seed_256, sizeof(seed_256), value, len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Verify failed: 0x%lx\n", rc);
        return rc;
    }
    printf("TLS_PRF_MAC 3411-2012-256 Signature Verify OK\n");

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

    return rc;
}
