#include "test_common.h"
#ifdef WIN32
#pragma warning(disable : 4996)
#endif


CK_FUNCTION_LIST_PTR funcs = NULL;
#ifdef WIN32
HMODULE              dll;
#else
void                 *dll;
#endif
CK_UTF8CHAR          *user_pin = (CK_UTF8CHAR *)STRINGIFY(USER_PIN);
CK_UTF8CHAR          *so_pin   = (CK_UTF8CHAR *)STRINGIFY(SO_PIN);
char				 *api_path = (char *)STRINGIFY(PKCS11_API_PATH);
CK_SLOT_ID           SlotId;
CK_SLOT_ID_PTR       SlotList  = NULL;
CK_ULONG             SlotCount = 0;

CK_C_INITIALIZE_ARGS cinit_args_os_locking = {
    NULL,
    NULL,
    NULL,
    NULL,
    CKF_OS_LOCKING_OK,
    NULL
};
CK_C_INITIALIZE_ARGS cinit_args_app_locking = {
    AppCreateMutex,
    AppDestroyMutex,
    AppLockMutex,
    AppUnlockMutex,
    0,
    NULL
};
CK_C_INITIALIZE_ARGS *pinit_args_os_locking =
    &cinit_args_os_locking;
CK_C_INITIALIZE_ARGS *pinit_args_app_locking =
    &cinit_args_app_locking;

typedef struct FLAG_INFO {
	CK_ULONG flag;
	char *name;
} FLAG_INFO;

long process_time(struct timeb t1, struct timeb t2)
{
    long   ms = t2.millitm - t1.millitm;
    time_t s  = t2.time - t1.time;

    while (ms < 0)
    {
        ms += 1000;
        s--;
    }
    ms += (long) (s * 1000);
    return ms;
}

