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

#include <Common.h>
/*************************************************************************
* Шаблон для поиска закрытого ключа отправителя                          *
*************************************************************************/
CK_ATTRIBUTE senderPrivateKeyTemplate[] =
{
	{ CKA_ID, keyPairIdGost1, sizeof(keyPairIdGost1) - 1},          // ID пары
	{ CKA_CLASS, &privateKeyObject, sizeof(privateKeyObject) },     // Класс - закрытый ключ
	{ CKA_KEY_TYPE, &keyTypeGostR3410, sizeof(keyTypeGostR3410)}    // Тип ключа - ГОСТ Р 34.10-2001
};

/*************************************************************************
* Шаблон для поиска закрытого ключа получателя                           *
*************************************************************************/
CK_ATTRIBUTE recipientPrivateKeyTemplate[] =
{
	{ CKA_ID, keyPairIdGost2, sizeof(keyPairIdGost2) - 1},          // ID пары
	{ CKA_CLASS, &privateKeyObject, sizeof(privateKeyObject) },     // Класс - закрытый ключ
	{ CKA_KEY_TYPE, &keyTypeGostR3410, sizeof(keyTypeGostR3410)}    // Тип ключа - ГОСТ Р 34.10-2001
};

/*************************************************************************
* Шаблон для поиска открытого ключа отправителя                          *
*************************************************************************/
CK_ATTRIBUTE senderPublicKeyTemplate[] =
{
	{ CKA_ID, keyPairIdGost1, sizeof(keyPairIdGost1) - 1},          // ID пары
	{ CKA_CLASS, &publicKeyObject, sizeof(publicKeyObject) },       // Класс - открытый ключ
	{ CKA_KEY_TYPE, &keyTypeGostR3410, sizeof(keyTypeGostR3410)}    // Тип ключа - ГОСТ Р 34.10-2001
};

/*************************************************************************
* Шаблон для поиска открытого ключа получателя                           *
*************************************************************************/
CK_ATTRIBUTE recipientPublicKeyTemplate[] =
{
	{ CKA_ID, keyPairIdGost2, sizeof(keyPairIdGost2) - 1},          // ID пары
	{ CKA_CLASS, &publicKeyObject, sizeof(publicKeyObject) },       // Класс - открытый ключ
	{ CKA_KEY_TYPE, &keyTypeGostR3410, sizeof(keyTypeGostR3410) }   // Тип ключа - ГОСТ Р 34.10-2001
};

/*************************************************************************
* Шаблон для создания ключа обмена                                       *
*************************************************************************/
CK_ATTRIBUTE derivedKeyTemplate[] =
{
	{ CKA_LABEL, &derivedKeyLabel, sizeof(derivedKeyLabel) - 1},          // Метка ключа
	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },             // Класс - секретный ключ
	{ CKA_KEY_TYPE, &keyTypeGost28147, sizeof(keyTypeGost28147)},         // Тип ключа - ГОСТ 28147-89
	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},                // Ключ является объектом сессии
	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},             // Ключ может быть изменен после создания
	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},                // Ключ доступен только после аутентификации на токене
	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},            // Ключ может быть извлечен в зашифрованном виде
	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}             // Ключ может быть извлечен в открытом виде
};

/*************************************************************************
* Шаблон маскируемого ключа                                              *
*************************************************************************/
CK_ATTRIBUTE sessionKeyTemplate[] =
{
	{ CKA_LABEL, &wrappedKeyLabel, sizeof(wrappedKeyLabel) - 1 },   // Метка ключа
	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },       // Класс - секретный ключ
	{ CKA_KEY_TYPE, &keyTypeGost28147, sizeof(keyTypeGost28147)},   // Тип ключа - ГОСТ 28147-89
	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},          // Ключ является объектом сессии
	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},       // Ключ может быть изменен после создания
	{ CKA_PRIVATE, &attributeTrue, sizeof(attributeTrue)},          // Ключ доступен только после аутентификации на токене
	{ CKA_VALUE, NULL_PTR, 0},                                      // Значение ключа
	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},      // Ключ может быть извлечен в зашифрованном виде
	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}       // Ключ может быть извлечен в открытом виде
};

