#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_CHAR *api_path = 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;

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;
}

void process_ret_code( CK_RV rc )
{
	switch (rc) {
		case CKR_OK:                               
			printf(" CKR_OK");                               break;
		case CKR_CANCEL:                           
			printf(" CKR_CANCEL");                           break;
		case CKR_HOST_MEMORY:                      
			printf(" CKR_HOST_MEMORY");                      break;
		case CKR_SLOT_ID_INVALID:                  
			printf(" CKR_SLOT_ID_INVALID");                  break;
		case CKR_GENERAL_ERROR:                    
			printf(" CKR_GENERAL_ERROR");                    break;
		case CKR_FUNCTION_FAILED:                  
			printf(" CKR_FUNCTION_FAILED");                  break;
		case CKR_ARGUMENTS_BAD:                    
			printf(" CKR_ARGUMENTS_BAD");                    break;
		case CKR_NO_EVENT:                         
			printf(" CKR_NO_EVENT");                         break;
		case CKR_NEED_TO_CREATE_THREADS:           
			printf(" CKR_NEED_TO_CREATE_THREADS");           break;
		case CKR_CANT_LOCK:                        
			printf(" CKR_CANT_LOCK");                        break;
		case CKR_ATTRIBUTE_READ_ONLY:              
			printf(" CKR_ATTRIBUTE_READ_ONLY");              break;
		case CKR_ATTRIBUTE_SENSITIVE:              
			printf(" CKR_ATTRIBUTE_SENSITIVE");              break;
		case CKR_ATTRIBUTE_TYPE_INVALID:           
			printf(" CKR_ATTRIBUTE_TYPE_INVALID");           break;
		case CKR_ATTRIBUTE_VALUE_INVALID:          
			printf(" CKR_ATTRIBUTE_VALUE_INVALID");          break;
		case CKR_DATA_INVALID:                     
			printf(" CKR_DATA_INVALID");                     break;
		case CKR_DATA_LEN_RANGE:                   
			printf(" CKR_DATA_LEN_RANGE");                   break;
		case CKR_DEVICE_ERROR:                     
			printf(" CKR_DEVICE_ERROR");                     break;
		case CKR_DEVICE_MEMORY:                    
			printf(" CKR_DEVICE_MEMORY");                    break;
		case CKR_DEVICE_REMOVED:                   
			printf(" CKR_DEVICE_REMOVED");                   break;
		case CKR_ENCRYPTED_DATA_INVALID:           
			printf(" CKR_ENCRYPTED_DATA_INVALID");           break;
		case CKR_ENCRYPTED_DATA_LEN_RANGE:         
			printf(" CKR_ENCRYPTED_DATA_LEN_RANGE");         break;
		case CKR_FUNCTION_CANCELED:                
			printf(" CKR_FUNCTION_CANCELED");                break;
		case CKR_FUNCTION_NOT_PARALLEL:            
			printf(" CKR_FUNCTION_NOT_PARALLEL");            break;
		case CKR_FUNCTION_NOT_SUPPORTED:           
			printf(" CKR_FUNCTION_NOT_SUPPORTED");           break;
		case CKR_KEY_HANDLE_INVALID:               
			printf(" CKR_KEY_HANDLE_INVALID");               break;
		case CKR_KEY_SIZE_RANGE:                   
			printf(" CKR_KEY_SIZE_RANGE");                   break;
		case CKR_KEY_TYPE_INCONSISTENT:            
			printf(" CKR_KEY_TYPE_INCONSISTENT");            break;
		case CKR_KEY_NOT_NEEDED:                   
			printf(" CKR_KEY_NOT_NEEDED");                   break;
		case CKR_KEY_CHANGED:                      
			printf(" CKR_KEY_CHANGED");                      break;
		case CKR_KEY_NEEDED:                       
			printf(" CKR_KEY_NEEDED");                       break;
		case CKR_KEY_INDIGESTIBLE:                 
			printf(" CKR_KEY_INDIGESTIBLE");                 break;
		case CKR_KEY_FUNCTION_NOT_PERMITTED:       
			printf(" CKR_KEY_FUNCTION_NOT_PERMITTED");       break;
		case CKR_KEY_NOT_WRAPPABLE:                
			printf(" CKR_KEY_NOT_WRAPPABLE");                break;
		case CKR_KEY_UNEXTRACTABLE:                
			printf(" CKR_KEY_UNEXTRACTABLE");                break;
		case CKR_MECHANISM_INVALID:                
			printf(" CKR_MECHANISM_INVALID");                break;
		case CKR_MECHANISM_PARAM_INVALID:          
			printf(" CKR_MECHANISM_PARAM_INVALID");          break;
		case CKR_OBJECT_HANDLE_INVALID:            
			printf(" CKR_OBJECT_HANDLE_INVALID");            break;
		case CKR_OPERATION_ACTIVE:                 
			printf(" CKR_OPERATION_ACTIVE");                 break;
		case CKR_OPERATION_NOT_INITIALIZED:        
			printf(" CKR_OPERATION_NOT_INITIALIZED");        break;
		case CKR_PIN_INCORRECT:                    
			printf(" CKR_PIN_INCORRECT");                    break;
		case CKR_PIN_INVALID:                      
			printf(" CKR_PIN_INVALID");                      break;
		case CKR_PIN_LEN_RANGE:                    
			printf(" CKR_PIN_LEN_RANGE");                    break;
		case CKR_PIN_EXPIRED:                      
			printf(" CKR_PIN_EXPIRED");                      break;
		case CKR_PIN_LOCKED:                       
			printf(" CKR_PIN_LOCKED");                       break;
		case CKR_SESSION_CLOSED:                   
			printf(" CKR_SESSION_CLOSED");                   break;
		case CKR_SESSION_COUNT:                    
			printf(" CKR_SESSION_COUNT");                    break;
		case CKR_SESSION_HANDLE_INVALID:           
			printf(" CKR_SESSION_HANDLE_INVALID");           break;
		case CKR_SESSION_PARALLEL_NOT_SUPPORTED:   
			printf(" CKR_SESSION_PARALLEL_NOT_SUPPORTED");   break;
		case CKR_SESSION_READ_ONLY:                
			printf(" CKR_SESSION_READ_ONLY");                break;
		case CKR_SESSION_EXISTS:                   
			printf(" CKR_SESSION_EXISTS");                   break;
		case CKR_SESSION_READ_ONLY_EXISTS:         
			printf(" CKR_SESSION_READ_ONLY_EXISTS");         break;
		case CKR_SESSION_READ_WRITE_SO_EXISTS:     
			printf(" CKR_SESSION_READ_WRITE_SO_EXISTS");     break;
		case CKR_SIGNATURE_INVALID:                
			printf(" CKR_SIGNATURE_INVALID");                break;
		case CKR_SIGNATURE_LEN_RANGE:              
			printf(" CKR_SIGNATURE_LEN_RANGE");              break;
		case CKR_TEMPLATE_INCOMPLETE:              
			printf(" CKR_TEMPLATE_INCOMPLETE");              break;
		case CKR_TEMPLATE_INCONSISTENT:            
			printf(" CKR_TEMPLATE_INCONSISTENT");            break;
		case CKR_TOKEN_NOT_PRESENT:                
			printf(" CKR_TOKEN_NOT_PRESENT");                break;
		case CKR_TOKEN_NOT_RECOGNIZED:             
			printf(" CKR_TOKEN_NOT_RECOGNIZED");             break;
		case CKR_TOKEN_WRITE_PROTECTED:            
			printf(" CKR_TOKEN_WRITE_PROTECTED");            break;
		case CKR_UNWRAPPING_KEY_HANDLE_INVALID:    
			printf(" CKR_UNWRAPPING_KEY_HANDLE_INVALID");    break;
		case CKR_UNWRAPPING_KEY_SIZE_RANGE:        
			printf(" CKR_UNWRAPPING_KEY_SIZE_RANGE");        break;
		case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT: 
			printf(" CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT"); break;
		case CKR_USER_ALREADY_LOGGED_IN:           
			printf(" CKR_USER_ALREADY_LOGGED_IN");           break;
		case CKR_USER_NOT_LOGGED_IN:               
			printf(" CKR_USER_NOT_LOGGED_IN");               break;
		case CKR_USER_PIN_NOT_INITIALIZED:         
			printf(" CKR_USER_PIN_NOT_INITIALIZED");         break;
		case CKR_USER_TYPE_INVALID:                
			printf(" CKR_USER_TYPE_INVALID");                break;
		case CKR_USER_ANOTHER_ALREADY_LOGGED_IN:   
			printf(" CKR_USER_ANOTHER_ALREADY_LOGGED_IN");   break;
		case CKR_USER_TOO_MANY_TYPES:              
			printf(" CKR_USER_TOO_MANY_TYPES");              break;
		case CKR_WRAPPED_KEY_INVALID:              
			printf(" CKR_WRAPPED_KEY_INVALID");              break;
		case CKR_WRAPPED_KEY_LEN_RANGE:            
			printf(" CKR_WRAPPED_KEY_LEN_RANGE");            break;
		case CKR_WRAPPING_KEY_HANDLE_INVALID:      
			printf(" CKR_WRAPPING_KEY_HANDLE_INVALID");      break;
		case CKR_WRAPPING_KEY_SIZE_RANGE:          
			printf(" CKR_WRAPPING_KEY_SIZE_RANGE");          break;
		case CKR_WRAPPING_KEY_TYPE_INCONSISTENT:   
			printf(" CKR_WRAPPING_KEY_TYPE_INCONSISTENT");   break;
		case CKR_RANDOM_SEED_NOT_SUPPORTED:        
			printf(" CKR_RANDOM_SEED_NOT_SUPPORTED");        break;
		case CKR_RANDOM_NO_RNG:                    
			printf(" CKR_RANDOM_NO_RNG");                    break;
		case CKR_BUFFER_TOO_SMALL:                 
			printf(" CKR_BUFFER_TOO_SMALL");                 break;
		case CKR_SAVED_STATE_INVALID:              
			printf(" CKR_SAVED_STATE_INVALID");              break;
		case CKR_INFORMATION_SENSITIVE:            
			printf(" CKR_INFORMATION_SENSITIVE");            break;
		case CKR_STATE_UNSAVEABLE:                 
			printf(" CKR_STATE_UNSAVEABLE");                 break;
		case CKR_CRYPTOKI_NOT_INITIALIZED:         
			printf(" CKR_CRYPTOKI_NOT_INITIALIZED");         break;
		case CKR_CRYPTOKI_ALREADY_INITIALIZED:     
			printf(" CKR_CRYPTOKI_ALREADY_INITIALIZED");     break;
		case CKR_MUTEX_BAD:                        
			printf(" CKR_MUTEX_BAD");                        break;
		case CKR_MUTEX_NOT_LOCKED:                 
			printf(" CKR_MUTEX_NOT_LOCKED");                 break;
	}
}

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

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;
}

