/*****************************************************************************
 *
 * Copyright (c) 2018-19, Eduard Gerlits, Paula Herber, Josef Treus
 * Westfälische Wilhelms-Universität Münster, AG Embedded Systems 
 * All rights reserved.
 *
 * This file is part of SysCSec
 *
 * SysCSec is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * SysCSec is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SysCSec.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *  Please report any problems or bugs to: sysc.sec@uni-muenster.de
 *
 ****************************************************************************/

#include "FW.h"


void FWModule::exe(){
	ecc_status.write(_FW_IDLE_);
	sc_uint<4> control;
	sc_biguint<160> result0;
	sc_biguint<160> result1;
	sc_biguint<160> result2;
	while(true){
		control = ecc_control->read();

		// Identify operation and write parameters
		switch(control){
			case _FW_NOOP_:
				wait();
				break;

			case _FW_ADD_:
				ecc_status.write(_FW_PROCESSING_);			 
				mod_add(data_in0->read(), data_in1->read(), data_in2->read(), result0);
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);
				wait();
				break;

			case _FW_SUB_:
				ecc_status.write(_FW_PROCESSING_);
				mod_sub(data_in0->read(), data_in1->read(), data_in2->read(), result0);
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);
				wait();				
				break;

			case _FW_MUL_:
				ecc_status.write(_FW_PROCESSING_);
				mod_mul(data_in0->read(), data_in1->read(), data_in2->read(), result0);
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);
				wait();				
				break;

			case _FW_DIV_:
				ecc_status.write(_FW_PROCESSING_);
				// num_check();
				mod_div(data_in0->read(), data_in1->read(), data_in2->read(), result0);
				// num_check();
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);
				wait();				
				break;

			case _FW_PT_ADD_:
				ecc_status.write(_FW_PROCESSING_);
				// if the operation should be executed with UAF (unified addition formulae)
				if(useUAF){
					unified_addition_formulae(data_in0->read(), data_in1->read(), data_in2->read(), data_in3->read(), data_in4->read(), data_in5->read(), result0, result1);
				}else{
					pt_add(data_in0->read(), data_in1->read(), data_in2->read(), data_in3->read(), data_in4->read(), data_in5->read(), result0, result1);
				}
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);
				data_out1->write(result1);
				wait();								
				break;

			case _FW_PT_DOUBLE_:
				ecc_status.write(_FW_PROCESSING_);
				// if the operation should be executed with UAF (unified addition formulae)
				if(useUAF){
					unified_addition_formulae(data_in0->read(), data_in1->read(),data_in0->read(), data_in1->read(), data_in2->read(), data_in3->read(), result0, result1);
				}else{
					pt_double(data_in0->read(), data_in1->read(), data_in2->read(), data_in3->read(), result0, result1);
				}
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);
				data_out1->write(result1);
				wait();									
				break;
			case _FW_PT_MULTIPLY_:
				ecc_status.write(_FW_PROCESSING_);
				pt_multiply(data_in0->read(), data_in1->read(), data_in2->read(), data_in3->read(), data_in4->read(), result0, result1);
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);
				data_out1->write(result1);
				wait();									
				break;

			case _FW_ECDSA_SIGN_:
				ecc_status.write(_FW_PROCESSING_);
				std::cout << std::endl;
				ecdsa_sign(data_in0->read(), data_in1->read(), data_in2->read(), data_in3->read(), data_in4->read(), data_in5->read(), data_in6->read(), result0, result1);
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);
				data_out1->write(result1);				
				wait();
				break;

			case _FW_ECDSA_VERIFY_:
				ecc_status.write(_FW_PROCESSING_);
				ecdsa_verify(data_in0->read(), data_in1->read(), data_in2->read(), data_in3->read(), data_in4->read(), data_in5->read(), data_in6->read(), data_in7->read(), data_in8->read(), data_in9->read(), result0);
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);			
				wait();
				break;

			case _FW_ECDSA_KEY_GENERATION_:
				ecc_status.write(_FW_PROCESSING_);
				// num_check();
				ecdsa_key_pair_generation(data_in0->read(), data_in1->read(), data_in2->read(), data_in3->read(), data_in4->read(), data_in5->read(), result0, result1, result2);
				// num_check();
				ecc_status.write(_FW_FINISHED_);
				data_out0->write(result0);
				data_out1->write(result1);
				data_out2->write(result2);
				wait();
				break;

		}

	}
}