const char *get_ckr_name(CK_RV rc)
{
    switch (rc)
    {
    case CKR_OK: return "CKR_OK";
    case CKR_CANCEL: return "CKR_CANCEL";
    case CKR_HOST_MEMORY: return "CKR_HOST_MEMORY";
    case CKR_SLOT_ID_INVALID: return "CKR_SLOT_ID_INVALID";
    case CKR_GENERAL_ERROR: return "CKR_GENERAL_ERROR";
    case CKR_FUNCTION_FAILED: return "CKR_FUNCTION_FAILED";
    case CKR_ARGUMENTS_BAD: return "CKR_ARGUMENTS_BAD";
    case CKR_NO_EVENT: return "CKR_NO_EVENT";
    case CKR_NEED_TO_CREATE_THREADS: return "CKR_NEED_TO_CREATE_THREADS";
    case CKR_CANT_LOCK: return "CKR_CANT_LOCK";
    case CKR_ATTRIBUTE_READ_ONLY: return "CKR_ATTRIBUTE_READ_ONLY";
    case CKR_ATTRIBUTE_SENSITIVE: return "CKR_ATTRIBUTE_SENSITIVE";
    case CKR_ATTRIBUTE_TYPE_INVALID: return "CKR_ATTRIBUTE_TYPE_INVALID";
    case CKR_ATTRIBUTE_VALUE_INVALID: return "CKR_ATTRIBUTE_VALUE_INVALID";
    case CKR_ACTION_PROHIBITED: return "CKR_ACTION_PROHIBITED";
    case CKR_DATA_INVALID: return "CKR_DATA_INVALID";
    case CKR_DATA_LEN_RANGE: return "CKR_DATA_LEN_RANGE";
    case CKR_DEVICE_ERROR: return "CKR_DEVICE_ERROR";
    case CKR_DEVICE_MEMORY: return "CKR_DEVICE_MEMORY";
    case CKR_DEVICE_REMOVED: return "CKR_DEVICE_REMOVED";
    case CKR_ENCRYPTED_DATA_INVALID: return "CKR_ENCRYPTED_DATA_INVALID";
    case CKR_ENCRYPTED_DATA_LEN_RANGE: return "CKR_ENCRYPTED_DATA_LEN_RANGE";
    case CKR_FUNCTION_CANCELED: return "CKR_FUNCTION_CANCELED";
    case CKR_FUNCTION_NOT_PARALLEL: return "CKR_FUNCTION_NOT_PARALLEL";
    case CKR_FUNCTION_NOT_SUPPORTED: return "CKR_FUNCTION_NOT_SUPPORTED";
    case CKR_KEY_HANDLE_INVALID: return "CKR_KEY_HANDLE_INVALID";
    case CKR_KEY_SIZE_RANGE: return "CKR_KEY_SIZE_RANGE";
    case CKR_KEY_TYPE_INCONSISTENT: return "CKR_KEY_TYPE_INCONSISTENT";
    case CKR_KEY_NOT_NEEDED: return "CKR_KEY_NOT_NEEDED";
    case CKR_KEY_CHANGED: return "CKR_KEY_CHANGED";
    case CKR_KEY_NEEDED: return "CKR_KEY_NEEDED";
    case CKR_KEY_INDIGESTIBLE: return "CKR_KEY_INDIGESTIBLE";
    case CKR_KEY_FUNCTION_NOT_PERMITTED: return "CKR_KEY_FUNCTION_NOT_PERMITTED";
    case CKR_KEY_NOT_WRAPPABLE: return "CKR_KEY_NOT_WRAPPABLE";
    case CKR_KEY_UNEXTRACTABLE: return "CKR_KEY_UNEXTRACTABLE";
    case CKR_MECHANISM_INVALID: return "CKR_MECHANISM_INVALID";
    case CKR_MECHANISM_PARAM_INVALID: return "CKR_MECHANISM_PARAM_INVALID";
    case CKR_OBJECT_HANDLE_INVALID: return "CKR_OBJECT_HANDLE_INVALID";
    case CKR_OPERATION_ACTIVE: return "CKR_OPERATION_ACTIVE";
    case CKR_OPERATION_NOT_INITIALIZED: return "CKR_OPERATION_NOT_INITIALIZED";
    case CKR_PIN_INCORRECT: return "CKR_PIN_INCORRECT";
    case CKR_PIN_INVALID: return "CKR_PIN_INVALID";
    case CKR_PIN_LEN_RANGE: return "CKR_PIN_LEN_RANGE";
    case CKR_PIN_EXPIRED: return "CKR_PIN_EXPIRED";
    case CKR_PIN_LOCKED: return "CKR_PIN_LOCKED";
    case CKR_SESSION_CLOSED: return "CKR_SESSION_CLOSED";
    case CKR_SESSION_COUNT: return "CKR_SESSION_COUNT";
    case CKR_SESSION_HANDLE_INVALID: return "CKR_SESSION_HANDLE_INVALID";
    case CKR_SESSION_PARALLEL_NOT_SUPPORTED: return "CKR_SESSION_PARALLEL_NOT_SUPPORTED";
    case CKR_SESSION_READ_ONLY: return "CKR_SESSION_READ_ONLY";
    case CKR_SESSION_EXISTS: return "CKR_SESSION_EXISTS";
    case CKR_SESSION_READ_ONLY_EXISTS: return "CKR_SESSION_READ_ONLY_EXISTS";
    case CKR_SESSION_READ_WRITE_SO_EXISTS: return "CKR_SESSION_READ_WRITE_SO_EXISTS";
    case CKR_SIGNATURE_INVALID: return "CKR_SIGNATURE_INVALID";
    case CKR_SIGNATURE_LEN_RANGE: return "CKR_SIGNATURE_LEN_RANGE";
    case CKR_TEMPLATE_INCOMPLETE: return "CKR_TEMPLATE_INCOMPLETE";
    case CKR_TEMPLATE_INCONSISTENT: return "CKR_TEMPLATE_INCONSISTENT";
    case CKR_TOKEN_NOT_PRESENT: return "CKR_TOKEN_NOT_PRESENT";
    case CKR_TOKEN_NOT_RECOGNIZED: return "CKR_TOKEN_NOT_RECOGNIZED";
    case CKR_TOKEN_WRITE_PROTECTED: return "CKR_TOKEN_WRITE_PROTECTED";
    case CKR_UNWRAPPING_KEY_HANDLE_INVALID: return "CKR_UNWRAPPING_KEY_HANDLE_INVALID";
    case CKR_UNWRAPPING_KEY_SIZE_RANGE: return "CKR_UNWRAPPING_KEY_SIZE_RANGE";
    case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT: return "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT";
    case CKR_USER_ALREADY_LOGGED_IN: return "CKR_USER_ALREADY_LOGGED_IN";
    case CKR_USER_NOT_LOGGED_IN: return "CKR_USER_NOT_LOGGED_IN";
    case CKR_USER_PIN_NOT_INITIALIZED: return "CKR_USER_PIN_NOT_INITIALIZED";
    case CKR_USER_TYPE_INVALID: return "CKR_USER_TYPE_INVALID";
    case CKR_USER_ANOTHER_ALREADY_LOGGED_IN: return "CKR_USER_ANOTHER_ALREADY_LOGGED_IN";
    case CKR_USER_TOO_MANY_TYPES: return "CKR_USER_TOO_MANY_TYPES";
    case CKR_WRAPPED_KEY_INVALID: return "CKR_WRAPPED_KEY_INVALID";
    case CKR_WRAPPED_KEY_LEN_RANGE: return "CKR_WRAPPED_KEY_LEN_RANGE";
    case CKR_WRAPPING_KEY_HANDLE_INVALID: return "CKR_WRAPPING_KEY_HANDLE_INVALID";
    case CKR_WRAPPING_KEY_SIZE_RANGE: return "CKR_WRAPPING_KEY_SIZE_RANGE";
    case CKR_WRAPPING_KEY_TYPE_INCONSISTENT: return "CKR_WRAPPING_KEY_TYPE_INCONSISTENT";
    case CKR_RANDOM_SEED_NOT_SUPPORTED: return "CKR_RANDOM_SEED_NOT_SUPPORTED";
    case CKR_RANDOM_NO_RNG: return "CKR_RANDOM_NO_RNG";
    case CKR_DOMAIN_PARAMS_INVALID: return "CKR_DOMAIN_PARAMS_INVALID";
    case CKR_CURVE_NOT_SUPPORTED: return "CKR_CURVE_NOT_SUPPORTED";
    case CKR_BUFFER_TOO_SMALL: return "CKR_BUFFER_TOO_SMALL";
    case CKR_SAVED_STATE_INVALID: return "CKR_SAVED_STATE_INVALID";
    case CKR_INFORMATION_SENSITIVE: return "CKR_INFORMATION_SENSITIVE";
    case CKR_STATE_UNSAVEABLE: return "CKR_STATE_UNSAVEABLE";
    case CKR_CRYPTOKI_NOT_INITIALIZED: return "CKR_CRYPTOKI_NOT_INITIALIZED";
    case CKR_CRYPTOKI_ALREADY_INITIALIZED: return "CKR_CRYPTOKI_ALREADY_INITIALIZED";
    case CKR_MUTEX_BAD: return "CKR_MUTEX_BAD";
    case CKR_MUTEX_NOT_LOCKED: return "CKR_MUTEX_NOT_LOCKED";
    case CKR_NEW_PIN_MODE: return "CKR_NEW_PIN_MODE";
    case CKR_NEXT_OTP: return "CKR_NEXT_OTP";
    case CKR_EXCEEDED_MAX_ITERATIONS: return "CKR_EXCEEDED_MAX_ITERATIONS";
    case CKR_FIPS_SELF_TEST_FAILED: return "CKR_FIPS_SELF_TEST_FAILED";
    case CKR_LIBRARY_LOAD_FAILED: return "CKR_LIBRARY_LOAD_FAILED";
    case CKR_PIN_TOO_WEAK: return "CKR_PIN_TOO_WEAK";
    case CKR_PUBLIC_KEY_INVALID: return "CKR_PUBLIC_KEY_INVALID";
    case CKR_FUNCTION_REJECTED: return "CKR_FUNCTION_REJECTED";
    default:
        if (rc >= CKR_VENDOR_DEFINED)
            return "CKR_VENDOR_DEFINED";
        else
            return "Unknown CKR code";
    }
}

void show_error(char *str, CK_RV rc)
{
    printf("%s returned:  0x%lx(%s)\n", str, rc, get_ckr_name(rc));
}

void print_bytes(CK_BYTE *buf, CK_ULONG len)
{
    CK_ULONG i = 0, j;

    while (i < len)
    {
        for (j = 0; (j < 8) && (i < len); j++, i++)
            printf("0x%02x, ", buf[i] & 0xff);
        printf("\n");
    }
    printf("\n");
}

int print_bytes_str(unsigned char *buf, unsigned long len, char *str)
{
    unsigned long i, j;
    for (i = 0, j = 0; i < len; i++)
    {
        if (i != 0 && i % 8 == 0)
        {
            j += sprintf(str + j, "\n");
        }
        j += sprintf(str + j, "0x%02x, ", buf[i]);
    }
    sprintf(str + j, "\n");
    return 0;
}

