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

#include "rdr_prj.h"
#include "WinCryptEx.h"

#define HASH_CTX_STATUS_CLEAR 0
#define HASH_CTX_STATUS_INIT 1
#define HASH_CTX_STATUS_VALUE 2

struct TRdrHashContext_
{
    TSupSysEContext * ctx_num;
    DWORD status;
    TRdrFkcOrder hashval;
    size_t hashlen;
    unsigned int algid;
};

// инициализация хэширования
DWORD rdr_crypt_hash_init(
    TSupSysEContext *context,
    unsigned int algid,
    TRdrHashContext ** hash_ctx)
{
    DWORD code = 0;
    TReaderFkcHashInit hi;
    TRdrHashContext * res = NULL;


    hi.hash_algid = algid;
    hi.handle = 0;

    code = supsys_call(context, READER_FUN_HASH_INIT, &hi);
    if (code)
	goto done;

    res = calloc(1, sizeof(TRdrHashContext));
    if (!res)
    {
	code = (DWORD)NTE_NO_MEMORY;
	goto done;
    }
    res->algid = algid;
    res->ctx_num = hi.handle;
    res->status = HASH_CTX_STATUS_INIT;
    switch (algid) {
	case CALG_GR3411:
	case CALG_GR3411_2012_256:
	    res->hashlen = RDR_FKC_HASH_VALUE_SIZE_256;
	    break;
	case CALG_GR3411_2012_512:
	    res->hashlen = RDR_FKC_HASH_VALUE_SIZE_512;
	    break;
	case CALG_SHA1:
	    res->hashlen = RDR_FKC_HASH_VALUE_SIZE_SHA1;
	    break;
	default:
	    code = (DWORD)ERROR_INVALID_PARAMETER;
	    goto done;
    }

    *hash_ctx = res; res = NULL;

done:
    free(res);
    LOGRETURN(code);
}

// хэширование данных
DWORD rdr_crypt_hash_data(
    TSupSysEContext *context,
    TRdrHashContext * hash_ctx,
    const void * data,
    size_t data_len,
    size_t * hashed_data_len)
{
    DWORD code = 0;
    TReaderFkcHashData hd;
    size_t tmp_hashed_len = 0;
    if (hash_ctx->status != HASH_CTX_STATUS_INIT)
	LOGRETURN((DWORD)ERROR_INVALID_PARAMETER);

    while (tmp_hashed_len < data_len) {
	hd.handle = hash_ctx->ctx_num;
	hd.hashed_len = 0;
	hd.info.info = (unsigned char*)data + tmp_hashed_len;
	hd.info.length = data_len - tmp_hashed_len;

	code = supsys_call(context, READER_FUN_HASH_DATA, &hd);
	if (code == (DWORD)CPR_ERR_BLOCK || code == ERROR_SUCCESS) {
	    tmp_hashed_len += hd.hashed_len;
	    if (hd.hashed_len == 0)
		LOGRETURN((DWORD)NTE_FAIL); /* считаем, что почему-то не можем продолжать хэширование */
	}
	else
	    LOGRETURN(code);
    }
    *hashed_data_len = tmp_hashed_len;
    LOGRETURN(code);
}

// получение результата хэширования
DWORD rdr_crypt_hash_getval(
    TSupSysEContext *context,
    TRdrHashContext * hash_ctx,
    void * buffer,
    size_t * buffer_len)
{
    if (hash_ctx->status < HASH_CTX_STATUS_INIT)
	LOGRETURN((DWORD)ERROR_INVALID_PARAMETER);

    if (*buffer_len < hash_ctx->hashlen) {
	*buffer_len = hash_ctx->hashlen;
	LOGRETURN((DWORD)ERROR_INVALID_PARAMETER);
    }
    if (hash_ctx->status != HASH_CTX_STATUS_VALUE) {
	DWORD code;
	TReaderFkcHashGetVal hgv;
	hgv.handle = hash_ctx->ctx_num;
	hgv.hash_len = hash_ctx->hashlen;
	memset(hgv.hashval, 0, sizeof(TRdrHashValue));
	code = supsys_call(context, READER_FUN_HASH_GETVAL, &hgv);
	if (code)
	    LOGRETURN(code);

	//TODO здесь очевидно нужен переворот, поскольку мы в результате получаем Order (число), а не хэш (набор байтов).
	memcpy(hash_ctx->hashval, hgv.hashval, hgv.hash_len);
	hash_ctx->hashlen = hgv.hash_len;
	hash_ctx->status = HASH_CTX_STATUS_VALUE;
    }
    memcpy(buffer, hash_ctx->hashval, hash_ctx->hashlen);
    *buffer_len = hash_ctx->hashlen;
    LOGRETURN(0);
}

// уничтожение контекста хэширования 
DWORD rdr_crypt_hash_destroy(
    TSupSysEContext *context,
    TRdrHashContext * hash_ctx)
{
    DWORD code = 0;
    code = supsys_call(context, READER_FUN_HASH_DESTROY, hash_ctx->ctx_num);
    support_zero_memory(hash_ctx, sizeof(TRdrHashContext));
    free(hash_ctx);
    LOGRETURN(code);
}

