/*
 * Copyright (C) 2011-2017 Intel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.challa
 *
 */

#define SGX_DH_MAC_SIZE 16
#define SGX_TARGET_INFO_RESERVED1_BYTES 4
#define SGX_TARGET_INFO_RESERVED2_BYTES 456
#define SGX_ECP256_KEY_SIZE 32
#define SGX_CPUSVN_SIZE 16
#define SGX_REPORT_DATA_SIZE 64
#define SGX_MAC_SIZE 16 /* Message Authentication Code - 16 bytes */
#define SGX_KEYID_SIZE 32
#define SGX_HASH_SIZE 32 /* SHA256 */

#define SGX_REPORT_BODY_RESERVED1 28
#define SGX_REPORT_BODY_RESERVED2 32
#define SGX_REPORT_BODY_RESERVED3 96
#define SGX_REPORT_BODY_RESERVED4 60

// App.cpp : Defines the entry point for the console application.
#include <stdio.h>
#include <map>
#include "../Decryptor/Decryptor_u.h"
#include "sgx_eid.h"
#include "sgx_urts.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include<unistd.h>

// For ecdsa signing key generation 
#include "sgx_tcrypto.h"

// For socket programming
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include<unistd.h>

// For google proto buffers
#include "dhmsgs.pb.h"
#include <inttypes.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
using namespace google::protobuf::io;

#define UNUSED(val) (void)(val)
#define TCHAR   char
#define _TCHAR  char
#define _T(str) str
#define scanf_s scanf
#define _tmain  main

// Not sure if I need this later - as such, I (decryptor app) will only ever need to talk to 1 enclave at a time - verifier enclave first and then the apache enclave.
//extern std::map<sgx_enclave_id_t, uint32_t>g_enclave_id_map;

sgx_enclave_id_t e2_enclave_id = 0;
#define Decryptor_PATH "libDecryptor.so"

int fit_32_into_uint8_t(google::protobuf::uint32 temp32, uint8_t* temp8)
{
 if(temp32 > UINT8_MAX)
	 return -1;
 else
 {
	 //		 *temp8 = *(uint8_t*)&temp32; // Probably works irrespective of endianness but not sure.
	 *temp8 = (uint8_t)temp32;
	 return 0;
 }
}

int fit_32_into_uint16_t(google::protobuf::uint32 temp32, uint16_t* temp16)
{
 if(temp32 > UINT16_MAX)
	 return -1;
 else
 {
	 //		 *temp8 = *(uint8_t*)&temp32; // Probably works irrespective of endianness but not sure.
	 *temp16 = (uint16_t)temp32;
	 return 0;
 }
}
// Sets up a socket to bind and listen to the given port. Returns FD of the socket on success, -1 on failure (and prints a msg to stdout with the errno)
int set_up_socket(int port,   sockaddr_in* address)
{
  int server_fd = 0;

  // Creating socket file descriptor for listening for attestation requests.
  server_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
  if (server_fd == -1)
  {
     printf("Error in creating a socket - %d", errno);
      return -1;
  }

  // Preparing the address struct for binding
  address->sin_family = AF_INET;
  address->sin_addr.s_addr = INADDR_ANY; // Todo: should this be localhost?
  address->sin_port = htons(port);
  //  memset(address->sin_zero,0,sizeof(address->sin_zero));

  socklen_t addrlen = sizeof(*address);
  // Binding
  if (bind(server_fd, (sockaddr*)address, addrlen)<0)
  {
      printf("Error in binding %d - port was %d - ", errno, port);
      return -1;
  }

  // Listening
  if (listen(server_fd, 128) < 0)
  {
      printf("Error in listening %d", errno);
      return -1;
  }

  return server_fd;
}