CK_RV get_function_list(char *api_path,
                        CK_FUNCTION_LIST_PTR *pfuncs)
{
    CK_RV                rc   = CKR_OK;
    CK_C_GetFunctionList pfoo = NULL;

//	printf("get_function_list...\n");
#ifdef WIN32
    dll = LoadLibrary(api_path);
#else 
#ifdef __APPLE__ 
    dll = dlopen(api_path, RTLD_LOCAL|RTLD_LAZY);
#else
    dll = dlopen(api_path, RTLD_LOCAL|RTLD_LAZY|RTLD_DEEPBIND);
#endif
#endif
    if (dll == NULL)
    {
        printf("Can't load PKCS#11 API library.\n");
#ifndef WIN32
        printf("dlerror: %s\n", dlerror());
#endif
        return CKR_FUNCTION_FAILED;
    }
#ifdef WIN32
    pfoo = (CK_C_GetFunctionList) GetProcAddress(
        dll, "C_GetFunctionList");
#else
    pfoo = (CK_C_GetFunctionList) dlsym(
        dll, "C_GetFunctionList");
#endif
    if (pfoo == NULL)
    {
        printf("Couldn't get C_GetFunctionList address\n");
        return CKR_FUNCTION_FAILED;
    }

    rc = pfoo(pfuncs);
    if (rc != CKR_OK)
    {
        show_error("Error in  C_GetFunctionList", rc);
        return rc;
    }
//	printf("Looks OK\n");
    return CKR_OK;
}

CK_RV init(char *api_path,
           CK_FUNCTION_LIST_PTR *pfuncs,
           CK_C_INITIALIZE_ARGS *pinit_args)
{
    CK_RV rc = CKR_OK;

    printf("get_function_list\n");
    rc = get_function_list(api_path, pfuncs);
    if (rc != CKR_OK)
        return rc;
    printf("C_Initialize\n");
    rc = (*pfuncs)->C_Initialize(pinit_args);
    if (rc != CKR_OK)
    {
        show_error("C_Initialize", rc);
        cleanup(*pfuncs);
    }
//	printf("C_Initialize OK\n");
    rc = CKR_OK;
    return rc;
}

CK_RV AppCreateMutex(void **pmutex)
{
#ifdef WIN32
    CRITICAL_SECTION *mutex;
#else
    pthread_mutex_t  *mutex;
#endif
#ifdef WIN32
    mutex = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION));
    InitializeCriticalSection(mutex);
    *(CRITICAL_SECTION * *) pmutex = mutex;
#else
    mutex = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t));
    pthread_mutex_init(mutex, NULL);
    *(pthread_mutex_t * *) pmutex = mutex;
#endif
    return CKR_OK;
}

CK_RV AppDestroyMutex(void *mutex)
{
    if (mutex == NULL)
    {
#ifdef LISSI_DEBUG
        fprintf(stderr, "AppDestroyMutex: NULL mutex\n");
#endif
    }
    else
    {
#ifdef WIN32
        DeleteCriticalSection((CRITICAL_SECTION *) mutex);
        free(mutex);
#else
        pthread_mutex_destroy((pthread_mutex_t *) mutex);
        free(mutex);
#endif
    }
    return CKR_OK;
}

CK_RV AppLockMutex(void *mutex)
{
    if (mutex == NULL)
    {
#ifdef LISSI_DEBUG
        fprintf(stderr, "AppLockMutex: NULL mutex\n");
#endif
    }
    else
    {
#ifdef WIN32
        EnterCriticalSection((CRITICAL_SECTION *) mutex);
#else
        pthread_mutex_lock((pthread_mutex_t *) mutex);
#endif
    }
    return CKR_OK;
}

CK_RV AppUnlockMutex(void *mutex)
{
    if (mutex == NULL)
    {
#ifdef LISSI_DEBUG
        fprintf(stderr, "AppUnlockMutex: NULL mutex\n");
#endif
    }
    else
    {
#ifdef WIN32
        LeaveCriticalSection((CRITICAL_SECTION *) mutex);
#else
        pthread_mutex_unlock((pthread_mutex_t *) mutex);
#endif
    }
    return CKR_OK;
}

CK_RV cleanup(CK_FUNCTION_LIST_PTR funcs)
{
    CK_RV rc;     // Return Code
    rc = funcs->C_Finalize(NULL);
    return rc;
}

CK_RV get_slot_list(CK_FUNCTION_LIST_PTR funcs,
                    CK_SLOT_ID **ppSlotList,
                    CK_ULONG *pulSlotCount)
{
    CK_RV rc;

    rc = funcs->C_GetSlotList(CK_FALSE, NULL, pulSlotCount);
    if (rc != CKR_OK)
    {
        *ppSlotList = NULL;
        printf("Get slot list result: 0x%lx\n", rc);
        return rc;
    }
    if (*pulSlotCount > 0)
    {
        *ppSlotList = (CK_SLOT_ID_PTR) malloc(
            *pulSlotCount * sizeof(CK_SLOT_ID));
        rc = funcs->C_GetSlotList(CK_FALSE, *ppSlotList, pulSlotCount);
        if (rc != CKR_OK)
        {
            *ppSlotList = NULL;
            printf("Get slot list result: 0x%lx\n", rc);
            return rc;
        }
    }
    else
    {
        *ppSlotList = NULL;
        return CKR_OK;
    }
//	printf("get_slot_list OK\n");
    return CKR_OK;
}

CK_RV find_slot_with_token(CK_FUNCTION_LIST_PTR funcs,
                           CK_SLOT_ID *pSlotList,
                           CK_ULONG ulSlotCount,
                           CK_SLOT_ID *pSlotId)
{
    CK_RV        rc;
    CK_ULONG     i;
    CK_SLOT_INFO sinfo;

    // Ищем слот с токеном
    for (i = 0; i < ulSlotCount; ++i)
    {
        rc = funcs->C_GetSlotInfo(pSlotList[i], &sinfo);
        if (rc == CKR_OK)
        {
            if ((sinfo.flags & CKF_TOKEN_PRESENT) == CKF_TOKEN_PRESENT)
            {
                // Хватаем первый попавшийся слот с токеном
                *pSlotId = pSlotList[i];
                printf("====== Token found in slot %ld ======\n", pSlotList[i]);
                rc = display_token_info(funcs, pSlotList[i]);
                printf("====================================\n");
                return rc;
            }
        }
    }
    return CKR_FUNCTION_FAILED;
}

