/*
* Creation date : Tues Mar 03 09:00:00 2007
* Last modified : %modify_time%
*/
/** @file
* \brief This file contains implementation of low level
* ECC signature function, which used LibTomCrypt. 
*
* \version CE2_ECPKI_ECDSA.c#1:csrc:1
* \author Yermalayeu Ihar
* \remarks Copyright (C) 2007 by Discretix Technologies Ltd.
* All Rights reserved
*/

/************************ Include Files ***********************/

#include "LLF_ECPKI_ECDSA.h"
#include "LLF_ECPKI_Common.h"
#include "LLF_ECPKI_BUILD.h"

#include "tommath.h"

/************************ Defines *****************************/

#define LLF_ECPKI_ECDSA_BUFFER_SIZE 128

/************************ Enums *******************************/
/************************ Typedefs ****************************/
/************************ Global Data *************************/
/************************ Private Functions *******************/

#if 0
void BigNumDebugPrint(void *num, const char* comment)
{
	FILE *out;
	DxUint8_t buffer[255];
	DxUint32_t i_byte, size = 255;

	fopen_s(&out, "int_log.txt", "a+");
	if (num != NULL) {
		size = ltc_mp.unsigned_size(num);
		ltc_mp.unsigned_write(num, buffer);
		for (i_byte = 0; i_byte < size; i_byte++) {
			fprintf(out, "%X", (buffer[i_byte]&0xf0)/0x10);
			fprintf(out, "%X ", buffer[i_byte]&0x0f);
		}
	}
	if (comment != NULL)
		fprintf(out, " %s", comment);
	fprintf(out, "\n");

	fclose(out);
}
#endif