/*
Checks whether a given point lies on an elliptic curve with parameters a and b
p_x: x coordinate of point P
p_y: y coordinate of point P
m: modulo value
ec_param_a: parameter a of the elliptic curve
ec_param_b: parameter b of the elliptic curve

return: true if (y^2 = x^3 + ax + b) holds
*/
bool FWModule::isOnCurve(sc_biguint<160> p_x, 
							sc_biguint<160> p_y, 
							sc_biguint<160> m, 
							sc_biguint<160> ec_param_a, 
							sc_biguint<160> ec_param_b) 
{
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	if(isInfinityPoint(p_x, p_y)) return true;

	sc_biguint<160> y_squared; 
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
	mod_mul(p_y, p_y, m, y_squared);

	sc_biguint<160> x_squared;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
	mod_mul(p_x, p_x, m, x_squared);

	sc_biguint<160> x_cubic;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
	mod_mul(x_squared, p_x, m, x_cubic);

	sc_biguint<160> a_x;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
	mod_mul(ec_param_a, p_x, m, a_x);

	sc_biguint<160> temp;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
	mod_add(x_cubic, a_x,m,temp);

	sc_biguint<160> right_expression;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
	mod_add(temp, ec_param_b, m, right_expression);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	return (y_squared==right_expression);
}

/*
writes (0,0) to the parameters out_x and out_y (by reference). (0,0) represents an infinite point and the neutral element for point addition.
*/
void FWModule::writeInfinityPoint(sc_biguint<160> &out_x, sc_biguint<160> &out_y){
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	out_x=0;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	out_y=0;
}

/*
return true if points P and Q have the same coordinates
*/
bool FWModule::equals(sc_biguint<160> p_x, sc_biguint<160> p_y, sc_biguint<160> q_x, sc_biguint<160> q_y){
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	return (p_x==q_x && p_y == q_y);
}

/*
returns true if point P is (0,0)
*/
bool FWModule::isInfinityPoint(sc_biguint<160> p_x, sc_biguint<160> p_y){
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	return (p_x==0 && p_y==0);
}

/*
returns true if P+Q=(0,0), i.e. if P and Q are inverse w.r.t point addition
*/
bool FWModule::isInverseTo(sc_biguint<160> p_x, sc_biguint<160> p_y, sc_biguint<160> q_x, sc_biguint<160> q_y, sc_biguint<160> m){
	sc_biguint<160> neg_p_y;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
	mod_sub(0,p_y,m, neg_p_y);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	return (p_x==q_x && neg_p_y==q_y);
}

/*
write to MAP ports. not externally accessible
*/
void FWModule::MAP_write(sc_biguint<160> x, sc_biguint<160> y, sc_biguint<160> m, sc_uint<3> opcode){
	map_x->write(x);
	map_y->write(y);
	map_m->write(m);
	map_opcode->write(opcode);
	wait();
}

/*
read from MAP ports. not externally accessible
*/
sc_biguint<160> FWModule::MAP_read(){
	// blocking
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	while(map_done->read()==false){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		wait();
	}
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	sc_biguint<160> out = map_out->read();
	return out;
}

/*
reset MAP
*/
void FWModule::MAP_reset(){
	map_opcode->write(0);
	wait();
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	while(map_done->read()==true){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		// std::cout << "[" << sc_time_stamp() << "]: " << "Blocking map_done->read()==true..." << std::endl;
		wait();
	}	
}

/*
Modular addition. Result is written to out.
*/
void FWModule::mod_add(
						sc_biguint<160> x, 
						sc_biguint<160> y, 
						sc_biguint<160> m, 
						sc_biguint<160> &out
						)
{
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
	x = x % m;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
	y = y % m;
	MAP_write(x, y, m, _OPCODE_ADD_);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	out = MAP_read();
	MAP_reset();
}

