#include "test_common.h"

#pragma warning(disable : 4996)

CK_FUNCTION_LIST_PTR funcs = NULL;
CK_C_Ctrl pctrl = NULL;
#ifdef WIN32
HMODULE dll;
#else
void *dll;
#endif
CK_UTF8CHAR *user_pin = "01234567";
CK_UTF8CHAR *so_pin = "76543210";
CK_UTF8CHAR *hw_user_pin = "01234567";
CK_UTF8CHAR *hw_so_pin =   "12345678";
CK_CHAR *api_path = PKCS11_API_PATH;
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;

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;

}

char  *get_ret_code_string( 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_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_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";                 
   }
   return NULL;
}

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

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

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

int print_bytes(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, "%.2x ", buf[i]);
    }
    sprintf(str+j,"\n");
    return 0;
}


int do_GetFunctionList(CK_CHAR *api_path, CK_FUNCTION_LIST_PTR *pfuncs)
{
	CK_RV            rc;
	CK_RV  (*pfoo)();

	printf("do_GetFunctionList...\n");
#ifdef WIN32
	dll = LoadLibrary(api_path);
#else   
	dll = dlopen(api_path, RTLD_NOW);
#endif   
	if ( dll == NULL ) {
		printf("Can't load PKCS#11 API library. Check API path.\n"); 
#ifndef WIN32
		printf("dlerror: %s\n", dlerror());
#endif 	  
		return FALSE;
	}
#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 FALSE;
	}
   
	rc = pfoo(pfuncs);
	if (rc != CKR_OK) {
		show_error("Error in  C_GetFunctionList", rc );
		return FALSE;
	}
#ifdef WIN32
	pctrl = (CK_C_Ctrl)GetProcAddress(dll,"C_Ctrl");
#else
	pctrl = (CK_C_Ctrl)dlsym(dll,"C_Ctrl");
#endif   
	printf("Looks okay...\n");
	return TRUE;

}

CK_RV init(CK_CHAR *api_path, 
		   CK_FUNCTION_LIST_PTR *pfuncs, 
		   CK_C_INITIALIZE_ARGS *pinit_args){
	CK_RV rc; // Return Code

	rc = do_GetFunctionList(api_path, pfuncs);
	if (!rc)
		return CKR_GENERAL_ERROR;
	rc = (*pfuncs)->C_Initialize(pinit_args);
	if (rc != CKR_OK) {
		show_error("C_Initialize", rc);
		cleanup(*pfuncs);
	}
	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) {
//		fprintf(stderr, "AppDestroyMutex: NULL mutex\n");
	} 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) {
//		fprintf(stderr, "AppLockMutex: NULL mutex\n");
	} 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) {
//		fprintf(stderr, "AppUnlockMutex: NULL mutex\n");
	} 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%x\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%x\n", rc);
            return rc;
        }
    } else {
        *ppSlotList = NULL;
        return CKR_OK;
    }
    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];
				rc = display_token_info(funcs, pSlotList[i]);
                return rc;
			}
		} else {
			printf("C_GetSlotInfo failed, rc = 0x%x\n", rc);
			return rc;
		}
    }
	printf("No token present\n");
    return CKR_TOKEN_NOT_PRESENT;
}

typedef struct {
	CK_MECHANISM_TYPE type;
	char *name;
} MECH_INFO;