DWORD rdr_get_random(
    TSupSysEContext *context,
    size_t * length,
    BYTE  *out)
{
    TSupSysInfoUntyped info;
    DWORD code = ERROR_SUCCESS;
    memset(&info, 0, sizeof (info));

    SUPSYS_PRE_WRITE_PTRS(length, sizeof(*length));
    if (out)
	SUPSYS_PRE_WRITE_PTRS(out, *length);
    if (out) {
	info.length = *length;
	info.info = out;
    }
    else
	info.length = 0;

    if (context)
	code = supsys_call(context, READER_FUN_RANDOM_GET, &info);
    else
	code = (DWORD)ERROR_NOT_SUPPORTED;
    if (code)
	LOGRETURN(code);

    *length = info.length;
    LOGRETURN(ERROR_SUCCESS);
}

DWORD rdr_crypt_signature(TSupSysEContext *context, int index, DWORD key_type, TRdrHashContext * hash_ctx, TRdrFkcOrder hash, size_t hash_length, TRdrFkcOrder r, size_t * r_length, TRdrFkcOrder s, size_t * s_length)
{
    TReaderFkcSignInfo sI;
    DWORD err;
    sI.index = index;
    if (hash_ctx)
	sI.hash_ctx = hash_ctx->ctx_num;
    else
	sI.hash_ctx = NULL;
    sI.Simple_Sign.e = hash;
    sI.Simple_Sign.e_length = hash_length;
    sI.Simple_Sign.r = r;
    sI.Simple_Sign.s = s;
    sI.key_type = key_type;
    err = supsys_call(context, READER_FUN_SIGNATURE, &sI);
    if (err)
	LOGRETURN(err);
    *r_length = sI.Simple_Sign.r_length;
    *s_length = sI.Simple_Sign.s_length;
    LOGRETURN((DWORD)ERROR_SUCCESS);
}

DWORD rdr_crypt_rsa_exptmod(TSupSysEContext *context, int index, DWORD key_type, TRdrHashContext *hash_ctx,
    size_t hash_length, DWORD hash_alg, const BYTE *in_data, BYTE *out_data, size_t data_length)
{
    TReaderFkcRsaExptmodInfo sI;
    DWORD err = ERROR_SUCCESS;

    sI.index = index;
    sI.hash_ctx = hash_ctx ? hash_ctx->ctx_num : NULL;
    sI.Simple_Sign.e = in_data;
    sI.Simple_Sign.e_length = data_length;
    sI.Simple_Sign.pl_length = hash_length;
    sI.Simple_Sign.s = out_data;
    sI.Simple_Sign.s_length = data_length;
    sI.key_type = key_type;
    sI.hash_alg = hash_alg;
    err = supsys_call(context, READER_FUN_EXPTMOD_RSA, &sI);
    if (err) {
	LOGRETURN(err);
    }
    if (data_length != sI.Simple_Sign.s_length) {
	LOGRETURN((DWORD)NTE_FAIL);
    }
    LOGRETURN((DWORD)ERROR_SUCCESS);
}

DWORD rdr_crypt_weak_signature_1(TSupSysEContext *context, TRdrFkcTrID transaction_id, int index, DWORD key_type, TRdrHashContext * hash_ctx, TRdrFkcOrder hash, size_t hash_length, TRdrFkcOrder r, size_t * r_length, TRdrFkcOrder s, size_t * s_length, TRdrFkcOrder w, size_t * w_length)
{
    TReaderFkcWeakSignFirstInfo sI;
    DWORD err;
    sI.transaction_id = transaction_id;
    sI.params.index = index;
    if (hash_ctx)
	sI.params.hash_ctx = hash_ctx->ctx_num;
    else
	sI.params.hash_ctx = NULL;
    sI.params.Simple_Sign.e = hash;
    sI.params.Simple_Sign.e_length = hash_length;
    sI.params.Simple_Sign.r = r;
    sI.params.Simple_Sign.s = s;
    sI.w = w;
    sI.params.key_type = key_type;
    err = supsys_call(context, READER_FUN_WEAK_SIGNATURE1, &sI);
    if (err)
	LOGRETURN(err);
    *r_length = sI.params.Simple_Sign.r_length;
    *s_length = sI.params.Simple_Sign.s_length;
    *w_length = sI.w_length;
    LOGRETURN((DWORD)ERROR_SUCCESS);
}

DWORD rdr_crypt_weak_signature_2(TSupSysEContext *context, TRdrFkcTrID transaction_id, TRdrFkcOrder l, size_t l_length, int index, TRdrFkcOrder v, size_t * v_length)
{
    TReaderFkcWeakSignSecondInfo sI;
    DWORD err;
    sI.transaction_id = transaction_id;
    sI.l_length = l_length;
    sI.l = l;
    sI.v = v;
    sI.index = index;
    err = supsys_call(context, READER_FUN_WEAK_SIGNATURE2, &sI);
    if (err)
	LOGRETURN(err);
    *v_length = sI.v_length;
    LOGRETURN((DWORD)ERROR_SUCCESS);
}