/*
Modular subtraction.  Result is written to out.
*/
void FWModule::mod_sub(sc_biguint<160> x, sc_biguint<160> y, sc_biguint<160> m, sc_biguint<160> &out){
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
	x = x % m;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
	y = y % m;
	MAP_write(x, y, m, _OPCODE_SUB_);
	out = MAP_read();
	MAP_reset();
}

/*
Modular multiplication. Result is written to out.
*/
void FWModule::mod_mul(sc_biguint<160> x, sc_biguint<160> y, sc_biguint<160> m, sc_biguint<160> &out){
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
	x = x % m;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
	y = y % m;
	MAP_write(x, y, m, _OPCODE_MUL_);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	out = MAP_read();
	MAP_reset();
}

/*
Modular division. Result is written to out.
*/
void FWModule::mod_div(
						sc_biguint<160> x, 
						sc_biguint<160> y, 
						sc_biguint<160> m, 
						sc_biguint<160> &out
						)
{
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
	x = x % m;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
	y = y % m;
	MAP_write(x, y, m, _OPCODE_DIV_);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	out = MAP_read();
	MAP_reset();
}

/*
* This method combines point addition and multiplication in one unified function.
* Is executed whenever the parameter useUAF is set to "UAF".
* (p1_x, p1_y): first point
* (p2_x, p2_y): second point
* a: parameters of the elliptic curve
* m: Modulo value from GF(m)
* (out_x, out_y): output point
*/
void FWModule::unified_addition_formulae(
					sc_biguint<160> p1_x, 
					sc_biguint<160> p1_y, 
					sc_biguint<160> p2_x, 
					sc_biguint<160> p2_y,
					sc_biguint<160> a, 
					sc_biguint<160> m, 
					sc_biguint<160> &out_x, 
					sc_biguint<160> &out_y){
						// if the given points are inverse, return infinity point (0,0)
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	if(isInverseTo(p1_x, p1_y, p2_x, p2_y, m)){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
		writeInfinityPoint(out_x, out_y);
	}
	// if one of the points is an infinity point, return the other point
	else if(isInfinityPoint(p1_x, p1_y)){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		out_x = p2_x;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		out_y = p2_y;
		
	}

	else if(isInfinityPoint(p2_x, p2_y)){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		out_x = p1_x;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		out_y = p1_y;
		
	}
	
	/* execute Unified Addition Formulae */
	else{
		//x1²
		sc_biguint<160> p2_x_quad;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(p2_x, p2_x, m, p2_x_quad);
		//x1*x0
		sc_biguint<160> p2_x_mul_p1_x;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(p2_x, p1_x, m, p2_x_mul_p1_x);
		//x0²
		sc_biguint<160> p1_x_quad;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(p1_x, p1_x, m, p1_x_quad);
		//x1²+x1*x0
		sc_biguint<160> nom1;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(p2_x_quad, p2_x_mul_p1_x, m, nom1);
		//x1²+x1*x0+x0²
		sc_biguint<160> nom2;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(nom1, p1_x_quad, m, nom2);
		//x1²+x1*x0+x0²
		sc_biguint<160> nom;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(nom2, a, m, nom);

		//y1+y2
		sc_biguint<160> denom;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(p2_y, p1_y, m, denom);

		//(x1²+x1*x0+x0²)/(y1+y2)
		sc_biguint<160> bracket_content;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, DIV);
		mod_div(nom, denom, m, bracket_content);

		//((x1²+x1*x0+x0²)/(y1+y2))²
		sc_biguint<160> bracket_squared;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(bracket_content, bracket_content, m, bracket_squared);

		//((x1²+x1*x0+x0²)/(y1+y2))²-x0
		sc_biguint<160> bracket_squared_minus_x0;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(bracket_squared, p1_x, m, bracket_squared_minus_x0);

		//out_x = ((x1²+x1*x0+x0²)/(y1+y2))²-x0-x1
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(bracket_squared_minus_x0, p2_x, m, out_x);


		//(x0-x2)
		sc_biguint<160> x0_minus_out_x;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(p1_x, out_x, m, x0_minus_out_x);
		
		//(x1²+x1*x0+x0²)/(y1+y2) * (x0-x2)
		sc_biguint<160> brackets_mult_other_brackets;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(bracket_content, x0_minus_out_x, m, brackets_mult_other_brackets);

		//out_y = (x1²+x1*x0+x0²)/(y1+y2) * (x0-x2) - y0
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(brackets_mult_other_brackets, p1_y, m, out_y);
	}
}
/*
point addition

(p1_x, p1_y): first point
(p2_x, p2_y): second point
a: elliptic curve
m: Modulo value from GF(m)
(out_x, out_y): output point
*/
void FWModule::pt_add(
					sc_biguint<160> p1_x, 
					sc_biguint<160> p1_y, 
					sc_biguint<160> p2_x, 
					sc_biguint<160> p2_y,
					sc_biguint<160> a, 
					sc_biguint<160> m, 
					sc_biguint<160> &out_x, 
					sc_biguint<160> &out_y
					)
{
	// if the two points are inverse, return infinity point (0,0)
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	if(isInverseTo(p1_x, p1_y, p2_x, p2_y, m)){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
		writeInfinityPoint(out_x, out_y);
	}
	// if one of the points is an infinity point, return the other
	else if(isInfinityPoint(p1_x, p1_y)){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		out_x = p2_x;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		out_y = p2_y;
		
	}

	else if(isInfinityPoint(p2_x, p2_y)){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		out_x = p1_x;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		out_y = p1_y;
		
	}

	// if the points are equal, perform point doubling
	else if(equals(p1_x, p1_y, p2_x, p2_y)){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
		// execute operation with UAF if useUAF is set
		if(useUAF){
			unified_addition_formulae(p1_x, p1_y, p1_x, p1_y, a, m,out_x, out_y);
		}else{
			pt_double(p1_x, p1_y, a, m,out_x, out_y);
		}
	}

	// compute point addition
	else{
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		// (y1-y2)
		sc_biguint<160> nom;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(p1_y, p2_y, m, nom);
		
		// (x1-x2)
		sc_biguint<160> denom;	
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(p1_x, p2_x, m, denom);
		
		// s=(y1-y2)/(x1-x2)
		sc_biguint<160> s;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, DIV);
		mod_div(nom, denom, m, s);
		
		// s^2
		sc_biguint<160> s_squared;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(s, s, m, s_squared); 
		
		// s^2 - x1
		sc_biguint<160> s_squared_d;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(s_squared, p1_x, m, s_squared_d);
		
		// out_x=s^2 - x1 - x2
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(s_squared_d, p2_x,m, out_x);
		
		// out_x - x1
		sc_biguint<160> x_diff;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(p1_x, out_x, m, x_diff);
		
		// s * (out_x - x1)
		sc_biguint<160> s_x_diff;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(s, x_diff, m, s_x_diff);
		
		// out_y = s*(out_x - x1) - y1 
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(s_x_diff, p1_y, m, out_y);
		
	}
}