static MECH_INFO mech[] = {
	{CKM_GOSTR3410_KEY_PAIR_GEN, "CKM_GOSTR3410_KEY_PAIR_GEN"},
	{CKM_GOSTR3410, "CKM_GOSTR3410"},
	{CKM_GOSTR3410_WITH_GOSTR3411, "CKM_GOSTR3410_WITH_GOSTR3411"},
	{CKM_GOSTR3410_KEY_WRAP, "CKM_GOSTR3410_KEY_WRAP"},
	{CKM_GOSTR3410_DERIVE, "CKM_GOSTR3410_DERIVE"},
	{CKM_GOSTR3411, "CKM_GOSTR3411"},
	{CKM_GOSTR3411_HMAC, "CKM_GOSTR3411_HMAC"},
	{CKM_GOST28147_KEY_GEN, "CKM_GOST28147_KEY_GEN"},
	{CKM_GOST28147_ECB, "CKM_GOST28147_ECB"},
	{CKM_GOST28147, "CKM_GOST28147"},
	{CKM_GOST28147_MAC, "CKM_GOST28147_MAC"},
	{CKM_GOST28147_KEY_WRAP, "CKM_GOST28147_KEY_WRAP"},
	{CKM_GOST28147_CNT, "CKM_GOST28147_CNT"},
	{CKM_GOST28147_KEY_CPDIVERSIFY, "CKM_GOST28147_KEY_CPDIVERSIFY"},
	{CKM_TLS_GOST_PRF, "CKM_TLS_GOST_PRF"},
	{CKM_TLS_GOST_PRE_MASTER_KEY_GEN, "CKM_TLS_GOST_PRE_MASTER_KEY_GEN"},
	{CKM_TLS_GOST_MASTER_KEY_DERIVE, "CKM_TLS_GOST_MASTER_KEY_DERIVE"},
	{CKM_TLS_GOST_KEY_AND_MAC_DERIVE, "CKM_TLS_GOST_KEY_AND_MAC_DERIVE"},
	{CKM_PBA_GOSTR3411_WITH_GOSTR3411_HMAC, "CKM_PBA_GOSTR3411_WITH_GOSTR3411_HMAC"},
	{CKM_GOST28147_PKCS8_KEY_WRAP, "CKM_GOST28147_PKCS8_KEY_WRAP"},
	{CKM_GOSTR3410_PUBLIC_KEY_DERIVE, "CKM_GOSTR3410_PUBLIC_KEY_DERIVE"},
	{CKM_PKCS5_PBKD2, "CKM_PKCS5_PBKD2"},	
};
static int MECH_NUM = sizeof(mech)/sizeof(MECH_INFO);

char *get_mechanism_name(CK_MECHANISM_TYPE mech_type) {
	int i;
	for (i=0; i<MECH_NUM; i++) {
		if (mech[i].type == mech_type) {
			return mech[i].name;
		}
	}
	return NULL;
}