const char *get_mechanism_name(CK_MECHANISM_TYPE mech_type)
{
    switch (mech_type)
    {
    case CKM_GOSTR3410_KEY_PAIR_GEN:
        return "CKM_GOSTR3410_KEY_PAIR_GEN";
    case CKM_GOSTR3410_512_KEY_PAIR_GEN:
        return "CKM_GOSTR3410_512_KEY_PAIR_GEN";
    case CKM_GOSTR3410:
        return "CKM_GOSTR3410";
    case CKM_GOSTR3410_512:
        return "CKM_GOSTR3410_512";
    case CKM_GOSTR3410_WITH_GOSTR3411:
        return "CKM_GOSTR3410_WITH_GOSTR3411";
    case CKM_GOSTR3410_WITH_GOSTR3411_12_256:
        return "CKM_GOSTR3410_WITH_GOSTR3411_12_256";
    case CKM_GOSTR3410_WITH_GOSTR3411_12_512:
        return "CKM_GOSTR3410_WITH_GOSTR3411_12_512";
    case CKM_GOSTR3410_KEY_WRAP:
        return "CKM_GOSTR3410_KEY_WRAP";
    case CKM_GOSTR3410_DERIVE:
        return "CKM_GOSTR3410_DERIVE";
    case CKM_GOSTR3410_12_DERIVE:
        return "CKM_GOSTR3410_12_DERIVE";
    case CKM_KDF_4357:
        return "CKM_KDF_4357";
    case CKM_KDF_GOSTR3411_2012_256:
        return "CKM_KDF_GOSTR3411_2012_256";
    case CKM_KDF_TREE_GOSTR3411_2012_256:
        return "CKM_KDF_TREE_GOSTR3411_2012_256";
    case CKM_GOSTR3411:
        return "CKM_GOSTR3411";
    case CKM_GOSTR3411_12_256:
        return "CKM_GOSTR3411_12_256";
    case CKM_GOSTR3411_12_512:
        return "CKM_GOSTR3411_12_512";
    case CKM_GOSTR3411_HMAC:
        return "CKM_GOSTR3411_HMAC";
    case CKM_GOSTR3411_12_256_HMAC:
        return "CKM_GOSTR3411_12_256_HMAC";
    case CKM_GOSTR3411_12_512_HMAC:
        return "CKM_GOSTR3411_12_512_HMAC";
    case CKM_GOST_GENERIC_SECRET_KEY_GEN:
        return "CKM_GOST_GENERIC_SECRET_KEY_GEN";
    case CKM_GOST_CIPHER_KEY_GEN:
        return "CKM_GOST_CIPHER_KEY_GEN";
    case CKM_GOST_CIPHER_ECB:
        return "CKM_GOST_CIPHER_ECB";
    case CKM_GOST_CIPHER_CBC:
        return "CKM_GOST_CIPHER_CBC";
    case CKM_GOST_CIPHER_CTR:
        return "CKM_GOST_CIPHER_CTR";
    case CKM_GOST_CIPHER_OFB:
        return "CKM_GOST_CIPHER_OFB";
    case CKM_GOST_CIPHER_CFB:
        return "CKM_GOST_CIPHER_CFB";
    case CKM_GOST_CIPHER_OMAC:
        return "CKM_GOST_CIPHER_OMAC";
    case CKM_GOST_CIPHER_ACPKM_CTR:
        return "CKM_GOST_CIPHER_ACPKM_CTR";
    case CKM_GOST_CIPHER_KEY_WRAP:
        return "CKM_GOST_CIPHER_KEY_WRAP";
    case CKM_GOST28147_KEY_GEN:
        return "CKM_GOST28147_KEY_GEN";
    case CKM_GOST28147_ECB:
        return "CKM_GOST28147_ECB";
    case CKM_GOST28147:
        return "CKM_GOST28147";
    case CKM_GOST28147_MAC:
        return "CKM_GOST28147_MAC";
    case CKM_GOST28147_KEY_WRAP:
        return "CKM_GOST28147_KEY_WRAP";
    case CKM_GOST28147_CNT:
        return "CKM_GOST28147_CNT";
    case CKM_KUZNYECHIK_KEY_GEN:
        return "CKM_KUZNYECHIK_KEY_GEN";
    case CKM_KUZNYECHIK_ECB:
        return "CKM_KUZNYECHIK_ECB";
    case CKM_KUZNYECHIK_CBC:
        return "CKM_KUZNYECHIK_CBC";
    case CKM_KUZNYECHIK_CTR:
        return "CKM_KUZNYECHIK_CTR";
    case CKM_KUZNYECHIK_OFB:
        return "CKM_KUZNYECHIK_OFB";
    case CKM_KUZNYECHIK_CFB:
        return "CKM_KUZNYECHIK_CFB";
    case CKM_KUZNYECHIK_OMAC:
        return "CKM_KUZNYECHIK_OMAC";
    case CKM_KUZNYECHIK_ACPKM_CTR:
        return "CKM_KUZNYECHIK_ACPKM_CTR";
    case CKM_KUZNYECHIK_KEY_WRAP:
        return "CKM_KUZNYECHIK_KEY_WRAP";
    case CKM_MAGMA_KEY_GEN:
        return "CKM_MAGMA_KEY_GEN";
    case CKM_MAGMA_ECB:
        return "CKM_MAGMA_ECB";
    case CKM_MAGMA_CBC:
        return "CKM_MAGMA_CBC";
    case CKM_MAGMA_CTR:
        return "CKM_MAGMA_CTR";
    case CKM_MAGMA_OFB:
        return "CKM_MAGMA_OFB";
    case CKM_MAGMA_CFB:
        return "CKM_MAGMA_CFB";
    case CKM_MAGMA_OMAC:
        return "CKM_MAGMA_OMAC";
    case CKM_MAGMA_ACPKM_CTR:
        return "CKM_MAGMA_ACPKM_CTR";
    case CKM_MAGMA_KEY_WRAP:
        return "CKM_MAGMA_KEY_WRAP";
    case CKM_TLS_GOST_PRF:
        return "CKM_TLS_GOST_PRF";
    case CKM_TLS_GOST_PRE_MASTER_KEY_GEN:
        return "CKM_TLS_GOST_PRE_MASTER_KEY_GEN";
    case CKM_TLS_GOST_MASTER_KEY_DERIVE:
        return "CKM_TLS_GOST_MASTER_KEY_DERIVE";
    case CKM_TLS_GOST_KEY_AND_MAC_DERIVE:
        return "CKM_TLS_GOST_KEY_AND_MAC_DERIVE";
    case CKM_TLS_GOST_PRF_2012_256:
        return "CKM_TLS_GOST_PRF_2012_256";
    case CKM_TLS_GOST_PRF_2012_512:
        return "CKM_TLS_GOST_PRF_2012_512";
    case CKM_TLS12_MASTER_KEY_DERIVE:
        return "CKM_TLS12_MASTER_KEY_DERIVE";
    case CKM_TLS12_KEY_AND_MAC_DERIVE:
        return "CKM_TLS12_KEY_AND_MAC_DERIVE";
    case CKM_TLS_MAC:
        return "CKM_TLS_MAC";
    case CKM_TLS_KDF:
        return "CKM_TLS_KDF";
    case CKM_TLS_TREE_GOSTR3411_2012_256:
        return "CKM_TLS_TREE_GOSTR3411_2012_256";
    case CKM_PBA_GOSTR3411_WITH_GOSTR3411_HMAC:
        return "CKM_PBA_GOSTR3411_WITH_GOSTR3411_HMAC";
    case CKM_GOST28147_PKCS8_KEY_WRAP:
        return "CKM_GOST28147_PKCS8_KEY_WRAP";
    case CKM_GOSTR3410_PUBLIC_KEY_DERIVE:
        return "CKM_GOSTR3410_PUBLIC_KEY_DERIVE";
    case CKM_LISSI_GOSTR3410_PUBLIC_KEY_DERIVE:
        return "CKM_LISSI_GOSTR3410_PUBLIC_KEY_DERIVE";
	case CKM_EXTRACT_KEY_FROM_KEY:
		return "CKM_EXTRACT_KEY_FROM_KEY";
    case CKM_PKCS5_PBKD2:
        return "CKM_PKCS5_PBKD2";
    case CKM_SHA_1:
        return "CKM_SHA_1";
    case CKM_MD5:
        return "CKM_MD5";
    default:
        if (mech_type >= CKM_VENDOR_DEFINED)
            return "CKM_VENDOR_DEFINED";
        else
            return "Unknown mechanism";
    }
    ;
}