/*
point doubling

(p1_x, p1_y): point
a: elliptic curve
m: Modulo value from GF(m)
(out_x, out_y): output point
*/
void FWModule::pt_double(
						sc_biguint<160> p1_x, 
						sc_biguint<160> p1_y, 
						sc_biguint<160> a, 
						sc_biguint<160> m, 
						sc_biguint<160> &out_x, 
						sc_biguint<160> &out_y)
{
	// if it is a self inverse point w.r.t. point addition, return infinity point (0,0)
	
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);	
	if(isInverseTo(p1_x, p1_y, p1_x, p1_y, m)){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
		writeInfinityPoint(out_x, out_y);
		
	}

	// compute point doubling
	else {
		// x^2
		sc_biguint<160> x_squared;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(p1_x, p1_x, m, x_squared);
		

		// 2x^2
		sc_biguint<160> twox_squared;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(x_squared, x_squared, m, twox_squared);
		
		// 3x^2
		sc_biguint<160> threex_squared;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(twox_squared, x_squared, m, threex_squared);
		
		// 3x^2 + a
		sc_biguint<160> nom;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(threex_squared, a, m, nom);
		
		// 2y
		sc_biguint<160> denom;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(p1_y, p1_y, m, denom);
		
		// s = (3x^2 + a) / 2y
		sc_biguint<160> s;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, DIV);
		mod_div(nom, denom, m, s);
		
		// s^2
		sc_biguint<160> s_squared;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(s, s, m, s_squared);
		
		// 2x
		sc_biguint<160> two_p1_x;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(p1_x, p1_x, m, two_p1_x);
		
		// out_x = s^2-2x
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(s_squared, two_p1_x, m, out_x);
		
		// x1-out_x
		sc_biguint<160> x_diff;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(p1_x, out_x, m, x_diff);
		
		// s*(x1-out_x)
		sc_biguint<160> s_x_diff;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(s, x_diff, m, s_x_diff);
		
		// out_y = y1-s*(x1-out_x)
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
		mod_sub(s_x_diff, p1_y, m, out_y);	
		
	}
}