CK_RV
display_pkcs11_info(CK_FUNCTION_LIST_PTR funcs){

   CK_RV rc;
   CK_INFO CryptokiInfo;
	memset(&CryptokiInfo, 0, sizeof(CK_INFO));
   /* 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 Info\n");
   printf("\tVersion %d.%d \n", CryptokiInfo.cryptokiVersion.major,
         CryptokiInfo.cryptokiVersion.minor);
   printf("\tManufacturer: %.32s \n", CryptokiInfo.manufacturerID);
   printf("\tFlags: 0x%X  \n", CryptokiInfo.flags);
   printf("\tLibrary Description: %.32s\n",
         CryptokiInfo.libraryDescription);
   printf("\tLibrary Version %d.%d \n",
         CryptokiInfo.libraryVersion.major,
         CryptokiInfo.libraryVersion.minor);

   return rc;
}

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
	memset(&SlotInfo, 0, sizeof(CK_SLOT_INFO)); 
	// Get the info for the slot we are examining and store in SlotInfo
	rc = funcs->C_GetSlotInfo(SlotId, &SlotInfo);
	if (rc != CKR_OK) {
		printf("Error getting slot info: 0x%X\n", rc);
		return rc;
	}
	// Display the slot information 
	printf("Slot #%d Info\n", SlotId);
	printf("\tDescription: %.64s\n", SlotInfo.slotDescription);
	printf("\tManufacturer: %.32s\n", SlotInfo.manufacturerID);
	printf("\tFlags: 0x%X\n", SlotInfo.flags);
	printf("\tHardware Version: %d.%d\n",
	SlotInfo.hardwareVersion.major,
	SlotInfo.hardwareVersion.minor);
	printf("\tFirmware Version: %d.%d\n",
	SlotInfo.firmwareVersion.major,
	SlotInfo.firmwareVersion.minor);

	return CKR_OK;
}

void display_tinfo(CK_SLOT_ID sid, CK_TOKEN_INFO *tinfo)
{
	// Display the token information 
	printf("Token #%d Info:\n", sid);
	printf("\tLabel: %.32s\n", tinfo->label);
	printf("\tManufacturer: %.32s\n", tinfo->manufacturerID);
	printf("\tModel: %.16s\n", tinfo->model);
	printf("\tSerial Number: %.16s\n", tinfo->serialNumber);
	printf("\tFlags: 0x%X\n", tinfo->flags);
	printf("\tSessions: %d/%d\n", tinfo->ulSessionCount,
		tinfo->ulMaxSessionCount);
	printf("\tR/W Sessions: %d/%d\n",
		tinfo->ulRwSessionCount, tinfo->ulMaxRwSessionCount);
	printf("\tPIN Length: %d-%d\n", tinfo->ulMinPinLen,
		tinfo->ulMaxPinLen);
	printf("\tPublic Memory: 0x%X/0x%X\n",
		tinfo->ulFreePublicMemory, tinfo->ulTotalPublicMemory);
	printf("\tPrivate Memory: 0x%X/0x%X\n",
		tinfo->ulFreePrivateMemory, tinfo->ulTotalPrivateMemory);
	printf("\tHardware Version: %d.%d\n", tinfo->hardwareVersion.major,
		tinfo->hardwareVersion.minor);
	printf("\tFirmware Version: %d.%d\n",
		tinfo->firmwareVersion.major,
		tinfo->firmwareVersion.minor);
	printf("\tTime: %.16s\n", tinfo->utcTime);
}

CK_RV
display_token_info(CK_FUNCTION_LIST_PTR funcs, CK_SLOT_ID SlotId){
	CK_RV rc; // Return Code
	CK_TOKEN_INFO TokenInfo; // Variable to hold Token Information
	// Get the Token info for each slot in the system
	memset(&TokenInfo, 0, sizeof(CK_TOKEN_INFO));
	rc = funcs->C_GetTokenInfo(SlotId, &TokenInfo);
	if (rc != CKR_OK) {
		printf("Error getting token info: 0x%X\n", rc);
		return rc;
	}
	display_tinfo(SlotId, &TokenInfo); 

	return CKR_OK;
}


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;
	char *mech_name = NULL;

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

	// For each Mechanism in the List
	for (i = 0; i < MechanismCount; i++){
		// Get the Mechanism Info and display it
		printf("Mechanism #%d\n", i);
		mech_name = get_mechanism_name(MechanismList[i]);
		if (mech_name) {
			printf("\tMechanism: 0x%X(%s)\n", MechanismList[i], mech_name);
		} else {
			printf("\tMechanism: 0x%X\n", MechanismList[i]);
		}
		rc = funcs->C_GetMechanismInfo(slot_id,
			MechanismList[i], &MechanismInfo);
		if (rc != CKR_OK) {
			fprintf(stderr, "C_GetMechanismInfo: 0x%x\n", rc);
//			return rc;
		} else {
			printf("\tKey Size: %d-%d\n", MechanismInfo.ulMinKeySize,
				MechanismInfo.ulMaxKeySize);
			printf("\tFlags: 0x%X\n", MechanismInfo.flags);
		}
	}

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

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

   /* Turn off echoing to the terminal when getting the password */
#ifndef WIN32    
   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] = getc(stdin);
#else      
      buff[count] = _getch();
	  if (buff[count] == END_OF_TEXT) 
	  {
		  // Input terminated (Ctrl-C)
		  return CKR_CANCEL;
	  }
#endif	  
      c = 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 = (unsigned char *)malloc(PIN_SIZE);

   /* 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] =  '\0'; //NULL; 
   memcpy(*pin, buff, strlen(buff)+1); // keep the trailing null for the strlen
   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_CHAR *pin_buf = NULL;
		CK_RV rc = CKR_OK;

		printf((CK_CHAR *)pApplication);
		rc = get_pin(&pin_buf);
		if (rc == CKR_OK) {
			memcpy(pApplication, pin_buf, strlen(pin_buf)+1);
			memset(pin_buf, 0, strlen(pin_buf)+1); 
			free(pin_buf);
			return CKR_OK;
		} else {
			if (pin_buf) {
				memset(pin_buf, 0, strlen(pin_buf)+1); 
				free(pin_buf);
			}
			return CKR_CANCEL;
		}
	} else {
		// By standard v2.30
		return CKR_OK;
	}
}

