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

#include <Common.h>

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

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

	CK_FUNCTION_LIST_EXTENDED_PTR functionListEx;      // Указатель на список функций расширения PKCS#11, хранящийся в структуре CK_FUNCTION_LIST_EXTENDED
	CK_C_EX_GetFunctionListExtended getFunctionListEx; // Указатель на функцию C_EX_GetFunctionListExtended

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

	CK_TOKEN_INFO_EXTENDED tokenInfoEx;                // Структура данных типа CK_TOKEN_INFO_EXTENDED с информацией о токене

	CK_ULONG driveSize;                                // Общий объем носителя

	CK_ULONG volumeRwSize = 0;                         // Размер раздела доступного для чтения и записи
	CK_ULONG volumeRoSize = 0;                         // Размер раздела доступного только для чтения
	CK_ULONG volumeHiSize = 0;                         // Размер скрытого раздела
	CK_ULONG volumeCdSize = 0;                         // Размер раздела CD-ROM

	CK_VOLUME_ID_EXTENDED volumeRw = 1;                // Идентификатор раздела доступного только для чтения и записи
	CK_VOLUME_ID_EXTENDED volumeRo = 2;                // Идентификатор раздела доступного только для чтения

	CK_ULONG CKU_LOCAL_1 = 0x03;                       // Идентификатор локального PIN-кода (в диапазоне от 0x03 до 0x1E)
	CK_ULONG CKU_LOCAL_2 = 0x1E;                       // Идентификатор локального PIN-кода (в диапазоне от 0x03 до 0x1E)

	/* Шаблон для разметки разделов */
	CK_VOLUME_FORMAT_INFO_EXTENDED initParams[] =
	{
		{ volumeRwSize, ACCESS_MODE_RW, CKU_USER, 0 },
		{ volumeRoSize, ACCESS_MODE_RO, CKU_SO, 0 },
		{ volumeHiSize, ACCESS_MODE_HIDDEN, CKU_LOCAL_1, 0 },
		{ volumeCdSize, ACCESS_MODE_CD, CKU_LOCAL_2, 0 }
	};

	CK_VOLUME_INFO_EXTENDED_PTR volumesInfo;           // Массив структур с информацией о разделах
	CK_ULONG volumesInfoCount;                         // Размер массива с информацией о разделах

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

	CK_RV rv;                                          // Код возврата. Могут быть возвращены только ошибки, определенные в PKCS#11

	int isFlashMemoryAvailable = 0;                    // Флаг для проверки наличия у токена флеш-памяти

	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 (C_GetFunctionList)", getFunctionList != NULL, unload_pkcs11);

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

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

	/*************************************************************************
	* Получить структуру с указателями на функции расширения                 *
	*************************************************************************/
	rv = getFunctionListEx(&functionListEx);
	CHECK_AND_LOG(" Get function list extended", 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);

	/*************************************************************************
	* Получить расширенную информацию о подключенном токене                  *
	*************************************************************************/
	tokenInfoEx.ulSizeofThisStructure = sizeof(tokenInfoEx);
	rv = functionListEx->C_EX_GetTokenInfoExtended(slots[0], &tokenInfoEx);
	CHECK_AND_LOG(" C_EX_GetTokenInfoExtended", rv == CKR_OK, rvToStr(rv), free_slots);

	/*************************************************************************
	* Определить наличие флеш-памяти у токена                                *
	*************************************************************************/
	if (tokenInfoEx.flags & TOKEN_FLAGS_HAS_FLASH_DRIVE) {
		isFlashMemoryAvailable = 1;
	}

	CHECK_AND_LOG(" Checking flash availability", isFlashMemoryAvailable, " Token has no flash drive", free_slots);

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

	/*************************************************************************
	* Создать локальный PIN-код токена с ID = 0x03                           *
	*************************************************************************/
	printf("\nSetting local PINs... \n");

	rv = functionListEx->C_EX_SetLocalPIN(slots[0], USER_PIN, USER_PIN_LEN, LOCAL_PIN, LOCAL_PIN_LEN, CKU_LOCAL_1);
	CHECK_AND_LOG(" C_EX_SetLocalPIN (ID = 0x03)", rv == CKR_OK, rvToStr(rv), free_slots);

	/*************************************************************************
	* Создать локальный PIN-код токена с ID = 0x1E                           *
	*************************************************************************/
	rv = functionListEx->C_EX_SetLocalPIN(slots[0], USER_PIN, USER_PIN_LEN, LOCAL_PIN, LOCAL_PIN_LEN, CKU_LOCAL_2);
	CHECK_AND_LOG(" C_EX_SetLocalPIN (ID = 0x1E)", rv == CKR_OK, rvToStr(rv), free_slots);

	printf("Local PINs have been set successfully.\n");

	/*************************************************************************
	* Демонстрация работы с флеш-памятью                                     *
	*************************************************************************/
	printf("\nWorking with flash memory...\n");

	/*************************************************************************
	* Получить объем флеш-памяти                                             *
	*************************************************************************/
	rv = functionListEx->C_EX_GetDriveSize(slots[0], &driveSize);
	CHECK_AND_LOG(" C_EX_GetDriveSize", rv == CKR_OK, rvToStr(rv), free_slots);

	printf(" Flash drive capacity: %d Mb\n", (int)driveSize);

	/*************************************************************************
	* Полное удаление информации с флеш-памяти с последующим созданием       *
	* разделов в соответствии с переданными параметрами                      *
	*************************************************************************/
	volumeRwSize = driveSize / 2;
	volumeRoSize = driveSize / 4;
	volumeHiSize = driveSize / 8;
	volumeCdSize = driveSize - volumeRwSize - volumeRoSize - volumeHiSize;

	initParams[0].ulVolumeSize = volumeRwSize;
	initParams[1].ulVolumeSize = volumeRoSize;
	initParams[2].ulVolumeSize = volumeHiSize;
	initParams[3].ulVolumeSize = volumeCdSize;

	rv = functionListEx->C_EX_FormatDrive(slots[0], CKU_SO, SO_PIN, SO_PIN_LEN, initParams, arraysize(initParams));
	CHECK_AND_LOG(" C_EX_FormatDrive", rv == CKR_OK, rvToStr(rv), free_slots);

	/*************************************************************************
	* Получить информацию о разделах флеш-памяти                             *
	*************************************************************************/
	rv = functionListEx->C_EX_GetVolumesInfo(slots[0], NULL_PTR, &volumesInfoCount);
	CHECK_AND_LOG(" C_EX_GetVolumesInfo", rv == CKR_OK, rvToStr(rv), free_slots);

	volumesInfo = (CK_VOLUME_INFO_EXTENDED*)malloc(volumesInfoCount * sizeof(CK_VOLUME_INFO_EXTENDED));
	CHECK(" Memory allocation for volumes", volumesInfo != NULL_PTR, free_slots);

	rv = functionListEx->C_EX_GetVolumesInfo(slots[0], volumesInfo, &volumesInfoCount);
	CHECK_AND_LOG(" C_EX_GetVolumesInfo", rv == CKR_OK, rvToStr(rv), free_volumes);

	for (i = 0; i < (unsigned)volumesInfoCount; ++i) {
		printf("\n Printing volume %1.2d info:\n", (int)i + 1);
		printf("  Volume id:         %2.2d \n", (int)volumesInfo[i].idVolume);
		printf("  Volume size:       %d Mb \n", (int)volumesInfo[i].ulVolumeSize);
		printf("  Access mode:       %2.2d \n", (int)volumesInfo[i].accessMode);
		printf("  Volume owner:      %2.2d \n", (int)volumesInfo[i].volumeOwner);
		printf("  Flags:             0x%8.8X \n", (unsigned)volumesInfo[i].flags);
	}

	/*************************************************************************
	* Изменить атрибут доступа раздела флеш-памяти на постоянной основе      *
	* (до следующего изменения атрибутов)                                    *
	*************************************************************************/
	rv = functionListEx->C_EX_ChangeVolumeAttributes(slots[0], CKU_SO, SO_PIN, SO_PIN_LEN, volumeRo, ACCESS_MODE_RW, CK_TRUE);

	CHECK_AND_LOG("\n C_EX_ChangeVolumeAttributes (permanent)", rv == CKR_OK, rvToStr(rv), free_volumes);

	/*************************************************************************
	* Временно изменить атрибут доступа к разделу флеш-памяти                *
	* (до первого извлечения устройства или следующего изменения атрибутов)  *
	*************************************************************************/
	rv = functionListEx->C_EX_ChangeVolumeAttributes(slots[0], CKU_USER, USER_PIN, USER_PIN_LEN, volumeRw, ACCESS_MODE_HIDDEN, CK_FALSE);

	CHECK_AND_LOG(" C_EX_ChangeVolumeAttributes (temporary)", rv == CKR_OK, rvToStr(rv), free_volumes);

	printf("Work with flash memory has been finished successfully.\n");

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

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

	/*************************************************************************
	* Очистить память, выделенную под информацию о разделах                  *
	*************************************************************************/
free_volumes:
	free(volumesInfo);

	/*************************************************************************
	* Очистить память, выделенную под слоты                                  *
	*************************************************************************/
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;
}

