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

#include "Common.h"

/* Структура для хранения информации об установленных криптопровайдерах */
typedef struct {
	DWORD Type;
	LPWSTR Name;
	DWORD Size;
} CSP;

int main(void)
{
	CSP pCSP[MAX_PROV] = { 0 };             // Массив структур с информацией об установленных криптопровайдерах
	HCRYPTPROV hProv = 0;                   // Дескриптор криптопровайдера
	DWORD i = 0;                            // Индекс криптопровайдера
	PROV_ENUMALGS* pbMechInfo = NULL;       // Буфер для получения информации о механизме
	DWORD dwMechInfoSize = 0;               // Размер буфера для получения информации о механизме
	DWORD dwFlag = CRYPT_FIRST;             // Переменная для хранения флага

	for (;; ) {
		/**********************************************************************
		* Шаг 1. Перечисление установленных в системе криптопровайдеров       *
		**********************************************************************/
		wprintf(L"Searching for existing providers");

		/**********************************************************************
		* Шаг 1.1 Определение длины имени криптопровайдера                    *
		**********************************************************************/
		while (CryptEnumProvidersW(
		                           i,              // Индекс криптопровайдера
		                           NULL,
		                           0,
		                           &pCSP[i].Type,    // Тип криптопровайдера
		                           NULL,             // Буфер для имени криптопровайдера
		                           &pCSP[i].Size)) { // Длина буфера с именем криптопровайдера
			/**********************************************************************
			* Шаг 1.2 Выделение памяти для получения имени криптопровайдера       *
			**********************************************************************/
			pCSP[i].Name = (LPWSTR)malloc(pCSP[i].Size * sizeof(BYTE));
			if (!pCSP[i].Name) {
				wprintf(L" -> FAILED, out of memory\n\n");
				break;
			}

			/**********************************************************************
			* Шаг 1.3 Получение имени криптопровайдера                            *
			**********************************************************************/
			if (!CryptEnumProvidersW(
			                         i,                // Индекс криптопровайдера
			                         NULL,
			                         0,
			                         &pCSP[i].Type,    // Тип криптопровайдера
			                         pCSP[i].Name,     // Буфер, в который возвращается имя криптопровайдера
			                         &pCSP[i].Size)) { // Длина буфера
				wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
				break;
			}

			/**********************************************************************
			* Шаг 1.4 Печать имени и типа криптопровайдера                        *
			**********************************************************************/
			wprintf(L"\n%d. Provider Name: %s, type %d ", i + 1, pCSP[i].Name, pCSP[i].Type);

			/**********************************************************************
			* Шаг 2. Перечисление поддерживаемых криптопровайдером алгоритмов     *
			**********************************************************************/

			/**********************************************************************
			* Шаг 2.1 Инициализация криптопровайдера                              *
			**********************************************************************/
			wprintf(L"\n   Initializing CSP");
			if (!CryptAcquireContextW(
			                          &hProv,                 // Указатель на дескриптор криптопровайдера
			                          NULL,                   // Имя ключевого контейнера
			                          pCSP[i].Name,           // Имя криптопровайдера (NULL -- по умолчанию)
			                          pCSP[i].Type,           // Тип криптопровайдера
			                          CRYPT_VERIFYCONTEXT)) { // Флаги операции
				wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
				break;
			}
			wprintf(L" -> OK\n");

			/**********************************************************************
			* Шаг 2.2 Получение длины буфера с информацией об алгоритме           *
			**********************************************************************/
			wprintf(L"   Getting algorithms info");
			dwFlag = CRYPT_FIRST;
			if (CryptGetProvParam(
			                      hProv,           // Дескриптор криптопровайдера
			                      PP_ENUMALGS,     // Флаг перечисления алгоритмов
			                      NULL,
			                      &dwMechInfoSize, // Максимальная длина буфера
			                      dwFlag)) {       // Флаг операции
				/**********************************************************************
				* Шаг 2.3 Выделение памяти для получения информации об алгоритме      *
				**********************************************************************/
				pbMechInfo = (PROV_ENUMALGS*)malloc(dwMechInfoSize * sizeof(BYTE));
				if (!pbMechInfo) {
					wprintf(L" -> FAILED, out of memory\n\n");
					CryptReleaseContext(hProv, 0);
					break;
				}

				wprintf(L"-> OK\n   Supported algorithms:\n");

				/**********************************************************************
				* Шаг 2.3 Получение структуры с информацией об алгоритме              *
				**********************************************************************/
				dwFlag = CRYPT_FIRST;
				while (CryptGetProvParam(
				                         hProv,             // Дескриптор криптопровайдера
				                         PP_ENUMALGS,       // Флаг перечисления алгоритмов
				                         (BYTE*)pbMechInfo, // Буфер для структуры с информацией о механизме
				                         &dwMechInfoSize,   // Длина буфера
				                         dwFlag)) {         // Флаг операции
					/**********************************************************************
					* Шаг 2.4 Печать информации об алгоритме                              *
					**********************************************************************/
					printf("   %-20s key length: %4i byte \t id: %0.8X\n",
					       pbMechInfo->szName, pbMechInfo->dwBitLen, pbMechInfo->aiAlgid);
					dwFlag = CRYPT_NEXT;
				}

				free(pbMechInfo);
				pbMechInfo = NULL;
			} else {
				wprintf(L" -> FAILED, error number: 0x%0.8x\n\n", GetLastError());
				CryptReleaseContext(hProv, 0);
				break;
			}
			CryptReleaseContext(hProv, 0);
			++i;
		}
		break;
	}

	/**********************************************************************
	* Шаг 3. Освобождение памяти                                          *
	**********************************************************************/
	for (i = 0; i < MAX_PROV; ++i) {
		free(pCSP[i].Name);
	}

	if (GetLastError() == ERROR_SUCCESS || GetLastError() == ERROR_NO_MORE_ITEMS) {
		wprintf(L"\nTest has been completed successfully.");
	} else {
		wprintf(L"Test has failed. Error number: 0x%0.8x.", GetLastError());
	}
	return 0;
}
