#include "test_common.h"
#include <stdarg.h>

CK_SLOT_ID SlotId;
CK_UTF8CHAR *new_user_pin = "22222222";
CK_UTF8CHAR *new_so_pin = "88888888";
CK_UTF8CHAR *sopin = NULL;
CK_UTF8CHAR *userpin = NULL;
CK_UTF8CHAR * bad_pin="33333333";

CK_SLOT_ID_PTR SlotList = NULL;
CK_ULONG SlotCount = 0;
CK_TOKEN_INFO ti; 

CK_RV nok = ~CKR_OK;


#include "pub_log.inc"

const char * get_rc_name(int 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";

/* CKR_FLAGS_INVALID was removed for v2.0 */

/* CKR_GENERAL_ERROR and CKR_FUNCTION_FAILED are new for v2.0 */
	case CKR_GENERAL_ERROR:return "CKR_GENERAL_ERROR";
	case CKR_FUNCTION_FAILED:return "CKR_FUNCTION_FAILED";

/* CKR_ARGUMENTS_BAD, CKR_NO_EVENT, CKR_NEED_TO_CREATE_THREADS,
 * and CKR_CANT_LOCK are new for v2.01 */
	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";
/* CKR_COPY_PROHIBITED is new for v2.30 */
	case CKR_COPY_PROHIBITED:return "CKR_COPY_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";

/* CKR_FUNCTION_NOT_SUPPORTED is new for v2.0 */
	case CKR_FUNCTION_NOT_SUPPORTED:return "CKR_FUNCTION_NOT_SUPPORTED";

	case CKR_KEY_HANDLE_INVALID:return "CKR_KEY_HANDLE_INVALID";

/* CKR_KEY_SENSITIVE was removed for v2.0 */

	case CKR_KEY_SIZE_RANGE:return "CKR_KEY_SIZE_RANGE";
	case CKR_KEY_TYPE_INCONSISTENT:return "CKR_KEY_TYPE_INCONSISTENT";

/* CKR_KEY_NOT_NEEDED, CKR_KEY_CHANGED, CKR_KEY_NEEDED,
 * CKR_KEY_INDIGESTIBLE, CKR_KEY_FUNCTION_NOT_PERMITTED,
 * CKR_KEY_NOT_WRAPPABLE, and CKR_KEY_UNEXTRACTABLE are new for
 * v2.0 */
	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";

/* CKR_OBJECT_CLASS_INCONSISTENT and CKR_OBJECT_CLASS_INVALID
 * were removed for v2.0 */
	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";

/* CKR_PIN_EXPIRED and CKR_PIN_LOCKED are new for v2.0 */
	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";

/* CKR_SESSION_READ_ONLY_EXISTS and
 * CKR_SESSION_READ_WRITE_SO_EXISTS are new for v2.0 */
	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";

/* CKR_USER_ANOTHER_ALREADY_LOGGED_IN and CKR_USER_TOO_MANY_TYPES
 * are new to v2.01 */
	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";

/* These are new to v2.0 */
	case CKR_RANDOM_NO_RNG:return "CKR_RANDOM_NO_RNG";
/* CKR_DOMAIN_PARAMS_INVALID is new for v2.11 */
	case CKR_DOMAIN_PARAMS_INVALID:return "CKR_DOMAIN_PARAMS_INVALID";
	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";

/* These are new to v2.01 */
	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_VENDOR_DEFINED:return "CKR_VENDOR_DEFINED";
	default: return "-=UNKNOWN=-";
	}
}

const char * get_flag_name(CK_FLAGS flag){
	switch(flag){
		case CKF_USER_PIN_INITIALIZED: return "CKF_USER_PIN_INITIALIZED";
		case CKF_USER_PIN_COUNT_LOW: return "CKF_USER_PIN_COUNT_LOW";
		case CKF_USER_PIN_FINAL_TRY: return "CKF_USER_PIN_FINAL_TRY";
		case CKF_USER_PIN_LOCKED: return "CKF_USER_PIN_LOCKED";
		case CKF_SO_PIN_COUNT_LOW: return "CKF_SO_PIN_COUNT_LOW";
		case CKF_SO_PIN_FINAL_TRY: return "CKF_SO_PIN_FINAL_TRY";
		case CKF_SO_PIN_LOCKED: return "CKF_SO_PIN_LOCKED";
		default: return "-=UNKNOWN=-";
	}
}

int must_on(int count,...){
	va_list ap;
	int i, flag;
	va_start (ap, count);
	for (i = 0; i < count; i++){
		flag = va_arg (ap, int);
		if(!(ti.flags&flag)){
			WRITE_PUB_LOG("==>> %s not set (error)\n",get_flag_name(flag));
			return 0;
		}
	}
	va_end (ap);
	return 1;
}

int must_off(int count,...){
	va_list ap;
	int i, flag;
	va_start (ap, count);
	for (i = 0; i < count; i++){
		flag = va_arg (ap, int);
		if(ti.flags&flag){
			WRITE_PUB_LOG("==>> %s set (error)\n",get_flag_name(flag));
			return 0;
		}
	}
	va_end (ap);
	return 1;
}

#define MUST_ON(...) if(!must_on(__VA_ARGS__))return nok;
#define MUST_OFF(...) if(!must_off(__VA_ARGS__))return nok;