CK_RV display_pkcs11_info(CK_FUNCTION_LIST_PTR funcs)
{
  CK_RV rc;
  CK_INFO CryptokiInfo;

  /* Get the PKCS11 infomation structure and if fails print message */
  rc = funcs->C_GetInfo(&CryptokiInfo);
  if (rc != CKR_OK) {
    show_error("C_GetInfo", rc);
    return rc;
  }

  /* display the header and information */
  printf("PKCS#11 Library Info\n");
  printf("\tVersion %d.%d \n", CryptokiInfo.cryptokiVersion.major,
    CryptokiInfo.cryptokiVersion.minor);
  printf("\tManufacturer: %.32s \n", CryptokiInfo.manufacturerID);
  printf("\tFlags: 0x%lX  \n", CryptokiInfo.flags);
  printf("\tLibrary Description: %.32s\n",
    CryptokiInfo.libraryDescription);
  printf("\tLibrary Version %d.%d \n",
    CryptokiInfo.libraryVersion.major,
    CryptokiInfo.libraryVersion.minor);
  fflush(stdout);

  return rc;
}

static FLAG_INFO slot_flags[] = {
	{CKF_TOKEN_PRESENT, "CKF_TOKEN_PRESENT"},
	{CKF_REMOVABLE_DEVICE, "CKF_REMOVABLE_DEVICE"},
	{CKF_HW_SLOT, "CKF_HW_SLOT"},
};

CK_RV display_slot_info(CK_FUNCTION_LIST_PTR funcs, CK_SLOT_ID SlotId)
{
  CK_RV          rc;        // Return Code
  CK_SLOT_INFO   SlotInfo;  // Structure to hold slot information
  unsigned int   lcv;       // Loop control Variable
	size_t slot_flags_len = sizeof(slot_flags)/sizeof(FLAG_INFO);
	size_t i;
	CK_BBOOL no_flag = CK_TRUE;

	for (lcv = 0; lcv < SlotCount; lcv++){
		if ((SlotId != (CK_ULONG)-1) && (SlotList[lcv] != SlotId)) {
			continue;
		}
		/* Get the info for the slot we are examining and store in SlotInfo*/
		rc = funcs->C_GetSlotInfo(SlotList[lcv], &SlotInfo);
		if (rc != CKR_OK) {
			show_error("C_GetSlotInfo", rc);
			return rc;
		}

		/* Display the slot information */
		printf("Slot with ID 0x%lX Info\n", SlotList[lcv]);
		printf("\tDescription: %.64s\n", SlotInfo.slotDescription);
		printf("\tManufacturer: %.32s\n", SlotInfo.manufacturerID);

		no_flag = CK_TRUE;
		printf("\tFlags: 0x%lX ( ", SlotInfo.flags);
		for (i=0; i<slot_flags_len; i++){
			if (SlotInfo.flags & slot_flags[i].flag){
				if (no_flag){
					no_flag = CK_FALSE;
				} else {
					printf("|");
				}
				printf(slot_flags[i].name);				
			}
		}
		printf(" )\n");

		printf("\tHardware Version: %d.%d\n",
            SlotInfo.hardwareVersion.major,
            SlotInfo.hardwareVersion.minor);
		printf("\tFirmware Version: %d.%d\n",
            SlotInfo.firmwareVersion.major,
            SlotInfo.firmwareVersion.minor);
		fflush(stdout);

	}
	return CKR_OK;
}

static FLAG_INFO token_flags[] = {
	{CKF_RNG, "CKF_RNG"},
	{CKF_WRITE_PROTECTED, "CKF_WRITE_PROTECTED"},
	{CKF_LOGIN_REQUIRED, "CKF_LOGIN_REQUIRED"},
	{CKF_USER_PIN_INITIALIZED, "CKF_USER_PIN_INITIALIZED"},
	{CKF_RESTORE_KEY_NOT_NEEDED, "CKF_RESTORE_KEY_NOT_NEEDED"},
	{CKF_CLOCK_ON_TOKEN, "CKF_CLOCK_ON_TOKEN"},
	{CKF_PROTECTED_AUTHENTICATION_PATH, "CKF_PROTECTED_AUTHENTICATION_PATH"},
	{CKF_DUAL_CRYPTO_OPERATIONS, "CKF_DUAL_CRYPTO_OPERATIONS"},
	{CKF_TOKEN_INITIALIZED, "CKF_TOKEN_INITIALIZED"},
	{CKF_SECONDARY_AUTHENTICATION, "CKF_SECONDARY_AUTHENTICATION"},
	{CKF_USER_PIN_COUNT_LOW, "CKF_USER_PIN_COUNT_LOW"},
	{CKF_USER_PIN_FINAL_TRY, "CKF_USER_PIN_FINAL_TRY"},
	{CKF_USER_PIN_LOCKED, "CKF_USER_PIN_LOCKED"},
	{CKF_USER_PIN_TO_BE_CHANGED, "CKF_USER_PIN_TO_BE_CHANGED"},
	{CKF_SO_PIN_COUNT_LOW, "CKF_SO_PIN_COUNT_LOW"},
	{CKF_SO_PIN_FINAL_TRY, "CKF_SO_PIN_FINAL_TRY"},
	{CKF_SO_PIN_LOCKED, "CKF_SO_PIN_LOCKED"},
	{CKF_SO_PIN_TO_BE_CHANGED, "CKF_SO_PIN_TO_BE_CHANGED"},
};

