/*************************************************************************
* Rutoken                                                                *
* Copyright (c) 2003-2017, CJSC Aktiv-Soft. All rights reserved.         *
* Подробная информация:  http://www.rutoken.ru                           *
*------------------------------------------------------------------------*
* Пример работы с Рутокен при помощи Rutoken Cryptographic Service       *
* Provider на языке C                                                    *
*------------------------------------------------------------------------*
* Пример шифрования на симметричном алгоритме RC2                        *
*------------------------------------------------------------------------*
* - получение контекста криптопровайдера                                 *
* - генерация ключа симметричного алгоритма RC2                          *
* - зашифрование строки на этом ключе                                    *
* - расшифрование строки                                                 *
*************************************************************************/

// Если не компилируется из-за основных типов CAPI, раскомментируйте:
// #define _WIN32_WINNT 0x0400

#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>


/* Имя используемого криптопровайдера */
#define MYPROV L"Aktiv ruToken CSP v1.0"

#define NULL_KEY (HCRYPTKEY)0
#define NULL_PROV (HCRYPTPROV)0

/* Симметричный алгоритм RC2 */
#define ALGID CALG_RC2
/* Размер буфера */
#define BUF_LEN 100


/************************************************************************
* Вывести сообщение об ошибке и завершить программу                     *
************************************************************************/

DWORD Error(IN WCHAR* s  /* Указатель на строку с комментарием */
            )
{
	wprintf(L"Error occurred: %s\n", s);
	wprintf(L"Error number: 0x%x.\n", GetLastError());
	return GetLastError();
}


int main(void)
{

	/* Строка для зашифрования*/
	WCHAR szStr2Encr[BUF_LEN] = L"String to be encrypted and decrypted";

	/* Хэндл CSP */
	HCRYPTPROV hCryptProv = NULL_PROV;
	/* Хэндл сессионного симметричного ключа */
	HCRYPTKEY hSymKey = NULL_KEY;
	/* Размер строки */
	DWORD dwStringLen = 0;
	/* Количество зашифрованных байт */
	DWORD dwNumberOfEncipheredBytes = 0;
	/* Длина блока шифрования */
	DWORD dwBlockLen = 0;
	/* Размер данных */
	DWORD dwDataLen = 4;
	/* Ошибка */
	DWORD dwError = 0;
	for (;; ) {
		/*---------------------------------------------------------------------*/
		/* Шаг 1 : Получение хэндла криптопровайдера Aktiv ruToken CSP v1.0.   */
		/*---------------------------------------------------------------------*/
		if (CryptAcquireContextW(&hCryptProv,
		                         NULL,
		                         MYPROV,
		                         PROV_RSA_FULL,
		                         CRYPT_VERIFYCONTEXT)) {
			wprintf(L"CryptAcquireContextW has been completed successfully.\n");
		} else {
			dwError = Error(L"CryptAcquireContextW failed.\n");
			break;
		}

		/*---------------------------------------------------------------------*/
		/* Шаг 2 : Генерация ключа алгоритма RC2.                              */
		/*---------------------------------------------------------------------*/

		if (CryptGenKey(hCryptProv,
		                ALGID,
		                0,
		                &hSymKey)) {
			wprintf(L"RC2 key has been generated successfully.\n");
		} else {
			dwError = Error(L"RC2 key generation failed.\n");
			break;
		}

		/*---------------------------------------------------------------------*/
		/* Шаг 3 : Запрос параметров (для RC2 они постоянны).                  */
		/*---------------------------------------------------------------------*/

		/* нахождение размера блока шифрования                                 */
		/* если ALGID не блочный шифр, а поточный                              */
		/* (например CALG_RC4), то получим dwBlockLen == 0                     */
		if (CryptGetKeyParam(hSymKey,
		                     KP_BLOCKLEN,
		                     (BYTE*)&dwBlockLen,
		                     &dwDataLen,
		                     0)) {
			wprintf(L"Block length obtained: %d bits.\n", dwBlockLen);
		} else {
			dwError = Error(L"Cannot obtain block length.\n");
			break;
		}

		/* длина строки вместе с 0 в конце:                                            */
		dwStringLen = (DWORD)((wcslen(szStr2Encr) + 1) * sizeof(WCHAR));

		/* Если шифр блочный, то длина шифруемого массива данных должна быть           */
		/* кратна длине блока. Поэтому, если  dwStringLen (длина массива               */
		/* данных для зашифровки) не кратна dwBlockLen, то он                          */
		/* дополняется до кратной длины (если, конечно же, позволяет размер буфера -   */
		/* последний параметр в CryptEncrypt) и шифруется.                             */
		/* Предпоследний параметр возвращает длину зашифрованных данных.               */

		dwNumberOfEncipheredBytes = dwStringLen;

		wprintf(L"Encrypt string: %s \n", szStr2Encr);

		/*---------------------------------------------------------------------*/
		/* Шаг 4 : Зашифрование.                                               */
		/*---------------------------------------------------------------------*/

		if (CryptEncrypt(hSymKey,
		                 0,
		                 TRUE, /* последний блок */
		                 0,
		                 (BYTE*)szStr2Encr,
		                 &dwNumberOfEncipheredBytes,
		                 BUF_LEN)) {
			wprintf(L"%d bytes have been encrypted successfully.\n", dwStringLen);
		} else {
			dwError = Error(L"Cannot encrypt block.\n");
			break;
		}

		/*---------------------------------------------------------------------*/
		/* Шаг 5 : Расшифрование.                                              */
		/*---------------------------------------------------------------------*/

		if (CryptDecrypt(hSymKey,
		                 0,
		                 TRUE, /* последний блок */
		                 0,
		                 (BYTE*)szStr2Encr,
		                 &dwNumberOfEncipheredBytes)) {
			wprintf(L"%d bytes have been decrypted successfully.\n", dwNumberOfEncipheredBytes);
		} else {
			dwError = Error(L"Cannot decrypt block.\n");
			break;
		}

		wprintf(L"Decrypted string: %s \n", szStr2Encr);
		break;
	}

	/*---------------------------------------------------------------------*/
	/* Шаг 6 : Освобождение хэндлов.                                       */
	/*---------------------------------------------------------------------*/
	if (hSymKey) {
		CryptDestroyKey(hSymKey);
	}

	if (hCryptProv) {
		if (!CryptReleaseContext(hCryptProv,
		                         0)) {
			dwError = Error(L"Cannot release context.\n");
		}
		wprintf(L"Context has been released successfully.\n");
	}
	return dwError;
}
