/*****************************************************************************
 *
 * 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
 *
 ****************************************************************************/


#ifndef _CLIENT_H_
#define _CLIENT_H_

#include <iostream>
#include <systemc.h>
#include <string>

#include "MAP.h"
#include "FW.h"
#include "XOREncrypt.h"
#include "IO.h"
#include "SHA1Module.h"

#include "RessourceHandler.cpp"

SC_MODULE(Client){

	/// Clock
	sc_clock clk;

	/// in Ports
	//sc_port<sc_clock> clk;
	sc_fifo_in<sc_biguint<960> > in_ca;
	sc_fifo_in<sc_biguint<960> > in_server;
	
	/// out Ports
	sc_fifo_out<sc_biguint<960> > out_ca;
	sc_fifo_out<sc_biguint<960> > out_server;

	/// Module
	MAPModule map;
	FWModule fw;
	XOREncryptionModule xor_enc;
	IOModule io;
	SHA1Module sha1;

	/// Internal channels

	// FW->MAP Signals
	sc_signal<sc_biguint<160> > x_sig;
	sc_signal<sc_biguint<160> > y_sig;
	sc_signal<sc_biguint<160> > m_sig;
	sc_signal<sc_uint<3> > opcode_sig;

	// MAP->FW Signals
	sc_signal<sc_biguint<160> > out_sig;
	sc_signal<bool> done_sig;


	// FW In-Fifos 
	sc_fifo<sc_uint<4> > ecc_control_fifo;
	sc_fifo<sc_biguint<160> > data_in0_fifo;
	sc_fifo<sc_biguint<160> > data_in1_fifo;
	sc_fifo<sc_biguint<160> > data_in2_fifo;
	sc_fifo<sc_biguint<160> > data_in3_fifo;
	sc_fifo<sc_biguint<160> > data_in4_fifo;
	sc_fifo<sc_biguint<160> > data_in5_fifo;
	sc_fifo<sc_biguint<160> > data_in6_fifo;
	sc_fifo<sc_biguint<160> > data_in7_fifo;
	sc_fifo<sc_biguint<160> > data_in8_fifo;
	sc_fifo<sc_biguint<160> > data_in9_fifo;

	// FW Out-Fifos
	sc_fifo<sc_uint<2> > ecc_status_fifo;
	sc_fifo<sc_biguint<160> > data_out0_fifo;
	sc_fifo<sc_biguint<160> > data_out1_fifo;
	sc_fifo<sc_biguint<160> > data_out2_fifo;
	sc_fifo<sc_biguint<160> > data_out3_fifo;
	sc_fifo<sc_biguint<160> > data_out4_fifo;
	sc_fifo<sc_biguint<160> > data_out5_fifo;


	// XOREncrypt In-Fifos
	sc_fifo<sc_biguint<160> > xor_key_in_fifo;
	sc_fifo<sc_biguint<160> > xor_data_in_fifo;

	// XOREncrypt Out-Fifos
	sc_fifo<sc_biguint<160> > xor_data_out_fifo; 


	// IO In-Fifos
	sc_fifo<sc_uint<3> > io_control_in_fifo;
	sc_fifo<sc_biguint<160> > io_data_in_fifo;
	sc_fifo<sc_biguint<960> > p_data_in_fifo;

	// IO Out-Fifos
	sc_fifo<sc_uint<3> > io_status_fifo;
	sc_fifo<sc_biguint<160> > io_data_out_fifo;
	sc_fifo<sc_biguint<960> > p_data_out_fifo;  

	// SHA1 In-Fifos
	sc_fifo<std::string> sha1_in_fifo;

	// SHA1 Out-Fifos
	sc_fifo<sc_biguint<160> > sha1_out_fifo;

	/// Internal ECC parameters

	// curve parameters for y² = x³ + ax + b
	sc_biguint<160> ec_param_a;
	sc_biguint<160> ec_param_b;

	// prime field modulo m
	sc_biguint<160> field_param_m;

	// point on curve
	sc_biguint<160> ec_pt_x;
	sc_biguint<160> ec_pt_y;

	// order of point n
	sc_biguint<160> ec_pt_order_n;

	/// keys

	// Private Key
	sc_biguint<160> d_u;

	// Public Key
	sc_biguint<160> q_x;
	sc_biguint<160> q_y;

	// Public Key CA
	sc_biguint<160> q_ca_x;
	sc_biguint<160> q_ca_y;

	// ECDH Key
	sc_biguint<160> q_k_x;

	// Client Info
	sc_biguint<160> I_u;
	sc_biguint<160> t_u;

	// Client Info Hash
	sc_biguint<160> e_u;

	// Digital Signature of the Client Info Hash
	sc_biguint<160> r_u;
	sc_biguint<160> s_u;

	void client_main();

	// helper functions
	void initialise_ecc_parameter(sc_biguint<160> ec_param_a_, 
								sc_biguint<160> ec_param_b_, 
								sc_biguint<160> field_param_m_, 
								sc_biguint<160> ec_pt_x_,
								sc_biguint<160> ec_pt_y_,
								sc_biguint<160> ec_pt_order_n_);

	void ecdsa_generate_key_pair(sc_biguint<160> &d, sc_biguint<160> &q_x, sc_biguint<160> &q_y);

	void send_data_ca(sc_biguint<960> p_data);
	void receive_data_ca(sc_biguint<960> &p_data);
	void send_data_server(sc_biguint<960> p_data);
	void receive_data_server(sc_biguint<960> &p_data);

	void symmetric_data_encryption(sc_biguint<160> q_k_x, 
										sc_biguint<160> &e_enc, 
										sc_biguint<160> &r_enc, 
										sc_biguint<160> &s_enc, 
										sc_biguint<160> &t_enc, 
										sc_biguint<160> &g_u_enc, 
										sc_biguint<160> &g_s_enc);


	void pack_data(sc_biguint<160> data0, 
					sc_biguint<160> data1, 
					sc_biguint<160> data2, 
					sc_biguint<160> data3, 
					sc_biguint<160> data4, 
					sc_biguint<160> data5,
					sc_biguint<960> &p_data);
	
	void unpack_data(sc_biguint<960> p_data, 
						sc_biguint<160> &data0, 
						sc_biguint<160> &data1,
						sc_biguint<160> &data2, 
						sc_biguint<160> &data3,
						sc_biguint<160> &data4, 
						sc_biguint<160> &data5);

	void hash_data(sc_biguint<160> first, sc_biguint<160> second, sc_biguint<160> third, sc_biguint<160> &hash);

	void pt_multiply(sc_biguint<160> q_x, sc_biguint<160> q_y, sc_biguint<160> d, sc_biguint<160> &out_x, sc_biguint<160> &out_y);

	sc_biguint<160> random_number_generation();

	bool ecdsa_verify(sc_biguint<160> hash, sc_biguint<160> r, sc_biguint<160> s);
	
	SC_HAS_PROCESS(Client);
	Client(sc_module_name instname, 
			bool useUAF,
			sc_biguint<160> ec_param_a_, 
			sc_biguint<160> ec_param_b_,
			sc_biguint<160> m_, 
			sc_biguint<160> g_x_, 
			sc_biguint<160> g_y_, 
			sc_biguint<160> n_ 
			) : sc_module(instname), clk("clk", 20, SC_NS), map("map", 1), fw("fw",useUAF, 1), xor_enc("xor_enc", 1), io("io"), sha1("sha1")
	{
		initialise_ecc_parameter(ec_param_a_, ec_param_b_, m_, g_x_, g_y_, n_);
		/// merge internal channels

		// Clock
		fw.clk(clk);
		map.clk(clk);

		// FW->MAP Signals
		fw.map_x(x_sig);
		map.x(x_sig);

		fw.map_y(y_sig);
		map.y(y_sig);

		fw.map_m(m_sig);
		map.m(m_sig);

		fw.map_opcode(opcode_sig);
		map.opcode(opcode_sig);



		// MAP->FW Signals
		fw.map_out(out_sig);
		map.out(out_sig);

		fw.map_done(done_sig);
		map.done(done_sig);



		// FW In-Fifo
		fw.ecc_control(ecc_control_fifo);
		fw.data_in0(data_in0_fifo);
		fw.data_in1(data_in1_fifo);
		fw.data_in2(data_in2_fifo);
		fw.data_in3(data_in3_fifo);
		fw.data_in4(data_in4_fifo);
		fw.data_in5(data_in5_fifo);
		fw.data_in6(data_in6_fifo);
		fw.data_in7(data_in7_fifo);
		fw.data_in8(data_in8_fifo);
		fw.data_in9(data_in9_fifo);

		// FW Out-Fifos
		fw.ecc_status(ecc_status_fifo);
		fw.data_out0(data_out0_fifo);
		fw.data_out1(data_out1_fifo);
		fw.data_out2(data_out2_fifo);
		fw.data_out3(data_out3_fifo);
		fw.data_out4(data_out4_fifo);
		fw.data_out5(data_out5_fifo);



		// XOREncrypt In-Fifos
		xor_enc.key_in(xor_key_in_fifo);
		xor_enc.data_in(xor_data_in_fifo);

		// XOREncrypt Out-Fifos
		xor_enc.data_out(xor_data_out_fifo); 



		// IO In-Fifos
		io.io_control_in(io_control_in_fifo);
		io.data_in(io_data_in_fifo);
		io.p_data_in(p_data_in_fifo);

		// IO Out-Fifos
		io.io_status(io_status_fifo);
		io.data_out(io_data_out_fifo);
		io.p_data_out(p_data_out_fifo);  

		// SHA1 In-Fifos
		sha1.in(sha1_in_fifo);

		// SHA1 Out-Fifos
		sha1.out(sha1_out_fifo);

		//initialise_ecc_parameter(ec_param_a, ec_param_b, m, g_x, g_y, n);

		// SHA1 Out-Fifos

		SC_THREAD(client_main);
		//dont_initialize();
	}

};

#endif