CK_RV display_token_info(CK_FUNCTION_LIST_PTR funcs, CK_SLOT_ID SlotId)
{
	CK_RV          rc;         // Return Code
	CK_SLOT_INFO   SlotInfo;  // Structure to hold slot information
	CK_TOKEN_INFO  TokenInfo;  // Variable to hold Token Information
	unsigned int   lcv;        // Loop control variable
	size_t token_flags_len = sizeof(token_flags)/sizeof(FLAG_INFO);
	size_t i;
	CK_BBOOL no_flag = CK_TRUE;

	for (lcv = 0; lcv < SlotCount; lcv++){
		if ((SlotId != (CK_ULONG)-1) && (SlotList[lcv] != SlotId)) {
			continue;
		}
		rc = funcs->C_GetSlotInfo(SlotList[lcv], &SlotInfo);
		if (rc != CKR_OK) {
			show_error("C_GetSlotInfo", rc);
			return rc;
		}
		if (SlotInfo.flags & CKF_TOKEN_PRESENT) {
			/* Get the Token info for each slot in the system */
			rc = funcs->C_GetTokenInfo(SlotList[lcv], &TokenInfo);
			if (rc != CKR_OK) {
				show_error("C_GetTokenInfo", rc);
				return rc;
			}

			/* Display the token information */
			printf("Slot 0x%lX Token Info:\n", SlotList[lcv]);
			printf("\tLabel: %.32s\n", TokenInfo.label);
			printf("\tManufacturer: %.32s\n", TokenInfo.manufacturerID);
			printf("\tModel: %.16s\n", TokenInfo.model);
			printf("\tSerial Number: %.16s\n", TokenInfo.serialNumber);

			no_flag = CK_TRUE;
			printf("\tFlags: 0x%lX ( ", TokenInfo.flags);
			for (i=0; i<token_flags_len; i++){
				if (TokenInfo.flags & token_flags[i].flag){
					if (no_flag){
						no_flag = CK_FALSE;
					} else {
						printf("|");
					}
					printf(token_flags[i].name);				
				}
			}
			printf(" )\n");

			printf("\tSessions: %lu/%lu\n", TokenInfo.ulSessionCount,
				TokenInfo.ulMaxSessionCount);
			printf("\tR/W Sessions: %lu/%lu\n",
				TokenInfo.ulRwSessionCount, TokenInfo.ulMaxRwSessionCount);
			printf("\tPIN Length: %lu-%lu\n", TokenInfo.ulMinPinLen,
				TokenInfo.ulMaxPinLen);
			if (TokenInfo.ulTotalPublicMemory == CK_UNAVAILABLE_INFORMATION) {
				printf("\tPublic Memory: %s/%s\n",
					"CK_UNAVAILABLE_INFORMATION", "CK_UNAVAILABLE_INFORMATION");
				printf("\tPrivate Memory: %s/%s\n",
					"CK_UNAVAILABLE_INFORMATION", "CK_UNAVAILABLE_INFORMATION");
			} else {
				printf("\tPublic Memory: %lu/%lu\n",
					TokenInfo.ulFreePublicMemory, TokenInfo.ulTotalPublicMemory);
				printf("\tPrivate Memory: %lu/%lu\n",
					TokenInfo.ulFreePrivateMemory, TokenInfo.ulTotalPrivateMemory);
			}
			printf("\tHardware Version: %d.%d\n", TokenInfo.hardwareVersion.major,
				TokenInfo.hardwareVersion.minor);
			printf("\tFirmware Version: %d.%d\n",
				TokenInfo.firmwareVersion.major,
				TokenInfo.firmwareVersion.minor);
			if (TokenInfo.utcTime[0] == '\0') {
				printf("\tTime: %s\n", "CK_UNAVAILABLE_INFORMATION");
			} else {
				printf("\tTime: %.16s\n", TokenInfo.utcTime);
			}
			fflush(stdout);
	  }
  }
  return CKR_OK;
}

void display_mech_flags(CK_FLAGS flags)
{
    printf("(");
    if ((flags & CKF_ENCRYPT) == CKF_ENCRYPT)
        printf(" CKF_ENCRYPT,");
    if ((flags & CKF_DECRYPT) == CKF_DECRYPT)
        printf(" CKF_DECRYPT,");
    if ((flags & CKF_DIGEST) == CKF_DIGEST)
        printf(" CKF_DIGEST,");
    if ((flags & CKF_SIGN) == CKF_SIGN)
        printf(" CKF_SIGN,");
    if ((flags & CKF_SIGN_RECOVER) == CKF_SIGN_RECOVER)
        printf(" CKF_SIGN_RECOVER,");
    if ((flags & CKF_VERIFY) == CKF_VERIFY)
        printf(" CKF_VERIFY,");
    if ((flags & CKF_VERIFY_RECOVER) == CKF_VERIFY_RECOVER)
        printf(" CKF_VERIFY_RECOVER,");
    if ((flags & CKF_GENERATE) == CKF_GENERATE)
        printf(" CKF_GENERATE,");
    if ((flags & CKF_GENERATE_KEY_PAIR) == CKF_GENERATE_KEY_PAIR)
        printf(" CKF_GENERATE_KEY_PAIR,");
    if ((flags & CKF_WRAP) == CKF_WRAP)
        printf(" CKF_WRAP,");
    if ((flags & CKF_UNWRAP) == CKF_UNWRAP)
        printf(" CKF_UNWRAP,");
    if ((flags & CKF_DERIVE) == CKF_DERIVE)
        printf(" CKF_DERIVE,");
    printf(")\n");
};


CK_RV display_mechanisms(
    CK_FUNCTION_LIST_PTR funcs, CK_SLOT_ID slot_id)
{
    CK_RV                 rc;
    CK_MECHANISM_TYPE_PTR MechanismList = NULL;
    CK_MECHANISM_INFO     MechanismInfo;
    CK_ULONG              MechanismCount = 0;
    CK_ULONG              i;
    const char            *mech_name = NULL;

    rc = funcs->C_GetMechanismList(slot_id, NULL_PTR,
                                   &MechanismCount);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetMechanismList: 0x%lx\n", rc);
        return rc;
    }

    // Allocate enough memory to store all the supported mechanisms
    MechanismList = (CK_MECHANISM_TYPE_PTR) malloc(MechanismCount *
                                                   sizeof(CK_MECHANISM_TYPE));

    // This time get the mechanism list */
    rc = funcs->C_GetMechanismList(slot_id, MechanismList,
                                   &MechanismCount);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetMechanismList: 0x%lx\n", rc);
        return rc;
    }
    printf("=== Slot %d Mechanisms: ===\n", (int) slot_id);
    // For each Mechanism in the List
    for (i = 0; i < MechanismCount; i++)
    {
        // Get the Mechanism Info and display it
        printf("%ld. ", i + 1);
        mech_name = get_mechanism_name(MechanismList[i]);
        printf("\tMechanism: 0x%lx (%s)\n",
               MechanismList[i], mech_name);
        rc = funcs->C_GetMechanismInfo(slot_id,
                                       MechanismList[i], &MechanismInfo);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_GetMechanismInfo: 0x%lx\n", rc);
//			return rc;
        }
        else
        {
            printf("\tKey Size: %ld-%ld\n",
                   MechanismInfo.ulMinKeySize,
                   MechanismInfo.ulMaxKeySize);
            printf("\tFlags: 0x%lx ", MechanismInfo.flags);
            display_mech_flags(MechanismInfo.flags);
        }
    }
    printf("=== End of Slot %ld Mechanisms: ===\n", slot_id);

    // Free the memory we allocated for the mechanism list
    free(MechanismList);
    return CKR_OK;
}