void encode_ec256_public_key_to_protobuf(protobuf_sgx_ec256_public_t* protobuf_g_a , sgx_ec256_public_t* g_a)
{
	int counter; google::protobuf::uint32 temp32;
	for(counter=0;counter<SGX_ECP256_KEY_SIZE;counter++)
	{
		temp32 = g_a->gx[counter];
		protobuf_g_a->add_gx(temp32);
		temp32 = g_a->gy[counter];
		protobuf_g_a->add_gy(temp32);
	}
}

int decode_ec256_public_key_from_protobuf(protobuf_sgx_ec256_public_t* protobuf_g_a , sgx_ec256_public_t* g_a)
{
  printf("\n ec256 pub key\n");
	int counter; google::protobuf::uint32 temp32;
	for(counter=0;counter<SGX_ECP256_KEY_SIZE;counter++)
	{
		temp32 = protobuf_g_a->gx(counter);
		if(fit_32_into_uint8_t(temp32, &(g_a->gx[counter]))!=0)
		 return -1;
    printf("%d ",g_a->gx[counter]);
		temp32 = protobuf_g_a->gy(counter);
		if(fit_32_into_uint8_t(temp32, &(g_a->gy[counter]))!=0)
		 return -1;
    printf("%d ",g_a->gy[counter]);
	}
  return 0;
}

void encode_attributes_to_protobuf(protobuf_sgx_attributes_t* protobuf_attributes, sgx_attributes_t* attributes)
{
	protobuf_attributes->set_flags(attributes->flags); // 64 bit
	protobuf_attributes->set_xfrm(attributes->xfrm); // 64 bit
}

int decode_attributes_from_protobuf(protobuf_sgx_attributes_t* protobuf_attributes, sgx_attributes_t* attributes)
{
	attributes->flags = protobuf_attributes->flags();
  printf("\n flags %" PRIu64 " \n", attributes->flags);
	attributes->xfrm = protobuf_attributes->xfrm();
  printf("\n xfrm %" PRIu64 " \n", attributes->xfrm);
	return 0; 
}

void encode_report_to_protobuf(protobuf_sgx_report_t* protobuf_report, sgx_report_t* report)
{
	int counter; google::protobuf::uint32 temp32;
	for(counter=0;counter<SGX_KEYID_SIZE;counter++)
	{
		temp32=report->key_id.id[counter];
		protobuf_report->add_key_id(temp32);
	}

	for(counter=0;counter<SGX_MAC_SIZE;counter++)
	{
		temp32=report->mac[counter];
		protobuf_report->add_mac(temp32);
	}

	protobuf_report->mutable_body()->set_misc_select(report->body.misc_select); // 32 bit
	protobuf_report->mutable_body()->set_isv_svn(report->body.isv_svn); // 16 bit
	protobuf_report->mutable_body()->set_isv_prod_id(report->body.isv_prod_id); // 16 bit
	encode_attributes_to_protobuf(protobuf_report->mutable_body()->mutable_attributes(), &(report->body.attributes));

	for(counter=0;counter<SGX_CPUSVN_SIZE;counter++)
	{
		temp32=report->body.cpu_svn.svn[counter];
		protobuf_report->mutable_body()->add_cpu_svn(temp32);
	}

	for(counter=0;counter<SGX_REPORT_BODY_RESERVED1;counter++)
	{
		temp32=report->body.reserved1[counter]; // TODO: Could be optimized out - if these are determined to be 0s.
		protobuf_report->mutable_body()->add_reserved1(temp32);
 	}

	for(counter=0;counter<SGX_REPORT_BODY_RESERVED2;counter++)
	{
		temp32=report->body.reserved2[counter]; // TODO: Could be optimized out - if these are determined to be 0s.
		protobuf_report->mutable_body()->add_reserved2(temp32);
	}

	for(counter=0;counter<SGX_REPORT_BODY_RESERVED3;counter++)
	{
		temp32=report->body.reserved3[counter]; // TODO: Could be optimized out - if these are determined to be 0s.
		protobuf_report->mutable_body()->add_reserved3(temp32);
 	}

	for(counter=0;counter<SGX_REPORT_BODY_RESERVED4;counter++)
	{
		temp32=report->body.reserved4[counter]; // TODO: Could be optimized out - if these are determined to be 0s.
		protobuf_report->mutable_body()->add_reserved4(temp32);
 	}

	for(counter=0;counter<SGX_HASH_SIZE;counter++)
	{
		temp32=report->body.mr_enclave.m[counter];
		protobuf_report->mutable_body()->add_mr_enclave(temp32);
 	}

	for(counter=0;counter<SGX_HASH_SIZE;counter++)
	{
		temp32=report->body.mr_signer.m[counter];
		protobuf_report->mutable_body()->add_mr_signer(temp32);
 	}

	for(counter=0;counter<SGX_REPORT_DATA_SIZE;counter++)
	{
		temp32=report->body.report_data.d[counter];
		protobuf_report->mutable_body()->add_report_data(temp32);
 	}
}

