#include "test_common.h"

CK_RV test_crypto();
CK_RV test_gost3410_key_wrap(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_Login(hSession,
                        CKU_USER, user_pin, (CK_ULONG)strlen((char *)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_GOSTR3410_KEY_WRAP, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_GOSTR3410_KEY_WRAP "
                "not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_GOSTR3410_KEY_WRAP "
                "test ==================\n");
        rc = test_gost3410_key_wrap(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_GOSTR3410_KEY_WRAP failed, rc = 0x%lx\n",
                    rc);
        }
        else
        {
            fprintf(stderr, "CKM_GOSTR3410_KEY_WRAP test passed.\n");
        }
    }

 out_close:
    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_gost3410_key_wrap(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc;
    CK_BYTE                *wrapped   = NULL;
    CK_BYTE                *unwrapped = NULL;
    CK_ULONG               len;
    CK_OBJECT_HANDLE       send_pub_key         = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       send_priv_key        = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       recp_pub_key         = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       recp_priv_key        = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       cipher_key           = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       unwrapped_cipher_key = CK_INVALID_HANDLE;
    CK_ATTRIBUTE           attr;
    CK_MECHANISM           mechanism_desc     = { CKM_GOSTR3410_KEY_WRAP, NULL, 0 };
    CK_MECHANISM_PTR       mechanism          = &mechanism_desc;
    CK_MECHANISM           mechanism_gen_desc =
    { CKM_GOSTR3410_KEY_PAIR_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_gen          = &mechanism_gen_desc;
    CK_MECHANISM           mechanism_gen_desc_512 =
    { CKM_GOSTR3410_512_KEY_PAIR_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_gen_512 = &mechanism_gen_desc_512;

    static CK_BBOOL        ltrue                 = CK_TRUE;
    static CK_BBOOL        lfalse                = CK_FALSE;
    static CK_OBJECT_CLASS oclass                = CKO_SECRET_KEY;
    static CK_OBJECT_CLASS oclass_pub            = CKO_PUBLIC_KEY;
    static CK_OBJECT_CLASS oclass_priv           = CKO_PRIVATE_KEY;
    static CK_KEY_TYPE     wrapping_key_type_256 = CKK_GOSTR3410;
    static CK_KEY_TYPE     wrapping_key_type_512 = CKK_GOSTR3410_512;
    static CK_KEY_TYPE     wrapped_key_type      = CKK_GOST28147;
    // PAR ECC A OID
    static CK_BYTE         gostR3410params_A[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01
    };
    // PAR ECC XA OID
    static CK_BYTE         gostR3410params_XA[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x24, 0x00
    };
    // PAR 512 A OID
    static CK_BYTE         gostR3410_2012_512_params[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x2, 0x01, 0x02, 0x01
    };
    // PAR HASH 1 OID
    static CK_BYTE         gostR3411params[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01
    };
    // PAR 3411-2012-256 OID
    static CK_BYTE         gostR3411_2012_256_params[] = {
        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02
    };
    // PAR 3411-2012-512 OID
    static CK_BYTE         gostR3411_2012_512_params[] = {
        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03
    };
    // PAR CIPHER A OID
    static CK_BYTE         gost28147params_A[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01
    };
    // Cipher key value
    static CK_BYTE cipher_key_val[] = {
        0xc3, 0x73, 0x0c, 0x5c, 0xbc, 0xca, 0xcf, 0x91,
        0x5a, 0xc2, 0x92, 0x67, 0x6f, 0x21, 0xe8, 0xbd,
        0x4e, 0xf7, 0x53, 0x31, 0xd9, 0x40, 0x5e, 0x5f,
        0x1a, 0x61, 0xdc, 0x31, 0x30, 0xa6, 0x50, 0x11
    };

    // Template for cipher key generation
    static CK_ATTRIBUTE cipher_template[] = {
        { CKA_VALUE,           cipher_key_val,    sizeof(cipher_key_val)   },
        { CKA_TOKEN,           &lfalse,           sizeof(lfalse)           },
        { CKA_SENSITIVE,       &lfalse,           sizeof(lfalse)           },
        { CKA_EXTRACTABLE,     &ltrue,            sizeof(ltrue)            },
        { CKA_ENCRYPT,         &ltrue,            sizeof(ltrue)            },
        { CKA_DECRYPT,         &ltrue,            sizeof(ltrue)            },
        { CKA_WRAP,            &ltrue,            sizeof(ltrue)            },
        { CKA_UNWRAP,          &ltrue,            sizeof(ltrue)            },
        { CKA_CLASS,           &oclass,           sizeof(oclass)           },
        { CKA_GOST28147PARAMS,
          gost28147params_A, sizeof(gost28147params_A) },
        { CKA_KEY_TYPE,        &wrapped_key_type, sizeof(wrapped_key_type) }
    };
    static CK_BYTE      ukm[] = {
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
        0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
    };
    // Вообще-то при упаковке значение UKM должно быть равным NULL,
    // чтобы сгенерировалось случайное значение.
    // Но мы здесь используем фиксированное значение, чтобы убедиться,
    // что именно оно используется и попадает в результирующую структуру.
    static CK_GOSTR3410_KEY_WRAP_PARAMS params = {
        gost28147params_A, sizeof(gost28147params_A),
        ukm,                                       8,
        CK_INVALID_HANDLE               // Generate and use temporary ephemeral key pair
    };

    // Key pair labels
    static CK_BYTE priv_label[] = "Private Key LISSI 5312";
    static CK_BYTE pub_label[]  = "Public Key LISSI 5312";

    // Templates for key pair generation.
    // Template for token public key generation and search.
    static CK_ATTRIBUTE pub_template_2001[] = {
        { CKA_LABEL,           pub_label,   sizeof(pub_label) - 1 },
        { CKA_TOKEN,           &ltrue,      sizeof(ltrue)         },
        { CKA_GOSTR3410PARAMS,
          gostR3410params_A, sizeof(gostR3410params_A) },
        { CKA_GOSTR3411PARAMS,
          gostR3411params, sizeof(gostR3411params) },
        { CKA_WRAP,            &ltrue,      sizeof(ltrue)         },
        { CKA_ENCRYPT,         &ltrue,      sizeof(CK_BBOOL)      },
        { CKA_CLASS,           &oclass_pub, sizeof(oclass_pub)    },
        { CKA_KEY_TYPE,
          &wrapping_key_type_256, sizeof(wrapping_key_type_256) },
    };
    static CK_ATTRIBUTE pub_template_2012_256[] = {
        { CKA_LABEL,           pub_label,   sizeof(pub_label) - 1 },
        { CKA_TOKEN,           &ltrue,      sizeof(ltrue)         },
        { CKA_GOSTR3410PARAMS,
          gostR3410params_A, sizeof(gostR3410params_A) },
        { CKA_GOSTR3411PARAMS,
          gostR3411_2012_256_params, sizeof(gostR3411_2012_256_params) },
        { CKA_WRAP,            &ltrue,      sizeof(ltrue)         },
        { CKA_ENCRYPT,         &ltrue,      sizeof(CK_BBOOL)      },
        { CKA_CLASS,           &oclass_pub, sizeof(oclass_pub)    },
        { CKA_KEY_TYPE,
          &wrapping_key_type_256, sizeof(wrapping_key_type_256) },
    };
    static CK_ATTRIBUTE pub_template_2012_512[] = {
        { CKA_LABEL,           pub_label,   sizeof(pub_label) - 1 },
        { CKA_TOKEN,           &ltrue,      sizeof(ltrue)         },
        { CKA_GOSTR3410PARAMS,
          gostR3410_2012_512_params, sizeof(gostR3410_2012_512_params) },
        { CKA_GOSTR3411PARAMS,
          gostR3411_2012_512_params, sizeof(gostR3411_2012_512_params) },
        { CKA_WRAP,            &ltrue,      sizeof(ltrue)         },
        { CKA_ENCRYPT,         &ltrue,      sizeof(CK_BBOOL)      },
        { CKA_CLASS,           &oclass_pub, sizeof(oclass_pub)    },
        { CKA_KEY_TYPE,
          &wrapping_key_type_512, sizeof(wrapping_key_type_512) },
    };
    // Template for token private key generation and search.
    static CK_ATTRIBUTE priv_template_2012_256[] = {
        { CKA_LABEL,       priv_label,   sizeof(priv_label) - 1 },
        { CKA_TOKEN,       &ltrue,       sizeof(ltrue)          },
        { CKA_PRIVATE,     &ltrue,       sizeof(ltrue)          },
        { CKA_EXTRACTABLE, &ltrue,       sizeof(ltrue)          },
        { CKA_SENSITIVE,   &lfalse,      sizeof(lfalse)         },
        { CKA_DERIVE,      &ltrue,       sizeof(ltrue)          },
        { CKA_UNWRAP,      &ltrue,       sizeof(ltrue)          },
        { CKA_DECRYPT,     &ltrue,       sizeof(CK_BBOOL)       },
        { CKA_CLASS,       &oclass_priv, sizeof(oclass_priv)    },
        { CKA_KEY_TYPE,
          &wrapping_key_type_256, sizeof(wrapping_key_type_256) },
    };
    static CK_ATTRIBUTE priv_template_2012_512[] = {
        { CKA_LABEL,       priv_label,   sizeof(priv_label) - 1 },
        { CKA_TOKEN,       &ltrue,       sizeof(ltrue)          },
        { CKA_PRIVATE,     &ltrue,       sizeof(ltrue)          },
        { CKA_EXTRACTABLE, &ltrue,       sizeof(ltrue)          },
        { CKA_SENSITIVE,   &lfalse,      sizeof(lfalse)         },
        { CKA_DERIVE,      &ltrue,       sizeof(ltrue)          },
        { CKA_UNWRAP,      &ltrue,       sizeof(ltrue)          },
        { CKA_DECRYPT,     &ltrue,       sizeof(CK_BBOOL)       },
        { CKA_CLASS,       &oclass_priv, sizeof(oclass_priv)    },
        { CKA_KEY_TYPE,
          &wrapping_key_type_512, sizeof(wrapping_key_type_512) },
    };
    // Buffer for public key value
    static CK_BYTE      pub_value[128];

    // Template for public key object creation
    static CK_ATTRIBUTE new_pub_template[] = {
        { CKA_CLASS,           &oclass_pub, sizeof(oclass_pub) },
        { CKA_KEY_TYPE,
          &wrapping_key_type_256, sizeof(wrapping_key_type_256) },
        { CKA_GOSTR3410PARAMS,
          gostR3410params_A, sizeof(gostR3410params_A) },
        { CKA_GOSTR3411PARAMS,
          gostR3411params, sizeof(gostR3411params) },
        { CKA_WRAP,            &ltrue,      sizeof(CK_BBOOL)   },
        { CKA_ENCRYPT,         &ltrue,      sizeof(CK_BBOOL)   },
        { CKA_VALUE,           pub_value,                   64 }
    };
    static CK_ATTRIBUTE new_pub_template_2012_256[] = {
        { CKA_CLASS,           &oclass_pub, sizeof(oclass_pub) },
        { CKA_KEY_TYPE,
          &wrapping_key_type_256, sizeof(wrapping_key_type_256) },
        { CKA_GOSTR3410PARAMS,
          gostR3410params_A, sizeof(gostR3410params_A) },
        { CKA_GOSTR3411PARAMS,
          gostR3411_2012_256_params, sizeof(gostR3411_2012_256_params) },
        { CKA_WRAP,            &ltrue,      sizeof(CK_BBOOL)   },
        { CKA_ENCRYPT,         &ltrue,      sizeof(CK_BBOOL)   },
        { CKA_VALUE,           pub_value,                   64 }
    };
    static CK_ATTRIBUTE new_pub_template_2012_512[] = {
        { CKA_CLASS,           &oclass_pub, sizeof(oclass_pub) },
        { CKA_KEY_TYPE,
          &wrapping_key_type_512, sizeof(wrapping_key_type_512) },
        { CKA_GOSTR3410PARAMS,
          gostR3410_2012_512_params, sizeof(gostR3410_2012_512_params) },
        { CKA_GOSTR3411PARAMS,
          gostR3411_2012_512_params, sizeof(gostR3411_2012_512_params) },
        { CKA_WRAP,            &ltrue,      sizeof(CK_BBOOL)   },
        { CKA_ENCRYPT,         &ltrue,      sizeof(CK_BBOOL)   },
        { CKA_VALUE,           pub_value,                  128 }
    };
    // Session public key object handle
    CK_OBJECT_HANDLE    new_pub_key = CK_INVALID_HANDLE;
/*
        // ТК 26 Закрытый ключ получателя
        30 49
                02 01 00
                30 1F
                        06 08 2A 85 03 07 01 01 01 01
                        30 13
                                06 07 2A 85 03 02 02 24 00
                                06 08 2A 85 03 07 01 01 02 02
                04 23
                        02 21
                                00 ...
 */
    static CK_BYTE tc26_priv_key_value[] = {
        0x01, 0xB4, 0x2C, 0x61, 0xA1, 0xAE, 0xEF, 0x62,
        0xCE, 0xB9, 0x90, 0x3F, 0x8B, 0x56, 0x24, 0xB3,
        0x91, 0x9E, 0xEC, 0xEC, 0x43, 0x7F, 0x8A, 0x85,
        0x12, 0xCA, 0x7E, 0x43, 0xD0, 0x2E, 0x54, 0xE3,
    };

    // ТК 26 KeyTransport
    CK_BYTE          tc26_data[] = {
        0x30, 0x81, 0xA9,
        0x30, 0x28,
        0x04, 0x20,
        0x10, 0x8A, 0x8B, 0xF9, 0x80, 0x51, 0x42, 0xAE,
        0xF9, 0x1F, 0x0E, 0xD5, 0xAD, 0xE6, 0xDD, 0x3A,
        0x5B, 0x4E, 0x6C, 0x01, 0x4D, 0xBB, 0x93, 0x28,
        0xC0, 0x31, 0xE8, 0x06, 0x49, 0x76, 0xF2, 0x95,
        0x04, 0x04,
        0xAB, 0x49, 0x7F, 0x56,
        0xA0, 0x7D,
        0x06, 0x09, 0x2A, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01,
        0xA0, 0x66,
        0x30, 0x1F,
        0x06, 0x08, 0x2A, 0x85, 0x03, 0x07, 0x01, 0x01, 0x01, 0x01,
        0x30, 0x13,
        0x06, 0x07, 0x2A, 0x85, 0x03, 0x02, 0x02, 0x24, 0x00,
        0x06, 0x08, 0x2A, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02,
        0x03, 0x43, 0x00,
        0x04, 0x40,
        0xB4, 0x83, 0xB2, 0xBC, 0xC3, 0x57, 0x27, 0x2E,
        0xAC, 0x57, 0x80, 0x0E, 0xC4, 0x9F, 0x5F, 0x9C,
        0x1B, 0x2E, 0xB9, 0x7E, 0x78, 0x04, 0xB3, 0xEA,
        0x34, 0xBC, 0x93, 0x57, 0x67, 0x4E, 0x43, 0x0C,
        0xC0, 0xF7, 0x58, 0x3E, 0x4C, 0x34, 0xEC, 0x11,
        0xCB, 0xDE, 0xAA, 0x3A, 0xC8, 0xA7, 0xA0, 0xFF,
        0xEF, 0x1E, 0x3A, 0x0B, 0xFE, 0xAE, 0xCA, 0xFD,
        0xA7, 0x45, 0x4C, 0xED, 0x1A, 0xB0, 0x61, 0x3D,
        0x04, 0x08,
        0x0D, 0x91, 0x85, 0x60, 0x71, 0xCF, 0x59, 0x8F,
    };
    CK_ATTRIBUTE     new_priv_template[] = {
        { CKA_CLASS,           &oclass_priv,        sizeof(oclass_priv)         },
        { CKA_KEY_TYPE,
          &wrapping_key_type_256, sizeof(wrapping_key_type_256) },
        { CKA_GOSTR3410PARAMS,
          gostR3410params_XA, sizeof(gostR3410params_XA) },
        { CKA_GOSTR3411PARAMS,
          gostR3411_2012_256_params, sizeof(gostR3411_2012_256_params) },
        { CKA_SIGN,            &ltrue,              sizeof(CK_BBOOL)            },
        { CKA_DECRYPT,         &ltrue,              sizeof(CK_BBOOL)            },
        { CKA_DERIVE,          &ltrue,              sizeof(CK_BBOOL)            },
        { CKA_UNWRAP,          &ltrue,              sizeof(CK_BBOOL)            },
        { CKA_VALUE,           tc26_priv_key_value, sizeof(tc26_priv_key_value) }
    };
    SYSTEMTIME       t1, t2;
    CK_ULONG         diff;

// В примерах ТК 26 при шифровании сообщений PKCS#7
// использована старая функция диверсификации VKO
// для новых алгоритмов 2012.
    printf("================ TC 26 example ====================\n");
    // Распаковка данных ТК 26
    rc = funcs->C_CreateObject(sess,
                               new_priv_template,
                               sizeof(new_priv_template) / sizeof(CK_ATTRIBUTE),
                               &recp_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_CreateObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    mechanism->pParameter     = NULL;
    mechanism->ulParameterLen = 0;

    rc = funcs->C_UnwrapKey(sess, mechanism,
                            recp_priv_key, tc26_data, sizeof(tc26_data), cipher_template + 1,
                            sizeof(cipher_template) / sizeof(CK_ATTRIBUTE) - 1,
                            &unwrapped_cipher_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_UnwrapKey failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    printf("Key unwrapped OK\n");
    rc = funcs->C_DestroyObject(sess, unwrapped_cipher_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, recp_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    printf("Cipher key to wrap:\n");
    print_bytes(cipher_key_val, 32);

    printf("================ GOSTR3410-2001 ====================\n");
    // GOSTR3410-2001
    // Generate sender key pair objects
    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen,
                                  pub_template_2001, sizeof(pub_template_2001) / sizeof(CK_ATTRIBUTE),
                                  priv_template_2012_256, sizeof(priv_template_2012_256) / sizeof(CK_ATTRIBUTE),
                                  &send_pub_key, &send_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKeyPair failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    printf("Sender key pair generation OK\n");

    // Generate recipient key pair objects
    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen,
                                  pub_template_2001, sizeof(pub_template_2001) / sizeof(CK_ATTRIBUTE),
                                  priv_template_2012_256, sizeof(priv_template_2012_256) / sizeof(CK_ATTRIBUTE),
                                  &recp_pub_key, &recp_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKeyPair failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    printf("Recipient key pair generation OK\n");

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

    // Create session public key object from its value.
    rc = funcs->C_CreateObject(sess, new_pub_template,
                               sizeof(new_pub_template) / sizeof(CK_ATTRIBUTE), &new_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_CreateObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    // Create cipher key object from its value
    rc = funcs->C_CreateObject(sess, cipher_template,
                               sizeof(cipher_template) / sizeof(CK_ATTRIBUTE), &cipher_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_CreateObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

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

    params.pWrapOID     = gost28147params_A;
    params.ulWrapOIDLen = sizeof(gost28147params_A);
    // Использовать закрытый ключ отправителя
    // или CK_INVALID_HANDLE для генерации эфемерной ключевой пары.
    params.hKey = CK_INVALID_HANDLE;    //send_priv_key;
    // Get wrapping key length only.
    len = 0;
    rc  = funcs->C_WrapKey(sess, mechanism,
                           new_pub_key, cipher_key, NULL, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_WrapKey failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    wrapped = malloc(len);

    // Wrap cipher key with public key to the value buffer.
    GetSystemTime(&t1);
    rc = funcs->C_WrapKey(sess, mechanism,
                          new_pub_key, cipher_key, wrapped, &len);
    GetSystemTime(&t2);
    diff = process_time(t1, t2);
    fprintf(stderr, "C_WrapKey time:  %ld msec\n", diff);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_WrapKey failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    printf("Key wrapped OK\n");
    print_bytes(wrapped, len);

/*
        // Без информации об открытом ключе должна получиться
        // структура следующего вида:
        0x30, 0x3f,
                0x30, 0x28,
                        0x04, 0x20,
                                0x76, 0x9e,	0xc1, 0x2e, 0x5c, 0x78, 0x66, 0x6a,
                                0x9f, 0x56,	0x4c, 0xfe, 0x79, 0x65, 0x79, 0x6e,
                                0x0e, 0x0e,	0xac, 0xd6, 0x36, 0x94, 0x9e, 0x39,
                                0xf3, 0xc4,	0xc6, 0x97, 0xc9, 0x15, 0x9a, 0xe3,
                        0x04, 0x04,
                                0xe3, 0xd4, 0x3b, 0xf7,
                0xa0, 0x13,
                        0x06, 0x07,
                                0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01,
                        0x04, 0x08,
                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,

        // При генерации эфемерной ключевой пары должна получиться
        // структура следующего вида:
        0x30, 0x81, 0xa4,
                0x30, 0x28,
                        0x04, 0x20,
                                0x65, 0x89, 0xd3, 0x41, 0x90, 0x9c, 0xb9, 0x03,
                                0x45, 0x92, 0x13, 0x84, 0x57, 0x9b, 0xb8, 0x4e,
                                0x82, 0x68, 0xb3, 0x7b, 0x80, 0x4f, 0x67, 0x15,
                                0x84, 0xf5, 0x90, 0x5d, 0x0d, 0x58, 0x95, 0x61,
                        0x04, 0x04,
                                0x42, 0x84, 0x55, 0x80,
                0xa0, 0x78,
                        0x06, 0x07,
                                0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01,
                        0xa0, 0x63,
                                0x30, 0x1c,
                                        0x06, 0x06,
                                                0x2a, 0x85,	0x03, 0x02, 0x02, 0x13,
                                        0x30, 0x12,
                                                0x06, 0x07,
                                                        0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01,
                                                0x06, 0x07,
                                                        0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01,
                                0x03, 0x43, 0x00,
                                        0x04, 0x40,
                                                0x0f, 0x1b, 0xd3, 0x67, 0x4d, 0xed, 0x83, 0x22,
                                                0x2a, 0xa0, 0x23, 0x4c, 0x55, 0x7a, 0x8a, 0xcd,
                                                0x71, 0x75, 0x11, 0x23, 0x67, 0x68, 0xcb, 0xfe,
                                                0xb8, 0x8f, 0x2d, 0xc5, 0x72, 0xb8, 0x3b, 0x6e,
                                                0xd2, 0x31, 0x38, 0xa6, 0xe4, 0xe3, 0x35, 0xbc,
                                                0x6d, 0x07, 0x1b, 0x57, 0xf8, 0xd7, 0x18, 0xa6,
                                                0x14, 0xf0, 0xe2, 0x3c, 0x65, 0x10, 0x5d, 0xa4,
                                                0x44, 0xaa, 0x1d, 0xea, 0x47, 0x81, 0x3e, 0x98,
                        0x04, 0x08,
                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
 */

    params.pWrapOID     = NULL;
    params.ulWrapOIDLen = 0;

    // Использовать открытый ключ отправителя
    // или CK_INVALID_HANDLE при генерации эфемерной ключевой пары.
    params.hKey = CK_INVALID_HANDLE;    //send_pub_key;

    GetSystemTime(&t1);
    // Unwrap cipher key with recipient private key from the value buffer.
    // Don't use the first CKA_VALUE attribute -
    // it will be added as a result of C_UnwrapKey for cipher_key.
    rc = funcs->C_UnwrapKey(sess, mechanism,
                            recp_priv_key, wrapped, len, cipher_template + 1,
                            sizeof(cipher_template) / sizeof(CK_ATTRIBUTE) - 1,
                            &unwrapped_cipher_key);
    GetSystemTime(&t2);
    diff = process_time(t1, t2);
    fprintf(stderr, "C_UnwrapKey time:  %ld msec\n", diff);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_UnwrapKey failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    free(wrapped);
    wrapped = NULL;
    printf("Key unwrapped OK\n");

    // Restore cipher key to the value buffer from the cipher_key object.
    attr.type       = CKA_VALUE;
    attr.pValue     = NULL;
    attr.ulValueLen = 0;
    rc              = funcs->C_GetAttributeValue(sess, unwrapped_cipher_key, &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GetAttributeValue failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    len         = attr.ulValueLen;
    unwrapped   = malloc(len);
    attr.pValue = unwrapped;
    rc          = funcs->C_GetAttributeValue(sess, unwrapped_cipher_key, &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GetAttributeValue failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    len = attr.ulValueLen;
    // Check cipher key value
    if (len != sizeof(cipher_key_val))
    {
        fprintf(stderr,
                "%4d: Invalid result length: %ld\n", __LINE__, len);
        return CKR_FUNCTION_FAILED;
    }
    if (memcmp(unwrapped, cipher_key_val, len) != 0)
    {
        fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
        return CKR_FUNCTION_FAILED;
    }
    free(unwrapped);
    unwrapped = NULL;
    printf("Unwrapped key is equal to source key\n");

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

    // Destroy session public key
    rc = funcs->C_DestroyObject(sess, new_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    // Destroy cipher key
    rc = funcs->C_DestroyObject(sess, cipher_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, recp_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, send_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, send_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, recp_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }


    printf("================ GOSTR3410-2012-256 ====================\n");
    // GOSTR3410-2012-256
    // Generate sender key pair objects
    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen,
                                  pub_template_2012_256,
                                  sizeof(pub_template_2012_256) / sizeof(CK_ATTRIBUTE),
                                  priv_template_2012_256,
                                  sizeof(priv_template_2012_256) / sizeof(CK_ATTRIBUTE),
                                  &send_pub_key, &send_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKeyPair failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    printf("Sender key pair generation OK\n");

    // Generate recipient key pair objects
    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen,
                                  pub_template_2012_256,
                                  sizeof(pub_template_2012_256) / sizeof(CK_ATTRIBUTE),
                                  priv_template_2012_256,
                                  sizeof(priv_template_2012_256) / sizeof(CK_ATTRIBUTE),
                                  &recp_pub_key, &recp_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKeyPair failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    printf("Recipient key pair generation OK\n");

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

    // Create session public key object from its value.
    rc = funcs->C_CreateObject(sess, new_pub_template_2012_256,
                               sizeof(new_pub_template_2012_256) / sizeof(CK_ATTRIBUTE),
                               &new_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_CreateObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    // Create cipher key object from its value
    rc = funcs->C_CreateObject(sess, cipher_template,
                               sizeof(cipher_template) / sizeof(CK_ATTRIBUTE), &cipher_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_CreateObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);

    params.pWrapOID     = gost28147params_A;
    params.ulWrapOIDLen = sizeof(gost28147params_A);
    // Использовать закрытый ключ отправителя
    // или CK_INVALID_HANDLE для генерации эфемерной ключевой пары.
    params.hKey = CK_INVALID_HANDLE;    //send_priv_key;
    // Get wrapping key length only.
    len = 0;
    rc  = funcs->C_WrapKey(sess, mechanism,
                           new_pub_key, cipher_key, NULL, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_WrapKey failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    wrapped = malloc(len);

    // Wrap cipher key with public key to the value buffer.
    GetSystemTime(&t1);
    rc = funcs->C_WrapKey(sess, mechanism,
                          new_pub_key, cipher_key, wrapped, &len);
    GetSystemTime(&t2);
    diff = process_time(t1, t2);
    fprintf(stderr, "C_WrapKey time:  %ld msec\n", diff);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_WrapKey failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    printf("Key wrapped OK\n");
    print_bytes(wrapped, len);

/*
        // Без информации об открытом ключе должна получиться
        // структура следующего вида:
        0x30, 0x3f,
                0x30, 0x28,
                        0x04, 0x20,
                                0x76, 0x9e,	0xc1, 0x2e, 0x5c, 0x78, 0x66, 0x6a,
                                0x9f, 0x56,	0x4c, 0xfe, 0x79, 0x65, 0x79, 0x6e,
                                0x0e, 0x0e,	0xac, 0xd6, 0x36, 0x94, 0x9e, 0x39,
                                0xf3, 0xc4,	0xc6, 0x97, 0xc9, 0x15, 0x9a, 0xe3,
                        0x04, 0x04,
                                0xe3, 0xd4, 0x3b, 0xf7,
                0xa0, 0x13,
                        0x06, 0x07,
                                0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01,
                        0x04, 0x08,
                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,

        // При генерации эфемерной ключевой пары
        // должна получиться структура следующего вида:
        0x30, 0x81, 0xa7,
                0x30, 0x28,
                        0x04, 0x20,
                                0x78, 0x3e, 0xff, 0xba, 0x7c, 0xc9, 0x9d, 0xcb,
                                0x45, 0x1f, 0x42, 0x76, 0xc9, 0x37, 0x47, 0xa2,
                                0x54, 0x97, 0xaa, 0x49, 0x05, 0x74, 0x86, 0xe9,
                                0xa9, 0x42, 0x88, 0xcb, 0x37, 0x67, 0x74, 0xfb,
                        0x04, 0x04,
                                0x9a, 0x7f, 0xcf, 0x45,
                0xa0, 0x7b,
                        0x06, 0x07,
                                0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01,
                        0xa0, 0x66,
                                0x30, 0x1f,
                                        0x06, 0x08,
                                                0x2a, 0x85,	0x03, 0x07, 0x01, 0x01, 0x01, 0x01,
                                        0x30, 0x13,
                                                0x06, 0x07,
                                                        0x2a, 0x85, 0x03, 0x02, 0x02, 0x23,	0x01,
                                                0x06, 0x08,
                                                        0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02,
                                0x03, 0x43, 0x00,
                                        0x04, 0x40,
                                                0xb4, 0xd9, 0x79, 0x33, 0x59, 0x89, 0x1c, 0x4b,
                                                0x63, 0x2b, 0x96, 0x34, 0x69, 0x11, 0x87, 0x67,
                                                0x4f, 0xa4, 0xdf, 0xf1, 0x5a, 0x43, 0xf2, 0xb8,
                                                0x22, 0xd2, 0x96, 0x1b, 0xa5, 0x48, 0xda, 0xff,
                                                0x31, 0x9e, 0xf5, 0x59, 0xc6, 0xdc, 0x4b, 0x4a,
                                                0xe6, 0x0b, 0x37, 0x63, 0xbc, 0xd3, 0x7f, 0x58,
                                                0x12, 0xed, 0x19, 0x88, 0x49, 0xb6, 0x07, 0x08,
                                                0x57, 0x9f, 0xa8, 0x2f, 0x1d, 0xb1, 0x05, 0x68,
                        0x04, 0x08,
                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06,	0x07, 0x08,
 */

    params.pWrapOID     = NULL;
    params.ulWrapOIDLen = 0;

    // Использовать открытый ключ отправителя
    // или CK_INVALID_HANDLE при генерации эфемерной ключевой пары.
    params.hKey = CK_INVALID_HANDLE;    //send_pub_key;

    GetSystemTime(&t1);
    // Unwrap cipher key with recipient private key from the value buffer.
    // Don't use the first CKA_VALUE attribute -
    // it will be added as a result of C_UnwrapKey for cipher_key.
    rc = funcs->C_UnwrapKey(sess, mechanism,
                            recp_priv_key, wrapped, len, cipher_template + 1,
                            sizeof(cipher_template) / sizeof(CK_ATTRIBUTE) - 1,
                            &unwrapped_cipher_key);
    GetSystemTime(&t2);
    diff = process_time(t1, t2);
    fprintf(stderr, "C_UnwrapKey time:  %ld msec\n", diff);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_UnwrapKey failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    free(wrapped);
    wrapped = NULL;
    printf("Key unwrapped OK\n");

    // Restore cipher key to the value buffer from the cipher_key object.
    attr.type       = CKA_VALUE;
    attr.ulValueLen = 0;
    attr.pValue     = NULL;
    rc              = funcs->C_GetAttributeValue(sess, unwrapped_cipher_key,
                                                 &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GetAttributeValue failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }
    len         = attr.ulValueLen;
    unwrapped   = malloc(len);
    attr.pValue = unwrapped;
    rc          = funcs->C_GetAttributeValue(sess, unwrapped_cipher_key,
                                             &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GetAttributeValue failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }
    len = attr.ulValueLen;
    // Check cipher key value
    if (len != sizeof(cipher_key_val))
    {
        fprintf(stderr,
                "%4d: Invalid result length: %ld\n", __LINE__, len);
        return CKR_FUNCTION_FAILED;
    }
    if (memcmp(unwrapped, cipher_key_val, len) != 0)
    {
        fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
        return CKR_FUNCTION_FAILED;
    }
    free(unwrapped);
    unwrapped = NULL;
    printf("Unwrapped key is equal to source key\n");
    rc = funcs->C_DestroyObject(sess, unwrapped_cipher_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

    // Destroy session public key
    rc = funcs->C_DestroyObject(sess, new_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    // Destroy cipher key
    rc = funcs->C_DestroyObject(sess, cipher_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, recp_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, recp_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, send_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, send_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    printf("================ GOSTR3410-2012-512 ====================\n");
    // GOSTR3410-2012-512
    // Generate sender key pair objects
    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen_512,
                                  pub_template_2012_512,
                                  sizeof(pub_template_2012_512) / sizeof(CK_ATTRIBUTE),
                                  priv_template_2012_512,
                                  sizeof(priv_template_2012_512) / sizeof(CK_ATTRIBUTE),
                                  &send_pub_key, &send_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKeyPair failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    printf("Sender key pair generation OK\n");

    // Generate recipient key pair objects
    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen_512,
                                  pub_template_2012_512,
                                  sizeof(pub_template_2012_512) / sizeof(CK_ATTRIBUTE),
                                  priv_template_2012_512,
                                  sizeof(priv_template_2012_512) / sizeof(CK_ATTRIBUTE),
                                  &recp_pub_key, &recp_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKeyPair failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }
    printf("Recipient key pair generation OK\n");

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

    // Create session public key object from its value.
    rc = funcs->C_CreateObject(sess, new_pub_template_2012_512,
                               sizeof(new_pub_template_2012_512) / sizeof(CK_ATTRIBUTE),
                               &new_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_CreateObject failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

    // Create cipher key object from its value
    rc = funcs->C_CreateObject(sess, cipher_template,
                               sizeof(cipher_template) / sizeof(CK_ATTRIBUTE), &cipher_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_CreateObject failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }

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

    params.pWrapOID     = gost28147params_A;
    params.ulWrapOIDLen = sizeof(gost28147params_A);
    // Использовать закрытый ключ отправителя
    // или CK_INVALID_HANDLE для генерации эфемерной ключевой пары.
    params.hKey     = CK_INVALID_HANDLE;//send_priv_key;
    params.ulUKMLen = 16;
    // Get wrapping key length only.
    len = 0;
    rc  = funcs->C_WrapKey(sess, mechanism,
                           new_pub_key, cipher_key, NULL, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_WrapKey failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    wrapped = malloc(len);

    // Wrap cipher key with public key to the value buffer.
    GetSystemTime(&t1);
    rc = funcs->C_WrapKey(sess, mechanism,
                          new_pub_key, cipher_key, wrapped, &len);
    GetSystemTime(&t2);
    diff = process_time(t1, t2);
    fprintf(stderr, "C_WrapKey time:  %ld msec\n", diff);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_WrapKey failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    printf("Key wrapped OK\n");
    print_bytes(wrapped, len);

/*
        // При отсутствии информации об открытом ключе отправителя
        // должна получиться структура следующего вида:
        0x30, 0x47,
                // Зашифрованный ключ
                0x30, 0x28,
                        0x04, 0x20,
                                0xc7, 0xf7, 0x4a, 0x6d, 0xdc, 0x98, 0x20, 0x60,
                                0x0e, 0xb9, 0x0d, 0xfa, 0x2e, 0x85, 0x76, 0xec,
                                0xe4, 0x2b, 0x18, 0x36, 0x95, 0x38, 0xa7, 0x74,
                                0x62, 0xf5, 0x23, 0x9b, 0x2d, 0x0d, 0xf4, 0x10,
                        0x04, 0x04,
                                0x5f, 0xf4, 0x84, 0x38,
                // Параметры шифрования
                0xa0, 0x1b,
                        // Параметры алгоритма шифрования
                        0x06, 0x07,
                                0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01,
                        // UKM
                        0x04, 0x10,
                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                                0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
        // Единственное отличие варианта 512 от варианта 256 - это 16 байтов UKM.

        // При генерации эфемерной ключевой пары
        // должна получиться структура следующего вида:
        0x30, 0x81, 0xf5,
                // Зашифрованный ключ
                0x30, 0x28,
                        0x04, 0x20,
                                0xc3, 0xa3, 0x8f, 0x1f, 0x52, 0x03, 0xce, 0xcd,
                                0xc7, 0x37, 0xde, 0xb1, 0xf3, 0xd8, 0xed, 0xe4,
                                0x3e, 0x79, 0xd4, 0xeb, 0x28, 0x2f, 0xef, 0x99,
                                0x95, 0x5e, 0xb2, 0xf9, 0x9f, 0x8a, 0x9a, 0xdc,
                        0x04, 0x04,
                                0xb5, 0xd2, 0x31, 0x0e,
                // Параметры шифрования
                0xa0, 0x81, 0xc8,
                        // Параметры алгоритма шифрования
                        0x06, 0x07,
                                0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f,	0x01,
                        // Информация об открытом ключе отправителя
                        0xa0, 0x81, 0xaa,
                                0x30, 0x21,
                                        0x06, 0x08,
                                                0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x01, 0x02,
                                        0x30, 0x15,
                                                0x06, 0x09,
                                                        0x2a, 0x85, 0x03, 0x07,	0x01, 0x02, 0x01, 0x02, 0x01,
                                                0x06, 0x08,
                                                        0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03,
                                0x03, 0x81, 0x84, 0x00,
                                        0x04, 0x81, 0x80,
                                                0x76, 0x88,	0x5d, 0x7e, 0x6c, 0xa1, 0x0a, 0x3b,
                                                0xa0, 0x3a,	0x1b, 0xd8, 0x44, 0x6d, 0xad, 0x3a,
                                                0x8a, 0xec,	0x3d, 0x0d, 0xd1, 0x7f, 0x78, 0xd7,
                                                0x02, 0xdc,	0x57, 0x37, 0xc0, 0x9e, 0xc0, 0xeb,
                                                0x2b, 0x68,	0xc5, 0x73, 0x5b, 0x97, 0x91, 0x46,
                                                0x7b, 0x30,	0x47, 0x8f, 0x3e, 0x76, 0x45, 0x11,
                                                0xe0, 0x97,	0x83, 0xa0, 0x34, 0xc5, 0x38, 0xee,
                                                0x95, 0x8b,	0x8b, 0xce, 0xf4, 0xb1, 0xad, 0x3d,
                                                0x8a, 0x5a,	0x2f, 0x5c, 0x93, 0x10, 0xb5, 0x2d,
                                                0x6b, 0x91,	0x82, 0xf7, 0x0f, 0x4c, 0xd7, 0x62,
                                                0x8f, 0x58,	0x2a, 0x2a, 0x9e, 0x2b, 0x9b, 0x96,
                                                0xce, 0x89,	0x66, 0x49, 0xc0, 0xc8, 0x69, 0x3b,
                                                0x1d, 0xdc,	0x6a, 0xba, 0x3c, 0x4a, 0xdf, 0x2e,
                                                0xad, 0x16,	0x96, 0x62, 0x42, 0xe4, 0x5d, 0xa9,
                                                0xbb, 0x85,	0x30, 0x37, 0xb0, 0xe0, 0xca, 0xc5,
                                                0x2f, 0x30,	0x12, 0x58, 0x6c, 0x0e, 0xd5, 0x3b,
                        // UKM
                        0x04, 0x10,
                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                                0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
 */

    params.pWrapOID     = NULL;
    params.ulWrapOIDLen = 0;

    // Использовать открытый ключ отправителя
    // или CK_INVALID_HANDLE при генерации эфемерной ключевой пары.
    params.hKey = CK_INVALID_HANDLE;    //send_pub_key;

    GetSystemTime(&t1);
    // Unwrap cipher key with recipient private key from the value buffer.
    // Don't use the first CKA_VALUE attribute -
    // it will be added as a result of C_UnwrapKey for cipher_key.
    rc = funcs->C_UnwrapKey(sess, mechanism,
                            recp_priv_key, wrapped, len, cipher_template + 1,
                            sizeof(cipher_template) / sizeof(CK_ATTRIBUTE) - 1,
                            &unwrapped_cipher_key);
    GetSystemTime(&t2);
    diff = process_time(t1, t2);
    fprintf(stderr, "C_UnwrapKey time:  %ld msec\n", diff);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_UnwrapKey failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }
    free(wrapped);
    wrapped = NULL;
    printf("Key unwrapped OK\n");

    // Restore cipher key to the value buffer from the cipher_key object.
    attr.type       = CKA_VALUE;
    attr.ulValueLen = 0;
    attr.pValue     = NULL;
    rc              = funcs->C_GetAttributeValue(sess, unwrapped_cipher_key,
                                                 &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GetAttributeValue failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }
    len         = attr.ulValueLen;
    unwrapped   = malloc(len);
    attr.pValue = unwrapped;
    rc          = funcs->C_GetAttributeValue(sess, unwrapped_cipher_key,
                                             &attr, 1);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GetAttributeValue failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }
    len = attr.ulValueLen;

    // Check cipher key value
    if (len != sizeof(cipher_key_val))
    {
        fprintf(stderr,
                "%4d: Invalid result length: %ld\n", __LINE__, len);
        return CKR_FUNCTION_FAILED;
    }
    if (memcmp(unwrapped, cipher_key_val, len) != 0)
    {
        fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
        return CKR_FUNCTION_FAILED;
    }
    free(unwrapped);
    unwrapped = NULL;
    printf("Unwrapped key is equal to source key\n");

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

    // Destroy session public key
    rc = funcs->C_DestroyObject(sess, new_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    // Destroy cipher key
    rc = funcs->C_DestroyObject(sess, cipher_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, recp_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, recp_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, send_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, send_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DestroyObject failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

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

    return rc;
}