// Функция для консольного ввода PIN
CK_RV
get_pin(CK_UTF8CHAR_PTR *pin)
{
    int  count = 0;
    CK_UTF8CHAR buff[PIN_SIZE] = { 0 };
	char c = 0;

#ifndef WIN32
    /* Turn off echoing to the terminal when getting the password */
    echo(FALSE);
#endif
    /* Get each character and print out a '*' for each input */
    for (count = 0; (c != LINE_FEED) &&
         (c != CARRIAGE_RETURN) && (count < PIN_SIZE); count++)
    {
#ifndef WIN32
        buff[count] = (CK_UTF8CHAR)getc(stdin);
#else
        buff[count] = (CK_UTF8CHAR)_getch();
        if (buff[count] == CANCEL_TEXT_INPUT)
        {
            // Input terminated (Ctrl-X)
            memset(buff, 0, sizeof(buff));
            *pin = NULL;
            return CKR_CANCEL;
        }
#endif
        c = (char)buff[count];
        if ((c != LINE_FEED) && (c != BACK_SPACE) &&
            (c != CARRIAGE_RETURN))
            printf("*");
        if (c == BACK_SPACE)
        {
            printf("%c%c%c", BACK_SPACE, ' ', BACK_SPACE);
            count -= 2;
        }
        fflush(stdout);
    }
#ifndef WIN32
    echo(TRUE);
#endif

    /* After we get the password go to the next line */
    printf("\n");
    fflush(stdout);

    // Allocate 80 bytes for the user PIN
    *pin = (CK_UTF8CHAR *) malloc(PIN_SIZE);
    if (*pin == NULL)
    {
        memset(buff, 0, sizeof(buff));
        return CKR_HOST_MEMORY;
    }

    /* Strip the carriage return from
     * the user input (it is not part of the PIN)
     * and put the PIN in the return buffer */
    buff[count - 1] = (CK_UTF8CHAR)'\0';    //NULL;
    // keep the trailing null for the strlen
    memcpy(*pin, buff, strlen((char *)buff) + 1);
    memset(buff, 0, sizeof(buff));
    return CKR_OK;
}

#ifndef WIN32
int
echo(int bbool)
{
    struct termios term;

    /* flush standard out to make sure everything
     * that needs to be displayed has
     * been displayed */
    fflush(stdout);

    /* get the current terminal attributes */
    if (tcgetattr(STDIN_FILENO, &term) != 0)
        return -1;

    /* Since we are calling this function
     * we must want to read in a char at a
     * time.  Therefore set the cc structure
     * before setting the terminal attrs */
    term.c_cc[VMIN]  = 1;
    term.c_cc[VTIME] = 0;

    /* If we are turning off the display
     * of input characters AND with the inverse
     * of the ECHO mask, if we are turning
     * on the display OR with the ECHO mask.
     * We also set if we are reading
     * in canonical or noncanonical mode.  */
    if (bbool)
        term.c_lflag |= (ECHO | ICANON);
    else
        term.c_lflag &= ~(ECHO | ICANON);

    /* Set the attributes, and flush the streams
     * so that any input already
     * displayed on the terminal is invalid */
    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term) != 0)
        return -1;

    return 0;
}
#endif

// Простенький колбэк для консольного ввода PIN.
// Эстеты могут написать здесь кроссплатформенное
// графическое приложение )
CK_RV pin_callback(
    CK_SESSION_HANDLE hSession,
    CK_NOTIFICATION event,              // CKN_LISSI_PIN_CALLBACK
    CK_VOID_PTR pApplication            // "Enter User/SO PIN please:"
    )
{
    if (event == CKN_LISSI_PIN_CALLBACK)
    {
        CK_UTF8CHAR *pin_buf = NULL;
        CK_RV   rc       = CKR_OK;

		// Чтобы компилятор не ругался на неиспользуемый параметр
		hSession = 0;
        // Выдача приглашения ввести PIN
        printf((const char *) pApplication);
        // get_pin сама выделяет память для pin_buf
        rc = get_pin(&pin_buf);
        if (rc == CKR_OK)
        {
            // Передаем значение PIN в библиотеку через pApplication
            memcpy(pApplication, pin_buf, strlen((char *)pin_buf) + 1);
            // Чистим буфер, выделенный get_pin
            memset(pin_buf, 0, strlen((char *)pin_buf) + 1);
            free(pin_buf);
            return CKR_OK;
        }
        else
        {
            // Ввод PIN был сброшен пользователем.
            // На всякий случай проверяем и чистим pin_buf,
            // хотя get_pin вроде-бы возвращает нулевой буфер
            // при возникновении ошибки.
            if (pin_buf)
            {
                memset(pin_buf, 0, strlen((char *)pin_buf) + 1);
                free(pin_buf);
            }
            return CKR_CANCEL;
        }
    }
    else
    {
        // По стандарту v2.30 нужно доброжелательно
        // игнорировать чуждые нам сообщения.
        return CKR_OK;
    }
}

CK_RV test_main(int argc, char *argv[])
{
    CK_RV rc = CKR_OK;
    int   i;

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

    rc = init(api_path, &funcs, NULL);
    PROCESS_ERROR("init", rc, end);

    rc = get_slot_list(funcs,
                       &SlotList,
                       &SlotCount);
    PROCESS_ERROR("get_slot_list", rc, end);

    rc = find_slot_with_token(funcs,
                              SlotList,
                              SlotCount,
                              &SlotId);
    PROCESS_ERROR("find_slot_with_token", rc, end);
 end:
    return rc;
}
CK_RV check_value(CK_BYTE *value, CK_ULONG len,
                  CK_BYTE *eth, CK_ULONG eth_len)
{
    CK_RV rc = CKR_OK;

    if (len != eth_len)
    {
        printf("Invalid value length: %lu != %lu\n", len, eth_len);
        rc = CKR_DATA_LEN_RANGE;
    }
    else
    {
        if (memcmp(value, eth, eth_len) != 0)
        {
            printf("Invalid value:\n");
            print_bytes(value, len);
            printf("Ethalon:\n");
            print_bytes(eth, eth_len);
            rc = CKR_DATA_INVALID;
        }
        else
        {
            printf("Test value OK\n");
        }
    }
    return rc;
}

void revert(unsigned char *buf, unsigned int len)
{
    unsigned char c = 0;
    unsigned int  i = 0;

    for (i = 0; i < len / 2; i++)
    {
        c                = buf[i];
        buf[i]           = buf[len - 1 - i];
        buf[len - 1 - i] = c;
    }
}