#define GET_FLAGS \
	memset(&ti,0,sizeof(struct CK_TOKEN_INFO)); \
	rc=funcs->C_GetTokenInfo(SlotId, (CK_TOKEN_INFO_PTR) &ti ); \
	if(rc != CKR_OK){ \
		WRITE_PUB_LOG("==>> C_GetTokenInfo failed, rc = 0x%x\n",rc); \
		return rc; \
	} \
	WRITE_PUB_LOG("==>> C_GetTokenInfo success\n");

#define MUST_RC(await) \
	if(rc!=await) { \
		WRITE_PUB_LOG("Wrong RetCode: await [%s], got [%s]\n", get_rc_name(await), get_rc_name(rc)); \
		return rc; \
	}

CK_RV CleanUp(int finalize){
	
	CK_RV rc; // Return Code
	
	CK_UTF8CHAR *label = "Token Label";
	CK_UTF8CHAR label32[32];
	
	CK_FLAGS flags = 0; // Bit mask for what options were passed in
	CK_SESSION_HANDLE sess;

	WRITE_PUB_LOG("==>> C_InitToken...\n");
	memset(label32, ' ', sizeof(label32));
	memcpy(label32, label, strlen(label));  
	rc = funcs->C_InitToken(SlotId, sopin, strlen(sopin), label32);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitToken failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_InitToken Ok\n");
	GET_FLAGS
	MUST_OFF(1,CKF_USER_PIN_INITIALIZED)

	WRITE_PUB_LOG("==>> C_OpenSession...\n");
	flags = CKF_SERIAL_SESSION | CKF_RW_SESSION;
	rc = funcs->C_OpenSession( SlotId, flags, NULL, NULL, &sess );
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_OpenSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_OpenSession Ok\n");

	WRITE_PUB_LOG("==>> C_Login (CKU_SO)...\n");
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Login (CKU_SO) success\n");

	WRITE_PUB_LOG("==>> C_InitPIN...\n");
	rc = funcs->C_InitPIN(sess, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_InitPIN success\n");
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_INITIALIZED)
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)

	WRITE_PUB_LOG("==>> C_Logout (CKU_SO)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO) success\n");

	WRITE_PUB_LOG("==>> C_Login (CKU_USER)...\n");
	rc = funcs->C_Login(sess, CKU_USER, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) success\n");

	WRITE_PUB_LOG("==>> C_Logout (CKU_USER)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER) success\n");

	WRITE_PUB_LOG("==>> C_CloseSession...\n");
	rc = funcs->C_CloseSession(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_CloseSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_CloseSession Ok\n");

	if(!finalize)return CKR_OK;

	WRITE_PUB_LOG("==>> C_Finalize...\n");
	if(SlotList){free (SlotList);SlotList=NULL;}
	rc = funcs->C_Finalize(NULL);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Finalize failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Finalize Ok\n");

	return CKR_OK;
}


/*
http://XXX.XXX.XXX.XXX:XXXXX/issues/426#note-35

Багрепорт от юзеров.

- блокируем пин пользователя
- форматируем токен
- инициализируем юзер-пин - не проходит, CKR_PIN_LOCKED
 */