int decode_report_from_protobuf(protobuf_sgx_report_t* protobuf_report, sgx_report_t* report)
{
  int counter; google::protobuf::uint32 temp32;

  printf("\nreport body keyid\n");
  for(counter=0;counter<SGX_KEYID_SIZE;counter++)
  {
    temp32=protobuf_report->key_id(counter);
    if(fit_32_into_uint8_t(temp32, &(report->key_id.id[counter]))!=0)
		  return -1;
    printf("%d ", report->key_id.id[counter]);
  }

  printf("\nreport mac\n");
  for(counter=0;counter<SGX_MAC_SIZE;counter++)
  {
    temp32=protobuf_report->mac(counter);
    if(fit_32_into_uint8_t(temp32, &(report->mac[counter]))!=0)
      return -1;
    printf("%d ", report->mac[counter]);
  }

  report->body.misc_select=protobuf_report->mutable_body()->misc_select(); // 32 bit
  temp32=protobuf_report->mutable_body()->isv_svn();
  if(fit_32_into_uint16_t(temp32, &(report->body.isv_svn))!=0)
   return -1;
  printf("\nmisc select %d \n", report->body.misc_select);

  temp32=protobuf_report->mutable_body()->isv_prod_id();
   if(fit_32_into_uint16_t(temp32, &(report->body.isv_prod_id))!=0)
    return -1;
  printf("\nprod id %d \n", report->body.isv_prod_id);

  decode_attributes_from_protobuf(protobuf_report->mutable_body()->mutable_attributes(), &(report->body.attributes));

  printf("\n cpu svn\n");
  for(counter=0;counter<SGX_CPUSVN_SIZE;counter++)
  {
    temp32=protobuf_report->mutable_body()->cpu_svn(counter);
    if(fit_32_into_uint8_t(temp32, &(report->body.cpu_svn.svn[counter]))!=0)
      return -1;
    printf("%d ", report->body.cpu_svn.svn[counter]);

  }

  printf("\n reserved1 \n");
  for(counter=0;counter<SGX_REPORT_BODY_RESERVED1;counter++)
  {
    temp32=protobuf_report->mutable_body()->reserved1(counter);
    if(fit_32_into_uint8_t(temp32, &(report->body.reserved1[counter]))!=0)
      return -1;
    printf("%d ", report->body.reserved1[counter]);
  }

  printf("\n reserved2 \n");
  for(counter=0;counter<SGX_REPORT_BODY_RESERVED2;counter++)
  {
    temp32=protobuf_report->mutable_body()->reserved2(counter);
    if(fit_32_into_uint8_t(temp32, &(report->body.reserved2[counter]))!=0)
      return -1;
    printf("%d ", report->body.reserved2[counter]);
  }

  printf("\n reserved3 \n");
  for(counter=0;counter<SGX_REPORT_BODY_RESERVED3;counter++)
  {
    temp32=protobuf_report->mutable_body()->reserved3(counter);
    if(fit_32_into_uint8_t(temp32, &(report->body.reserved3[counter]))!=0)
      return -1;
    printf("%d ", report->body.reserved3[counter]);
  }

  printf("\n reserved4 \n");
  for(counter=0;counter<SGX_REPORT_BODY_RESERVED4;counter++)
  {
    temp32=protobuf_report->mutable_body()->reserved4(counter);
    if(fit_32_into_uint8_t(temp32, &(report->body.reserved4[counter]))!=0)
      return -1;
    printf("%d ", report->body.reserved4[counter]);

  }

  printf("\n mrenclave \n");
  for(counter=0;counter<SGX_HASH_SIZE;counter++)
  {
    temp32=protobuf_report->mutable_body()->mr_enclave(counter);
    if(fit_32_into_uint8_t(temp32, &(report->body.mr_enclave.m[counter]))!=0)
      return -1;
    printf("%x ", report->body.mr_enclave.m[counter]);
  }

  printf("\n mrsigner \n");
  for(counter=0;counter<SGX_HASH_SIZE;counter++)
  {
    temp32=protobuf_report->mutable_body()->mr_signer(counter);
    if(fit_32_into_uint8_t(temp32, &(report->body.mr_signer.m[counter]))!=0)
      return -1;
    printf("%x ", report->body.mr_signer.m[counter]);
  }

  printf("\n report data\n");
  for(counter=0;counter<SGX_REPORT_DATA_SIZE;counter++)
  {
    temp32=protobuf_report->mutable_body()->report_data(counter);
    if(fit_32_into_uint8_t(temp32, &(report->body.report_data.d[counter]))!=0)
      return -1;
    printf("%d ", report->body.report_data.d[counter]);
  }
	return 0;
}

