#include "sgx_eid.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "ProtobufLAMessages.pb.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;
uint8_t iv[12];

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;

  //No verification checks here: no security guarantees obtained from any such checks
  memset(iv, 0, 12);
  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 decrypt_wrapper(unsigned char* ciphertext, int ciphertext_len, unsigned char* op_plaintext, int* op_plaintext_len, unsigned char* ip_encrypted_tag)
{
        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];
	printf("Doing decryption\n"); fflush(stdout); 
        return_status=aes_cipher(0, key, iv, ciphertext, ciphertext_len, op_plaintext,  op_plaintext_len, ip_encrypted_tag);
        return return_status;
}
*/

uint32_t aes_gcm_wrapper(int enc, unsigned char* ciphertext, int ciphertext_len, unsigned char* op_plaintext, int* op_plaintext_len, unsigned char* ip_tag)
{
        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];
        printf("About to do enc/dec\n"); fflush(stdout); 
        return_status=aes_gcm(enc, key, iv, ciphertext, ciphertext_len, op_plaintext,  op_plaintext_len, ip_tag);
        return return_status;
}

