#include "test_common.h"
CK_RV test_crypto();
CK_RV test_tls12_key_and_mac_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");
    rc = funcs->C_GetMechanismInfo(SlotId,
                                   CKM_TLS12_KEY_AND_MAC_DERIVE, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_TLS12_KEY_AND_MAC_DERIVE "
                "not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_TLS12_KEY_AND_MAC_DERIVE "
                "test ==================\n");
        rc = test_tls12_key_and_mac_derive(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_TLS12_KEY_AND_MAC_DERIVE failed, "
                    "rc = 0x%lx\n", rc);
        }
        else
        {
            fprintf(stderr,
                    "CKM_TLS12_KEY_AND_MAC_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_key_and_mac_derive(CK_SESSION_HANDLE sess)
{
    CK_RV rc = CKR_OK;
    CK_BYTE                value[1024];
    CK_ULONG               len;
    CK_MECHANISM           mechanism_desc =
    { CKM_TLS12_KEY_AND_MAC_DERIVE, 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     generic_key_type = CKK_GENERIC_SECRET;
    static CK_KEY_TYPE     secret_key_type  = CKK_GOST28147;
    static CK_BBOOL        ltrue            = CK_TRUE;
    static CK_BBOOL        lfalse           = CK_FALSE;
    CK_ATTRIBUTE           attr;
    static CK_BYTE         client_random[] = {
        0x48, 0xdc, 0xdb, 0x79, 0xfb, 0x11, 0x16, 0xaf,
        0xed, 0x6a, 0x72, 0xda, 0xbf, 0x7a, 0xb2, 0x76,
        0x8c, 0x35, 0xa7, 0x54, 0x52, 0xda, 0xa1, 0x00,
        0x47, 0x83, 0x29, 0x72, 0xa9, 0x8d, 0xd9, 0x78
    };
    static CK_BYTE         server_random[] = {
        0x48, 0xdc, 0xda, 0xaa, 0x83, 0xeb, 0x5c, 0x08,
        0x51, 0x20, 0xc6, 0x8e, 0x29, 0xb4, 0x30, 0xff,
        0xc5, 0x9e, 0x4f, 0x1c, 0xa9, 0x75, 0xa7, 0x35,
        0x62, 0x89, 0x29, 0x17, 0x28, 0x70, 0x56, 0x06
    };
    static CK_BYTE         master[] = {
        0x68, 0x0d, 0x31, 0x53, 0x6a, 0x20, 0x68, 0x50,
        0xca, 0x66, 0x01, 0x3f, 0xb6, 0xfa, 0xe3, 0x26,
        0x51, 0xb5, 0xba, 0x03, 0x16, 0xa3, 0xdf, 0xc1,
        0x33, 0x99, 0xd0, 0x61, 0xda, 0x74, 0x12, 0x4e,
        0x96, 0x6f, 0x9a, 0x1c, 0x25, 0x24, 0x89, 0x42,
        0xa9, 0xeb, 0x0c, 0x56, 0xe1, 0x04, 0xbb, 0x6a
    };
/*
    static CK_BYTE         ClientMacSecret_94[] = {
        0x12, 0x9d, 0xf5, 0xe1, 0x00, 0xfe, 0x51, 0xf5,
        0xe9, 0xf3, 0xcf, 0xff, 0x4f, 0x02, 0xcc, 0xc9,
        0x4a, 0xe8, 0x32, 0x11, 0xbd, 0x35, 0x63, 0xa8,
        0xe2, 0xa9, 0x71, 0xd2, 0x61, 0x24, 0x7b, 0x23
    };
    static CK_BYTE         ServerMacSecret_94[] = {
        0xc0, 0xfc, 0x04, 0x6f, 0xa2, 0xc6, 0x30, 0x97,
        0xa2, 0x90, 0x8a, 0x47, 0x56, 0x26, 0x9d, 0xc9,
        0xe9, 0x87, 0x71, 0x41, 0xc4, 0x3d, 0x81, 0x81,
        0xb0, 0x30, 0x8f, 0xea, 0xa3, 0x86, 0x4f, 0xae
    };
    static CK_BYTE         ClientKey_94[] = {
        0x0e, 0xfc, 0x85, 0x03, 0x2b, 0x40, 0xbd, 0x65,
        0xce, 0x39, 0x70, 0xb0, 0xed, 0xd2, 0x48, 0xdd,
        0x6e, 0xfc, 0x97, 0x29, 0xb4, 0xd5, 0xd8, 0x91,
        0x42, 0x7a, 0xd0, 0x55, 0x9c, 0x7c, 0x6e, 0x61
    };
    static CK_BYTE         ServerKey_94[] = {
        0x17, 0x34, 0xe4, 0x2c, 0x8e, 0x9c, 0x6f, 0xda,
        0x3c, 0x30, 0xbb, 0xc8, 0xfa, 0xe7, 0x8d, 0x76,
        0x11, 0x0c, 0xfc, 0x5b, 0xeb, 0x00, 0x92, 0xb2,
        0xed, 0x36, 0x3e, 0x27, 0x60, 0x12, 0x50, 0xa5
    };
    static CK_BYTE         IVs_94[] = {
        0x99, 0x38, 0xb5, 0x71, 0x78, 0x47, 0x92, 0xba,
        0xc4, 0x43, 0xb9, 0xd3, 0x24, 0x75, 0x09, 0x79
    };
*/
    static CK_BYTE         ClientMacSecret_2012_256[] = {
        0xda, 0x34, 0x80, 0x58, 0x2c, 0x3d, 0x3d, 0x0c,
        0x72, 0x90, 0x17, 0xc1, 0x84, 0xaf, 0x48, 0xe6,
        0xda, 0x72, 0x1f, 0x9f, 0x7e, 0x14, 0x67, 0x3e,
        0xcd, 0x68, 0xb3, 0xef, 0xf8, 0xa7, 0x83, 0xc5,
    };
    static CK_BYTE         ServerMacSecret_2012_256[] = {
        0x27, 0x5e, 0xfa, 0x85, 0xf7, 0x0e, 0x29, 0x59,
        0x6d, 0x0d, 0x4a, 0x8c, 0xed, 0xd4, 0xe2, 0x41,
        0x36, 0x7b, 0x83, 0x8d, 0x13, 0xe9, 0xea, 0xd4,
        0x9e, 0x7e, 0xaa, 0x26, 0x27, 0xdc, 0x5c, 0xe8,
    };
    static CK_BYTE         ClientKey_2012_256[] = {
        0xa2, 0x90, 0xca, 0x47, 0xce, 0x0e, 0x40, 0xa8,
        0x53, 0xb2, 0x78, 0x0e, 0xc0, 0x5f, 0x05, 0xf3,
        0x1f, 0xcb, 0x4b, 0xc2, 0x54, 0x04, 0x62, 0xe8,
        0x07, 0xfc, 0xdc, 0x40, 0x49, 0xd9, 0xf2, 0x4f,
    };
    static CK_BYTE         ServerKey_2012_256[] = {
        0xb6, 0x8a, 0xd8, 0x7e, 0xa8, 0xe7, 0xaa, 0x86,
        0x96, 0x8c, 0xfd, 0xcd, 0x58, 0x26, 0x66, 0x51,
        0x2d, 0x97, 0x17, 0x73, 0x5d, 0xab, 0x11, 0xe3,
        0xff, 0x8f, 0xd2, 0x92, 0x11, 0xc8, 0x79, 0xfa,
    };
    static CK_BYTE         IVs_2012_256[] = {
        0xe4, 0x33, 0x67, 0x07, 0xec, 0x94, 0x9d, 0xcd,
        0xde, 0x7c, 0x3a, 0xa1, 0x9f, 0x7c, 0x41, 0xda,
    };
    static CK_BYTE         ClientMacSecret_2012_512[] = {
        0x6b, 0x19, 0x9f, 0xf5, 0x37, 0x80, 0x16, 0xc1,
        0xc1, 0x67, 0x92, 0x77, 0xad, 0x22, 0xb9, 0xd2,
        0x95, 0x1c, 0xac, 0x40, 0xeb, 0x44, 0x47, 0x9f,
        0xe5, 0xe0, 0xa0, 0x09, 0x0a, 0xe1, 0xad, 0x65,
    };
    static CK_BYTE         ServerMacSecret_2012_512[] = {
        0x93, 0x25, 0x4c, 0x12, 0xa6, 0x39, 0x6e, 0xf7,
        0x34, 0xeb, 0x6c, 0x47, 0x53, 0x41, 0x36, 0x0e,
        0x06, 0xd7, 0x19, 0x46, 0x69, 0x36, 0x5d, 0x6a,
        0xf5, 0xae, 0xe6, 0x89, 0xf4, 0x45, 0xc2, 0x73,
    };
    static CK_BYTE         ClientKey_2012_512[] = {
        0xc4, 0x3b, 0x32, 0x78, 0xb0, 0x75, 0xcd, 0x7f,
        0x34, 0xa4, 0x98, 0xe3, 0xad, 0x1d, 0x9c, 0x0a,
        0x69, 0x7d, 0xc0, 0x40, 0x79, 0x42, 0xfd, 0x69,
        0x31, 0xec, 0x87, 0x6f, 0x05, 0xae, 0x88, 0x8e,
    };
    static CK_BYTE         ServerKey_2012_512[] = {
        0x4c, 0x7f, 0xad, 0xa0, 0xb6, 0xb9, 0x8f, 0xda,
        0xff, 0x6d, 0xb8, 0xec, 0x13, 0x31, 0x54, 0xd7,
        0x6c, 0xfb, 0xfc, 0x0e, 0xd3, 0xdd, 0xe0, 0xf0,
        0x89, 0x48, 0x8e, 0x8a, 0x5d, 0x96, 0x65, 0x71,
    };
    static CK_BYTE         IVs_2012_512[] = {
        0x6d, 0x21, 0xda, 0x52, 0xaa, 0xcf, 0x84, 0x01,
        0x4d, 0x94, 0xec, 0x24, 0x6a, 0x17, 0xeb, 0x05,
    };

    // В зависимости от значения OID для ГОСТ Р 34.11
    // в функции TLS PRF будут использоваться алгоритмы ГОСТ Р 34.11-94,
    // ГОСТ Р 34.11-2012 (256 бит) или ГОСТ Р 34.11-2012 (512 бит).
//    static CK_BYTE             oid_3411_94[] = {
//        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01
//    };
//    static CK_BYTE             oid_3411_2012_256[] = {
//        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02
//    };
//    static CK_BYTE             oid_3411_2012_512[] = {
//        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03
//    };

    static CK_BYTE             gost28147_params_A[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01
    };

    static CK_ATTRIBUTE        master_key_template[] = {
        { CKA_VALUE,       master,  sizeof(master) },
        { CKA_CLASS,       &oclass, sizeof(oclass) },
        { CKA_KEY_TYPE,
          &generic_key_type, sizeof(generic_key_type) },
        { CKA_DERIVE,      &ltrue,  sizeof(ltrue)  },
        { CKA_SENSITIVE,   &lfalse, sizeof(lfalse) },
        { CKA_EXTRACTABLE, &ltrue,  sizeof(ltrue)  }
    };

    static CK_ATTRIBUTE        secret_key_template[] = {
        { CKA_CLASS,            &oclass,          sizeof(oclass)          },
        { CKA_KEY_TYPE,         &secret_key_type, sizeof(secret_key_type) },
        { CKA_GOST28147_PARAMS,
          gost28147_params_A, sizeof(gost28147_params_A) },
        { CKA_ENCRYPT,          &ltrue,           sizeof(ltrue)           },
        { CKA_DECRYPT,          &ltrue,           sizeof(ltrue)           },
        { CKA_SIGN,             &ltrue,           sizeof(ltrue)           },
        { CKA_VERIFY,           &ltrue,           sizeof(ltrue)           },
        { CKA_SENSITIVE,        &lfalse,          sizeof(lfalse)          },
        { CKA_EXTRACTABLE,      &ltrue,           sizeof(ltrue)           }
    };
    static CK_BYTE             IVClient[8];
    static CK_BYTE             IVServer[8];
    static CK_SSL3_KEY_MAT_OUT km = {
        CK_INVALID_HANDLE, CK_INVALID_HANDLE,
        CK_INVALID_HANDLE, CK_INVALID_HANDLE,
        IVClient,          IVServer
    };
    // Интересно, что в данной структуре нет поля pVersion,
    // как в структуре CK_TLS12_MASTER_KEY_DERIVE_PARAMS.
    // Пока непонятно, какое значение имеет для российского
    // варианта версия протокола.
    // Точнее, непонятно, есть ли в протоколе 1.2 какие-то
    // особенности для российских алгоритмов.
    // В проекте документа ТК 26 говорится, что в prfHashMechanism
    // всегда нужно подставлять механизмы PRF, хотя в стандарте 2.40
    // сказано, что для протокола 1.2 нужно указывать механизм
    // хеширования, а для 1.0 и 1.1 - механизм PRF.
/*
   typedef struct CK_TLS12_KEY_MAT_PARAMS {
        CK_ULONG ulMacSizeInBits;
        CK_ULONG ulKeySizeInBits;
        CK_ULONG ulIVSizeInBits;
        CK_BBOOL bIsExport;
        CK_SSL3_RANDOM_DATA RandomInfo;
        CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
        CK_MECHANISM_TYPE prfHashMechanism;
   } CK_TLS12_KEY_MAT_PARAMS;
 */
    static CK_TLS12_KEY_MAT_PARAMS params =
    {
        256, 256, 64, CK_FALSE,
        {
            client_random, sizeof(client_random),
            server_random, sizeof(server_random)
        },
        &km,
        CKM_TLS_GOST_PRF_2012_256
    };


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

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

    rc = funcs->C_DeriveKey(sess, mechanism, keyh,
                            secret_key_template,
                            sizeof(secret_key_template) / sizeof(CK_ATTRIBUTE),
                            NULL_PTR);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey failed: 0x%lx\n", rc);
        return rc;
    }
    attr.type       = CKA_VALUE;
    attr.pValue     = value;
    attr.ulValueLen = sizeof(value);
    rc              = funcs->C_GetAttributeValue(sess,
                                                 km.hClientMacSecret, &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
        return rc;
    }
    len = attr.ulValueLen;

    printf("ClientMacSecret 3411-2012 (256):\n");
    print_bytes(value, len);
    CHECK(value, len, ClientMacSecret_2012_256);

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

    printf("ServerMacSecret 3411-2012 (256):\n");
    print_bytes(value, len);
    CHECK(value, len, ServerMacSecret_2012_256);

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

    printf("ClientKey 3411-2012 (256):\n");
    print_bytes(value, len);
    CHECK(value, len, ClientKey_2012_256);

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

    printf("ServerKey 3411-2012 (256):\n");
    print_bytes(value, len);
    CHECK(value, len, ServerKey_2012_256);

    memcpy(value, IVClient, 8);
    memcpy(value + 8, IVServer, 8);
    len = 16;

    printf("IVClient & IVServer 3411-2012 (256):\n");
    print_bytes(value, len);
    CHECK(value, len, IVs_2012_256);

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

    params.prfHashMechanism = CKM_TLS_GOST_PRF_2012_512;

    rc = funcs->C_DeriveKey(sess, mechanism, keyh,
                            secret_key_template,
                            sizeof(secret_key_template) / sizeof(CK_ATTRIBUTE),
                            NULL_PTR);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey failed: 0x%lx\n", rc);
        return rc;
    }
    attr.type       = CKA_VALUE;
    attr.pValue     = value;
    attr.ulValueLen = sizeof(value);
    rc              = funcs->C_GetAttributeValue(sess,
                                                 km.hClientMacSecret, &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
        return rc;
    }
    len = attr.ulValueLen;

    printf("ClientMacSecret 3411-2012 (512):\n");
    print_bytes(value, len);
    CHECK(value, len, ClientMacSecret_2012_512);

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

    printf("ServerMacSecret 3411-2012 (512):\n");
    print_bytes(value, len);
    CHECK(value, len, ServerMacSecret_2012_512);

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

    printf("ClientKey 3411-2012 (512):\n");
    print_bytes(value, len);
    CHECK(value, len, ClientKey_2012_512);

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

    printf("ServerKey 3411-2012 (512):\n");
    print_bytes(value, len);
    CHECK(value, len, ServerKey_2012_512);

    memcpy(value, IVClient, 8);
    memcpy(value + 8, IVServer, 8);
    len = 16;

    printf("IVClient & IVServer 3411-2012 (512):\n");
    print_bytes(value, len);
    CHECK(value, len, IVs_2012_512);

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

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

