/*************************************************************************
* Rutoken                                                                *
* Copyright (c) 2003-2017, CJSC Aktiv-Soft. All rights reserved.         *
* Подробная информация:  http://www.rutoken.ru                           *
*------------------------------------------------------------------------*
* Пример работы с Рутокен при помощи библиотеки PKCS#11 на языке C       *
*------------------------------------------------------------------------*
* Использование команд шифрования/расшифрования на ключе RSA:            *
*  - установление соединения с Рутокен в первом доступном слоте;         *
*  - выполнение аутентификации Пользователя;                             *
*  - шифрование сообщения на демонстрационном ключе RSA;                 *
*  - расшифрование зашифрованнного сообщения на демонстрационном ключе;  *
*  - сброс прав доступа Пользователя на Рутокен и закрытие соединения    *
*    с Рутокен.                                                          *
*------------------------------------------------------------------------*
* Пример использует объекты, созданные в памяти Рутокен примером         *
* CreateRSA.                                                             *
*************************************************************************/

#include <Common.h>

/*************************************************************************
* Шаблон для поиска открытого ключа RSA                                  *
*************************************************************************/
CK_ATTRIBUTE rsaPublicKeyTemplate[] =
{
	{ CKA_ID, &keyPairIdRsa, sizeof(keyPairIdRsa) - 1},     // Идентификатор ключа
	{ CKA_KEY_TYPE, &keyTypeRsa, sizeof(keyTypeRsa) },      // Тип ключа - RSA
	{ CKA_CLASS, &publicKeyObject, sizeof(publicKeyObject)} // Класс - открытый ключ
};

/*************************************************************************
* Шаблон для поиска закрытого ключа RSA                                  *
*************************************************************************/
CK_ATTRIBUTE rsaPrivateKeyTemplate[] =
{
	{ CKA_ID, &keyPairIdRsa, sizeof(keyPairIdRsa) - 1},         // Идентификатор ключа
	{ CKA_KEY_TYPE, &keyTypeRsa, sizeof(keyTypeRsa) },          // Тип ключа - RSA
	{ CKA_CLASS, &privateKeyObject, sizeof(privateKeyObject)}   // Класс - закрытый ключ
};

/*************************************************************************
* Данные для шифрования                                                  *
*************************************************************************/
CK_BYTE data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };

int main(void)
{
	HMODULE module;                                                         // Хэндл загруженной библиотеки PKCS#11
	CK_SESSION_HANDLE session;                                              // Хэндл открытой сессии

	CK_FUNCTION_LIST_PTR functionList;                                      // Указатель на список функций PKCS#11, хранящийся в структуре CK_FUNCTION_LIST
	CK_C_GetFunctionList getFunctionList;                                   // Указатель на функцию C_GetFunctionList

	CK_SLOT_ID_PTR slots;                                                   // Массив идентификаторов слотов
	CK_ULONG slotCount;                                                     // Количество идентификаторов слотов в массиве

	CK_OBJECT_HANDLE_PTR pubKeys;                                           // Массив хэндлов открытых ключей
	CK_ULONG pubKeysCount;                                                  // Количество хэндлов открытых ключей в массиве

	CK_BYTE_PTR encryptedPKCS11;                                            // Указатель на буфер, содержащий зашифрованные в PKCS11 данные
	CK_ULONG encryptedPKCS11Size;                                           // Размер буфера с данными, зашифрованными в PKCS11, в байтах

	CK_OBJECT_HANDLE_PTR privKeys;                                          // Массив хэндлов закрытых ключей
	CK_ULONG privKeysCount;                                                 // Количество хэндлов закрытых ключей в массиве

	CK_BYTE_PTR decryptedPKCS11;                                            // Указатель на буфер, содержащий зашифрованные в PKCS11 и расшифрованные в PKCS11 данные
	CK_ULONG decryptedPKCS11Size;                                           // Размер буфера с данными, зашифрованными в PKCS11 и расшифрованные в PKCS11, в байтах

	CK_RV rv;                                                               // Код возврата. Могут быть возвращены только ошибки, определенные в PKCS#11
	int r;                                                                  // Код возврата для функций, возвращающих int

	int errorCode = 1;                                                      // Флаг ошибки

#ifdef _WIN32
	HCRYPTPROV msProv;                                                      // Хэндл криптопровайдера
	HCRYPTKEY msKey;                                                        // Хэндл ключа
	BLOBHEADER blobHeader;                                                  // Структура данных типа BLOBHEADER, является частью открытого ключа в формате MS CryptoAPI
	RSAPUBKEY pubKey;                                                       // Структура данных типа RSAPUBKEY, является частью открытого ключа в формате MS CryptoAPI

	CK_ATTRIBUTE modulusAttr = { CKA_MODULUS, NULL_PTR, 0 };                // Структура данных типа CK_ATTRIBUTE для хранения значения атрибута CKA_MODULUS
	CK_ATTRIBUTE pubExponentAttr = { CKA_PUBLIC_EXPONENT, NULL_PTR, 0 };    // Структура данных типа CK_ATTRIBUTE для хранения значения атрибута CKA_PUBLIC_EXPONENT

	DWORD exponent;                                                         // Экспонента
	DWORD dataSize;                                                         // Размер данных
	PBYTE capiPubKey;                                                       // Указатель на буфер с открытым ключом в формате MS CryptoAPI
	DWORD capiPubKeySize;                                                   // Размер буфера с открытым ключом в формате MS CryptoAPI, в байтах

	PBYTE encryptedCapi;                                                    // Указатель на буфер, содержащий зашифрованные в CryptoAPI данные
	DWORD encryptedCapiSize;                                                // Размер буфера с данными, зашифрованными в CryptoAPI, в байтах

	CK_BYTE_PTR decryptedCapi;                                              // Указатель на буфер, содержащий зашифрованные в MS CryptoAPI и расшифрованные в PKCS11 данные
	CK_ULONG decryptedCapiSize;                                             // Размер буфера с данными, зашифрованными в CryptoAPI и расшифрованными в PKCS11, в байтах

	CK_ULONG i;                                                             // Вспомогательная переменная-счетчик в циклах
	CK_BYTE temp;                                                           // Переменная для хранения временного значения
#endif

	/*************************************************************************
	* Выполнить действия для начала работы с библиотекой PKCS#11             *
	*************************************************************************/
	printf("Initialization...\n");

	/*************************************************************************
	* Загрузить библиотеку                                                   *
	*************************************************************************/
	module = LoadLibrary(PKCS11ECP_LIBRARY_NAME);
	CHECK(" LoadLibrary", module != NULL, exit);

	/*************************************************************************
	* Получить адрес функции запроса структуры с указателями на функции      *
	*************************************************************************/
	getFunctionList = (CK_C_GetFunctionList)GetProcAddress(module, "C_GetFunctionList");
	CHECK(" GetProcAddress", getFunctionList != NULL, unload_pkcs11);

	/*************************************************************************
	* Получить структуру с указателями на функции                            *
	*************************************************************************/
	rv = getFunctionList(&functionList);
	CHECK_AND_LOG(" Get function list", rv == CKR_OK, rvToStr(rv), unload_pkcs11);

	/*************************************************************************
	* Инициализировать библиотеку                                            *
	*************************************************************************/
	rv = functionList->C_Initialize(NULL_PTR);
	CHECK_AND_LOG(" C_Initialize", rv == CKR_OK, rvToStr(rv), unload_pkcs11);

	/*************************************************************************
	* Получить количество слотов c подключенными токенами                    *
	*************************************************************************/
	rv = functionList->C_GetSlotList(CK_TRUE, NULL_PTR, &slotCount);
	CHECK_AND_LOG(" C_GetSlotList (number of slots)", rv == CKR_OK, rvToStr(rv), finalize_pkcs11);

	CHECK_AND_LOG(" Checking available tokens", slotCount > 0, " No tokens available", finalize_pkcs11);

	/*************************************************************************
	* Получить список слотов c подключенными токенами                        *
	*************************************************************************/
	slots = (CK_SLOT_ID_PTR)malloc(slotCount * sizeof(CK_SLOT_ID));
	CHECK("  Memory allocation for slots", slots != NULL_PTR, finalize_pkcs11);

	rv = functionList->C_GetSlotList(CK_TRUE, slots, &slotCount);
	CHECK_AND_LOG(" C_GetSlotList", rv == CKR_OK, rvToStr(rv), free_slots);
	printf(" Slots available: %d\n", (int)slotCount);

	/*************************************************************************
	* Открыть RW сессию в первом доступном слоте                             *
	*************************************************************************/
	rv = functionList->C_OpenSession(slots[0], CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &session);
	CHECK_AND_LOG(" C_OpenSession", rv == CKR_OK, rvToStr(rv), free_slots);

	/*************************************************************************
	* Выполнить аутентификацию Пользователя                                  *
	*************************************************************************/
	rv = functionList->C_Login(session, CKU_USER, USER_PIN, USER_PIN_LEN);
	CHECK_AND_LOG(" C_Login", rv == CKR_OK, rvToStr(rv), close_session);
	printf("Initialization has been completed successfully.\n");

	/*************************************************************************
	* Зашифровать данные по алгоритму RSA с использованием PKCS11            *
	*************************************************************************/
	printf("\nEncrypting via PKCS11...\n");
	/*************************************************************************
	* Получить массив хэндлов открытых ключей                                *
	*************************************************************************/
	printf(" Getting public key...\n");
	r = findObjects(functionList, session, rsaPublicKeyTemplate, arraysize(rsaPublicKeyTemplate), &pubKeys, &pubKeysCount);
	CHECK(" findObjects", r == 0, logout);

	CHECK_AND_LOG(" Checking number of keys found", pubKeysCount > 0, "No objects found\n", logout);

	/*************************************************************************
	* Инициализировать операцию шифрования                                   *
	*************************************************************************/
	rv = functionList->C_EncryptInit(session, &rsaEncDecMech, pubKeys[0]);
	CHECK_AND_LOG(" C_EncryptInit", rv == CKR_OK, rvToStr(rv), free_pubKeys);

	/*************************************************************************
	* Зашифровать данные                                                     *
	*************************************************************************/
	rv = functionList->C_Encrypt(session, data, arraysize(data), NULL_PTR, &encryptedPKCS11Size);
	CHECK_AND_LOG(" C_Encrypt (get data size)", rv == CKR_OK, rvToStr(rv), free_pubKeys);

	encryptedPKCS11 = (CK_BYTE_PTR)malloc(encryptedPKCS11Size * sizeof(CK_BYTE));
	CHECK("  Memory allocation for encrypted data", encryptedPKCS11 != NULL_PTR, free_pubKeys);

	rv = functionList->C_Encrypt(session, data, arraysize(data), encryptedPKCS11, &encryptedPKCS11Size);
	CHECK_AND_LOG(" C_Encrypt", rv == CKR_OK, rvToStr(rv), free_encryptedPKCS11);

	/*************************************************************************
	* Распечатать буфер, содержащий зашифрованные данные                     *
	*************************************************************************/
	printf(" Encrypted buffer is:\n");
	printHex(encryptedPKCS11, encryptedPKCS11Size);

	printf("Encryption has been completed successfully.\n");

	/*************************************************************************
	* Расшифровать по алгоритму RSA данные, ранее зашифрованные              *
	* на PKCS#11, с использованием PKCS#11                                   *
	*************************************************************************/
	printf("\nDecrypting data, previously encrypted via PKCS#11, via PKCS#11...\n");
	/*************************************************************************
	* Получить массив хэндлов закрытых ключей                                *
	*************************************************************************/
	printf(" Getting private key...\n");
	r = findObjects(functionList, session, rsaPrivateKeyTemplate, arraysize(rsaPrivateKeyTemplate), &privKeys, &privKeysCount);
	CHECK(" findObjects", r == 0, free_encryptedPKCS11);

	CHECK_AND_LOG(" Checking number of keys found", privKeysCount > 0, "No objects found\n", free_encryptedPKCS11);

	/*************************************************************************
	* Инициализировать операцию расшифрования                                *
	*************************************************************************/
	rv = functionList->C_DecryptInit(session, &rsaEncDecMech, privKeys[0]);
	CHECK_AND_LOG(" C_DecryptInit", rv == CKR_OK, rvToStr(rv), free_privKeys);

	/*************************************************************************
	* Расшифровать данные                                                    *
	*************************************************************************/
	rv = functionList->C_Decrypt(session, encryptedPKCS11, encryptedPKCS11Size, NULL_PTR, &decryptedPKCS11Size);
	CHECK_AND_LOG(" C_Decrypt (get data size)", rv == CKR_OK, rvToStr(rv), free_privKeys);

	decryptedPKCS11 = (CK_BYTE_PTR)malloc(decryptedPKCS11Size * sizeof(CK_BYTE));
	CHECK("  Memory allocation for decrypted data", decryptedPKCS11 != NULL_PTR, free_privKeys);

	rv = functionList->C_Decrypt(session, encryptedPKCS11, encryptedPKCS11Size, decryptedPKCS11, &decryptedPKCS11Size);
	CHECK_AND_LOG(" C_Decrypt", rv == CKR_OK, rvToStr(rv), free_decryptedPKCS11);

	/*************************************************************************
	* Распечатать буфер, содержащий расшифрованные данные                    *
	*************************************************************************/
	printf(" Decrypted buffer is:\n");
	printHex(decryptedPKCS11, decryptedPKCS11Size);

	printf("Decryption has been completed successfully.\n\n");
	/*************************************************************************
	* Сравнить исходные данные с расшифрованными                             *
	*************************************************************************/
	r = (decryptedPKCS11Size == arraysize(data)) && (memcmp(data, decryptedPKCS11, decryptedPKCS11Size) == 0);
	CHECK("Compare decrypted and plain texts", r, free_decryptedPKCS11);

	/*************************************************************************
	* Следующая часть примера использует интерфейс CryptoAPI, доступный      *
	* только на ОС семейства Windows                                         *
	*************************************************************************/

#ifndef _WIN32
	/*************************************************************************
	* Выставить признак успешного завершения программы                       *
	*************************************************************************/
	errorCode = 0;
	printf("\nFinalizing... \n");
	goto free_decryptedPKCS11;
#else //_WIN32

	/*************************************************************************
	* Зашифровать данные по алгоритму RSA с использованием MS CryptoAPI      *
	*************************************************************************/
	printf("\nEncrypting via MS CryptoAPI...\n\n");
	/*************************************************************************
	* Сформировать открытый ключ MS CryptoAPI на основе открытого            *
	* ключа PKCS11                                                           *
	*************************************************************************/
	printf("Making CryptoAPI key in MS format...\n");
	/*************************************************************************
	* Получить значение атрибута CKA_MODULUS                                 *
	*************************************************************************/
	rv = functionList->C_GetAttributeValue(session, pubKeys[0], &modulusAttr, 1);
	CHECK_AND_LOG(" Get modulus value size", rv == CKR_OK, rvToStr(rv), free_decryptedPKCS11);

	modulusAttr.pValue = (CK_BYTE_PTR)malloc(modulusAttr.ulValueLen * sizeof(CK_BYTE));
	CHECK("  Memory allocation for modulus data", modulusAttr.pValue != NULL_PTR, free_decryptedPKCS11);

	rv = functionList->C_GetAttributeValue(session, pubKeys[0], &modulusAttr, 1);
	CHECK_AND_LOG(" Get modulus value size", rv == CKR_OK, rvToStr(rv), free_modulus);

	/*************************************************************************
	* Получить значение атрибута CKA_PUBLIC_EXPONENT                         *
	*************************************************************************/
	rv = functionList->C_GetAttributeValue(session, pubKeys[0], &pubExponentAttr, 1);
	CHECK_AND_LOG(" Get exponent value size", rv == CKR_OK, rvToStr(rv), free_modulus);

	pubExponentAttr.pValue = (CK_BYTE_PTR)malloc(pubExponentAttr.ulValueLen * sizeof(CK_BYTE));
	CHECK("  Memory allocation for exponent data", pubExponentAttr.pValue != NULL_PTR, free_modulus);

	rv = functionList->C_GetAttributeValue(session, pubKeys[0], &pubExponentAttr, 1);
	CHECK_AND_LOG(" Get exponent value size", rv == CKR_OK, rvToStr(rv), free_exponent);

	/*************************************************************************
	* Инвертировать порядок байтов в буфере, содержащем значение             *
	* атрибута CKA_MODULUS                                                   *
	*************************************************************************/
	for (i = 0; i < modulusAttr.ulValueLen / 2; ++i) {
		temp = ((CK_BYTE_PTR)modulusAttr.pValue)[i];
		((CK_BYTE_PTR)modulusAttr.pValue)[i] = ((CK_BYTE_PTR)modulusAttr.pValue)[modulusAttr.ulValueLen - i - 1];
		((CK_BYTE_PTR)modulusAttr.pValue)[modulusAttr.ulValueLen - i - 1] = temp;
	}

	/*************************************************************************
	* Сформировать открытый ключ в формате MS CryptoAPI                      *
	*************************************************************************/
	CHECK(" Check modulus length", modulusAttr.ulValueLen % 64 == 0, free_exponent);
	CHECK(" Check exponent length", pubExponentAttr.ulValueLen <= sizeof(exponent), free_exponent);

	exponent = 0;
	memcpy(&exponent, pubExponentAttr.pValue, pubExponentAttr.ulValueLen);

	/*************************************************************************
	* Заполнить структуру данных типа BLOBHEADER                             *
	*************************************************************************/
	blobHeader.bType = PUBLICKEYBLOB;
	blobHeader.bVersion = CUR_BLOB_VERSION;
	blobHeader.aiKeyAlg = CALG_RSA_KEYX;
	blobHeader.reserved = 0;

	/*************************************************************************
	* Заполнить структуру данных типа RSAPUBKEY                              *
	*************************************************************************/
	pubKey.bitlen = (DWORD)modulusAttr.ulValueLen * 8;
	pubKey.magic = RSAENH_MAGIC_RSA1;
	pubKey.pubexp = exponent;

	/*************************************************************************
	* Сформировать открытый ключ в формате MS CryptoAPI                      *
	*************************************************************************/
	capiPubKeySize = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + (DWORD)modulusAttr.ulValueLen;
	capiPubKey = (PBYTE)malloc(capiPubKeySize * sizeof(BYTE));
	CHECK(" Memory allocation for CryptoAPI public key data", capiPubKey != NULL, free_exponent);

	memcpy(capiPubKey, &blobHeader, sizeof(BLOBHEADER));
	memcpy(capiPubKey + sizeof(BLOBHEADER), &pubKey, sizeof(RSAPUBKEY));
	memcpy(capiPubKey + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), modulusAttr.pValue, modulusAttr.ulValueLen);

	printf("Public key in MS format has been retrieved successfully.\n");

	printf("\nAcquiring CryptoAPI key handle...\n");
	/*************************************************************************
	* Открыть контекст криптопровайдера                                      *
	*************************************************************************/
	r = CryptAcquireContext(&msProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
	CHECK(" CryptAcquireContext", r, free_capiPubKey);

	/*************************************************************************
	* Импортировать открытый ключ в контекст криптопровайдера                *
	*************************************************************************/
	r = CryptImportKey(msProv, capiPubKey, capiPubKeySize, 0, 0, &msKey);
	CHECK(" CryptImportKey", r, release_context);

	printf("CryptoAPI key handle acquired...\n");

	/*************************************************************************
	* Зашифровать данные                                                     *
	*************************************************************************/
	printf("\nEncrypting via CryptoAPI...\n");
	encryptedCapiSize = arraysize(data);
	r = CryptEncrypt(msKey, 0, TRUE, 0, NULL, &encryptedCapiSize, 0);
	CHECK(" CryptEncrypt (get encrypted size)", r, destroy_key);

	encryptedCapi = (PBYTE)malloc(encryptedCapiSize * sizeof(BYTE));
	CHECK("  Memory allocation for encrypted data", encryptedCapi != NULL, destroy_key);

	memcpy(encryptedCapi, data, arraysize(data));
	dataSize = arraysize(data);

	r = CryptEncrypt(msKey, 0, TRUE, 0, encryptedCapi, &dataSize, encryptedCapiSize);
	CHECK(" CryptEncrypt (get encrypted size)", r, free_encryptedCapi);

	/*************************************************************************
	* Инвертировать порядок байтов в буфере, содержащем шифротекст           *
	*************************************************************************/
	for (i = 0; i < (encryptedCapiSize / 2); ++i) {
		temp = encryptedCapi[i];
		encryptedCapi[i] = encryptedCapi[encryptedCapiSize - i - 1];
		encryptedCapi[encryptedCapiSize - i - 1] = temp;
	}

	/*************************************************************************
	* Распечатать буфер, содержащий зашифрованные данные                     *
	*************************************************************************/
	printf(" Encrypted buffer text:\n");
	printHex(encryptedCapi, encryptedPKCS11Size);

	printf("Encryption has been completed successfully.\n");

	/*************************************************************************
	* Расшифровать по алгоритму RSA данные, ранее зашифрованные              *
	* на MS CryptoAPI, с использованием PKCS#11                              *
	*************************************************************************/

	printf("\nDecrypting data, previously encrypted via CryptoAPI, via PKCS#11...\n");
	/*************************************************************************
	* Инициализировать операцию расшифрования                                *
	*************************************************************************/
	rv = functionList->C_DecryptInit(session, &rsaEncDecMech, privKeys[0]);
	CHECK_AND_LOG(" C_DecryptInit", rv == CKR_OK, rvToStr(rv), free_encryptedCapi);

	/*************************************************************************
	* Расшифровать данные                                                    *
	*************************************************************************/
	rv = functionList->C_Decrypt(session, encryptedCapi, encryptedCapiSize, NULL_PTR, &decryptedCapiSize);
	CHECK_AND_LOG(" C_Decrypt (get data size)", rv == CKR_OK, rvToStr(rv), free_encryptedCapi);

	decryptedCapi = (CK_BYTE_PTR)malloc(decryptedCapiSize * sizeof(CK_BYTE));
	CHECK("  Memory allocation for decrypted data", decryptedCapi != NULL_PTR, free_encryptedCapi);

	rv = functionList->C_Decrypt(session, encryptedCapi, encryptedCapiSize, decryptedCapi, &decryptedCapiSize);
	CHECK_AND_LOG(" C_Decrypt", rv == CKR_OK, rvToStr(rv), free_decryptedCapi);

	/*************************************************************************
	* Распечатать буфер, содержащий расшифрованные данные                    *
	*************************************************************************/
	printf(" Decrypted buffer is:\n");
	printHex(decryptedCapi, decryptedCapiSize);

	printf("Decryption has been completed successfully.\n\n");
	/*************************************************************************
	* Сравнить исходные данные с расшифрованными                             *
	*************************************************************************/
	r = (decryptedCapiSize == arraysize(data)) && (memcmp(data, decryptedCapi, decryptedCapiSize) == 0);
	CHECK("Compare decrypted and plain texts", r, free_decryptedCapi);

	errorCode = 0;
	/*************************************************************************
	* Выполнить действия для завершения работы с библиотекой PKCS#11         *
	*************************************************************************/
	printf("\nFinalizing... \n");

	/*************************************************************************
	* Очистить память, выделенную под зашифрованные и расшифрованные данные, *
	* ключи и атрибуты, закрыть хэндлы                                       *
	*************************************************************************/

free_decryptedCapi:
	free(decryptedCapi);
free_encryptedCapi:
	free(encryptedCapi);
destroy_key:
	r = CryptDestroyKey(msKey);
	CHECK_RELEASE(" CryptDestroyKey", r, errorCode);
release_context:
	r = CryptReleaseContext(msProv, 0);
	CHECK_RELEASE(" CryptReleaseContext", r, errorCode);
free_capiPubKey:
	free(capiPubKey);
free_exponent:
	free(pubExponentAttr.pValue);
free_modulus:
	free(modulusAttr.pValue);
#endif //_WIN32

	/*************************************************************************
	* Очистить память, выделенную под зашифрованные и расшифрованные данные, *
	* объекты ключей                                                         *
	*************************************************************************/
free_decryptedPKCS11:
	free(decryptedPKCS11);
free_privKeys:
	free(privKeys);
free_encryptedPKCS11:
	free(encryptedPKCS11);
free_pubKeys:
	free(pubKeys);

	/*************************************************************************
	* Сбросить права доступа                                                 *
	*************************************************************************/
logout:
	rv = functionList->C_Logout(session);
	CHECK_RELEASE_AND_LOG(" C_Logout", rv == CKR_OK, rvToStr(rv), errorCode);

	/*************************************************************************
	* Закрыть открытую сессию в слоте                                        *
	*************************************************************************/
close_session:
	rv = functionList->C_CloseSession(session);
	CHECK_RELEASE_AND_LOG(" C_CloseSession", rv == CKR_OK, rvToStr(rv), errorCode);

	/*************************************************************************
	* Очистить память, выделенную под слоты                                  *
	*************************************************************************/
free_slots:
	free(slots);

	/*************************************************************************
	* Деинициализировать библиотеку                                          *
	*************************************************************************/
finalize_pkcs11:
	rv = functionList->C_Finalize(NULL_PTR);
	CHECK_RELEASE_AND_LOG(" C_Finalize", rv == CKR_OK, rvToStr(rv), errorCode);

	/*************************************************************************
	* Выгрузить библиотеку из памяти                                         *
	*************************************************************************/
unload_pkcs11:
	CHECK_RELEASE(" FreeLibrary", FreeLibrary(module), errorCode);

exit:
	if (errorCode) {
		printf("\n\nSome error occurred. Sample failed.\n");
	} else {
		printf("\n\nSample has been completed successfully.\n");
	}

	return errorCode;
}