void encode_msg1_to_protobuf( protobuf_sgx_dh_msg1_t& protobuf_dhmsg1, sgx_dh_msg1_t* native_dhmsg1)
{
   int counter; google::protobuf::uint32 temp32; // google::protobuf::uint64 temp64;

   encode_ec256_public_key_to_protobuf(protobuf_dhmsg1.mutable_g_a(), &(native_dhmsg1->g_a));

   for(counter=0;counter<SGX_HASH_SIZE;counter++)
   {
     temp32=native_dhmsg1->target.mr_enclave.m[counter];
     protobuf_dhmsg1.mutable_target()->add_mr_enclave(temp32);
   }

   for(counter=0;counter<SGX_TARGET_INFO_RESERVED1_BYTES;counter++)
   {
     temp32=native_dhmsg1->target.reserved1[counter];
     protobuf_dhmsg1.mutable_target()->add_reserved1(temp32);
   }

   for(counter=0;counter<SGX_TARGET_INFO_RESERVED2_BYTES;counter++)
   {
     temp32=native_dhmsg1->target.reserved2[counter];
     protobuf_dhmsg1.mutable_target()->add_reserved2(temp32);
   }

   encode_attributes_to_protobuf(protobuf_dhmsg1.mutable_target()->mutable_attributes(), &(native_dhmsg1->target.attributes));

   temp32=native_dhmsg1->target.misc_select ;
   protobuf_dhmsg1.mutable_target()->set_misc_select(temp32);
}

void encode_msg3_to_protobuf(protobuf_sgx_dh_msg3_t& protobuf_dhmsg3, sgx_dh_msg3_t* native_dhmsg3)
{
  int counter; google::protobuf::uint32 temp32;
  for(counter=0;counter<SGX_DH_MAC_SIZE;counter++)
	{
		temp32=native_dhmsg3->cmac[counter];
		protobuf_dhmsg3.add_cmac(temp32);
	}

  encode_report_to_protobuf(protobuf_dhmsg3.mutable_msg3_body()->mutable_report(), &(native_dhmsg3->msg3_body.report));
  int max_counter=native_dhmsg3->msg3_body.additional_prop_length;
  unsigned char*temp;
  for(counter=0,temp=native_dhmsg3->msg3_body.additional_prop;counter<max_counter;counter++,temp++)
  {
    protobuf_dhmsg3.mutable_msg3_body()->add_additional_prop(*temp);
  }
}

