#include "sgx_eid.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "ProtobufLAMessages.h"
#include <stdio.h>

#include "sgx_trts.h"
#include "sgx_utils.h"
#include "error_codes.h"
#include "sgx_ecp_types.h"
#include "sgx_thread.h"
#include <map>
#include "sgx_dh.h"
#include "dh_session_protocol.h"
#include "sgx_tcrypto.h"
#include "datatypes.h"
#include "SgxProtobufLAInitiator_Transforms.h"
#define MAX_SESSION_COUNT  16
#define SGX_CAST(type, item) ((type)(item))
#include <string.h>
#include "crypto.h"
#include "stdio.h"

dh_session_t global_session_info;

sgx_dh_session_t sgx_dh_session;
//  sgx_key_128bit_t dh_aek;        // Session Key

uint32_t verify_peer_enclave_trust(sgx_dh_session_enclave_identity_t* peer_enclave_identity, uint8_t* expected_mr_enclave, uint8_t* expected_mr_signer);


uint32_t process_protobuf_dh_msg1_generate_protobuf_dh_msg2(protobuf_sgx_dh_msg1_t& protobuf_msg1, protobuf_sgx_dh_msg2_t& protobuf_msg2, uint32_t* session_id)
{
  sgx_dh_msg1_t dh_msg1;            //Diffie-Hellman Message 1
  sgx_dh_msg2_t dh_msg2;
  memset(&dh_msg1, 0, sizeof(sgx_dh_msg1_t));
  uint32_t ret_status;

  if(decode_msg1_from_protobuf(protobuf_msg1, &dh_msg1)!=0)
    return -1;

  //Intialize the session as a session initiator
  ret_status = sgx_dh_init_session(SGX_DH_SESSION_INITIATOR, &sgx_dh_session);
  if(ret_status != SGX_SUCCESS)
    return ret_status;

  //Process the message 1 obtained from desination enclave and generate message 2
  ret_status = sgx_dh_initiator_proc_msg1(&dh_msg1, &dh_msg2, &sgx_dh_session);
  if(SGX_SUCCESS != ret_status)
    return ret_status;

  encode_msg2_to_protobuf(protobuf_msg2, &dh_msg2);
  return 0;
}

uint32_t process_protobuf_dh_msg3(protobuf_sgx_dh_msg3_t& protobuf_msg3, uint32_t* session_id) {

  uint32_t ret_status;
  sgx_dh_msg3_t dh_msg3;
  sgx_key_128bit_t dh_aek;        // Session Key
  sgx_dh_session_enclave_identity_t responder_identity;

  memset(&dh_aek,0, sizeof(sgx_key_128bit_t));

  if(decode_msg3_from_protobuf(protobuf_msg3, &dh_msg3)!=0)
    return -1;

  //Process Message 3 obtained from the destination enclave
  ret_status = sgx_dh_initiator_proc_msg3(&dh_msg3, &sgx_dh_session, &dh_aek, &responder_identity);
  if(SGX_SUCCESS != ret_status)
    return ret_status;

  // Verify the identity of the destination enclave
  ret_status = verify_peer_enclave_trust(&responder_identity, NULL, NULL);
  if(ret_status != 0)
    return ret_status;

  memcpy(global_session_info.active.AEK, &dh_aek, sizeof(sgx_key_128bit_t));
  global_session_info.session_id = 1; // TODO: session_id;
  global_session_info.active.counter = 0;
  global_session_info.status = ACTIVE;
  memset(&dh_aek,0, sizeof(sgx_key_128bit_t));

  return 0;
}

uint32_t generate_encrypted_rsa_keypair_hash(uint8_t* op_ciphertext, uint8_t* op_encrypted_tag)
{
	uint8_t hash[32]; uint32_t return_status;
	unsigned char key[16]; uint32_t count;
	for(count=0;count<16;count++)
		key[count]=global_session_info.active.AEK[count]; 
	return_status=generate_rsa_keypair_hash(hash); 
	if(return_status!=0)
		return return_status; 
	
	uint8_t ciphertext[32];// uint8_t expected_plaintext[48]; 
	uint8_t encryption_tag[16]; 
	int ciphertext_len=32;// int plaintext_len=32;
	uint8_t iv[12];  
//	memset(ciphertext, 0, 32); //memset(expected_plaintext, 0, 48); 
	memset(iv, 0, 12);  // memset(expected_plaintext, 0, 32); 
//	memset(encryption_tag, 0, 16);  
//	memset(encrypted_hash_and_tag, 0, 48); 
	return_status=aes_cipher(1, key, iv, hash, 32, op_ciphertext,  &ciphertext_len, op_encrypted_tag);
/*	if(return_status != 0)
	{
//		memcpy(encrypted_hash_and_tag, ciphertext, 32);	 // For some reason, memcpy does not work
//		memcpy(encrypted_hash_and_tag+32, encryption_tag,  16); 
		for(count=0;count<32; count++)
			*(op_ciphertext+count) = ciphertext[count];

		for(count=0;count<16;count++)
			*(op_encrypted_tag+count) = encryption_tag[count]; 
	}
//	printf("ciphertext len: %d\n", ciphertext_len); fflush(stdout);
//	printf("Encryption return status: 0x%x", return_status);  fflush(stdout); 
//	return_status=aes_cipher(0, key, iv, ciphertext, ciphertext_len, expected_plaintext, &plaintext_len, encryption_tag); 

	for(count=0;count<32;count++)
	{
		printf("0x%02x ", hash[count]);
	}
	printf("\n");
	fflush(stdout); 
*/	for(count=0;count<32;count++)
	{
		printf("0x%02x ", op_ciphertext[count]); 
	}
	printf("\n");
	fflush(stdout);
	for(count=0;count<16;count++)
	{	
		printf("0x%02x ", op_encrypted_tag[count]); 
	}
	printf("\n"); fflush(stdout);
/*        for(count=0;count<32;count++)
        {       
                printf("0x%02x ", op_ciphertext[count]); 
        }
        printf("\n"); fflush(stdout);
*/
	return return_status;
}

// TODO: Private function
uint32_t verify_peer_enclave_trust(sgx_dh_session_enclave_identity_t* peer_enclave_identity, uint8_t* expected_mr_enclave, uint8_t* expected_mr_signer)
{
  int count=0;
  if(!peer_enclave_identity)
    return INVALID_PARAMETER_ERROR;

  sgx_measurement_t actual_mr_enclave = peer_enclave_identity->mr_enclave;
  sgx_measurement_t actual_mr_signer = peer_enclave_identity->mr_signer;

	if(expected_mr_enclave != NULL)
	{
		for(count=0; count<SGX_HASH_SIZE; count++)
		{
			if( actual_mr_enclave.m[count] != expected_mr_enclave[count] )
			        return ENCLAVE_TRUST_ERROR;
  		}
	}
	if(expected_mr_signer !=NULL)
	{
		for(count=0; count<SGX_HASH_SIZE; count++)
		{
			if( actual_mr_signer.m[count] != expected_mr_signer[count] )
			        return ENCLAVE_TRUST_ERROR; // TODO: Different error here.
		}
	}

  return SUCCESS;
}