CK_RV get_function_list(CK_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   
	dll = dlopen(api_path, RTLD_NOW);
#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;
	}
#ifdef WIN32
	pctrl = (CK_C_Ctrl)GetProcAddress(dll, "C_Ctrl");
#else
	pctrl = (CK_C_Ctrl)dlsym(dll, "C_Ctrl");
#endif   
	printf("Looks OK\n");
	return CKR_OK;
}

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

	rc = get_function_list(api_path, pfuncs);
	if (rc != CKR_OK)
		return rc;
	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
	DWORD pid;
	HANDLE *mutex;
#else
	pthread_mutex_t *mutex;
#endif
	char name[128];
	static int imtx = 0;
#ifdef WIN32
	pid = GetCurrentProcessId();
	sprintf(name, "APPMTX_%d_PID_%ld", imtx++, pid);
	mutex = (HANDLE *)malloc(sizeof(HANDLE));
	*mutex = CreateMutex(NULL, FALSE, name);
    *(HANDLE **)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 )
{
#ifdef WIN32
	CloseHandle(*(HANDLE *)mutex);
	free(mutex);
#else
    pthread_mutex_destroy((pthread_mutex_t *)mutex);
	free(mutex);
#endif
    return CKR_OK;
}

CK_RV AppLockMutex( void *mutex )
{
#ifdef WIN32
	WaitForSingleObject(*(HANDLE *)mutex, INFINITE);
#else
    pthread_mutex_lock( (pthread_mutex_t *)mutex);
#endif
    return CKR_OK;
}

