#include "test_common.h"

int main(int argc, char* argv[])
{
    CK_RV                rvResult;
#ifdef WIN32
    HMODULE              hPkcsLib  = NULL;
    HMODULE              hPkcsLib2 = NULL;
#else
    void                 *hPkcsLib  = NULL;
    void                 *hPkcsLib2 = NULL;
#endif
    CK_C_GetFunctionList pcGetFunctionList  = 0;
    CK_C_GetFunctionList pcGetFunctionList2 = 0;
    CK_FUNCTION_LIST_PTR Pkcs11FuncList     = NULL;
    CK_FUNCTION_LIST_PTR Pkcs11FuncList2    = NULL;
    CK_SLOT_ID_PTR       pSlotList          = NULL;
    CK_SLOT_ID_PTR       pSlotList2         = NULL;
    CK_SLOT_ID           SlotId = 0;
    CK_SLOT_ID           SlotId2 = 0;
    CK_ULONG             ulSlotCount;
    CK_ULONG             ulSlotCount2;
    CK_SLOT_INFO         SlotInfo;
    CK_SESSION_HANDLE    hSession;
    CK_SESSION_HANDLE    hSession2;
    static CK_BBOOL             blTrue              = CK_TRUE,
                         blFalse             = CK_FALSE;
    static CK_ULONG ulKeyType = CKK_GENERIC_SECRET,
                         ulClass_SecKey      = CKO_SECRET_KEY;
    // PAR 512 A OID
    static CK_BYTE      gostR3410params[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x2, 0x01, 0x02, 0x01
    };
    // PAR 3411-2012-512 OID
    static CK_BYTE      gostR3411params[] = {
        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03
    };
    CK_ATTRIBUTE caGOST_PublicKeyTemplate[] =
    {
        { CKA_PRIVATE,          &blFalse,          sizeof(CK_BBOOL)          },
        { CKA_GOSTR3410_PARAMS, gostR3410params,   sizeof(gostR3410params)   },
        { CKA_GOSTR3411_PARAMS, gostR3411params,   sizeof(gostR3411params)   },
    };
    CK_ATTRIBUTE caGOST_PrivateKeyTemplate[] =
    {
        { CKA_PRIVATE, &blTrue, sizeof(CK_BBOOL) },
        { CKA_DERIVE,  &blTrue, sizeof(CK_BBOOL) },
    };
    // template for derived key
    CK_ATTRIBUTE caDeriveKey[] =
    {
        { CKA_CLASS,           &ulClass_SecKey,      sizeof(CK_ULONG)          },
        { CKA_KEY_TYPE,        &ulKeyType,			 sizeof(CK_ULONG)          },
        { CKA_SENSITIVE,       &blFalse,             sizeof(CK_BBOOL)          },
        { CKA_EXTRACTABLE,     &blTrue,              sizeof(CK_BBOOL)          },
    };

    CK_ULONG
        ulPubKeyCount =
        sizeof(caGOST_PublicKeyTemplate) / sizeof(CK_ATTRIBUTE),
        ulPriKeyCount =
        sizeof(caGOST_PrivateKeyTemplate) / sizeof(CK_ATTRIBUTE),
        ulDeriveKeyCount =
        sizeof(caDeriveKey) / sizeof(CK_ATTRIBUTE);

    CK_OBJECT_HANDLE
        hSendPubKey = 0,             // handle to public key of sender
        hSendPriKey = 0,             // handle to private key of sender
        hRecpPubKey = 0,             // handle to public key of recipient
        hRecpPriKey = 0,             // handle to private key of recipient
        hSendDH_Key = 0,             // Diffy-Hellman key of the sender
        hRecpDH_Key = 0;             // Diffy-Hellman key of the recipient
    CK_MECHANISM
        cmKeyGenMechanism,           // mechanism for key pair genaration
        cmDeriveMechanism;           // mechanism for key derivation


    CK_ULONG
        i;

    CK_BYTE_PTR
        pbSend_PubKeyValue = NULL,
        pbRecp_PubKeyValue = NULL,
        pbSendDH_Key_Value = NULL,
        pbRecpDH_Key_Value = NULL;

    CK_ATTRIBUTE
        caSend_PubKeyValue = { CKA_VALUE, NULL, 0 },
        caRecp_PubKeyValue = { CKA_VALUE, NULL, 0 },
        SendDH_Key_Value   = { CKA_VALUE, NULL, 0 },
        RecpDH_Key_Value   = { CKA_VALUE, NULL, 0 };

    // parameters for derivation mechanism
    CK_TC26_GOSTR3410_2012_VKO_PARAMS *DeriveParams = NULL;
    // UKM must be non-zero
    CK_BYTE                        fixed_ukm[16] = {
        0x9d, 0x23, 0x98, 0xc0, 0x12, 0x31, 0x2a, 0x8e,
        0x23, 0xf4, 0xca, 0xdd, 0x03, 0xa8, 0x17, 0x97,
    };
    char                        *api_path           = STRINGIFY(PKCS11_API_PATH);
    CK_UTF8CHAR                    *user_pin           = (CK_UTF8CHAR *)STRINGIFY(USER_PIN);
//    CK_ULONG                       slot_num            = 0;
    char                        *api_path2          = STRINGIFY(PKCS11_API_PATH);
    CK_UTF8CHAR                    *user_pin2          = (CK_UTF8CHAR *)STRINGIFY(USER_PIN);
//    CK_ULONG                       slot_num2           = 0;
    SYSTEMTIME                     t1, t2;
    CK_ULONG                       diff /*, min_time, max_time, avg_time*/;

    printf("Starting CKM_GOSTR3410_2012_512_VKO_256 test\n");

	caSend_PubKeyValue.pValue = pbSend_PubKeyValue;
	caRecp_PubKeyValue.pValue = pbRecp_PubKeyValue;
	SendDH_Key_Value.pValue = pbSendDH_Key_Value;
	RecpDH_Key_Value.pValue = pbRecpDH_Key_Value;

    for (i = 1; i < (CK_ULONG) argc; i++)
    {
        if (strcmp("-api", argv[i]) == 0)
        {
            ++i;
            api_path = argv[i];
        }
/*
        else if (strcmp("-slot", argv[i]) == 0)
        {
            ++i;
            slot_num = atoi(argv[i]);
        }
*/
        else if (strcmp("-user_pin", argv[i]) == 0)
        {
            ++i;
            user_pin = (CK_UTF8CHAR *)argv[i];
        }
    }

    api_path2 = api_path;
    user_pin2 = user_pin;
//    slot_num2 = slot_num;

#ifdef WIN32
    hPkcsLib = LoadLibrary(api_path);
#else
#ifdef __APPLE__
    hPkcsLib = dlopen(api_path, RTLD_LOCAL|RTLD_LAZY);
#else
    hPkcsLib = dlopen(api_path, RTLD_LOCAL|RTLD_LAZY|RTLD_DEEPBIND);
#endif
#endif
    if (hPkcsLib == NULL)
    {
        printf(
            "Can't load PKCS#11 API library. "
            "Check API library path.\n");
#ifndef WIN32
        printf("dlerror: %s\n", dlerror());
#endif
        return -1;
    }
#ifdef WIN32
    pcGetFunctionList =
        (CK_C_GetFunctionList) GetProcAddress(
            hPkcsLib, "C_GetFunctionList");
#else
    pcGetFunctionList =
        (CK_C_GetFunctionList) dlsym(
            hPkcsLib, "C_GetFunctionList");
#endif

    // get PKCS #11 function list
    rvResult = pcGetFunctionList(&Pkcs11FuncList);
    printf("Load PKCS #11 function list result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    // initialize Cryptoki
    rvResult = Pkcs11FuncList->C_Initialize(NULL);
    printf("Initialize Cryptoki result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    // get slot list
    rvResult = Pkcs11FuncList->C_GetSlotList(CK_FALSE, NULL, &ulSlotCount);
    printf("Get slot list result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    if (ulSlotCount > 0)
    {       // allocate memory for slot list
        pSlotList = (CK_SLOT_ID_PTR) malloc(
            ulSlotCount * sizeof(CK_SLOT_ID));

        rvResult =
            Pkcs11FuncList->C_GetSlotList(
                CK_FALSE, pSlotList, &ulSlotCount);

        if (rvResult != CKR_OK)
            return rvResult;
        printf("Slot count: %ld\n", ulSlotCount);
    }
    else
        return -3;

    // get information about sender and recipient slots.
    for (i = 0; i < ulSlotCount; ++i)
    {
        rvResult = Pkcs11FuncList->C_GetSlotInfo(pSlotList[i], &SlotInfo);
        if (rvResult == CKR_OK)
        {          // if a token is present in this slot
            if ((SlotInfo.flags & CKF_TOKEN_PRESENT) == CKF_TOKEN_PRESENT)
            {
                SlotId = pSlotList[i];
                break;
            }
        }
    }
    if (i >= ulSlotCount)
    {
        printf("No slots with token present\n");
        return -3;
    }

    printf("Slot ID: %ld\n", SlotId);

    // open session for slot with ID = SlotId[0]
    rvResult = Pkcs11FuncList->C_OpenSession(SlotId,
                                             (CKF_SERIAL_SESSION | CKF_RW_SESSION),
                                             NULL,
                                             0,
                                             &hSession);
    printf("Open session result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    // user login
    rvResult = Pkcs11FuncList->C_Login(hSession,
                                       CKU_USER,
                                       user_pin,
                                       (CK_ULONG)strlen((char *)user_pin));
    printf("Login result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;
    // load library PKCS #11
#ifdef WIN32
    hPkcsLib2 = LoadLibrary(api_path2);
#else
#ifdef __APPLE__
    hPkcsLib2 = dlopen(api_path2, RTLD_LOCAL|RTLD_LAZY);
#else
    hPkcsLib2 = dlopen(api_path2, RTLD_LOCAL|RTLD_LAZY|RTLD_DEEPBIND);
#endif
#endif
    if (hPkcsLib2 == NULL)
    {
        printf("Can't load PKCS#11 API library. "
               "Check API library path.\n");
#ifndef WIN32
        printf("dlerror: %s\n", dlerror());
#endif
        return FALSE;
    }
#ifdef WIN32
    pcGetFunctionList2 =
        (CK_C_GetFunctionList) GetProcAddress(
            hPkcsLib2, "C_GetFunctionList");
#else
    pcGetFunctionList2 =
        (CK_C_GetFunctionList) dlsym(
            hPkcsLib2, "C_GetFunctionList");
#endif

    // get PKCS #11 function list
    rvResult = pcGetFunctionList2(&Pkcs11FuncList2);
    printf("Load PKCS #11 function list result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    // initialize Cryptoki
    rvResult = Pkcs11FuncList2->C_Initialize(NULL);
    printf("Initialize Cryptoki result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK &&
        rvResult != CKR_CRYPTOKI_ALREADY_INITIALIZED)
        return rvResult;

    // get slot list
    rvResult = Pkcs11FuncList2->C_GetSlotList(CK_FALSE, NULL, &ulSlotCount2);
    printf("Get slot list result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    if (ulSlotCount2 > 0)
    {       // allocate memory for slot list
        pSlotList2 =
            (CK_SLOT_ID_PTR) malloc(
                ulSlotCount2 * sizeof(CK_SLOT_ID));

        rvResult =
            Pkcs11FuncList2->C_GetSlotList(CK_FALSE,
                                           pSlotList2, &ulSlotCount2);

        if (rvResult != CKR_OK)
            return rvResult;
        printf("Slot count: %ld\n", ulSlotCount2);
    }
    else
        return -3;

    // get information about sender and recipient slots.
    for (i = 0; i < ulSlotCount2; ++i)
    {
        rvResult = Pkcs11FuncList2->C_GetSlotInfo(
            pSlotList2[i], &SlotInfo);
        if (rvResult == CKR_OK)
        {          // if a token is present in this slot
            if ((SlotInfo.flags & CKF_TOKEN_PRESENT) == CKF_TOKEN_PRESENT)
            {
                SlotId2 = pSlotList2[i];
                break;
            }
        }
    }
    if (i >= ulSlotCount2)
    {
        printf("No slots with token present\n");
        return -3;
    }

    printf("Slot ID 2: %ld\n", SlotId2);
    {
        CK_TOKEN_INFO tinfo;
        rvResult = Pkcs11FuncList2->C_GetTokenInfo(pSlotList2[i], &tinfo);
        if (rvResult != CKR_OK)
        {
            printf("Can't get token info\n");
            return -4;
        }
    }

    // open session for slot with ID = SlotId2[0]
    rvResult = Pkcs11FuncList2->C_OpenSession(SlotId2,
                                              (CKF_SERIAL_SESSION | CKF_RW_SESSION),
                                              NULL,
                                              0,
                                              &hSession2);
    printf("Open session result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    // User login may return CKR_USER_ALREADY_LOGGED_IN
    // if the same token used for recipient.
    rvResult = Pkcs11FuncList2->C_Login(hSession2,
                                        CKU_USER,
                                        user_pin2,
                                        (CK_ULONG)strlen((char *)user_pin2));
    printf("Login result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK &&
        rvResult != CKR_USER_ALREADY_LOGGED_IN)
        return rvResult;

    // generate sender key pair with GOST R 34.10-2012 (512)
    printf("Generate key pair of sender\n");
    printf("   mechanism type: CKM_GOSTR3410_512_KEY_PAIR_GEN\n");

    cmKeyGenMechanism.mechanism      = CKM_GOSTR3410_512_KEY_PAIR_GEN;
    cmKeyGenMechanism.pParameter     = NULL;
    cmKeyGenMechanism.ulParameterLen = 0;
    rvResult                         = Pkcs11FuncList->C_GenerateKeyPair(hSession,
                                                                         &cmKeyGenMechanism,
                                                                         caGOST_PublicKeyTemplate,
                                                                         ulPubKeyCount,
                                                                         caGOST_PrivateKeyTemplate,
                                                                         ulPriKeyCount,
                                                                         &hSendPubKey,
                                                                         &hSendPriKey);
    printf("   generate sender key pair: result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    printf("   sender public key handle: %ld\n",
           (unsigned long) hSendPubKey);
    printf("   sender private key handle: %ld\n",
           (unsigned long) hSendPriKey);

    // generate recipient key pair with GOST R 34.10-2012 (512)
    printf("Generate key pair of recipient\n");
    printf("   mechanism type: CKM_GR3410_512_KEY_PAIR_GEN\n");

    rvResult = Pkcs11FuncList2->C_GenerateKeyPair(hSession2,
                                                  &cmKeyGenMechanism,
                                                  caGOST_PublicKeyTemplate,
                                                  ulPubKeyCount,
                                                  caGOST_PrivateKeyTemplate,
                                                  ulPriKeyCount,
                                                  &hRecpPubKey,
                                                  &hRecpPriKey);
    printf("   generate recipient key pair: result: 0x%lx\n",
           rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    printf("   recipient public key handle: %ld\n",
           (unsigned long) hRecpPubKey);
    printf("   recipient private key handle: %ld\n",
           (unsigned long) hRecpPriKey);

    // get value of sender public key
    printf("Get value of sender public key\n");

    rvResult = Pkcs11FuncList->C_GetAttributeValue(hSession,
                                                   hSendPubKey,
                                                   &caSend_PubKeyValue,
                                                   1);
    if (rvResult == CKR_OK)
    {
        caSend_PubKeyValue.pValue =
            (CK_BYTE_PTR) malloc(caSend_PubKeyValue.ulValueLen);
        rvResult = Pkcs11FuncList->C_GetAttributeValue(hSession,
                                                       hSendPubKey,
                                                       &caSend_PubKeyValue,
                                                       1);
    }
    printf("   Get sender public key value: result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    pbSend_PubKeyValue = (CK_BYTE_PTR) caSend_PubKeyValue.pValue;
    printf("   Sender public key value: ");

    for (i = 0; i < caSend_PubKeyValue.ulValueLen; ++i)
        printf("%02x ", pbSend_PubKeyValue[i]);
    printf("\n");

    // get value of recipient public key
    printf("Get value of recipient public key\n");
    rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSession2,
                                                    hRecpPubKey,
                                                    &caRecp_PubKeyValue,
                                                    1);
    if (rvResult == CKR_OK)
    {
        caRecp_PubKeyValue.pValue =
            (CK_BYTE_PTR) malloc(caRecp_PubKeyValue.ulValueLen);
        rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSession2,
                                                        hRecpPubKey,
                                                        &caRecp_PubKeyValue,
                                                        1);
    }

    printf("   Get recipient public key value: result: 0x%lx\n",
           rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    printf("   Recipient public key value: ");
    pbRecp_PubKeyValue = (CK_BYTE_PTR) caRecp_PubKeyValue.pValue;

    for (i = 0; i < caRecp_PubKeyValue.ulValueLen; ++i)
        printf("%02x ", pbRecp_PubKeyValue[i]);
    printf("\n");

    // fill parameters for derivation mechanism
    DeriveParams = (CK_TC26_GOSTR3410_2012_VKO_PARAMS *)
                   malloc(sizeof(CK_TC26_GOSTR3410_2012_VKO_PARAMS));
    DeriveParams->pPublicData     = (CK_BYTE_PTR) caRecp_PubKeyValue.pValue;
    DeriveParams->ulPublicDataLen = caRecp_PubKeyValue.ulValueLen;
    DeriveParams->pUKM            = fixed_ukm;
    DeriveParams->ulUKMLen        = sizeof(fixed_ukm);

    cmDeriveMechanism.mechanism      = CKM_GOSTR3410_2012_VKO_256;
    cmDeriveMechanism.pParameter     = DeriveParams;
    cmDeriveMechanism.ulParameterLen = sizeof(CK_TC26_GOSTR3410_2012_VKO_PARAMS);

    printf("Derive Diffie-Hellman key\n");
    printf("   derivation mechanism: CKM_GOSTR3410_2012_VKO_256\n");

    // derive Diffi-Hellman key of sender
    printf("   derive Diffie-Hellman key for sender\n");
    GetSystemTime(&t1);
    rvResult = Pkcs11FuncList->C_DeriveKey(hSession,
                                           &cmDeriveMechanism,
                                           hSendPriKey,
                                           caDeriveKey,
                                           ulDeriveKeyCount,
                                           &hSendDH_Key);
    GetSystemTime(&t2);
    diff = process_time(t1, t2);
    fprintf(stderr, "C_DeriveKey time:  %ld msec\n", diff);
    printf("   derive key result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    printf("   sender's Diffie-Hellman key handle: %ld\n",
           (unsigned long) hSendDH_Key);

    // get value of sender derived key
    printf("Get value of sender derived key\n");
    rvResult = Pkcs11FuncList->C_GetAttributeValue(hSession,
                                                   hSendDH_Key,
                                                   &SendDH_Key_Value,
                                                   1);
    if (rvResult == CKR_OK)
    {
        SendDH_Key_Value.pValue =
            (CK_BYTE_PTR) malloc(SendDH_Key_Value.ulValueLen);
        rvResult = Pkcs11FuncList->C_GetAttributeValue(hSession,
                                                       hSendDH_Key,
                                                       &SendDH_Key_Value,
                                                       1);
    }

    printf("   Get sender derived key value: result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    pbSendDH_Key_Value = (CK_BYTE_PTR) SendDH_Key_Value.pValue;

    printf("   Sender derived key value: ");

    for (i = 0; i < SendDH_Key_Value.ulValueLen; ++i)
        printf("%02x ", pbSendDH_Key_Value[i]);
    printf("\n");

    // derive Diffi-Hellman key of recipient
    printf("   derive Diffie-Hellman key for recipient\n");
    DeriveParams->pPublicData     = (CK_BYTE_PTR) caSend_PubKeyValue.pValue;
    DeriveParams->ulPublicDataLen = caSend_PubKeyValue.ulValueLen;

    rvResult = Pkcs11FuncList2->C_DeriveKey(hSession2,
                                            &cmDeriveMechanism,
                                            hRecpPriKey,
                                            caDeriveKey,
                                            ulDeriveKeyCount,
                                            &hRecpDH_Key);
    printf("   derive key result: 0x%lx\n", rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    printf("   recipient's Diffie-Hellman key handle: %ld\n",
           (unsigned long) hRecpDH_Key);

    // get value of recipient derived key
    printf("Get value of recipient derived key\n");
    rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSession2,
                                                    hRecpDH_Key,
                                                    &RecpDH_Key_Value,
                                                    1);
    if (rvResult == CKR_OK)
    {
        RecpDH_Key_Value.pValue =
            (CK_BYTE_PTR) malloc(RecpDH_Key_Value.ulValueLen);
        rvResult = Pkcs11FuncList2->C_GetAttributeValue(hSession2,
                                                        hRecpDH_Key,
                                                        &RecpDH_Key_Value,
                                                        1);
    }

    printf("   Get recipient derived key value: result: 0x%lx\n",
           rvResult);
    if (rvResult != CKR_OK)
        return rvResult;

    pbRecpDH_Key_Value = (CK_BYTE_PTR) RecpDH_Key_Value.pValue;

    printf("   Recipient derived key value: ");

    for (i = 0; i < RecpDH_Key_Value.ulValueLen; ++i)
        printf("%x ", pbRecpDH_Key_Value[i]);
    printf("\n");

    // Sender and recipient DH key values must be equal
    if (RecpDH_Key_Value.ulValueLen != SendDH_Key_Value.ulValueLen)
    {
        printf("Sender and recipient VKO keys are of different length\n");
        return -1;
    }
    for (i = 0; i < RecpDH_Key_Value.ulValueLen; ++i)
    {
        if (pbRecpDH_Key_Value[i] != pbSendDH_Key_Value[i])
        {
            printf(
                "Sender and recipient VKO keys "
                "are different at position %ld\n", i);
            return -1;
        }
    }
    printf("Sender and recipient VKO keys are equal\n");
    rvResult = Pkcs11FuncList->C_DestroyObject(hSession, hSendPubKey);
    printf("   C_DestroyObject result: 0x%lx\n", rvResult);
    rvResult = Pkcs11FuncList->C_DestroyObject(hSession, hSendPriKey);
    printf("   C_DestroyObject result: 0x%lx\n", rvResult);

    rvResult = Pkcs11FuncList2->C_DestroyObject(hSession2, hRecpPubKey);
    printf("   C_DestroyObject result: 0x%lx\n", rvResult);
    rvResult = Pkcs11FuncList2->C_DestroyObject(hSession2, hRecpPriKey);
    printf("   C_DestroyObject result: 0x%lx\n", rvResult);

    rvResult = Pkcs11FuncList->C_DestroyObject(hSession, hSendDH_Key);
    printf("   C_DestroyObject result: 0x%lx\n", rvResult);

    rvResult = Pkcs11FuncList2->C_DestroyObject(hSession2, hRecpDH_Key);
    printf("   C_DestroyObject result: 0x%lx\n", rvResult);
    rvResult = Pkcs11FuncList->C_Logout(hSession);
    printf("Sender logout result: 0x%lx\n", rvResult);

    // User logout may return CKR_USER_NOT_LOGGED_IN
    // if the same token used for recipient.
    rvResult = Pkcs11FuncList2->C_Logout(hSession2);
    printf("Recipient logout result: 0x%lx\n", rvResult);
    // Close session
    rvResult = Pkcs11FuncList->C_CloseSession(hSession);
    printf("Sender close session result: 0x%lx\n", rvResult);

    rvResult = Pkcs11FuncList2->C_CloseSession(hSession2);
    printf("Recipient close session result: 0x%lx\n", rvResult);

    printf("CKM_GOSTR3410_2012_512_VKO_256 test SUCCESS\n");
    return 0;
}
