#include "test_common.h"

CK_RV test_crypto();
CK_RV test_pkcs5_pbkd2_key_gen(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_PKCS5_PBKD2, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_PKCS5_PBKD2 not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_PKCS5_PBKD2 test ==================\n");
        rc = test_pkcs5_pbkd2_key_gen(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_PKCS5_PBKD2 failed, rc = 0x%lx\n", rc);
        }
        else
        {
            fprintf(stderr, "CKM_PKCS5_PBKD2 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_pkcs5_pbkd2_key_gen(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
//    char                   str[2048];
    CK_BYTE                value[256];
    CK_ULONG               len;
    CK_MECHANISM           mechanism_desc = { CKM_PKCS5_PBKD2, NULL, 0 };
    CK_MECHANISM_PTR       mechanism      = &mechanism_desc;

    CK_OBJECT_HANDLE       key           = CK_INVALID_HANDLE;
    static CK_BBOOL        ltrue         = CK_TRUE;
    static CK_BBOOL        lfalse        = CK_FALSE;
    static CK_BYTE         gost28147_A[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01
    };
    static CK_OBJECT_CLASS        lclass   = CKO_SECRET_KEY;
    static CK_KEY_TYPE            keyType  = CKK_GOST28147;
    static CK_KEY_TYPE            keyType2 = CKK_GENERIC_SECRET;
    static CK_ULONG               keyLen   = 32;
    CK_PKCS5_PBKD2_PARAMS2  params;
    // CryptoPro gostR3411 HASH1 Param Set
    static CK_BYTE         gost_3411_94_par_oid[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01
    };
/*
    static CK_BYTE         gost_3411_2012_256_oid[] = {
        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02
    };
*/
    static CK_BYTE         gost_3411_2012_512_oid[] = {
        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03
    };
    CK_ATTRIBUTE           attr;

    static CK_BYTE         salt1[] =
    { 0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34 };
    static CK_ULONG        iter1         = 5;
    static CK_UTF8CHAR_PTR password1     = (CK_UTF8CHAR_PTR)"password+";
    static CK_ULONG        password1_len = 9;
    static CK_BYTE         et1[]         = {
        0x35, 0x0e, 0x07, 0xc5, 0xb7, 0xfc, 0xe8, 0xe2,
        0xa0, 0xa8, 0xdf, 0xc3, 0x92, 0xc0, 0x49, 0x96,
        0x4c, 0xb1, 0x66, 0x31, 0x9b, 0x58, 0x6e, 0x9f,
        0x46, 0x22, 0x86, 0x76, 0x5d, 0x63, 0xdb, 0xf3,
    };
    static CK_BYTE         salt2[] = {
        0xbf, 0xb2, 0xd3, 0x23, 0x3e, 0xe6, 0x21, 0xd8
    };
    static CK_ULONG        iter2         = 2048;
    static CK_UTF8CHAR_PTR password2     = (CK_UTF8CHAR_PTR)"1111";
    static CK_ULONG        password2_len = 4;
    static CK_BYTE         et2[]         = {
        0x76, 0xf1, 0xf3, 0x45, 0x6b, 0x43, 0x48, 0x99,
        0xe0, 0xfc, 0xda, 0xf8, 0x49, 0xc2, 0x68, 0x46,
        0x7b, 0xd5, 0x34, 0x5b, 0x25, 0x09, 0x74, 0x4e,
        0x0b, 0x32, 0x7b, 0xb9, 0x1e, 0x8f, 0xfa, 0x1a,
    };
    static CK_BYTE         salt3[] = {
        0x8C, 0x5B, 0x3E, 0xE5, 0xCD, 0x27, 0xFA, 0x28
    };
    static CK_ULONG        iter3         = 2048;
    static CK_UTF8CHAR_PTR password3     = (CK_UTF8CHAR_PTR)"4444";
    static CK_ULONG        password3_len = 4;
    static CK_BYTE         et3[]         = {
        0xDB, 0xD1, 0x28, 0x79, 0xF6, 0xFC, 0x2C, 0x0C,
        0x8D, 0xC8, 0x06, 0x18, 0x15, 0xDB, 0xF9, 0x8B,
        0x58, 0xA8, 0x9C, 0x3F, 0x55, 0x96, 0x13, 0xDC,
        0xDB, 0x18, 0xD9, 0x0A, 0x84, 0xF2, 0x53, 0x8E,
    };
    static CK_BYTE         salt4[]       = "salt";
    static CK_ULONG        iter4         = 1;
    static CK_UTF8CHAR_PTR password4     = (CK_UTF8CHAR_PTR)"password";
    static CK_ULONG        password4_len = 8;
    static CK_BYTE         et4[]         = {
        0x64, 0x77, 0x0a, 0xf7, 0xf7, 0x48, 0xc3, 0xb1,
        0xc9, 0xac, 0x83, 0x1d, 0xbc, 0xfd, 0x85, 0xc2,
        0x61, 0x11, 0xb3, 0x0a, 0x8a, 0x65, 0x7d, 0xdc,
        0x30, 0x56, 0xb8, 0x0c, 0xa7, 0x3e, 0x04, 0x0d,
    };
/*
    static CK_ULONG        iter5 = 4096;
    static CK_BYTE         et5[] = {
        0xe5, 0x2d, 0xeb, 0x9a, 0x2d, 0x2a, 0xaf, 0xf4,
        0xe2, 0xac, 0x9d, 0x47, 0xa4, 0x1f, 0x34, 0xc2,
        0x03, 0x76, 0x59, 0x1c, 0x67, 0x80, 0x7f, 0x04,
        0x77, 0xe3, 0x25, 0x49, 0xdc, 0x34, 0x1b, 0xc7,
    };
*/
    // PRF TC 26 v1 generates 96 bytes and get last 32 bytes
    // Password (23 bytes)
    unsigned char p12_password[] =
    {
        0xd0, 0x9f, 0xd0, 0xb0, 0xd1, 0x80, 0xd0, 0xbe,
        0xd0, 0xbb, 0xd1, 0x8c, 0x20, 0xd0, 0xb4, 0xd0,
        0xbb, 0xd1, 0x8f, 0x20, 0x50, 0x46, 0x58,
    };
    unsigned long p12_password_len = sizeof(p12_password);
    unsigned long p12_hmac_iter    = 2000;
    unsigned char p12_hmac_salt[]  =
    {
        0xc8, 0x23, 0xa9, 0x7d, 0x60, 0x81, 0x5f, 0x79,
        0xe4, 0xa0, 0x45, 0x54, 0x03, 0x5b, 0x8c, 0x5c,
        0x50, 0xb9, 0xbd, 0xc5, 0x1a, 0x7f, 0xf9, 0x8b,
        0x9b, 0x4e, 0x71, 0xfd, 0x2e, 0xdf, 0xc9, 0x30,
    };
    unsigned long p12_hmac_salt_len = sizeof(p12_hmac_salt);
    // Generated key (LCSSL test value, 32 bytes):
    unsigned char p12_hmac_key[] =
    {
        0xa0, 0xe5, 0x61, 0xa9, 0x45, 0x9d, 0x89, 0x2b,
        0x1b, 0x17, 0xdc, 0x77, 0x1d, 0x0a, 0x7d, 0xa9,
        0xf7, 0x6a, 0xc7, 0x86, 0xa9, 0x31, 0xe1, 0xd3,
        0xd1, 0x4d, 0x22, 0x23, 0xe1, 0xcd, 0x16, 0x9d,
    };
    unsigned char p12_hmac_2012_512_key[] =
    {
        0xd7, 0xb8, 0x47, 0x97, 0xc0, 0x1f, 0xc3, 0x26,
        0xe5, 0xa8, 0x76, 0xce, 0x9e, 0x47, 0x92, 0x68,
        0xc0, 0xe6, 0x7a, 0xd1, 0x80, 0xe5, 0xf7, 0x0a,
        0x08, 0xa7, 0xcf, 0xc5, 0x86, 0x1a, 0xdd, 0xfa,
    };
    CK_ATTRIBUTE  key_template[] = {
        { CKA_CLASS,            &lclass,  sizeof(lclass)  },
        { CKA_KEY_TYPE,         &keyType, sizeof(keyType) },
        { CKA_TOKEN,            &lfalse,  sizeof(lfalse)  },
        { CKA_ENCRYPT,          &ltrue,   sizeof(ltrue)   },
        { CKA_GOST28147_PARAMS,
          gost28147_A, sizeof(gost28147_A) },
        { CKA_SENSITIVE,        &lfalse,  sizeof(lfalse)  },
        { CKA_EXTRACTABLE,      &ltrue,   sizeof(ltrue)   }
    };
    // Ключ типа CKK_GENERIC_SECRET может быть разной длины,
    // поэтому его длину можно заказать в атрибуте CKA_VALUE_LEN.
    // По умолчанию, длина полагается равной 32 байта.
    CK_ATTRIBUTE key_template_2[] = {
        { CKA_CLASS,       &lclass,   sizeof(lclass)   },
        { CKA_KEY_TYPE,    &keyType2, sizeof(keyType2) },
        { CKA_VALUE_LEN,   &keyLen,   sizeof(keyLen)   },
        { CKA_TOKEN,       &lfalse,   sizeof(lfalse)   },
        { CKA_SIGN,        &ltrue,    sizeof(ltrue)    },
        { CKA_VERIFY,      &ltrue,    sizeof(ltrue)    },
        { CKA_SENSITIVE,   &lfalse,   sizeof(lfalse)   },
        { CKA_EXTRACTABLE, &ltrue,    sizeof(ltrue)    }
    };
// TODO: Задействовать исправленную структуру CK_PKCS5_PBKD2_PARAMS2,
// в которой длина пароля CK_ULONG, а не CK_ULONG_PTR.
/*
   typedef struct CK_PKCS5_PBKD2_PARAMS {
        CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE           saltSource;
        CK_VOID_PTR                                pSaltSourceData;
        CK_ULONG                                   ulSaltSourceDataLen;
        CK_ULONG                                   iterations;
        CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
        CK_VOID_PTR                                pPrfData;
        CK_ULONG                                   ulPrfDataLen;
        CK_UTF8CHAR_PTR                            pPassword;
        CK_ULONG_PTR                               ulPasswordLen;
   } CK_PKCS5_PBKD2_PARAMS;
 */

    printf("GOST R34.11-94 PBE key 1\n");
    params.saltSource          = CKZ_SALT_SPECIFIED;
    params.pSaltSourceData     = salt1;
    params.ulSaltSourceDataLen = sizeof(salt1);
    params.iterations          = iter1;
    params.prf                 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411;
    params.pPrfData            = gost_3411_94_par_oid;
    params.ulPrfDataLen        = sizeof(gost_3411_94_par_oid);
    params.pPassword           = password1;
    params.ulPasswordLen       = password1_len;

    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_GenerateKey(sess, mechanism,
                                                     key_template,
                                                     sizeof(key_template) / sizeof(CK_ATTRIBUTE), &key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKey failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

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

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

    CHECK(value, len, et1);

    printf("GOST R34.11-94 PBE key 2\n");
    params.saltSource          = CKZ_SALT_SPECIFIED;
    params.pSaltSourceData     = salt2;
    params.ulSaltSourceDataLen = sizeof(salt2);
    params.iterations          = iter2;
    params.prf                 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411;
    params.pPrfData            = gost_3411_94_par_oid;
    params.ulPrfDataLen        = sizeof(gost_3411_94_par_oid);
    params.pPassword           = password2;
    params.ulPasswordLen       = password2_len;

    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_GenerateKey(sess, mechanism,
                                                     key_template,
                                                     sizeof(key_template) / sizeof(CK_ATTRIBUTE), &key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKey failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

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

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

    CHECK(value, len, et2);

    printf("GOST R34.11-94 PBE key 3\n");
    params.saltSource          = CKZ_SALT_SPECIFIED;
    params.pSaltSourceData     = salt3;
    params.ulSaltSourceDataLen = sizeof(salt3);
    params.iterations          = iter3;
    params.prf                 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411;
    params.pPrfData            = gost_3411_94_par_oid;
    params.ulPrfDataLen        = sizeof(gost_3411_94_par_oid);
    params.pPassword           = password3;
    params.ulPasswordLen       = password3_len;

    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_GenerateKey(sess, mechanism,
                                                     key_template,
                                                     sizeof(key_template) / sizeof(CK_ATTRIBUTE), &key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKey failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

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

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

    CHECK(value, len, et3);

    printf("GOST R34.11-94 PBE key 4\n");
    params.saltSource          = CKZ_SALT_SPECIFIED;
    params.pSaltSourceData     = salt4;
    params.ulSaltSourceDataLen = (CK_ULONG)strlen((char *)salt4);
    params.iterations          = iter4;
    params.prf                 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411;
    params.pPrfData            = gost_3411_2012_512_oid;
    params.ulPrfDataLen        = sizeof(gost_3411_2012_512_oid);
    params.pPassword           = password4;
    params.ulPasswordLen       = password4_len;

    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_GenerateKey(sess, mechanism,
                                                     key_template,
                                                     sizeof(key_template) / sizeof(CK_ATTRIBUTE), &key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKey failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

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

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

    CHECK(value, len, et4);

/*
        // Выполняется примерно 13 секунд.
        printf("GOST R34.11-2012-512 PBE key 5\n");
        params.iterations = iter5;

        mechanism->pParameter = &params;
        mechanism->ulParameterLen = sizeof(params);
        rc = funcs->C_GenerateKey(sess, mechanism,
                key_template, sizeof(key_template)/sizeof(CK_ATTRIBUTE), &key);
        if (rc != CKR_OK) {
                fprintf(stderr,
                        "%4d: C_GenerateKey failed, rc = 0x%lx\n", __LINE__, rc);
                return rc;
        }

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

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

        CHECK(value, len, et5);
 */

    // Генерация ключа аутентификации для PKCS#12 с помощью 3411-94
    // Проверяем дополнительную PRF-функцию для HMAC
    // CKP_PKCS5_PBKD2_HMAC_GOSTR3411_TC26_V1,
    // которая сначала генерирует ключ длиной 96 байтов,
    // из которых затем возвращает последние 32 байта.
    printf("GOST R34.11-94 PBA key\n");
    params.saltSource          = CKZ_SALT_SPECIFIED;
    params.pSaltSourceData     = p12_hmac_salt;
    params.ulSaltSourceDataLen = p12_hmac_salt_len;
    params.iterations          = p12_hmac_iter;
    params.prf                 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411_TC26_V1;
    params.pPrfData            = gost_3411_94_par_oid;
    params.ulPrfDataLen        = sizeof(gost_3411_94_par_oid);
    params.pPassword           = p12_password;
    params.ulPasswordLen       = p12_password_len;

    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_GenerateKey(sess, mechanism,
                                                     key_template_2,
                                                     sizeof(key_template_2) / sizeof(CK_ATTRIBUTE), &key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKey failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

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

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

    CHECK(value, len, p12_hmac_key);

    // Генерация ключа аутентификации для PKCS#12 с помощью 3411-2012-512
    printf("GOST R34.11-2012-512 PBA key\n");
    params.saltSource          = CKZ_SALT_SPECIFIED;
    params.pSaltSourceData     = p12_hmac_salt;
    params.ulSaltSourceDataLen = p12_hmac_salt_len;
    params.iterations          = p12_hmac_iter;
    params.prf                 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411_TC26_V1;
    params.pPrfData            = gost_3411_2012_512_oid;
    params.ulPrfDataLen        = sizeof(gost_3411_2012_512_oid);
    params.pPassword           = p12_password;
    params.ulPasswordLen       = p12_password_len;

    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_GenerateKey(sess, mechanism,
                                                     key_template_2,
                                                     sizeof(key_template_2) / sizeof(CK_ATTRIBUTE), &key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKey failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

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

    rc = funcs->C_DestroyObject(sess, key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }
//	print_bytes_str(p12_hmac_2012_512_key, sizeof(p12_hmac_2012_512_key), str);
//	fprintf(stderr, "p12_hmac_2012_512_key:\n%s", str);
    CHECK(value, len, p12_hmac_2012_512_key);

    // Генерация ключа шифрования с помощью
    // CKP_PKCS5_PBKD2_HMAC_GOSTR3411_2012_512.
    printf("GOST R34.11-2012-512 PBE key\n");
    params.saltSource          = CKZ_SALT_SPECIFIED;
    params.pSaltSourceData     = salt4;
    params.ulSaltSourceDataLen = (CK_ULONG)strlen((char *)salt4);
    params.iterations          = iter4;
    params.prf                 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411_2012_512;
    params.pPrfData            = NULL;
    params.ulPrfDataLen        = 0;
    params.pPassword           = password4;
    params.ulPasswordLen       = password4_len;

    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_GenerateKey(sess, mechanism,
                                                     key_template,
                                                     sizeof(key_template) / sizeof(CK_ATTRIBUTE), &key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKey failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

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

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

    CHECK(value, len, et4);

    printf("SUCCESS\n");
    rc = CKR_OK;

    return rc;
}