CK_RV AppUnlockMutex( void *mutex )
{
#ifdef WIN32
	ReleaseMutex(*(HANDLE *)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;
    }
	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];
				rc = display_token_info(funcs, pSlotList[i]);
                return rc;
			}
        }
    }
    return CKR_FUNCTION_FAILED;
}

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));
	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)); 
	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;
}

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

	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 the token information 
	printf("Token #%d Info:\n", SlotId);
	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);
	printf("\tFlags: 0x%X\n", TokenInfo.flags);
	printf("\tSessions: %d/%d\n", TokenInfo.ulSessionCount,
	TokenInfo.ulMaxSessionCount);
	printf("\tR/W Sessions: %d/%d\n",
	TokenInfo.ulRwSessionCount, TokenInfo.ulMaxRwSessionCount);
	printf("\tPIN Length: %d-%d\n", TokenInfo.ulMinPinLen,
	TokenInfo.ulMaxPinLen);
	printf("\tPublic Memory: 0x%X/0x%X\n",
	TokenInfo.ulFreePublicMemory, TokenInfo.ulTotalPublicMemory);
	printf("\tPrivate Memory: 0x%X/0x%X\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);
	printf("\tTime: %.16s\n", TokenInfo.utcTime);

	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;

#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] = getc(stdin);
#else      
		buff[count] = _getch();
		if (buff[count] == END_OF_TEXT) 
		{
			// Input terminated (Ctrl-C)
			memset(buff, 0, sizeof(buff));
			*pin = NULL;
			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);
	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] =  '\0'; //NULL; 
	// keep the trailing null for the strlen
	memcpy(*pin, buff, strlen(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_CHAR *pin_buf = NULL;
		CK_RV rc = CKR_OK;

		// Выдача приглашения ввести PIN
		printf((CK_CHAR *)pApplication);
		// get_pin сама выделяет память для pin_buf
		rc = get_pin(&pin_buf);
		if (rc == CKR_OK) {
			// Передаем значение PIN в библиотеку через pApplication
			memcpy(pApplication, pin_buf, strlen(pin_buf)+1);
			// Чистим буфер, выделенный get_pin 
			memset(pin_buf, 0, strlen(pin_buf)+1); 
			free(pin_buf);
			return CKR_OK;
		} else {
			// Ввод PIN был сброшен пользователем.
			// На всякий случай проверяем и чистим pin_buf,
			// хотя get_pin вроде-бы возвращает нулевой буфер
			// при возникновении ошибки.
			if (pin_buf) {
				memset(pin_buf, 0, strlen(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 = argv[i];
		}
	}

	rc = init(api_path, &funcs, NULL);
	if (rc != CKR_OK) {
		fprintf(stderr, "ERROR calling init, rc = 0x%x\n", rc);
		return rc;
	}
	rc = get_slot_list(funcs, 
                    &SlotList, 
                    &SlotCount);
	if (rc != CKR_OK) {
		printf("get_slot_list failed, rc = 0x%x\n", rc );
		return rc;
	}
	rc = find_slot_with_token(funcs, 
                           SlotList, 
                           SlotCount, 
                           &SlotId);
	if (rc != CKR_OK) {
		printf("find_slot_with_token failed, rc = 0x%x\n", rc );
		return rc;
	}
	return CKR_OK;
}
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: %u != %u\n", len, eth_len);
		rc = CKR_DATA_LEN_RANGE;
	} else {
		if (memcmp(value, eth, eth_len) != 0) {
			printf("Invalid value:\n");
			print_hex(value, len);
			printf("Ethalon:\n"); 
			print_hex(eth, eth_len);
			rc = CKR_DATA_INVALID;
		} else {
			printf("Test value OK\n");
		}
	}
	return rc;
}