static char *class_name[] = {
	"CKO_DATA",
	"CKO_CERTIFICATE",
	"CKO_PUBLIC_KEY",
	"CKO_PRIVATE_KEY",
	"CKO_SECRET_KEY",
	"CKO_HW_FEATURE",
	"CKO_DOMAIN_PARAMETERS",
	"CKO_MECHANISM",
	"CKO_OTP_KEY"
};

CK_RV enum_objects(CK_FUNCTION_LIST_PTR funcs, CK_SLOT_ID SlotId, CK_UTF8CHAR *user_pin, CK_ULONG user_pin_len)
{
    static CK_BBOOL   ltrue       = CK_TRUE;
//    static CK_BBOOL   lfalse      = CK_FALSE;
    CK_ATTRIBUTE      attr_find[] = {
        { CKA_TOKEN, &ltrue, sizeof(ltrue) },
    };
    CK_ATTRIBUTE      attr_class[] = {
        { CKA_CLASS, NULL, 0 },
    };
    CK_ATTRIBUTE      attr_label[] = {
        { CKA_LABEL, NULL, 0 },
    };
    CK_OBJECT_HANDLE  hObj = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE  hObjArr[10];
    CK_ULONG          nObj = 0;
    CK_RV             rc   = CKR_OK;
    CK_UTF8CHAR       label[1024];
    CK_OBJECT_CLASS   oclass  = 0;
    int               i       = 0;
    CK_ULONG          j       = 0;
    CK_SESSION_HANDLE session = CK_INVALID_HANDLE;

    rc = funcs->C_OpenSession(
        SlotId,
        (CKF_SERIAL_SESSION | CKF_RW_SESSION),
        NULL,
        0,
        &session);
    show_error("C_OpenSession", rc);
    if (rc != CKR_OK)
    {
        return rc;
    }
    rc = funcs->C_Login(session, CKU_USER, user_pin, user_pin_len);
    if ((rc != CKR_OK) && (rc != CKR_USER_ALREADY_LOGGED_IN))
    {
        printf("C_Login failed, enumerate public objects only\n");
    }
    show_error("C_Login", rc);

    rc = funcs->C_FindObjectsInit(
        session,
        attr_find, sizeof(attr_find) / sizeof(CK_ATTRIBUTE));
    PROCESS_ERROR("C_FindObjectsInit", rc, end);
    printf("=== Slot %d token objects: ===\n", (int) SlotId);
    do
    {
        nObj = 0;
        rc   = funcs->C_FindObjects(
            session,
            hObjArr, 10, &nObj);
        if (rc != CKR_OK)
        {
			show_error("C_FindObjects", rc);
            nObj = 1;
            continue;
        }

        for (j = 0; j < nObj; j++)
        {
            hObj = hObjArr[j];
            if (hObj != CK_INVALID_HANDLE)
            {
                memset(label, 0, sizeof(label));
                attr_class[0].pValue     = &oclass;
                attr_class[0].ulValueLen = sizeof(oclass);
                attr_label[0].pValue     = label;
                attr_label[0].ulValueLen = sizeof(label);
                rc                       = funcs->C_GetAttributeValue(
                    session,
                    hObj, attr_class, sizeof(attr_class) / sizeof(CK_ATTRIBUTE));
				if (rc != CKR_OK) {
					show_error("C_FindObjects", rc);
					goto end;
				}

                rc = funcs->C_GetAttributeValue(
                    session,
                    hObj, attr_label, sizeof(attr_label) / sizeof(CK_ATTRIBUTE));
				if (rc != CKR_OK) {
					show_error("C_FindObjects", rc);
				} else {
					i++;
					if (oclass <= CKO_OTP_KEY) {
						printf("%d. class = 0x%lx(%s), label: '%s'\n", 
							i, oclass, class_name[oclass], label);
					} else {
						printf("%d. class = 0x%lx, label: '%s'\n", 
							i, oclass, label);
					}
                }
            }
        }
    } while (nObj > 0);
    printf("=== End of Slot %ld token objects: ===\n", SlotId);

    rc = funcs->C_FindObjectsFinal(
        session);
    PROCESS_ERROR("C_FindObjectsFinal", rc, end);
 end:
    funcs->C_CloseSession(session);

    return rc;
}

CK_ULONG get_random_size(CK_SESSION_HANDLE sess, CK_ULONG size_min, CK_ULONG size_max) 
{
	CK_ULONG size;
	CK_RV rc = CKR_OK;
	static CK_ULONG ulong_max = 0UL - 1UL;

    rc = funcs->C_GenerateRandom(sess, (CK_BYTE *)&size, sizeof(size));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GenerateRandom failed: 0x%lx\n", rc);
        return 0;
    }

	size = (CK_ULONG)(size_min + (size_max - size_min) * (double)size / ulong_max);
	return size;
}

CK_RV find_obj(CK_SESSION_HANDLE sess,
               CK_ATTRIBUTE *attr, CK_ULONG count, 
			   CK_OBJECT_HANDLE *hObj_res)
{
    CK_RV            rc;
    CK_OBJECT_HANDLE hObj = CK_INVALID_HANDLE;
    CK_ULONG         number;

    // Ищем объекты по шаблону
    rc = funcs->C_FindObjectsInit(sess, attr, count);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_FindObjectsFinal failed, rc = 0x%lx\n", rc);
        return rc;
    }

    number = 0;
    hObj   = CK_INVALID_HANDLE;
    rc     = funcs->C_FindObjects(sess, &hObj, 1, &number);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_FindObjects failed, rc = 0x%lx\n", rc);
        return rc;
    }
    if (number == 0 || hObj == CK_INVALID_HANDLE)
    {
        fprintf(stderr, "Object not found\n");
        return CKR_FUNCTION_FAILED;
    }
    *hObj_res = hObj;
//    printf("Object is found\n");
    rc = funcs->C_FindObjectsFinal(sess);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_FindObjectsFinal failed, rc = 0x%lx\n", rc);
        return rc;
    }
    return CKR_OK;
}

CK_RV init_all(void)
{
    CK_RV rc = CKR_OK;
//    printf("init\n");
    rc = init(api_path, &funcs, NULL);
	if (rc != CKR_OK) {
        fprintf(stderr, "init failed, rc = 0x%lx\n", rc);
        return rc;
	}
//    printf("get_slot_list\n");
    rc = get_slot_list(funcs, &SlotList, &SlotCount);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "get_slot_list failed, rc = 0x%lx\n", rc);
        return rc;
    }
//    printf("get_slot_list\n");
    rc = find_slot_with_token(funcs, SlotList, SlotCount, &SlotId);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "find_slot_with_token failed, rc = 0x%lx\n", rc);
        return rc;
    }
//    printf("init_all OK\n");
    return rc;
}