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

#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_SLOT_ID_PTR slots;                        // Массив идентификаторов всех доступных слотов
	CK_ULONG slotCount;                          // Количество идентификаторов всех доступных слотов в массиве

	CK_SLOT_ID_PTR tokens;                       // Массив идентификаторов слотов с подключенными токенами
	CK_ULONG tokenCount;                         // Количество идентификаторов слотов с подключенными токенами в массиве

	CK_INFO libraryInfo;                         // Структура данных типа CK_INFO с общей информацией о библиотеке
	CK_SLOT_INFO slotInfo;                       // Структура данных типа CK_SLOT_INFO с информацией о слоте
	CK_TOKEN_INFO tokenInfo;                     // Структура данных типа CK_TOKEN_INFO с информацией о токене
	CK_MECHANISM_INFO mechInfo;                  // Структура данных типа CK_MECHANISM_INFO с информацией о механизме

	CK_MECHANISM_TYPE_PTR mechanisms = NULL_PTR; // Указатель на механизмы, поддерживаемые слотом
	CK_ULONG mechanismCount;                     // Количество поддерживаемых механизмов

	CK_UTF8CHAR labelForPrinting[33] = { 0 };    // Буфер, используемый для печати метки Рутокен

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

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

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

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

	/*************************************************************************
	* Загрузить библиотеку                                                   *
	*************************************************************************/
	module = LoadLibrary(PKCS11_LIBRARY_NAME); // или 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);

	/*************************************************************************
	* Получить информацию о библиотеке                                       *
	*************************************************************************/
	rv = functionList->C_GetInfo(&libraryInfo);
	CHECK_AND_LOG(" C_GetInfo", rv == CKR_OK, rvToStr(rv), finalize_pkcs11);

	/*************************************************************************
	* Распечатать информацию о библиотеке                                    *
	*************************************************************************/
	printf("Printing library info: \n");
	printf(" PKCS#11 version:     %d.%d \n", (&libraryInfo)->cryptokiVersion.major, (&libraryInfo)->cryptokiVersion.minor);
	printf(" Manufacturer:        %.*s \n", (int)sizeof((&libraryInfo)->manufacturerID), (&libraryInfo)->manufacturerID);
	printf(" Flags:               0x%8.8X \n", (int)(&libraryInfo)->flags);
	printf(" Library description: %.*s \n\n", (int)sizeof((&libraryInfo)->libraryDescription), (&libraryInfo)->libraryDescription);

	/*************************************************************************
	* Определить количество всех доступных слотов                            *
	*************************************************************************/
	printf("Getting number of all slots\n");

	rv = functionList->C_GetSlotList(CK_FALSE, NULL_PTR, &slotCount);
	CHECK_AND_LOG(" C_GetSlotList (number of all slots)", rv == CKR_OK, rvToStr(rv), finalize_pkcs11);

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

	/*************************************************************************
	* Получить список всех доступных слотов                                  *
	*************************************************************************/
	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_FALSE, slots, &slotCount);
	CHECK_AND_LOG(" C_GetSlotList", rv == CKR_OK, rvToStr(rv), free_slots);
	printf(" Slots available: %d\n", (int)slotCount);

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

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

	rv = functionList->C_GetSlotList(CK_TRUE, tokens, &tokenCount);
	CHECK_AND_LOG(" C_GetSlotList", rv == CKR_OK, rvToStr(rv), free_tokens);
	printf(" Slots with tokens available: %d\n", (int)tokenCount);

	/*************************************************************************
	* Распечатать информацию:                                                *
	*         - о слотах;                                                    *
	*         - о подключенных токенах;                                      *
	*         - о поддерживаемых механизмах.                                 *
	*************************************************************************/
	for (i = 0; i < slotCount; ++i) {
		printf("\nSlot number: %d\n", (int)i);

		/*************************************************************************
		* Получить информацию о слоте                                            *
		*************************************************************************/
		rv = functionList->C_GetSlotInfo(slots[i], &slotInfo);
		CHECK_AND_LOG(" C_GetSlotInfo", rv == CKR_OK, rvToStr(rv), free_tokens);

		/*************************************************************************
		* Распечатать информацию о слоте                                         *
		*************************************************************************/
		printf("Printing slot info:\n");
		printf(" Slot description:  %.*s \n", (int)sizeof(slotInfo.slotDescription), slotInfo.slotDescription);
		printf(" Manufacturer:      %.*s \n", (int)sizeof(slotInfo.manufacturerID), slotInfo.manufacturerID);
		printf(" Flags:             0x%8.8X \n", (int)slotInfo.flags);
		printf(" Hardware version:  %d.%d \n", slotInfo.hardwareVersion.major, slotInfo.hardwareVersion.minor);
		printf(" Firmware version:  %d.%d \n\n", slotInfo.firmwareVersion.major, slotInfo.firmwareVersion.minor);

		if (slotInfo.flags & CKF_TOKEN_PRESENT) {
			/*************************************************************************
			* Получить информацию о токене                                           *
			*************************************************************************/
			rv = functionList->C_GetTokenInfo(slots[i], &tokenInfo);
			CHECK_AND_LOG(" C_GetTokenInfo", rv == CKR_OK, rvToStr(rv), free_tokens);

			/*************************************************************************
			* Распечатать информацию о токене                                        *
			*************************************************************************/
			printf("Printing token info:\n");
			printf(" Token label:               ");
			memcpy(labelForPrinting, tokenInfo.label, arraysize(tokenInfo.label));
			printUTF8String(labelForPrinting);
			printf("\n");
			printf(" Manufacturer:              %.*s \n", (int)sizeof(tokenInfo.manufacturerID), tokenInfo.manufacturerID);
			printf(" Token model:               %.*s \n", (int)sizeof(tokenInfo.model), tokenInfo.model);
			printf(" Token #:                   %.*s \n", (int)sizeof(tokenInfo.serialNumber), tokenInfo.serialNumber);
			printf(" Flags:                     0x%8.8X \n", (int)tokenInfo.flags);
			printf(" Max session count:         %d \n", (int)tokenInfo.ulMaxSessionCount);
			printf(" Current session count:     %d \n", (int)tokenInfo.ulSessionCount);
			printf(" Max RW session count:      %d \n", (int)tokenInfo.ulMaxRwSessionCount);
			printf(" Current RW session count:  %d \n", (int)tokenInfo.ulRwSessionCount);
			printf(" Max PIN length:            %d \n", (int)tokenInfo.ulMaxPinLen);
			printf(" Min PIN length:            %d \n", (int)tokenInfo.ulMinPinLen);
			printf(" Total public memory:       %d \n", (int)tokenInfo.ulTotalPublicMemory);
			printf(" Free public memory:        %d \n", (int)tokenInfo.ulFreePublicMemory);
			printf(" Total private memory:      %d \n", (int)tokenInfo.ulTotalPrivateMemory);
			printf(" Free private memory:       %d \n", (int)tokenInfo.ulFreePrivateMemory);
			printf(" Hardware version:          %d.%d \n", tokenInfo.hardwareVersion.major, tokenInfo.hardwareVersion.minor);
			printf(" Firmware version:          %d.%d \n", tokenInfo.firmwareVersion.major, tokenInfo.firmwareVersion.minor);
			printf(" Timer #:                   %.*s \n\n", (int)sizeof(tokenInfo.utcTime), tokenInfo.utcTime);

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

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

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

			for (j = 0; j < mechanismCount; ++j) {
				printf("\nMechanism %d info:\n", (int)j);

				/*************************************************************************
				* Получить информацию о механизме                                        *
				*************************************************************************/
				rv = functionList->C_GetMechanismInfo(slots[i], mechanisms[j], &mechInfo);
				CHECK_AND_LOG(" C_GetMechanismInfo", rv == CKR_OK, rvToStr(rv), free_mechanisms);

				/*************************************************************************
				* Распечатать информацию о механизме                                     *
				*************************************************************************/
				printf(" Mechanism type:    0x%8.8X\n", (int)mechanisms[j]);
				printf(" Min key size:      %d\n", (int)mechInfo.ulMinKeySize);
				printf(" Max key size:      %d\n", (int)mechInfo.ulMaxKeySize);
				printf(" Mechanism flags:   0x%8.8X\n", (int)mechInfo.flags);
			}
		}
	}

	errorCode = 0;

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

free_tokens:
	free(tokens);

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