/*************************************************************************
* Шаблон демаскированного ключа                                          *
*************************************************************************/
CK_ATTRIBUTE unwrappedKeyTemplate[] =
{
	{ CKA_LABEL, &unwrappedKeyLabel, sizeof(unwrappedKeyLabel) - 1},        // Метка ключа
	{ CKA_CLASS, &secretKeyObject, sizeof(secretKeyObject) },               // Класс - секретный ключ
	{ CKA_KEY_TYPE, &keyTypeGost28147, sizeof(keyTypeGost28147)},           // Тип ключа - ГОСТ 28147-89
	{ CKA_TOKEN, &attributeFalse, sizeof(attributeFalse)},                  // Ключ является объектом сессии
	{ CKA_MODIFIABLE, &attributeTrue, sizeof(attributeTrue)},               // Ключ может быть изменен после создания
	{ CKA_PRIVATE, &attributeFalse, sizeof(attributeFalse)},                // Ключ доступен без аутентификации на токене
	{ CKA_EXTRACTABLE, &attributeTrue, sizeof(attributeTrue)},              // Ключ может быть извлечен в зашифрованном виде
	{ CKA_SENSITIVE, &attributeFalse, sizeof(attributeFalse)}               // Ключ может быть извлечен в открытом виде
};

/*************************************************************************
* Функция выработки ключа обмена                                         *
*************************************************************************/
static int deriveKey(CK_FUNCTION_LIST_PTR functionList,   // Указатель на список функций PKCS#11, хранящийся в структуре CK_FUNCTION_LIST
                     CK_SESSION_HANDLE session,           // Хэндл сессии
                     CK_ATTRIBUTE_PTR privateKeyTemplate, // Указатель на шаблон для поиска закрытого ключа
                     CK_ULONG privateKeyTemplateSize,     // Количество атрибутов в шаблоне для поиска закрытого ключа
                     CK_ATTRIBUTE_PTR publicKeyTemplate,  // Указатель на шаблон для поиска открытого ключа
                     CK_ULONG publicKeyTemplateSize,      // Количество атрибутов в шаблоне для поиска открытого ключа
                     CK_BYTE_PTR ukm,                     // Указатель на буфер, содержащий UKM
                     CK_ULONG ukmSize,                    // Размер буфера с UKM
                     CK_OBJECT_HANDLE_PTR derivedKey      // Хэндл выработанного общего ключа
                     )
{
	CK_OBJECT_HANDLE_PTR privateKeys;                    // Массив с хэндлами закрытых ключей
	CK_ULONG privateKeysCount;                           // Количество хэндлов объектов в массиве
	CK_OBJECT_HANDLE_PTR publicKeys;                     // Массив с хэндлами открытых ключей
	CK_ULONG publicKeysCount;                            // Количество хэндлов объектов в массиве

	CK_BYTE publicKeyValue[GOST_3410_KEY_SIZE];          // Буфер, содержащий значение открытого ключа
	CK_BYTE derivedKeyValue[GOST_28147_KEY_SIZE];        // Буфер, содержащий тело ключа обмена

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

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

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

	/*************************************************************************
	* Получить массив хэндлов закрытых ключей                                *
	*************************************************************************/
	printf(" Getting private key...\n");

	r = findObjects(functionList, session, privateKeyTemplate, privateKeyTemplateSize,
	                &privateKeys, &privateKeysCount);
	CHECK(" findObjects", r == 0, exit);

	CHECK_AND_LOG(" Checking number of keys found", privateKeysCount > 0, " No objects found", exit);

	/*************************************************************************
	* Получить массив хэндлов открытых ключей                                *
	*************************************************************************/
	printf(" Getting public key...\n");

	r = findObjects(functionList, session, publicKeyTemplate, publicKeyTemplateSize,
	                &publicKeys, &publicKeysCount);
	CHECK(" findObjects", r == 0, free_privateKeys);

	CHECK_AND_LOG(" Checking number of keys found", publicKeysCount > 0, " No objects found", free_privateKeys);

	/*************************************************************************
	* Получить значение открытого ключа                                      *
	*************************************************************************/
	printf(" Getting public key value...\n");

	valueAttr.pValue = publicKeyValue;
	valueAttr.ulValueLen = sizeof(publicKeyValue);

	rv = functionList->C_GetAttributeValue(session, publicKeys[0], &valueAttr, 1);
	CHECK_AND_LOG("  C_GetAttributeValue", rv == CKR_OK, rvToStr(rv), free_publicKeys);

	/*************************************************************************
	* Поместить в структуру типа CK_MECHANISM параметры, необходимые         *
	* для выработки общего ключа                                             *
	*************************************************************************/
	deriveParameters.kdf = CKD_CPDIVERSIFY_KDF;
	deriveParameters.pPublicData = publicKeyValue;
	deriveParameters.ulPublicDataLen = sizeof(publicKeyValue);
	deriveParameters.pUKM = ukm;
	deriveParameters.ulUKMLen = ukmSize;

	gostR3410DerivationMech.pParameter = &deriveParameters;
	gostR3410DerivationMech.ulParameterLen = sizeof(deriveParameters);

	rv = functionList->C_DeriveKey(session, &gostR3410DerivationMech, privateKeys[0],
	                               derivedKeyTemplate, arraysize(derivedKeyTemplate), derivedKey);

	CHECK_AND_LOG(" C_DeriveKey", rv == CKR_OK, rvToStr(rv), free_publicKeys);

	/*************************************************************************
	* Получить и распечатать значение выработанного ключа                    *
	*************************************************************************/
	valueAttr.pValue = derivedKeyValue;
	valueAttr.ulValueLen = sizeof(derivedKeyValue);

	rv = functionList->C_GetAttributeValue(session, *derivedKey, &valueAttr, 1);
	CHECK_AND_LOG(" C_GetAttributeValue", rv == CKR_OK, rvToStr(rv), destroy_derivedKey);

	printf(" Derived key value:\n");
	printHex(derivedKeyValue, sizeof(derivedKeyValue));

	errorCode = 0;

	/*************************************************************************
	* Очистить память, выделенную под объекты ключей,                        *
	* уничтожить выработанный ключ (при необходимости)                       *
	*************************************************************************/
destroy_derivedKey:
	if (errorCode != 0) {
		rv = functionList->C_DestroyObject(session, *derivedKey);
		CHECK_RELEASE_AND_LOG(" C_DestroyObject", rv == CKR_OK, rvToStr(rv), errorCode);
		*derivedKey = CK_INVALID_HANDLE;
	}
free_publicKeys:
	free(publicKeys);
free_privateKeys:
	free(privateKeys);
exit:
	return errorCode;
}

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_MECHANISM_TYPE_PTR mechanisms;                    // Массив поддерживаемых механизмов
	CK_ULONG mechanismCount;                             // Количество поддерживаемых механизмов

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

	CK_OBJECT_HANDLE derivedKey1;                        // Хэндл выработанного на стороне отправителя общего ключа
	CK_OBJECT_HANDLE derivedKey2;                        // Хэндл выработанного на стороне получателя общего ключа

	CK_BYTE sessionKeyValue[GOST_28147_KEY_SIZE];        // Буфер, содержащий сессионный ключ
	CK_BYTE unwrappedKeyValue[GOST_28147_KEY_SIZE];      // Буфер, содержащий демаскированный на стороне получателя сессионный ключ

	CK_BYTE_PTR wrappedKeyValue;                         // Указатель на буфер, содержащий маскированный на стороне отправителя сессионный ключ
	CK_ULONG wrappedKeyValueSize;                        // Размер буфера со значением маскированного на стороне отправителя сессионного ключа, в байтах

	CK_BYTE ukm[UKM_LENGTH];                             // Буфер, содержащий UKM

	CK_OBJECT_HANDLE sessionKey;                         // Хэндл сессионного ключа, который будет маскироваться
	CK_OBJECT_HANDLE unwrappedKey;                       // Хэндл демаскированного ключа

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

	CK_ULONG i;                                          // Вспомогательная переменная-счетчик в циклах

	int isGostR3410DeriveSupported = 0;                  // Флаги для проверки поддержки токеном CKM_GOSTR3410_DERIVE
	int isGostWrapSupported = 0;                         // Флаги для проверки поддержки токеном CKM_GOST28147_KEY_WRAP

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

	/*************************************************************************
	* Выполнить действия для начала работы с библиотекой 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);

	/*************************************************************************
	* Получить список поддерживаемых токеном механизмов                      *
	*************************************************************************/
	rv = functionList->C_GetMechanismList(slots[0], NULL_PTR, &mechanismCount);
	CHECK_AND_LOG(" C_GetMechanismList (number of mechanisms)", rv == CKR_OK, rvToStr(rv), free_slots);

	CHECK_AND_LOG(" Checking mechanisms available", mechanismCount > 0, " No mechanisms available", free_slots);

	mechanisms = (CK_MECHANISM_TYPE_PTR)malloc(mechanismCount * sizeof(CK_MECHANISM_TYPE));
	CHECK(" Memory allocation for mechanisms", mechanisms != NULL_PTR, free_slots);

	rv = functionList->C_GetMechanismList(slots[0], mechanisms, &mechanismCount);
	CHECK_AND_LOG(" C_GetMechanismList", rv == CKR_OK, rvToStr(rv), free_mechanisms);

	/*************************************************************************
	* Определение поддерживаемых токеном механизмов                          *
	*************************************************************************/
	for (i = 0; i < mechanismCount; ++i) {
		if (mechanisms[i] == CKM_GOSTR3410_DERIVE) {
			isGostR3410DeriveSupported = 1;
			break;
		}
	}

	for (i = 0; i < mechanismCount; ++i) {
		if (mechanisms[i] == CKM_GOST28147_KEY_WRAP) {
			isGostWrapSupported = 1;
			break;
		}
	}

	CHECK_AND_LOG(" Checking CKM_GOSTR3410_DERIVE support", isGostR3410DeriveSupported,
	              " CKM_GOSTR3410_DERIVE isn`t supported!\n", free_mechanisms);
	CHECK_AND_LOG(" Checking CKM_GOST28147_KEY_WRAP support", isGostWrapSupported,
	              " CKM_GOST28147_KEY_WRAP isn`t supported!\n", free_mechanisms);

	/*************************************************************************
	* Открыть 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_mechanisms);

	/*************************************************************************
	* Выполнить аутентификацию Пользователя                                  *
	*************************************************************************/
	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");

	/*************************************************************************
	* Выполнить предварительные действия для выработки ключа и маскирования  *
	*************************************************************************/
	printf("\nPreparing data for deriving and wrapping...\n");
	/*************************************************************************
	* Установить параметры в структуре типа CK_GOSTR3410_DERIVE_PARAMS       *
	* для выработки общего ключа                                             *
	*************************************************************************/
	rv = functionList->C_GenerateRandom(session, ukm, sizeof(ukm));
	CHECK_AND_LOG(" C_GenerateRandom", rv == CKR_OK, rvToStr(rv), logout);

	/*************************************************************************
	* Для маскирования ключа должно использоваться то же значение UKM,       *
	* что и для выработки                                                    *
	*************************************************************************/
	gost28147WrapMech.ulParameterLen = sizeof(ukm);
	gost28147WrapMech.pParameter = ukm;

	/*************************************************************************
	* Сгенерировать значение сессионного ключа                               *
	*************************************************************************/
	rv = functionList->C_GenerateRandom(session, sessionKeyValue, sizeof(sessionKeyValue));
	CHECK_AND_LOG(" C_GenerateRandom", rv == CKR_OK, rvToStr(rv), logout);

	for (i = 0; i < arraysize(sessionKeyTemplate); ++i) {
		if (sessionKeyTemplate[i].type == CKA_VALUE) {
			sessionKeyTemplate[i].pValue = sessionKeyValue;
			sessionKeyTemplate[i].ulValueLen = sizeof(sessionKeyValue);
			break;
		}
	}

	printf(" Session key data is:\n");
	printHex(sessionKeyValue, sizeof(sessionKeyValue));

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

	/*************************************************************************
	* Выработать общий ключ на стороне отправителя                           *
	*************************************************************************/
	printf("\nDeriving key on the sender's side...\n");

	r = deriveKey(functionList, session, senderPrivateKeyTemplate, arraysize(senderPrivateKeyTemplate),
	              recipientPublicKeyTemplate, arraysize(recipientPublicKeyTemplate), ukm, sizeof(ukm), &derivedKey1);
	CHECK(" deriveKey", r == 0, logout);

	printf("Key has been derived successfully.\n");

	/*************************************************************************
	* Маскировать сессионный ключ с помощью общего выработанного ключа на    *
	* стороне отправителя                                                    *
	*************************************************************************/
	printf("\nWrapping key...\n");
	/*************************************************************************
	* Создать ключ, который будет замаскирован                               *
	*************************************************************************/
	printf(" Creating the GOST 28147-89 key to wrap...\n");
	rv = functionList->C_CreateObject(session, sessionKeyTemplate, arraysize(sessionKeyTemplate), &sessionKey);
	CHECK_AND_LOG("  C_CreateObject", rv == CKR_OK, rvToStr(rv), destroy_derived1);

	/*************************************************************************
	* Замаскировать ключ                                                     *
	*************************************************************************/
	rv = functionList->C_WrapKey(session, &gost28147WrapMech, derivedKey1, sessionKey, NULL_PTR, &wrappedKeyValueSize);
	CHECK_AND_LOG("  C_WrapKey", rv == CKR_OK, rvToStr(rv), destroy_sessionKey);

	wrappedKeyValue = (CK_BYTE_PTR)malloc(wrappedKeyValueSize * sizeof(CK_BYTE));
	CHECK("  Memory allocation for wrapped key", wrappedKeyValue != NULL_PTR, destroy_sessionKey);

	rv = functionList->C_WrapKey(session, &gost28147WrapMech, derivedKey1, sessionKey, wrappedKeyValue, &wrappedKeyValueSize);
	CHECK_AND_LOG("  C_WrapKey", rv == CKR_OK, rvToStr(rv), free_wrapped);

	/*************************************************************************
	* Распечатать буфер, содержащий маскированный ключ                       *
	*************************************************************************/
	printf(" Wrapped key data is:\n");
	printHex(wrappedKeyValue, wrappedKeyValueSize);

	printf("Key has been wrapped successfully.\n");

	/*************************************************************************
	* Выработать общий ключ на стороне получателя                            *
	*************************************************************************/
	printf("\nDeriving key on the recipient's side...\n");

	r = deriveKey(functionList, session, recipientPrivateKeyTemplate, arraysize(recipientPrivateKeyTemplate),
	              senderPublicKeyTemplate, arraysize(senderPublicKeyTemplate), ukm, sizeof(ukm), &derivedKey2);
	CHECK(" deriveKey", r == 0, free_wrapped);

	printf("Key has been derived successfully.\n");

	/*************************************************************************
	* Демаскировать сессионный ключ с помощью общего выработанного           *
	* ключа на стороне получателя                                            *
	*************************************************************************/
	printf("\nUnwrapping key...\n");
	/*************************************************************************
	* Демаскировать ключ                                                     *
	*************************************************************************/
	rv = functionList->C_UnwrapKey(session, &gost28147WrapMech, derivedKey2, wrappedKeyValue, wrappedKeyValueSize,
	                               unwrappedKeyTemplate, arraysize(unwrappedKeyTemplate), &unwrappedKey);
	CHECK_AND_LOG(" C_UnwrapKey", rv == CKR_OK, rvToStr(rv), destroy_derived2);

	/*************************************************************************
	* Получить буфер со значением демаскированного ключа                     *
	*************************************************************************/
	valueAttr.pValue = unwrappedKeyValue;
	valueAttr.ulValueLen = sizeof(unwrappedKeyValue);

	rv = functionList->C_GetAttributeValue(session, unwrappedKey, &valueAttr, 1);
	CHECK_AND_LOG(" C_GetAttributeValue", rv == CKR_OK, rvToStr(rv), destroy_unwrapped);

	/*************************************************************************
	* Распечатать буфер со значением демаскированного ключа                  *
	*************************************************************************/
	printf(" Unwrapped key data:\n");
	printHex(unwrappedKeyValue, sizeof(unwrappedKeyValue));

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

	/*************************************************************************
	* Сравнить значение исходного ключа с расшифрованным                     *
	*************************************************************************/
	r = (memcmp(unwrappedKeyValue, sessionKeyValue, GOST_28147_KEY_SIZE) == 0);
	CHECK("Compare session and unwrapped keys", r, destroy_unwrapped);

	/*************************************************************************
	* Выставить признак успешного завершения программы                       *
	*************************************************************************/
	errorCode = 0;

	/*************************************************************************
	* Выполнить действия для завершения работы с библиотекой PKCS#11         *
	*************************************************************************/
	printf("\nFinalizing... \n");
destroy_unwrapped:
	rv = functionList->C_DestroyObject(session, unwrappedKey);
	CHECK_RELEASE_AND_LOG(" C_DestroyObject (unwrappedKey)", rv == CKR_OK, rvToStr(rv), errorCode);
destroy_derived2:
	rv = functionList->C_DestroyObject(session, derivedKey2);
	CHECK_RELEASE_AND_LOG(" C_DestroyObject (derivedKey2)", rv == CKR_OK, rvToStr(rv), errorCode);
free_wrapped:
	free(wrappedKeyValue);
destroy_sessionKey:
	rv = functionList->C_DestroyObject(session, sessionKey);
	CHECK_RELEASE_AND_LOG(" C_DestroyObject (sessionKey)", rv == CKR_OK, rvToStr(rv), errorCode);
destroy_derived1:
	rv = functionList->C_DestroyObject(session, derivedKey1);
	CHECK_RELEASE_AND_LOG(" C_DestroyObject (derivedKey1)", rv == CKR_OK, rvToStr(rv), errorCode);

	/*************************************************************************
	* Сбросить права доступа                                                 *
	*************************************************************************/
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_mechanisms:
	free(mechanisms);

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