/*
scalar point multiplication on elliptic curve
*/
void FWModule::pt_multiply(
							sc_biguint<160> p1_x, 
							sc_biguint<160> p1_y, 
							sc_biguint<160> val, 
							sc_biguint<160> a, 
							sc_biguint<160> m, 
							sc_biguint<160> &out_x, 
							sc_biguint<160> &out_y)
{
	// accumulation point, initialized with (0,0)
	sc_biguint<160> temp_x;
	sc_biguint<160> temp_y;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
	writeInfinityPoint(temp_x, temp_y);	
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	while(val>0){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		if(val[0]==1){
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
			// use UAF if useUAF is set
			if(useUAF){
				unified_addition_formulae(temp_x, temp_y, p1_x, p1_y, a, m, temp_x, temp_y);
			}else{
				pt_add(temp_x, temp_y, p1_x, p1_y, a, m, temp_x, temp_y);
			}
		}
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
		// use UAF if useUAF is set
		if(useUAF){
			unified_addition_formulae(p1_x, p1_y, p1_x, p1_y, a, m, p1_x, p1_y);
		}else{
			pt_double(p1_x, p1_y, a, m, p1_x, p1_y);
		}
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, SHIFT);	
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);		
		val=val>>1;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	}
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	out_x=temp_x;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	out_y=temp_y;
}

/*
ECDSA signature algorithm
*/
void FWModule::ecdsa_sign(
										sc_biguint<160> ec_param_a,  
										sc_biguint<160> p_x, 
										sc_biguint<160> p_y,
										sc_biguint<160> d, 
										sc_biguint<160> n, 
										sc_biguint<160> m, 
										sc_biguint<160> hash,
										sc_biguint<160> &r, 
										sc_biguint<160> &s)
{
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	sc_biguint<160> max = n-2;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	sc_biguint<160> min = 2;

	// point
	sc_biguint<160> q_x;
	sc_biguint<160> q_y;

	// random number
	sc_biguint<160> k; 
	do
	{

		// compute r
		do 
		{
			// r = R.x
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
			k = rand() % (max-min + 1) + min;
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
			pt_multiply(p_x,p_y,k,ec_param_a,m,q_x,q_y);
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
			r = q_x % n;
			RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
		} while(r==0);

		sc_biguint<160> temp;

		// s = (H(m) + d*r)/k
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
		mod_mul(d,r,n,temp);		
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
		mod_add(hash,temp,n,temp);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, DIV);
		mod_div(temp,k,n,temp);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		s = temp % n;
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	} while(s==0);
}