CE2Error_t LLF_ECPKI_HASH(CE2_ECPKI_HASH_OpMode_t   HashMode,
						  CE2_ECPKI_DomainID_t      domainId,
						  DxUint8_t                 *DataIn_ptr,
						  DxUint32_t                DataInSize,
						  DxUint8_t                 *DataOut_ptr,
						  DxUint32_t                *DataOutSize_ptr)
{
	int hash_index = -1, make_hash = 1;
	int error_code = CRYPT_OK;
	DxUint32_t orderSize, orderSizeInBits, i, shift;
	DxUint8_t empty_buffer;

	/* Which mode to use: SHA1, SHA224, SHA256, SHA384, SHA512. */
	switch(HashMode)
	{
	case CE2_ECPKI_HASH_SHA1_mode:
		if (register_hash(&sha1_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha1");
		break;
	case CE2_ECPKI_HASH_SHA224_mode:
		if (register_hash(&sha224_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha224");
		break;
	case CE2_ECPKI_HASH_SHA256_mode:
		if (register_hash(&sha256_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha256");
		break;
	case CE2_ECPKI_HASH_SHA384_mode:
		if (register_hash(&sha384_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha384");
		break;
	case CE2_ECPKI_HASH_SHA512_mode:
		if (register_hash(&sha512_desc) == -1) 
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
		hash_index = find_hash("sha512");
		break;
	case CE2_ECPKI_After_HASH_SHA1_mode:
		*DataOutSize_ptr = CE2_HASH_SHA1_DIGEST_SIZE_IN_BYTES;
		make_hash = 0;
		break;
	case CE2_ECPKI_After_HASH_SHA224_mode:
		*DataOutSize_ptr = CE2_HASH_SHA224_DIGEST_SIZE_IN_BYTES;
		make_hash = 0;
		break;
	case CE2_ECPKI_After_HASH_SHA256_mode:
		*DataOutSize_ptr = CE2_HASH_SHA256_DIGEST_SIZE_IN_BYTES;
		make_hash = 0;
		break;
	case CE2_ECPKI_After_HASH_SHA384_mode:
		*DataOutSize_ptr = CE2_HASH_SHA384_DIGEST_SIZE_IN_BYTES;
		make_hash = 0;
		break;
	case CE2_ECPKI_After_HASH_SHA512_mode:
		*DataOutSize_ptr = CE2_HASH_SHA512_DIGEST_SIZE_IN_BYTES;
		make_hash = 0;
		break;
	default:
		return CE2_LLF_HASH_MODULE_ERROR_BASE;
	}

	if (make_hash) {
		if (hash_index == -1)
			return CE2_LLF_HASH_MODULE_ERROR_BASE;

		/* Case of input message is absent */
		if (DataIn_ptr == NULL) {
			DataInSize = 0;
			DataIn_ptr = &empty_buffer;
		}

		/* Perform hash operation. */
		error_code = hash_memory(hash_index, DataIn_ptr, DataInSize,
			DataOut_ptr, DataOutSize_ptr);
		if (error_code != CRYPT_OK)
			return CE2_LLF_HASH_MODULE_ERROR_BASE;
	} else {
		if (DataIn_ptr != NULL) {
			memset(DataOut_ptr, 0, *DataOutSize_ptr);
			memcpy(DataOut_ptr, DataIn_ptr, min(*DataOutSize_ptr, DataInSize));
		} else {
			*DataOutSize_ptr = 0;
		}
	}

	orderSize = LLF_ECPKI_DomainIdToOrderSizeInBytes(domainId);
	orderSizeInBits = LLF_ECPKI_DomainIdToOrderSizeInBits(domainId);

	/* Get leftmost orderSizeInBits from DataOut_ptr */ 
	if (orderSizeInBits < (*DataOutSize_ptr)*8) {
		*DataOutSize_ptr = orderSize;
		shift = 8 - (orderSizeInBits & 7);
		if(shift != 8) { 
			for(i = orderSize - 1; i > 0; i--)
				DataOut_ptr[i] = (DataOut_ptr[i] >> shift) | 
				(DataOut_ptr[i - 1] << (8 - shift));
			DataOut_ptr[0] = DataOut_ptr[0] >> shift;
		}        
	}

	return CE2_OK;
} /* End of LLF_ECPKI_HASH */

/************************ Public Functions ********************/

/**
****************************************************************
* Function Name: 
*  LLF_ECDSA_Sign
*
* Inputs:
* @param SignerPrivKey_ptr [in] - A pointer to a user private key structure.    				
* @param HashMode [in] - The enumerator variable defines hash function to be used.                         
* @param MessageDataIn_ptr [in] - A message data for calculation of hash.   			
* @param MessageSizeInBytes [in] - A size of block of message data in bytes. 
* @param SignatureOut_ptr [out] - A pointer to a buffer for output of signature.                         
* @param SignatureOutSize_ptr [in] - Size of user passed buffer for signature (in)
*                         and size of actual signature (out).
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  Performs all of the ECDSA signing operations simultaneously.
*
* \b 
* Algorithm:
*  -# Create digest for input massage data
*  -# Create and initialize Pseudo-Random Number Generators
*  -# Initialization of LibTomCrypt primitives
*  -# Convert digest to little-endian form
*  -# Make up a key and export the public copy
*  -# Build output signature
***************************************************************/
CE2Error_t LLF_ECDSA_Sign(
						  CE2_ECPKI_UserPrivKey_t      *SignerPrivKey_ptr,       /*in*/
						  CE2_ECPKI_HASH_OpMode_t       HashMode,                /*in*/
						  DxUint8_t                     *MessageDataIn_ptr,      /*in*/ 
						  DxUint32_t                     MessageSizeInBytes,     /*in*/
						  DxUint8_t                     *SignatureOut_ptr,       /*out*/ 
						  DxUint32_t                    *SignatureOutSize_ptr,   /*in*/
						  DxInt8_t                      IsEphemerKeyInternal,    /*in*/
						  DxUint8_t                     *EphemerPublKeyIn_ptr,   /*in*/ 
						  DxUint32_t                     EphemerPublKeySizeBytes /*in*/)
{
	CE2_ECPKI_UserPublKey_t EphemerPublKey;
	CE2Error_t error = CE2_OK, result = CE2_OK;
	DxUint8_t buffer[LLF_ECPKI_ECDSA_BUFFER_SIZE];
	DxUint32_t size, pos, orderSize, frontZero;
	int error_code = CRYPT_OK, wprng;
	ecc_key key, public_key;
	void *r, *s, *hash, *order;
	prng_state prng;

	/* Initialize ltc_mp structure */
	ltc_mp = ltm_desc;

	/* Create digest for input massage data */
	size = LLF_ECPKI_ECDSA_BUFFER_SIZE;
	error = LLF_ECPKI_HASH(HashMode, SignerPrivKey_ptr->DomainID, 
		MessageDataIn_ptr, MessageSizeInBytes, buffer, &size);
	if (error != CE2_OK) 
		return error;

	if (IsEphemerKeyInternal == TRUE) {
		/* Create and initialize Pseudo-Random Number Generators */
		error = LLF_ECPKI_PRNG_Init(&prng, &wprng);
		if (error != CE2_OK) {
			fortuna_done(&prng);
			return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		}
	}

	/* Import signer private key to LibTomCrypt format */
	error_code =  ecc_import(SignerPrivKey_ptr->PrivKeyDbBuff, 
		SignerPrivKey_ptr->valid_tag, (DomainID_t)SignerPrivKey_ptr->DomainID, &key);
	if (error_code != CRYPT_OK) {
		fortuna_done(&prng);
		return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
	}

	/* Initialization of LibTomCrypt primitives */
	error_code = ltc_init_multi(&r, &s, &hash, &order, NULL);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}
	error_code = ltc_mp.read_radix(order, (char *)ltc_ecc_sets[key.idx].order, 16);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}

	/* Convert digest to little-endian form */ 
	error_code = ltc_mp.unsigned_read(hash, buffer, size);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}

	/* Make up a key and export the public copy */
	if (IsEphemerKeyInternal == TRUE) {
		for (;;) {
			error_code =  ecc_make_key(&prng, wprng, 
				(DomainID_t)SignerPrivKey_ptr->DomainID, &public_key);
			if (error_code != CRYPT_OK) {
				ecc_free(&public_key);
				result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
				goto error_case;
			}

			/* find r = x1 mod order ; x1 = public_key.pubkey.x */
			error_code = ltc_mp.mpdiv(public_key.pubkey.x, order, NULL, r);
			if (error_code != CRYPT_OK) {
				ecc_free(&public_key);
				result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
				goto error_case;
			}

			/* verify if r == 0 */
			if (ltc_mp.compare_d(r, 0) == LTC_MP_EQ) {
				ecc_free(&public_key);
			} else {
				/* find s = (hash + xr)/k ; x = key.k, k = public_key.k */
				/* k = 1/k */
				error_code = ltc_mp.invmod(public_key.k, order, public_key.k);
				if (error_code != CRYPT_OK) {
					ecc_free(&public_key);
					result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
					goto error_case;
				}

				/* s = xr */
				error_code = ltc_mp.mulmod(key.k, r, order, s);
				if (error_code != CRYPT_OK) {
					ecc_free(&public_key);
					result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
					goto error_case;
				}

				/* s = hash +  xr */
				error_code = ltc_mp.add(hash, s, s);
				if (error_code != CRYPT_OK) {
					ecc_free(&public_key);
					result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
					goto error_case;
				}
				error_code = ltc_mp.mpdiv(s, order, NULL, s);
				if (error_code != CRYPT_OK) {
					ecc_free(&public_key);
					result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
					goto error_case;
				}

				/* s = (e + xr)/k */
				error_code = ltc_mp.mulmod(s, public_key.k, order, s);
				if (error_code != CRYPT_OK) {
					ecc_free(&public_key);
					result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
					goto error_case;
				}

				/* verify if s == 0 */
				if (ltc_mp.compare_d(s, 0) == LTC_MP_EQ) {
					ecc_free(&public_key);
				} else {
					break;
				}	
			}
		}
	}else{
		error = LLF_ECPKI_BuildPublKey(SignerPrivKey_ptr->DomainID,
			EphemerPublKeyIn_ptr, EphemerPublKeySizeBytes, &EphemerPublKey); 
		if (error != CRYPT_OK) {
			return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		}
		error_code =  ecc_import(EphemerPublKey.PublKeyDbBuff, 
			EphemerPublKey.valid_tag, (DomainID_t)EphemerPublKey.DomainID, &public_key);
		if (error_code != CRYPT_OK) {
			return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		}
	}

	orderSize = LLF_ECPKI_DomainIdToOrderSizeInBytes(SignerPrivKey_ptr->DomainID);

	if (SignerPrivKey_ptr->DomainID == CE2_ECPKI_DomainID_WMDRM10) {
		/* Build output signature from (s + r) */
		pos = 0;
		size = LLF_ECPKI_ECDSA_BUFFER_SIZE;
		error_code = mp_to_unsigned_bin_n(s, buffer, &size);
		if(error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
		for (frontZero = 0; size + frontZero < orderSize; frontZero++) {
			SignatureOut_ptr[pos] = 0x00;
			pos++;
		}
		memcpy(SignatureOut_ptr + pos, buffer, size);
		pos += size;

		size = LLF_ECPKI_ECDSA_BUFFER_SIZE;
		error_code = mp_to_unsigned_bin_n(r, buffer, &size);
		if(error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
		for (frontZero = 0; size + frontZero < orderSize; frontZero++) {
			SignatureOut_ptr[pos] = 0x00;
			pos++;
		}
		memcpy(SignatureOut_ptr + pos, buffer, size);
		pos += size;
		*SignatureOutSize_ptr = pos;
	} else {
		/* Build output signature from (r + s) */
		pos = 0;
		size = LLF_ECPKI_ECDSA_BUFFER_SIZE;
		error_code = mp_to_unsigned_bin_n(r, buffer, &size);
		if(error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
		for (frontZero = 0; size + frontZero < orderSize; frontZero++) {
			SignatureOut_ptr[pos] = 0x00;
			pos++;
		}
		memcpy(SignatureOut_ptr + pos, buffer, size);
		pos += size;

		size = LLF_ECPKI_ECDSA_BUFFER_SIZE;
		error_code = mp_to_unsigned_bin_n(s, buffer, &size);
		if(error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
		for (frontZero = 0; size + frontZero < orderSize; frontZero++) {
			SignatureOut_ptr[pos] = 0x00;
			pos++;
		}
		memcpy(SignatureOut_ptr + pos, buffer, size);
		pos += size;
		*SignatureOutSize_ptr = pos;
	}

error_case:
	ltc_deinit_multi(r, s, hash, order, NULL);
	fortuna_done(&prng);
	ecc_free(&key);
	return result;
} /* LLF_ECDSA_Sign */

/**
****************************************************************
* Function Name: 
*  LLF_ECDSA_Verify
*
* Inputs:
* @param UserPublKey_ptr [in] - A pointer to a user public key structure.
* @param HashMode [in] - The enumerator variable defines the hash function to be used.
* @param MessageDataIn_ptr [in] - Message data for calculating hash.   			
* @param MessageSizeInBytes [in] - Size of block of message data in bytes.       
* @param SignatureIn_ptr [in] - A pointer to a buffer for output of signature. 
* @param SignatureSizeBytes [in] - Size of signature, in bytes
*
* Outputs:
* @returns \b CE2Error_t 
*  - CE2_OK - on success
*  - Otherwise - error code:
*
* \brief \b 
* Description:
*  Performs all of the ECDSA verifying operations simultaneously.
*
* \b 
* Algorithm:
*  -# Create digest for input massage data
*  -# Initialization of LibTomCrypt primitives
*  -# Convert digest to little-endian form
*  -# Read signature value
*  -# Signature verifying
***************************************************************/
CE2Error_t LLF_ECDSA_Verify (
							 CE2_ECPKI_UserPublKey_t       *UserPublKey_ptr,        /*in*/
							 CE2_ECPKI_HASH_OpMode_t        HashMode,               /*in*/
							 DxUint8_t                      *SignatureIn_ptr,        /*in*/
							 DxUint32_t                      SignatureSizeBytes,     /*in*/
							 DxUint8_t                      *MessageDataIn_ptr,      /*in*/ 
							 DxUint32_t                      MessageSizeInBytes      /*in*/ )
{
	DxUint8_t buffer[LLF_ECPKI_ECDSA_BUFFER_SIZE];
	CE2Error_t result = CE2_OK, error = CE2_OK;
	DxUint32_t size, pos, orderSize;
	int error_code = CRYPT_OK;
	ecc_key key;
	ecc_point *mG = NULL, *mQ = NULL;
	void *r, *s, *v, *w, *u1, *u2, *hash, *order, *prime, *a;
	void *mp =  NULL;
	int isZeroHash = 0;

	/* Initialize ltc_mp structure */
	ltc_mp = ltm_desc;

	/* Create digest for input massage data */
	size = LLF_ECPKI_ECDSA_BUFFER_SIZE;
	error = LLF_ECPKI_HASH(HashMode, UserPublKey_ptr->DomainID,
		MessageDataIn_ptr, MessageSizeInBytes, buffer, &size);
	if (error != CE2_OK) 
		return error;

	/* Import signer public key to LibTomCrypt format */
	error_code =  ecc_import(UserPublKey_ptr->PublKeyDbBuff, 
		UserPublKey_ptr->valid_tag, (DomainID_t)UserPublKey_ptr->DomainID, &key);
	if (error_code != CRYPT_OK) {
		return CE2_LLF_ECPKI_MODULE_ERROR_BASE;
	}

	/* Initialization of LibTomCrypt primitives */
	error_code = ltc_init_multi(&r, &s, &v, &w, &u1, &u2, 
		&order, &hash, &prime, &a, NULL);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}
	mG = ltc_ecc_new_point();
	mQ = ltc_ecc_new_point();
	if (mQ  == NULL || mG == NULL) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}
	error_code  = ltc_mp.read_radix(order, (char *)ltc_ecc_sets[key.idx].order, 16);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}
	error_code  = ltc_mp.read_radix(prime, (char *)ltc_ecc_sets[key.idx].prime, 16);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}
	error_code  = ltc_mp.read_radix(mG->x, (char *)ltc_ecc_sets[key.idx].Gx, 16);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}
	error_code  = ltc_mp.read_radix(mG->y, (char *)ltc_ecc_sets[key.idx].Gy, 16);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}
	error_code  = ltc_mp.read_radix(a, (char *)ltc_ecc_sets[key.idx].A, 16);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}
	ltc_mp.set_int(mG->z, 1);  

	/* Convert digest to little-endian form  */
	error_code = ltc_mp.unsigned_read(hash, buffer, size);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}
	if (ltc_mp.compare_d(hash, 0) == LTC_MP_EQ) 
		isZeroHash = 1;

	orderSize = LLF_ECPKI_DomainIdToOrderSizeInBytes(UserPublKey_ptr->DomainID);
	if ( SignatureSizeBytes != 2 * orderSize )	{
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}

	if (UserPublKey_ptr->DomainID == CE2_ECPKI_DomainID_WMDRM10) {
		/* Read signature value (s and r) */
		pos = 0; 
		error_code = ltc_mp.unsigned_read(s, SignatureIn_ptr + pos, orderSize);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
		pos += orderSize;
		error_code = ltc_mp.unsigned_read(r, SignatureIn_ptr + pos, orderSize);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
	} else {
		/* Read signature value (r and s) */
		pos = 0; 
		error_code = ltc_mp.unsigned_read(r, SignatureIn_ptr + pos, orderSize);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
		pos += orderSize;
		error_code = ltc_mp.unsigned_read(s, SignatureIn_ptr + pos, orderSize);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
	}

	/* check for zero */
	if (ltc_mp.compare_d(r, 0) == LTC_MP_EQ || 
		ltc_mp.compare_d(s, 0) == LTC_MP_EQ || 
		ltc_mp.compare(r, order) != LTC_MP_LT || 
		ltc_mp.compare(s, order) != LTC_MP_LT) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
	}

	/* Start verifying */ 
	/* --------------- */ 
	/*  w  = s^-1 mod order */
	error_code = ltc_mp.invmod(s, order, w);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}

	/* u1 = hash*w */
	error_code = ltc_mp.mulmod(hash, w, order, u1);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}

	/* u2 = r*w */
	error_code = ltc_mp.mulmod(r, w, order, u2);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}

	if (isZeroHash) {
		/* if hash = 0 then u1 = hash*w = 0 -> u2*Q + u1*G = u2*Q*/
		/* find mG = u2*Q */
		error_code = ltc_mp.ecc_kptmul(u2, &key.pubkey, mG, prime, 1, a);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
	} else {
		/* find mG = u1*G */
		error_code = ltc_mp.ecc_kptmul(u1, mG, mG, prime, 0, a);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}

		/* find mQ = u2*Q */
		error_code = ltc_mp.ecc_kptmul(u2, &key.pubkey, mQ, prime, 0, a);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}

		/* find the montgomery mp */
		error_code = ltc_mp.montgomery_setup(prime, &mp);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}

		/* add them */
		error_code = ltc_mp.ecc_kptadd(mQ, mG, mG, prime, mp, a);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}

		/* reduce */
		error_code = ltc_mp.ecc_map(mG, prime, mp);
		if (error_code != CRYPT_OK) {
			result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
			goto error_case;
		}
	}

	/* v = X_x1 mod order */
	error_code = ltc_mp.mpdiv(mG->x, order, NULL, v);
	if (error_code != CRYPT_OK) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	}

	/* does v == r */
	if (ltc_mp.compare(v, r) != LTC_MP_EQ) {
		result = CE2_LLF_ECPKI_MODULE_ERROR_BASE;
		goto error_case;
	} 
	/* End verifying */ 
	/* ------------- */ 

error_case:
	ecc_free(&key);
	if (mG != NULL) {
		ltc_ecc_del_point(mG);
	}
	if (mQ != NULL) {
		ltc_ecc_del_point(mQ);
	}
	ltc_deinit_multi(r, s, v, w, u1, u2, order, hash, prime, a, NULL);
	if (mp != NULL) { 
		ltc_mp.montgomery_deinit(mp);
	}
	return result;
} /* LLF_ECDSA_Verify */



