/*
* Copyright(C) 2000-2014
*
* Этот файл содержит информацию, являющуюся
* собственностью компании Крипто Про.
*
* Любая часть этого файла не может быть скопирована,
* исправлена, переведена на другие языки,
* локализована или модифицирована любым способом,
* откомпилирована, передана по сети с или на
* любую компьютерную систему без предварительного
* заключения соглашения с компанией Крипто Про.
*
* Программный код, содержащийся в этом файле, предназначен
* исключительно для целей обучения и не может быть использован
* для защиты информации.
*
* Компания Крипто-Про не несет никакой
* ответственности за функционирование этого кода.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#   include <windows.h>
#   include <wincrypt.h>
#else
#   include <stdlib.h>
#   include <CSP_WinDef.h>
#   include <CSP_WinCrypt.h>
#endif
#include <WinCryptEx.h>

// Начало примера (не следует удалять данный комментарий, он используется 
// для автоматической сборки документации)
//-------------------------------------------------------------------------------------------------------------
// Данный пример демонстрирует использование объекта хэша PRFKEYMAT (CALG_GR3411_PRFKEYMAT, 
// CALG_GR3411_2012_256_PRFKEYMAT и CALG_GR3411_2012_512_PRFKEYMAT) для получения последовательности
// псевдослучайных данных произвольной длины, а также для получения ключей CALG_G28147 и CALG_SYMMETRIC_512.
// Аналогичным способом могут быть получены ключи CALG_GR3412_2015_M и CALG_GR3412_2015_K.
//-------------------------------------------------------------------------------------------------------------



static void HandleError(const char *s);

int main() {

    //------------------------------------------------------------------------
    // Объявление переменных.

    HCRYPTPROV hProv = 0;
    HCRYPTKEY hPrfKey = 0;
    HCRYPTHASH hPrfHash = 0;

    CRYPT_DATA_BLOB prfSeedBlob;
    BYTE pbPrfSeedData[] = { 0x11, 0x22, 0x33, 0x44 };

    BYTE pbDataFirst[10];
    DWORD cbDataFirst = 0;

    HCRYPTKEY hDerivedKey256 = 0;
    
    BYTE pbDataSecond[100];
    DWORD cbDataSecond = 0;

    HCRYPTKEY hDerivedKey512 = 0;
    

    //------------------------------------------------------------------------
    // Получение дескриптора контекста криптографического провайдера.

    if(CryptAcquireContext(
	&hProv, 
	NULL, 
	NULL, 
	PROV_GOST_2012_256, 
	CRYPT_VERIFYCONTEXT)) 
    {
	printf("CSP context acquired.\n");
    }
    else
    {
	HandleError("Error during CryptAcquireContext.");
    }

    //------------------------------------------------------------------------
    // Генерация ключа для PRF.

    if(CryptGenKey(     
	hProv,      
	CALG_G28147,      
	0, 
	&hPrfKey))
    {   
	printf("PRF key is created. \n");
    }
    else
    {
	HandleError("Error during CryptGenKey.");
    }

    //------------------------------------------------------------------------
    // Создание объекта хэша PRF.

    if(CryptCreateHash(
	hProv,
	CALG_GR3411_2012_256_PRFKEYMAT,
	hPrfKey,
	0,
	&hPrfHash))
    {
	printf("PRF hash is created. \n");
    }
    else
    {
	HandleError("Error during CryptCreateHash.");
    }

    //------------------------------------------------------------------------
    // Устанавливаем seed для PRF.

    prfSeedBlob.pbData = pbPrfSeedData;
    prfSeedBlob.cbData = sizeof(pbPrfSeedData);

    if(CryptSetHashParam(
	hPrfHash,
	HP_PRFKEYMAT_SEED,
	(LPBYTE)&prfSeedBlob,
	0))
    {
	printf("PRF seed is set. \n");
    }
    else
    {
	HandleError("Error during CryptSetHashParam.");
    }

    //------------------------------------------------------------------------
    // Получаем последовательность данных необходимой длины.

    cbDataFirst = sizeof(pbDataFirst);

    if(CryptGetHashParam(
	hPrfHash,
	HP_HASHVAL,
	pbDataFirst,
	&cbDataFirst,
	0))
    {
	printf("First part of data produced. \n");
    }
    else
    {
	HandleError("Error during CryptGetHashParam.");
    }

    //------------------------------------------------------------------------
    // Получаем ключ 256 бит.

    if(CryptDeriveKey(
	hProv,
	CALG_G28147,
	hPrfHash,
	0,
	&hDerivedKey256))
    {
	printf("256-bit key derived. \n");
    }
    else
    {
	HandleError("Error during CryptDeriveKey.");
    }

    //------------------------------------------------------------------------
    // Получаем еще последовательность данных необходимой длины.

    cbDataSecond = sizeof(pbDataSecond);

    if(CryptGetHashParam(
	hPrfHash,
	HP_HASHVAL,
	pbDataSecond,
	&cbDataSecond,
	0))
    {
	printf("Second part of data produced. \n");
    }
    else
    {
	HandleError("Error during CryptGetHashParam.");
    }

    //------------------------------------------------------------------------
    // Получаем ключ 512 бит.

    if(CryptDeriveKey(
	hProv,
	CALG_SYMMETRIC_512,
	hPrfHash,
	0,
	&hDerivedKey512))
    {
	printf("512-bit key derived. \n");
    }
    else
    {
	HandleError("Error during CryptDeriveKey.");
    }

    //------------------------------------------------------------------------
    // Освобождение памяти.

    if (hDerivedKey256) {
	CryptDestroyKey(hDerivedKey256);
    }

    if (hDerivedKey512) {
	CryptDestroyKey(hDerivedKey512);
    }

    if (hPrfHash) {
	CryptDestroyHash(hPrfHash);
    }

    if (hPrfKey) {
	CryptDestroyKey(hPrfKey);
    }

    if (hProv) {
	CryptReleaseContext(hProv, 0);
    }

    return 0;
}



// Конец примера
// (не следует удалять данный комментарий, он используется 
//  для автоматической сборки документации)

//  В этом примере используется функция HandleError, функция обработки
//  простых ошибок, для печати сообщения об ошибке в стандартный файл 
//  ошибок (stderr) и выхода из программы. 
//  В большинстве приложений эта функция заменяется другой функцией, 
//  которая выводит более полное сообщение об ошибке.

static void HandleError(const char *s) {
    fprintf(stderr, "An error occurred in running the program. \n");
    fprintf(stderr, "%s\n", s);
    fprintf(stderr, "Error number %x.\n", GetLastError());
    fprintf(stderr, "Program terminating. \n");
    exit(1);
}