CK_RV br001(){

	CK_RV rc; // Return Code

	CK_FLAGS flags = 0; // Bit mask for what options were passed in
	CK_SESSION_HANDLE sess;
	
	int i,ft;

	WRITE_PUB_LOG("==>> init...\n");
	rc = init(api_path, &funcs, NULL);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> init failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> init Ok\n");

	WRITE_PUB_LOG("==>> get_slot_list...\n");
	rc = get_slot_list(funcs,
                    &SlotList,
                    &SlotCount);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> get_slot_list failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> get_slot_list Ok\n");

	WRITE_PUB_LOG("==>> find_slot_with_token...\n");
	rc = find_slot_with_token(funcs,
                           SlotList,
                           SlotCount,
                           &SlotId);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> find_slot_with_token failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> find_slot_with_token Ok\n");

	userpin = hw_user_pin;
	sopin = hw_so_pin;

	//форматируем карту, заводим пин юзеру, проверяем, что пин "работает"
	WRITE_PUB_LOG("\nbr001:[1/3] ~~~ FORMAT CARD, SET USER PIN, TRY TO LOGIN AS USER (ok) ~~~\n\n");

	WRITE_PUB_LOG("==>> C_OpenSession...\n");
	flags = CKF_SERIAL_SESSION | CKF_RW_SESSION;
	rc = funcs->C_OpenSession( SlotId, flags, NULL, NULL, &sess );
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_OpenSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_OpenSession Ok\n");

	WRITE_PUB_LOG("==>> C_Login (CKU_SO)...\n");
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Login (CKU_SO) success\n");

	WRITE_PUB_LOG("==>> C_InitPIN...\n");
	rc = funcs->C_InitPIN(sess, new_user_pin, strlen(new_user_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_InitPIN success\n");
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_INITIALIZED)
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)

	WRITE_PUB_LOG("==>> C_Logout (CKU_SO)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO) success\n");

	WRITE_PUB_LOG("==>> C_Login (CKU_USER)...\n");
	rc = funcs->C_Login(sess, CKU_USER, new_user_pin, strlen(new_user_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) success\n");

	WRITE_PUB_LOG("==>> C_Logout (CKU_USER)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER) success\n");

	//заходим юзером и блокируем пин, проверяем, что пин заблокировался
	WRITE_PUB_LOG("\nbr001:[2/3] ~~~ LOGIN AS USER WITH BAD PIN (till block pin), LOGIN AS USER WITH GOOD PIN (fail)  ~~~\n\n");

	i=0;ft=0;
	while(1){
		i++;
		WRITE_PUB_LOG("==>> C_Login with wrong PIN (CKU_USER)...[try %d]\n", i);
		rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
		if (rc != CKR_OK) {
			WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
			MUST_RC(CKR_PIN_INCORRECT)
		} else {
			WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
			return nok;
		}
		GET_FLAGS
		MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
		if(!ft){
			MUST_OFF(1,CKF_USER_PIN_LOCKED)
		} else {
			MUST_ON(1,CKF_USER_PIN_LOCKED)
			MUST_OFF(1,CKF_USER_PIN_FINAL_TRY)
			break;
		}
		if(ti.flags&CKF_USER_PIN_FINAL_TRY)ft=1;
	}
	WRITE_PUB_LOG("==>> user's pin flags ok\n");

	//ВНИМАНИЕ: здесь - нештатный new_user_pin (так как админ нам поменял пин)
	WRITE_PUB_LOG("==>> C_Login with true PIN (CKU_USER)...\n");
	rc = funcs->C_Login(sess, CKU_USER, new_user_pin, strlen(new_user_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_LOCKED)
	} else {
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(2,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED)
	MUST_OFF(1,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
	
	//форматируем карту, заводим пин юзеру, проверяем, что пин "работает"
	WRITE_PUB_LOG("\nbr001:[3/3] ~~~ FORMAT CARD, SET USER PIN, TRY TO LOGIN AS USER (ok) ~~~\n\n");

	WRITE_PUB_LOG("==>> C_CloseSession...\n");
	rc = funcs->C_CloseSession(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_CloseSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_CloseSession Ok\n");

	return CleanUp(1);
}

/*
http://XXX.XXX.XXX.XXX:XXXXX/issues/426#note-49

б) 
- пин заблокировать, 
- потом вызвать C_SetPin (в режиме "без логина"), т.е. меняем юзера.

 */

CK_RV br002(){

	CK_RV rc; // Return Code

	CK_FLAGS flags = 0; // Bit mask for what options were passed in
	CK_SESSION_HANDLE sess;
	
	int i,ft;

	WRITE_PUB_LOG("==>> init...\n");
	rc = init(api_path, &funcs, NULL);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> init failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> init Ok\n");

	WRITE_PUB_LOG("==>> get_slot_list...\n");
	rc = get_slot_list(funcs,
                    &SlotList,
                    &SlotCount);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> get_slot_list failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> get_slot_list Ok\n");

	WRITE_PUB_LOG("==>> find_slot_with_token...\n");
	rc = find_slot_with_token(funcs,
                           SlotList,
                           SlotCount,
                           &SlotId);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> find_slot_with_token failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> find_slot_with_token Ok\n");

	userpin = hw_user_pin;
	sopin = hw_so_pin;

	//заходим юзером, блокируем пин, проверяем, что заблокировался
	WRITE_PUB_LOG("\nbr002:[1/4] ~~~ LOGIN AS USER WITH BAD PIN (till block pin), LOGIN AS USER WITH GOOD PIN (fail)  ~~~\n\n");

	WRITE_PUB_LOG("==>> C_OpenSession...\n");
	flags = CKF_SERIAL_SESSION | CKF_RW_SESSION;
	rc = funcs->C_OpenSession( SlotId, flags, NULL, NULL, &sess );
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_OpenSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_OpenSession Ok\n");

	i=0;ft=0;
	while(1){
		i++;
		WRITE_PUB_LOG("==>> C_Login with wrong PIN (CKU_USER)...[try %d]\n", i);
		rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
		if (rc != CKR_OK) {
			WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
			MUST_RC(CKR_PIN_INCORRECT)
		} else {
			WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
			return nok;
		}
		GET_FLAGS
		MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
		if(!ft){
			MUST_OFF(1,CKF_USER_PIN_LOCKED)
		} else {
			MUST_ON(1,CKF_USER_PIN_LOCKED)
			MUST_OFF(1,CKF_USER_PIN_FINAL_TRY)
			break;
		}
		if(ti.flags&CKF_USER_PIN_FINAL_TRY)ft=1;
	}
	WRITE_PUB_LOG("==>> user's pin flags ok\n");

	//ВНИМАНИЕ: здесь - штатный userpin (никто нам пин не менял)
	WRITE_PUB_LOG("==>> C_Login with true PIN (CKU_USER)...\n");
	rc = funcs->C_Login(sess, CKU_USER, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_LOCKED)
	} else {
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(2,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED)
	MUST_OFF(1,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");

	// пытаемся установить новый пин пользователя "без логина" и при заблокированном старом пине
	WRITE_PUB_LOG("\nbr002:[2/4] ~~~ SET NEW PIN WHEN NOBODY IS LOGGED IN AND OLD PIN IS LOCKED (fail) ~~~\n\n");

	WRITE_PUB_LOG("==>> C_SetPIN (nobody logged in, old pin locked)...\n");
	rc = funcs->C_SetPIN(sess, userpin, strlen(userpin), new_user_pin, strlen(new_user_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_SetPIN failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_LOCKED) /*~1~*/
	} else {
		WRITE_PUB_LOG("==>> C_SetPIN success. It is wrong\n");
		return nok;
	}

	//заходим админом и пробуем сделать InitPIN юзеру
	WRITE_PUB_LOG("\nbr002:[3/4] ~~~ LOGIN AS SO, INIT USER PIN (fail) ~~~\n\n");

	WRITE_PUB_LOG("==>> C_Login (CKU_SO)...\n");
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Login (CKU_SO) success\n");

	WRITE_PUB_LOG("==>> C_InitPIN...\n");
	rc = funcs->C_InitPIN(sess, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	} else {
		WRITE_PUB_LOG("==>> C_InitPIN success\n");
	}
	GET_FLAGS
	MUST_ON( 1,CKF_USER_PIN_INITIALIZED)
	MUST_OFF(3,CKF_USER_PIN_FINAL_TRY,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED)

	//форматируем карту и восстанавливаем статус-кво
	WRITE_PUB_LOG("\nbr002:[4/4] ~~~ FORMAT CARD, SET USER PIN, TRY TO LOGIN AS USER (ok) ~~~\n\n");

	WRITE_PUB_LOG("==>> C_Logout (CKU_SO)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO) success\n");

	WRITE_PUB_LOG("==>> C_CloseSession...\n");
	rc = funcs->C_CloseSession(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_CloseSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_CloseSession Ok\n");

	return CleanUp(1);
}

/*
http://XXX.XXX.XXX.XXX:XXXXX/issues/426#note-49

в)
- проверка всех флагов после всех операций, которые должны на эти флаги влиять
*/

/*
http://XXX.XXX.XXX.XXX:XXXXX/issues/426#note-80

Начальное состояние: на карте заданы пины юзера и админа по умолчанию, 
неправильных предъявлений - не было.
*/
CK_RV br003(){

	CK_RV rc; // Return Code

	CK_FLAGS flags = 0; // Bit mask for what options were passed in
	CK_SESSION_HANDLE sess;
	
	int i,ft;
	CK_UTF8CHAR *label = "Token Label";
	CK_UTF8CHAR label32[32];

	WRITE_PUB_LOG("==>> init...\n");
	rc = init(api_path, &funcs, NULL);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> init failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> init Ok\n");

	WRITE_PUB_LOG("==>> get_slot_list...\n");
	rc = get_slot_list(funcs,
                    &SlotList,
                    &SlotCount);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> get_slot_list failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> get_slot_list Ok\n");

	WRITE_PUB_LOG("==>> find_slot_with_token...\n");
	rc = find_slot_with_token(funcs,
                           SlotList,
                           SlotCount,
                           &SlotId);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> find_slot_with_token failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> find_slot_with_token Ok\n");

	userpin = hw_user_pin;
	sopin = hw_so_pin;
/*
============= операции без логина, логин юзера без инит-пина, логаут без логина
- закрыть все сессии
1. InitToken - успешно
- Открыть сессию
2. SetPin - облом
3. InitPin - облом
4. Login (USER) - облом
5. Logout - облом
*/	
	WRITE_PUB_LOG("\nbr003: [1/6] ~~~ InitToken, then - operations w/o login, login w/o InitPIN, logout w/o login ~~~\n\n");
/*изначально нет никаких сессий*/
/*1*/	
	WRITE_PUB_LOG("==>> C_InitToken...\n");
	memset(label32, ' ', sizeof(label32));
	memcpy(label32, label, strlen(label));  
	rc = funcs->C_InitToken(SlotId, sopin, strlen(sopin), label32);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitToken failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_InitToken Ok\n");
	GET_FLAGS
	MUST_OFF(1,CKF_USER_PIN_INITIALIZED)
/*открыть сессию*/
	WRITE_PUB_LOG("==>> C_OpenSession...\n");
	flags = CKF_SERIAL_SESSION | CKF_RW_SESSION;
	rc = funcs->C_OpenSession( SlotId, flags, NULL, NULL, &sess );
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_OpenSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_OpenSession Ok\n");
/*2*/
	//пытаемся установить новый пин пользователя "без логина" и в случае если пин вообще не устанавливался (InitPIN не выполнялся)
	WRITE_PUB_LOG("==>> C_SetPIN (nobody logged in, no InitPIN was made)...\n");
	rc = funcs->C_SetPIN(sess, userpin, strlen(userpin), new_user_pin, strlen(new_user_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_SetPIN failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_INCORRECT) /*~2~*/
	} else {
		WRITE_PUB_LOG("==>> C_SetPIN success. It is wrong\n");
		return nok;
	}
/*3*/
	WRITE_PUB_LOG("==>> C_InitPIN (nobody logged in)...\n");
	rc = funcs->C_InitPIN(sess, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitPIN failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_USER_NOT_LOGGED_IN)
	}else{
		WRITE_PUB_LOG("==>> C_InitPIN success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(4,CKF_USER_PIN_INITIALIZED,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
/*4*/
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) (InitPIN wasn't made)...\n");
	rc = funcs->C_Login(sess, CKU_USER, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_USER_PIN_NOT_INITIALIZED)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(4,CKF_USER_PIN_INITIALIZED,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
/*5*/
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER) (user wasn't logged in)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_USER_NOT_LOGGED_IN)
	}else{
		WRITE_PUB_LOG("==>> C_Logout (CKU_USER) success. It is wrong\n");
		return nok;
	}
/*
====== проверка флагов SO-пина
1. Login (So, неверное значение пина) - облом
2. Login (So, верное значение пина) - успешно, флаг каунт_лоу сбросился
3. Logout
4. Login (So, неверное значение пина) - N раз до появления флага Final_Try
5. Login (So, верное значение пина) - успешно, флаги каунт_лоу и файнал_трай сбросились
*/
	WRITE_PUB_LOG("\nbr003: [2/6] ~~~ SO's PIN flags checking ~~~\n\n");
/*1*/	
	WRITE_PUB_LOG("==>> C_Login (CKU_SO) (bad pin)...\n");
	rc = funcs->C_Login(sess, CKU_SO, bad_pin, strlen(bad_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_INCORRECT)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_SO) success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(1,CKF_SO_PIN_COUNT_LOW)
	MUST_OFF(2,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*2*/
	WRITE_PUB_LOG("==>> C_Login (CKU_SO)...\n");
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_SO) success\n");
	}
	GET_FLAGS
	MUST_OFF(3,CKF_SO_PIN_COUNT_LOW,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*3*/
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO) success\n");
/*4*/
	//в отличие от аналогичных блоков с юзер-пином, здесь не доводим дело до блокировки админ-пина (иначе карту не форматнем потом)
	i=0;
	while(1){
		i++;
		WRITE_PUB_LOG("==>> C_Login with wrong PIN (CKU_SO)...[try %d]\n", i);
		rc = funcs->C_Login(sess, CKU_SO, bad_pin, strlen(bad_pin));
		if (rc != CKR_OK) {
			WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
			MUST_RC(CKR_PIN_INCORRECT)
		} else {
			WRITE_PUB_LOG("==>> C_Login (CKU_SO) success. It is wrong\n");
			return nok;
		}
		GET_FLAGS
		MUST_ON(1,CKF_SO_PIN_COUNT_LOW)
		MUST_OFF(1,CKF_SO_PIN_LOCKED)
		if(ti.flags&CKF_SO_PIN_FINAL_TRY){
			break;
		}
	}
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*5*/
	WRITE_PUB_LOG("==>> C_Login with true PIN (CKU_SO)...\n");
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	} else {
		WRITE_PUB_LOG("==>> C_Login (CKU_SO) success\n");
	}
	GET_FLAGS
	MUST_OFF(3,CKF_SO_PIN_COUNT_LOW,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");

/*
======= сброс флагов при форматировании
0. InitToken (So, верное значение пина) - облом
- закрыть все сессии
1. InitToken (So, неверное значение пина) - облом
2. InitToken - успешно
3. InitToken (So, неверное значение пина) - N раз до появления флага Final_Try
4. InitToken - успешно
- открыть рв-сессию
5. Login (So, неверное значение пина) - облом (нужно, чтоб проверить, что там счётчик сбрасывается по-честному, а не только во флагах)
6. Login (So, верное значение пина) - успешно, флаг каунт_лоу сбросился
*/
	WRITE_PUB_LOG("\nbr003: [3/6] ~~~ drop SO's PIN flags during format ~~~\n\n");
/*0*/
	WRITE_PUB_LOG("==>> C_InitToken (session is opened)...\n");
	memset(label32, ' ', sizeof(label32));
	memcpy(label32, label, strlen(label));  
	rc = funcs->C_InitToken(SlotId, bad_pin, strlen(bad_pin), label32);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitToken failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_SESSION_EXISTS)
	} else {
		WRITE_PUB_LOG("==>> C_InitToken success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(3,CKF_SO_PIN_COUNT_LOW,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
/*закрыть сессию*/
	WRITE_PUB_LOG("==>> C_CloseSession...\n");
	rc = funcs->C_CloseSession(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_CloseSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_CloseSession Ok\n");
/*1*/
	WRITE_PUB_LOG("==>> C_InitToken (wrong so pin)...\n");
	memset(label32, ' ', sizeof(label32));
	memcpy(label32, label, strlen(label));  
	rc = funcs->C_InitToken(SlotId, bad_pin, strlen(bad_pin), label32);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitToken failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_INCORRECT)
	} else {
		WRITE_PUB_LOG("==>> C_InitToken success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(1,CKF_SO_PIN_COUNT_LOW)
	MUST_OFF(3,CKF_USER_PIN_INITIALIZED /*т.к. не было-таки инит-пина*/,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*2*/	
	WRITE_PUB_LOG("==>> C_InitToken...\n");
	memset(label32, ' ', sizeof(label32));
	memcpy(label32, label, strlen(label));  
	rc = funcs->C_InitToken(SlotId, sopin, strlen(sopin), label32);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitToken failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_InitToken success\n");
	GET_FLAGS
	MUST_OFF(4,CKF_USER_PIN_INITIALIZED,CKF_SO_PIN_COUNT_LOW,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*3*/
	//в отличие от аналогичных блоков с юзер-пином, здесь не доводим дело до блокировки админ-пина (иначе карту не форматнем потом)
	i=0;
	while(1){
		i++;
		WRITE_PUB_LOG("==>> C_InitToken (wrong so pin)...[try %d]\n",i);
		memset(label32, ' ', sizeof(label32));
		memcpy(label32, label, strlen(label));  
		rc = funcs->C_InitToken(SlotId, bad_pin, strlen(bad_pin), label32);
		if (rc != CKR_OK) {
			WRITE_PUB_LOG("==>> C_InitToken failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
			MUST_RC(CKR_PIN_INCORRECT)
		} else {
			WRITE_PUB_LOG("==>> C_InitToken success. It is wrong\n");
			return nok;
		}
		GET_FLAGS
		MUST_ON(1,CKF_SO_PIN_COUNT_LOW)
		MUST_OFF(1,CKF_SO_PIN_LOCKED)
		if(ti.flags&CKF_SO_PIN_FINAL_TRY){
			break;
		}
	}
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*4*/
	WRITE_PUB_LOG("==>> C_InitToken...\n");
	memset(label32, ' ', sizeof(label32));
	memcpy(label32, label, strlen(label));  
	rc = funcs->C_InitToken(SlotId, sopin, strlen(sopin), label32);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitToken failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_InitToken success\n");
	GET_FLAGS
	MUST_OFF(4,CKF_USER_PIN_INITIALIZED,CKF_SO_PIN_COUNT_LOW,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
//-- открыть сессию
	WRITE_PUB_LOG("==>> C_OpenSession...\n");
	flags = CKF_SERIAL_SESSION | CKF_RW_SESSION;
	rc = funcs->C_OpenSession( SlotId, flags, NULL, NULL, &sess );
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_OpenSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_OpenSession Ok\n");
/*5*/
	WRITE_PUB_LOG("==>> C_Login (CKU_SO) (bad pin)...\n");
	rc = funcs->C_Login(sess, CKU_SO, bad_pin, strlen(bad_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_INCORRECT)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_SO) success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(1,CKF_SO_PIN_COUNT_LOW)
	MUST_OFF(2,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*6*/
	WRITE_PUB_LOG("==>> C_Login (CKU_SO)...\n");
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_SO) success\n");
	}
	GET_FLAGS
	MUST_OFF(3,CKF_SO_PIN_COUNT_LOW,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");

/*
======= инициализация юзер-пина
1. InitPin - успешно
2. Login(So, неверное значение пина) - облом, т.к. юзер уже залогинен, флаг каунт_лоу не меняется
3. Login(So, верное значение пина) - облом, т.к. юзер уже залогинен, флаг каунт_лоу не меняется
4. Login(User, неверное значение пина) - облом, т.к. юзер уже залогинен, флаг каунт_лоу не меняется
5. Login(User, верное значение пина) - облом, т.к. юзер уже залогинен, флаг каунт_лоу не меняется
6. InitPin (повторный) - успешно (???)
7. Logout - успешно
8. Login (User, неверное значение пина) - N раз до блокировки
9. Login (SO, верный пин)
10. InitPin - успешно (??? - долже быть ровно тот же результат, как и у прочих вопросов)
11. Logout
*/
	WRITE_PUB_LOG("\nbr003: [4/6] ~~~ USER PIN initialization ~~~\n\n");
/*1*/	
	WRITE_PUB_LOG("==>> C_InitPIN...\n");
	rc = funcs->C_InitPIN(sess, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_InitPIN success\n");
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_INITIALIZED)
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
/*2*/
	WRITE_PUB_LOG("==>> C_Login (CKU_SO) (bad pin) (SO already logged in)...\n");
	rc = funcs->C_Login(sess, CKU_SO, bad_pin, strlen(bad_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_USER_ALREADY_LOGGED_IN)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_SO) success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(3,CKF_SO_PIN_COUNT_LOW,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*3*/
	WRITE_PUB_LOG("==>> C_Login (CKU_SO) (true pin) (SO already logged in)...\n");
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_USER_ALREADY_LOGGED_IN)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_SO) success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(3,CKF_SO_PIN_COUNT_LOW,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*4*/
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) (bad pin) (another user (SO) already logged in)...\n");
	rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_USER_ANOTHER_ALREADY_LOGGED_IN)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*5*/
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) (true pin) (another user (SO) already logged in)...\n");
	rc = funcs->C_Login(sess, CKU_USER, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_USER_ANOTHER_ALREADY_LOGGED_IN)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*6*/
	WRITE_PUB_LOG("==>> C_InitPIN...\n");
	rc = funcs->C_InitPIN(sess, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_InitPIN success\n");
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_INITIALIZED)
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
/*7*/
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO) success\n");
/*8*/
	i=0;ft=0;
	while(1){
		i++;
		WRITE_PUB_LOG("==>> C_Login with wrong PIN (CKU_USER)...[try %d]\n", i);
		rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
		if (rc != CKR_OK) {
			WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
			MUST_RC(CKR_PIN_INCORRECT)
		} else {
			WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
			return nok;
		}
		GET_FLAGS
		MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
		if(!ft){
			MUST_OFF(1,CKF_USER_PIN_LOCKED)
		} else {
			MUST_ON(1,CKF_USER_PIN_LOCKED)
			MUST_OFF(1,CKF_USER_PIN_FINAL_TRY)
			break;
		}
		if(ti.flags&CKF_USER_PIN_FINAL_TRY)ft=1;
	}
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
	MUST_OFF(3,CKF_SO_PIN_COUNT_LOW,CKF_SO_PIN_LOCKED,CKF_SO_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> so's pin flags ok\n");
/*9*/
	WRITE_PUB_LOG("==>> C_Login (CKU_SO)...\n");
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_SO) success\n");
	}
/*10*/
	WRITE_PUB_LOG("==>> C_InitPIN (user pin blocked)...\n");
	rc = funcs->C_InitPIN(sess, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	} else {
		WRITE_PUB_LOG("==>> C_InitPIN success\n");
	}
	GET_FLAGS
	MUST_ON( 1,CKF_USER_PIN_INITIALIZED)
	MUST_OFF(3,CKF_USER_PIN_FINAL_TRY,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED)
/*11*/
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO) success\n");
/*форматнем и установим пин юзеру*/
	//------------------------------------------------------------------
	WRITE_PUB_LOG("==>> C_CloseSession...\n");
	rc = funcs->C_CloseSession(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_CloseSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_CloseSession Ok\n");
	
	CleanUp(0);
	
	WRITE_PUB_LOG("==>> C_OpenSession...\n");
	flags = CKF_SERIAL_SESSION | CKF_RW_SESSION;
	rc = funcs->C_OpenSession( SlotId, flags, NULL, NULL, &sess );
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_OpenSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_OpenSession Ok\n");
	//------------------------------------------------------------------

/*
======= проверка флагов юзер-пина
0. Login (User, неверная длина пина) - облом, но флаги не меняются
1. Login (User, неверное значение пина) - облом
2. Login (User, верное значение пина) - успешно, флаг каунт_лоу сбросился
3. Logout
4. Login (User, неверное значение пина) - N раз до появления флага Final_Try
5. Login (User, верное значение пина) - успешно, флаги каунт_лоу и файнал_трай сбросились
6. SetPin (User) - успешно
7. Logout
*/
	WRITE_PUB_LOG("\nbr003: [5/6] ~~~ Check USER's PIN flags ~~~\n\n");
/*0*/
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) (wrong pin length)...\n");
	rc = funcs->C_Login(sess, CKU_USER, userpin, strlen(userpin)*10);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_LEN_RANGE)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It's wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*1*/
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) (wrong pin)...\n");
	rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_INCORRECT)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It's wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
	MUST_OFF(2,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*2*/
	WRITE_PUB_LOG("==>> C_Login (CKU_USER)...\n");
	rc = funcs->C_Login(sess, CKU_USER, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) success\n");
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*3*/
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER) success\n");
/*4*/
	i=0;
	while(1){
		i++;
		WRITE_PUB_LOG("==>> C_Login with wrong PIN (CKU_USER)...[try %d]\n", i);
		rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
		if (rc != CKR_OK) {
			WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
			MUST_RC(CKR_PIN_INCORRECT)
		} else {
			WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
			return nok;
		}
		GET_FLAGS
		MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
		MUST_OFF(1,CKF_USER_PIN_LOCKED)
		if(ti.flags&CKF_USER_PIN_FINAL_TRY){
			break;
		}
	}
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*5*/
	WRITE_PUB_LOG("==>> C_Login with true PIN (CKU_USER)...\n");
	rc = funcs->C_Login(sess, CKU_USER, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	} else {
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success\n");
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*6*/
	WRITE_PUB_LOG("==>> C_SetPIN ...\n");
	rc = funcs->C_SetPIN(sess, userpin, strlen(userpin), userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_SetPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	} else {
		WRITE_PUB_LOG("==>> C_SetPIN success\n");
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*7*/
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER) success\n");

/*
======== флаги юзер-пина без логина юзера
-4. Login (User, неверное значение пина) - облом
-3. SetPin (User, неверная длина NewPin) - облом; флаги - не восстанавливаются...
-2. восстанавливаем флаг через логин-логаут
-1. SetPin (User, неверная длина OldPin) - облом; флаги не меняются... СЕЙЧАС ТУТ ОШИБКА (сейчас идёт попытка предъявления и флаг меняется)!
0. SetPin (User, old_pin - неверный, newPin - неверной длины) - облом, флаги не меняются
1. Login (User, неверное значение пина) - облом
2. SetPin (User) - да-да, без логина; смотрим, сбросило ли это флаги - мне и вправду это любопытно;
3. SetPin (User, в качестке old_pin - неверное значение)
4. Login (User, неверное значение пина) - N раз до появления флага Final_Try
5. SetPin (User) - смотрим, сбросило ли это флаги;
6. Login (User, неверное значение пина) - N раз до появления флага Final_Try
7. Login(So, верное значение пина)
8. InitPin - успешно (???)
*/
	WRITE_PUB_LOG("\nbr003: [6/6] ~~~ Check USER's PIN flags w/o user login ~~~\n\n");
/*-4*/
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) (wrong pin)...\n");
	rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_INCORRECT)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It's wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
	MUST_OFF(2,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*-3*/
	WRITE_PUB_LOG("==>> C_SetPIN (wrong new pin length) ...\n");
	rc = funcs->C_SetPIN(sess, userpin, strlen(userpin), userpin, strlen(userpin)*10);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_SetPIN failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_LEN_RANGE)
	} else {
		WRITE_PUB_LOG("==>> C_SetPIN (CKU_USER) success. It's wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
	MUST_OFF(2,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*-2*/
	WRITE_PUB_LOG("==>> C_Login with true PIN (CKU_USER)...\n");
	rc = funcs->C_Login(sess, CKU_USER, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	} else {
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success\n");
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*-1*/
	WRITE_PUB_LOG("==>> C_SetPIN (wrong old pin length) ...\n");
	rc = funcs->C_SetPIN(sess, userpin, strlen(userpin)*10, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_SetPIN failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_LEN_RANGE)
	} else {
		WRITE_PUB_LOG("==>> C_SetPIN (CKU_USER) success. It's wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*0*/
	WRITE_PUB_LOG("==>> C_SetPIN (wrong old pin, wrong new pin length) ...\n");
	rc = funcs->C_SetPIN(sess, bad_pin, strlen(bad_pin), userpin, strlen(userpin)*10);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_SetPIN failed (OK), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_LEN_RANGE)
	} else {
		WRITE_PUB_LOG("==>> C_SetPIN (CKU_USER) success. It's wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*разлогинимся*/
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER) success\n");
/*1*/
	WRITE_PUB_LOG("==>> C_Login (CKU_USER) (wrong pin)...\n");
	rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_INCORRECT)
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It's wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
	MUST_OFF(2,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*2*/
	// пытаемся установить новый (на самом деле старый) пин "без логина" при не заблокированном старом пине
	WRITE_PUB_LOG("==>> C_SetPIN (nobody logged in, pin not locked)...\n");
	rc = funcs->C_SetPIN(sess, userpin, strlen(userpin), userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_SetPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	} else {
		WRITE_PUB_LOG("==>> C_SetPIN success\n"); /*~3~*/
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*3*/
	//устанавливаем снова старый пин, но в качестве старого пина подаем бэдпин
	WRITE_PUB_LOG("==>> C_SetPIN (nobody logged in,  oldpin==bad_pin)...\n");
	rc = funcs->C_SetPIN(sess, bad_pin, strlen(bad_pin), userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_SetPIN failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		MUST_RC(CKR_PIN_INCORRECT)
	} else {
		WRITE_PUB_LOG("==>> C_SetPIN success. It's wrong\n");
		return nok;
	}
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
	MUST_OFF(2,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*4*/
	i=0;
	while(1){
		i++;
		WRITE_PUB_LOG("==>> C_Login with wrong PIN (CKU_USER)...[try %d]\n", i);
		rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
		if (rc != CKR_OK) {
			WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
			MUST_RC(CKR_PIN_INCORRECT)
		} else {
			WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
			return nok;
		}
		GET_FLAGS
		MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
		MUST_OFF(1,CKF_USER_PIN_LOCKED)
		if(ti.flags&CKF_USER_PIN_FINAL_TRY){
			break;
		}
	}
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*5*/
	// пытаемся установить новый (на самом деле старый) пин "без логина" при не заблокированном старом пине
	WRITE_PUB_LOG("==>> C_SetPIN (nobody logged in, pin not locked)...\n");
	rc = funcs->C_SetPIN(sess, userpin, strlen(userpin), userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_SetPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	} else {
		WRITE_PUB_LOG("==>> C_SetPIN success\n");
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*6*/
	i=0;
	while(1){
		i++;
		WRITE_PUB_LOG("==>> C_Login with wrong PIN (CKU_USER)...[try %d]\n", i);
		rc = funcs->C_Login(sess, CKU_USER, bad_pin, strlen(bad_pin));
		if (rc != CKR_OK) {
			WRITE_PUB_LOG("==>> C_Login failed (Ok), rc: %s (0x%08X)\n", get_rc_name(rc),rc);
			MUST_RC(CKR_PIN_INCORRECT)
		} else {
			WRITE_PUB_LOG("==>> C_Login (CKU_USER) success. It is wrong\n");
			return nok;
		}
		GET_FLAGS
		MUST_ON(1,CKF_USER_PIN_COUNT_LOW)
		MUST_OFF(1,CKF_USER_PIN_LOCKED)
		if(ti.flags&CKF_USER_PIN_FINAL_TRY){
			break;
		}
	}
	WRITE_PUB_LOG("==>> user's pin flags ok\n");
/*7*/
	WRITE_PUB_LOG("==>> C_Login (CKU_SO)...\n");
	rc = funcs->C_Login(sess, CKU_SO, sopin, strlen(sopin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}else{
		WRITE_PUB_LOG("==>> C_Login (CKU_SO) success\n");
	}
/*8*/
	WRITE_PUB_LOG("==>> C_InitPIN...\n");
	rc = funcs->C_InitPIN(sess, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_InitPIN failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_InitPIN success\n");
	GET_FLAGS
	MUST_ON(1,CKF_USER_PIN_INITIALIZED)
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)

	WRITE_PUB_LOG("==>> C_Logout (CKU_SO)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_SO) success\n");

	WRITE_PUB_LOG("==>> C_Login with true PIN (CKU_USER)...\n");
	rc = funcs->C_Login(sess, CKU_USER, userpin, strlen(userpin));
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Login failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	} else {
		WRITE_PUB_LOG("==>> C_Login (CKU_USER) success\n");
	}
	GET_FLAGS
	MUST_OFF(3,CKF_USER_PIN_COUNT_LOW,CKF_USER_PIN_LOCKED,CKF_USER_PIN_FINAL_TRY)
	WRITE_PUB_LOG("==>> user's pin flags ok\n");

	//зачистка

	WRITE_PUB_LOG("==>> C_Logout (CKU_USER)...\n");
	rc = funcs->C_Logout(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_Logout failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_Logout (CKU_USER) success\n");

	WRITE_PUB_LOG("==>> C_CloseSession...\n");
	rc = funcs->C_CloseSession(sess);
	if (rc != CKR_OK) {
		WRITE_PUB_LOG("==>> C_CloseSession failed, rc: %s (0x%08X)\n", get_rc_name(rc),rc);
		return rc;
	}
	WRITE_PUB_LOG("==>> C_CloseSession Ok\n");

	return CleanUp(1);
}

typedef CK_RV (*PF)();
//по мере написания функций-тестов, будем их добавлять сюда
PF arr_fun[]={&br001,&br002,&br003};
const int sz_arr_fun = sizeof arr_fun / sizeof *arr_fun;

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4996 )
#endif
int main(int argc, char *argv[]){
	int k;
	CK_RV rc;
	PF * fun = arr_fun;
	WRITE_PUB_LOG("Run All BugReport's Tests\n");
	for(k=0;k<sz_arr_fun;k++){
		WRITE_PUB_LOG("Run br%03d test\n",k+1);
		rc=fun[k]();
		if(rc!=CKR_OK){
			WRITE_PUB_LOG("Not Ok\n");
			return 0;
		}
		WRITE_PUB_LOG("Ok\n");
	}
	return 0;
}
#ifdef WIN32
#pragma warning( pop )
#endif

