#include "test_common.h"

CK_RV test_crypto();
CK_RV test_tls12_master_key_derive(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_TLS12_MASTER_KEY_DERIVE, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_TLS12_MASTER_KEY_DERIVE "
                "not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_TLS12_MASTER_KEY_DERIVE "
                "test ==================\n");
        rc = test_tls12_master_key_derive(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_TLS12_MASTER_KEY_DERIVE failed, "
                    "rc = 0x%lx\n", rc);
        }
        else
        {
            fprintf(stderr,
                    "CKM_TLS12_MASTER_KEY_DERIVE 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_tls12_master_key_derive(CK_SESSION_HANDLE sess)
{
    CK_RV rc = CKR_OK;
    CK_BYTE                value[1024];
    CK_ULONG               len;
    CK_MECHANISM           mechanism_desc =
    { CKM_TLS12_MASTER_KEY_DERIVE, NULL, 0 };
    CK_MECHANISM_PTR       mechanism = &mechanism_desc;
    CK_OBJECT_HANDLE       keyh      = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       mkeyh     = 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;
    static CK_BBOOL        lfalse    = CK_FALSE;
    CK_ATTRIBUTE           attr;
    static CK_BYTE         premaster[] = {
        0xdc, 0x2f, 0x75, 0x53, 0x8a, 0x02, 0xc7, 0x4d,
        0x67, 0x32, 0x58, 0xf5, 0xf5, 0x27, 0xc3, 0xc9,
        0x27, 0xd6, 0x65, 0x8f, 0x72, 0x38, 0x7d, 0x4b,
        0x98, 0x06, 0xe0, 0x91, 0x1b, 0xa5, 0x8a, 0x30
    };
    static CK_BYTE         client_random[] = {
        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
    };
    static CK_BYTE         server_random[] = {
        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
    };
    static CK_BYTE         master_2012_256[] = {
        0x2f, 0x3d, 0xdf, 0x52, 0x10, 0x3b, 0x90, 0xc8,
        0x7a, 0x3d, 0x1c, 0x43, 0xc0, 0xbc, 0xe9, 0x20,
        0x10, 0xa5, 0x75, 0xa5, 0xee, 0x17, 0x0c, 0x3d,
        0xb4, 0x24, 0xb0, 0xf5, 0x26, 0xd6, 0xe2, 0xbc,
        0x70, 0x46, 0x73, 0x3b, 0x2f, 0x60, 0x7e, 0xae,
        0x9a, 0x3b, 0x21, 0xe3, 0x36, 0x7e, 0x0e, 0x84,
    };
    static CK_BYTE         master_2012_512[] = {
        0x7c, 0xcf, 0xa7, 0x35, 0x3d, 0xb2, 0x14, 0x75,
        0x43, 0x0c, 0xe8, 0x7c, 0x0d, 0x25, 0x84, 0x66,
        0x07, 0x4d, 0x06, 0x94, 0xa3, 0x66, 0x01, 0xf5,
        0x74, 0xe0, 0xeb, 0x03, 0x1b, 0x98, 0x84, 0xd1,
        0xc5, 0x19, 0xcc, 0xa6, 0x3d, 0x33, 0xb7, 0x05,
        0x1e, 0xd4, 0x9c, 0x9e, 0x77, 0x0f, 0xe6, 0x2b,
    };
    static CK_ATTRIBUTE    key_template[] = {
        { CKA_VALUE,       premaster, sizeof(premaster) },
        { CKA_CLASS,       &oclass,   sizeof(oclass)    },
        { CKA_KEY_TYPE,    &key_type, sizeof(key_type)  },
        { CKA_SENSITIVE,   &lfalse,   sizeof(lfalse)    },
        { CKA_EXTRACTABLE, &ltrue,    sizeof(ltrue)     },
        { CKA_DERIVE,      &ltrue,    sizeof(ltrue)     },
        { CKA_SIGN,        &ltrue,    sizeof(ltrue)     },
        { CKA_VERIFY,      &ltrue,    sizeof(ltrue)     }
    };
    // Интересно, что в данной структуре имеется поле pVersion,
    // в отдичие от  структуры CK_TLS12_KEY_MAT_PARAMS.
    // Пока непонятно, какое значение имеет для российского
    // варианта версия протокола.
    // Точнее, непонятно, есть ли в протоколе 1.2 какие-то
    // особенности для российских алгоритмов.
    // В проекте документа ТК 26 говорится, что в prfHashMechanism
    // всегда нужно подставлять механизмы PRF, хотя в стандарте 2.40
    // сказано, что для протокола 1.2 нужно указывать механизм
    // хеширования, а для 1.0 и 1.1 - механизм PRF.
    static CK_VERSION version = { 1, 0 };               // TLS 1.0
/*
   typedef struct CK_TLS12_MASTER_KEY_DERIVE_PARAMS {
        CK_SSL3_RANDOM_DATA RandomInfo;
        CK_VERSION_PTR pVersion;
        CK_MECHANISM_TYPE prfHashMechanism;
   } CK_TLS12_MASTER_KEY_DERIVE_PARAMS;
 */
    static CK_TLS12_MASTER_KEY_DERIVE_PARAMS params = {
        { client_random, sizeof(client_random),
          server_random, sizeof(server_random) }, // RandomInfo
        &version,                                 // pVersion
        CKM_TLS_GOST_PRF_2012_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);
        goto end;
    }

    params.prfHashMechanism   = CKM_TLS_GOST_PRF_2012_256;
    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);

    rc = funcs->C_DeriveKey(sess, mechanism, keyh,
                            key_template + 1,
                            sizeof(key_template) / sizeof(CK_ATTRIBUTE) - 1,
                            &mkeyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey failed: 0x%lx\n", rc);
        goto end;
    }

    attr.type       = CKA_VALUE;
    attr.pValue     = value;
    attr.ulValueLen = sizeof(value);
    rc              = funcs->C_GetAttributeValue(sess, mkeyh, &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
        goto end;
    }
    len = attr.ulValueLen;

    printf("TLS master key 3411-2012 (256):\n");
    print_bytes(value, len);
    CHECK(value, len, master_2012_256);

    funcs->C_DestroyObject(sess, mkeyh);

    params.prfHashMechanism = CKM_TLS_GOST_PRF_2012_512;
    rc                      = funcs->C_DeriveKey(sess, mechanism, keyh,
                                                 key_template + 1,
                                                 sizeof(key_template) / sizeof(CK_ATTRIBUTE) - 1,
                                                 &mkeyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey failed: 0x%lx\n", rc);
        goto end;
    }

    attr.type       = CKA_VALUE;
    attr.pValue     = value;
    attr.ulValueLen = sizeof(value);
    rc              = funcs->C_GetAttributeValue(sess, mkeyh, &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "C_GetAttributeValue failed: 0x%lx\n", rc);
        goto end;
    }
    len = attr.ulValueLen;

    printf("TLS master key 3411-2012 (512):\n");
    print_bytes(value, len);
    CHECK(value, len, master_2012_512);

    funcs->C_DestroyObject(sess, mkeyh);

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

    return rc;
}