int decode_msg2_from_protobuf(protobuf_sgx_dh_msg2_t& protobuf_dhmsg2, sgx_dh_msg2_t* native_dhmsg2)
{
  int counter; google::protobuf::uint32 temp32; //google::protobuf::uint64 temp64;
  printf("\ncmac\n");
  for(counter=0;counter<SGX_DH_MAC_SIZE;counter++)
  {
    temp32=protobuf_dhmsg2.cmac(counter);
    if(fit_32_into_uint8_t(temp32, &(native_dhmsg2->cmac[counter]))!=0)
     return -1;
    printf("%d ",native_dhmsg2->cmac[counter]);
  }

  if(decode_ec256_public_key_from_protobuf(protobuf_dhmsg2.mutable_g_b(), &(native_dhmsg2->g_b)) !=0)
	return -1;

  if(decode_report_from_protobuf(protobuf_dhmsg2.mutable_report(), &(native_dhmsg2->report)) !=0)
	return -1;

  return 0;
}

int print_initialized_msg1( protobuf_sgx_dh_msg1_t& protobuf_dhmsg1, sgx_dh_msg1_t* native_dhmsg1)
{
   int counter;
	 printf("gx\n");
   for(counter=0;counter<SGX_ECP256_KEY_SIZE;counter++)
   {
      printf("%d ", protobuf_dhmsg1.g_a().gx(counter));
      printf("%d ", native_dhmsg1->g_a.gx[counter]);
   }
   printf("\ngy\n");
   for(counter=0;counter<SGX_ECP256_KEY_SIZE;counter++)
   {
      printf("%d ", protobuf_dhmsg1.g_a().gy(counter));
      printf("%d ", native_dhmsg1->g_a.gy[counter]);
   }

   printf("\nmrenclave in target\n");
   for(counter=0;counter<SGX_HASH_SIZE;counter++)
   {
      printf("%" PRIu32 " ", protobuf_dhmsg1.target().mr_enclave(counter));
      printf("%d ", native_dhmsg1->target.mr_enclave.m[counter]);

   }
   printf("\nreserved1 in target\n");
   for(counter=0;counter<SGX_TARGET_INFO_RESERVED1_BYTES;counter++)
   {
      printf("%" PRIu32 " ", protobuf_dhmsg1.target().reserved1(counter));
      printf("%d ", native_dhmsg1->target.reserved1[counter]);

   }
   printf("\nreserved2 in target\n");
   for(counter=0;counter<SGX_TARGET_INFO_RESERVED2_BYTES;counter++)
   {
      printf("%" PRIu32 " ", protobuf_dhmsg1.target().reserved2(counter));
      printf("%d ", native_dhmsg1->target.reserved2[counter]);
   }

   printf("\n %" PRIu64 "\n", native_dhmsg1->target.attributes.flags);
   printf("\n %" PRIu64 "\n", protobuf_dhmsg1.target().attributes().flags());
   printf("\n %" PRIu64 "\n", native_dhmsg1->target.attributes.xfrm);
   printf("\n %" PRIu64 "\n", protobuf_dhmsg1.target().attributes().xfrm());
   printf("\n %" PRIu32 "\n", native_dhmsg1->target.misc_select);
   printf("\n %" PRIu32 "\n", protobuf_dhmsg1.target().misc_select());

	return 0;
}

int read_protobuf_msg_from_fd(int accept_fd, google::protobuf::MessageLite& message)
{
  ZeroCopyInputStream* raw_input;
  CodedInputStream* coded_input;
  uint32_t size;
  CodedInputStream::Limit limit;
  raw_input = new FileInputStream(accept_fd);
  coded_input = new CodedInputStream(raw_input);
  if(!coded_input->ReadVarint32(&size))
  {
    printf("Error in reading size of msg");
    fflush(stdout);
    return -1;
  }
  //printf("size of msg was read to be %" PRIu32 " \n", size);
  fflush(stdout);
  limit = coded_input->PushLimit(size);
  if(!message.ParseFromCodedStream(coded_input))
  {
    printf("Error in parsing msg");
    fflush(stdout);
    return -1;
  }
  coded_input->PopLimit(limit);
  return 0;
}