/*
ECDSA verification algorithm
*/
void FWModule::ecdsa_verify(sc_biguint<160> ec_param_a, 
						sc_biguint<160> p_x, 
						sc_biguint<160> p_y,
						sc_biguint<160> q_x, 
						sc_biguint<160> q_y, 
						sc_biguint<160> r,
						sc_biguint<160> s,
						sc_biguint<160> n,
						sc_biguint<160> m,
						sc_biguint<160> hash,
						sc_biguint<160> &verification_status)
{

	// s_inv = 1/s
	sc_biguint<160> s_inv;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, DIV);
	mod_div(1, s, n, s_inv);

	// u1 = H(m)*s_inv
	sc_biguint<160> u1;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
	mod_mul(hash, s_inv, n, u1);

	// u2 = r * s_inv
	sc_biguint<160> u2;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MUL);
	mod_mul(r, s_inv, n, u2);

	// V = u1*Public_Key+u2*generator point
	sc_biguint<160> p_temp_x;
	sc_biguint<160> p_temp_y;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
	pt_multiply(p_x, p_y, u1, ec_param_a, m, p_temp_x, p_temp_y);
	sc_biguint<160> q_temp_x;
	sc_biguint<160> q_temp_y;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
	pt_multiply(q_x, q_y, u2, ec_param_a, m, q_temp_x, q_temp_y);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	if(equals(p_temp_x, p_temp_y, q_temp_x, q_temp_y)){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);

		if(useUAF){
			unified_addition_formulae(p_temp_x, p_temp_y, p_temp_x, p_temp_y, ec_param_a, m, p_temp_x, p_temp_y);
		}else{
			pt_double(p_temp_x, p_temp_y, ec_param_a, m, p_temp_x, p_temp_y);
		}
	}
	else {
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);

		if(useUAF){
			unified_addition_formulae(p_temp_x, p_temp_y, q_temp_x, q_temp_y, ec_param_a, m, p_temp_x, p_temp_y);
		}else{
			pt_add(p_temp_x, p_temp_y, q_temp_x, q_temp_y, ec_param_a, m, p_temp_x, p_temp_y);
		}
	}
	
	// v = V.x
	sc_biguint<160> v = p_temp_x % n;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, COMPARE);
	if(v==r){
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		verification_status = _FW_ECDSA_VERIFICATION_SUCCESS_;
	}
	else{
		RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
		verification_status = _FW_ECDSA_VERIFICATION_FAILURE_;
	}
}

/*
compute private and public key from elliptic curve according to ECDSA
*/
void FWModule::ecdsa_key_pair_generation(
										sc_biguint<160> ec_param_a, 
										sc_biguint<160> ec_param_b, 
										sc_biguint<160> p_x, 
										sc_biguint<160> p_y, 
										sc_biguint<160> n, 
										sc_biguint<160> m, 
										sc_biguint<160> &d, 
										sc_biguint<160> &q_x, 
										sc_biguint<160> &q_y)
{
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	sc_biguint<160> max = n-2;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	sc_biguint<160> min = 2;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, SUB);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, MOD);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ADD);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, ASSIGN);
	d = rand() % (max-min + 1) + min;
	RessourceHandler::getInstance()->logOperationPowerExpense(mode, CALL);
	pt_multiply(p_x,p_y,d,ec_param_a,m,q_x,q_y);
}

void FWModule::num_check(){
	std::cout << "data_in0->num_available(): " << data_in0->num_available() << std::endl;
	std::cout << "data_in1->num_available(): " << data_in1->num_available() << std::endl;
	std::cout << "data_in2->num_available(): " << data_in2->num_available() << std::endl;
	std::cout << "data_in3->num_available(): " << data_in3->num_available() << std::endl;
	std::cout << "data_in4->num_available(): " << data_in4->num_available() << std::endl;
	std::cout << "data_in5->num_available(): " << data_in5->num_available() << std::endl;
	std::cout << "data_in6->num_available(): " << data_in6->num_available() << std::endl;
	std::cout << "data_in7->num_available(): " << data_in7->num_available() << std::endl;
	std::cout << "data_in8->num_available(): " << data_in8->num_available() << std::endl;
	std::cout << "data_in9->num_available(): " << data_in9->num_available() << std::endl;
	std::cout << std::endl;
}