int write_protobuf_msg_to_fd(int accept_fd, google::protobuf::MessageLite& message)
{
  ZeroCopyOutputStream* raw_output = new FileOutputStream(accept_fd);
  CodedOutputStream* coded_output  = new CodedOutputStream(raw_output);
  coded_output->WriteVarint32(message.ByteSize());
  if(!message.SerializeToCodedStream(coded_output))
  {
    printf("SerializeToCodedStream failed");
    fflush(stdout);
    return -1;
  }
  // As per this - https://stackoverflow.com/questions/22881876/protocol-buffers-how-to-serialize-and-deserialize-multiple-messages-into-a-file?noredirect=1&lq=1
  // TODO: There may be a better way to do this - 1) this happens with every accept now and 2) make it happen on the stack vs heap - destructor will be called on return from this function (main) and the items will then be written out. (We probably don't want that, actually)
  delete coded_output;
  delete raw_output;
  fflush(stdout);
  return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    uint32_t ret_status;
    sgx_status_t status;
    // For sgx setup
    int launch_token_updated;
    sgx_launch_token_t launch_token;
    // declare msg1, msg2, msg3 protobuf objects and native SGX structs
    protobuf_sgx_dh_msg1_t protobuf_msg1;
    protobuf_sgx_dh_msg2_t protobuf_msg2;
    protobuf_sgx_dh_msg3_t protobuf_msg3;
    // For sgx - ecalls and storing msgs rcvd from pipes for processing.
    uint32_t session_id;
    sgx_dh_msg1_t dh_msg1;            //Diffie-Hellman Message 1
    sgx_dh_msg2_t dh_msg2;            //Diffie-Hellman Message 2
    sgx_dh_msg3_t dh_msg3;            //Diffie-Hellman Message 3
    sgx_key_128bit_t dh_aek;        // Session Key
    // initializing native sgx structs
    memset(&dh_aek,0, sizeof(sgx_key_128bit_t));
    memset(&dh_msg1, 0, sizeof(sgx_dh_msg1_t));
    memset(&dh_msg2, 0, sizeof(sgx_dh_msg2_t));
    memset(&dh_msg3, 0, sizeof(sgx_dh_msg3_t));
    // For socket to listen to the Apache enclave.
    int apache_port = 3825; int server_fd=0; int accept_fd = 0;
    struct sockaddr_in own_addr;
    struct sockaddr_storage apache_addr; socklen_t apache_addr_size = sizeof(apache_addr);
    int counter;
    UNUSED(argc);
    UNUSED(argv);

    server_fd=set_up_socket(apache_port, &own_addr);
    if(server_fd==-1)
      return -1;
    printf("Successfully set up a socket to communicate with the Apache enclave.");
    fflush(stdout);

    status = sgx_create_enclave(Decryptor_PATH, SGX_DEBUG_FLAG, &launch_token, &launch_token_updated, &e2_enclave_id, NULL);
    if(status != SGX_SUCCESS)
    {
        printf("\nLoad Enclave Failure");
        close(server_fd);
        return -1;
    }
    printf("\nDecryptor - EnclaveID %" PRIx64, e2_enclave_id);
    fflush(stdout);

    // Generating a signing ECDSA key to sign the encryption key. 
    sgx_ec256_public_t pub_key; 
    Decryptor_create_ecdsa_signing_key_pair(e2_enclave_id, &ret_status, &pub_key);
    if(ret_status != SGX_SUCCESS) 
    {
	printf("\n error in generating the ecdsa signing key pair \n"); 
	fflush(stdout); 
	return 0xFFFFFFFF;
    }
    printf("\n Generated the ecdsa key pair successfully - gx, gy\n"); 
    fflush(stdout); 
    for(counter=0; counter<SGX_ECP256_KEY_SIZE; counter++) 
    {
	printf("%x ", pub_key.gx[counter]); 
    }
    printf("\n"); fflush(stdout); 
    for(counter=0; counter<SGX_ECP256_KEY_SIZE; counter++) 
    {
        printf("%x ", pub_key.gy[counter]); 
    }
    printf("\n"); fflush(stdout);  

    Decryptor_session_request(e2_enclave_id, &ret_status, &dh_msg1, &session_id); // TODO: Check Return status 
    encode_msg1_to_protobuf(protobuf_msg1, &dh_msg1);
    print_initialized_msg1(protobuf_msg1, &dh_msg1);
    printf("Done initialization");
    fflush(stdout);
    //
    int no_of_msgs_xchanged=0;
//    do
//    {
      // accept incoming connection
    accept_fd = accept(server_fd, (struct sockaddr *)&apache_addr,&apache_addr_size);
    if (accept_fd <0)
    {
      printf("Error in accepting %d", errno);
      return -1;
    }
    printf("Accepted the fd");
    do
    {
      // read msg(none,2) and then write(1,3)
      if(no_of_msgs_xchanged==0){
        // call SerializeToFileDescriptor on msg1 object, accept_fd.
        if(write_protobuf_msg_to_fd(accept_fd, protobuf_msg1)!=0)
		return -1; 
      }
      else
      {
	       printf("about to read msg2\n");
	        fflush(stdout);
        // read msg2 -
        if(read_protobuf_msg_from_fd(accept_fd, protobuf_msg2)!=0)
		return -1;

        if(decode_msg2_from_protobuf(protobuf_msg2, &dh_msg2)!=0)
		return -1; 
	printf("Done decoding msg2\n"); 
        // TODO: call fn to generate msg3 for the responder.
	Decryptor_exchange_report(e2_enclave_id, &ret_status, &dh_msg2, &dh_msg3, &session_id);
	if(ret_status!=SGX_SUCCESS)
	{
		fflush(stdout);
		printf("exchange report failed:%x\n", ret_status);
		fflush(stdout);
		return -1;
	}
	printf("return status %d", ret_status);
	fflush(stdout);
        // convert msg3 sgx_dh_msg3_t object to a protobuf msg3 object.
        encode_msg3_to_protobuf(protobuf_msg3, &dh_msg3);
	printf("about to write msg3\n");
	fflush(stdout);
        // write msg3 -
        if(write_protobuf_msg_to_fd(accept_fd, protobuf_msg3)!=0)
		return -1; 
        no_of_msgs_xchanged+=1;
      }
      no_of_msgs_xchanged++;

    }
    while(no_of_msgs_xchanged!=3);
	printf("Out of while loop\n");
	fflush(stdout);
    // TODO: Continue with other msgs.
//    sleep(500);
    sgx_destroy_enclave(e2_enclave_id);

    return 0;
}

/*
// Reads from a server fd that is set up successfully for accept, into a buffer buf and upto the length len.
// Returns the number of characters read on success and -1 on error. Also, on error, prints the errno for the accept system call.
int accept_and_read(int server_fd, char* buf, int len)
{
int initialize_msg1( protobuf_sgx_dh_msg1_t& protobuf_dhmsg1, sgx_dh_msg1_t* native_dhmsg1)
     int accept_fd=0;
     // accept connections and then forward them to the main enclave.
     if ((accept_fd = accept(server_fd, (struct sockaddr *)&address,
                       (socklen_t*)&addrlen))<0)
    {
        printf("Error in accepting %d", errno);
        return -1;
    }
    valread = read(accept_fd, buf, len);
    if(valread == -1)
        printf("Error in reading %d", errno);
    return valread;
}
*/

