/*Listing 1:
 * Simple "Hello, World!" server
 * Ivan Griffin (ivan.griffin@ul.ie)
 */

#include <stdio.h>	/* */
#include <stdlib.h>	/* exit() */
#include <string.h>	/* memset(), memcpy() */
#include <sys/utsname.h>	/* uname() */
#include <sys/types.h>
#include <sys/socket.h>   /* socket(), bind(),listen(), accept() */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>	/* fork(), write(), close() */

#include <math.h> //for pow Function

#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/engine.h>

#include "include/cc.h"

/*
 * prototypes
 */
int _GetHostName(char *buffer, int length);

/*
 * Definitions
 */

struct certificate
{
char name[100];
char pubkey[2048];
char sig[2048];
};

int sendFile(char *filePath,int slaveSocket)
{  
FILE *fileP;
unsigned char* buffer;
int size;
char* str_size;

fileP=fopen(filePath,"rb");
if(fileP==NULL){
	printf("NULL= %s\n",filePath);
	exit(0);
}
printf("send file= %s\n",filePath);
buffer=malloc(4096);
str_size=calloc(20,1);
size=fread(buffer,1,4096,fileP);
sprintf(str_size,"%d",size);
printf("size=%d\n",size);
str_size[strlen(str_size)]='\n'; 
fclose(fileP);
#if 1
int i;
for(i=0;i<size;i++)
{
    printf("%x",buffer[i]);
}
    printf("\n");
#endif
i=send(slaveSocket,str_size,strlen(str_size),0);
printf("Sent Bytes=%d\n",i);
i=send(slaveSocket,buffer,size,0); 
if(i!=size)
{
  printf("Error Error\n");
  return -1;
}
free(buffer);
free(str_size);	
return 1;	
}

int recvFile(char *fpath,unsigned char *fdata, FILE *fp)
{
 FILE *fileP;

int size;
int i;
char* str_size;

fileP=fopen(fpath,"wb+");
if(fileP==NULL){
	printf("NULL= %s\n",fpath);
	exit(0);
}
printf("recv file= %s\n",fpath);
str_size=calloc(20,1);
        fgets(str_size,512,fp);
        size=atoi(str_size);
        printf("Size received=%d\n",size);
        i=  fread(fdata,1,size,fp);
        if(i!=size)
        return -1;
i=fwrite(fdata,1,size,fileP);
      if(i!=size)
        return -1;
fclose(fileP);

#if 1
for(i=0;i<size;i++)
   printf("%x",fdata[i]);
          printf("\n");
#endif
return size;
}

int sendstring(char *string,int size,int sockfd)
{
char *str_size=calloc(1,size);
int ret=0;
sprintf(str_size,"%d",size);
str_size[strlen(str_size)]='\n';
send(sockfd,str_size,strlen(str_size),0);
ret=send(sockfd,string,size,0); 
#if 0
          printf("Sent Bytes in sendstring for Name=%d\n",ret);
          printf("Name=%s\n",buffer);
#endif
free(str_size);
return ret;
}

int recvstring(char *string,FILE *fp)
{
char *str_size=calloc(1,100);
int size=0;
int ret=0;
fgets(str_size,512,fp);
size=atoi(str_size);
ret=fread(string,1,size,fp); 
#if 0
    printf("str_Size received=%s\n",str_size);
    printf("Size received=%d\n",size);
    printf("Str received=%s\n",string);
    printf("Bytes Actually Read in recvstring=%d\n",ret); 
#endif
free(str_size);
return ret;
}

TSS_RESULT step4(int slaveSocket, FILE *fp, char* key, char* gxgy){
	int i, j, ret;
	int size=0;
	int digestInLen, digestLocalLen;
	int ck_size, vd_size, pk_size;
        unsigned char digestIn[DIGEST_SIZE], digestLocal[DIGEST_SIZE];
	unsigned char fdata[MAX_SIZE], buffer[MAX_SIZE], fileMac[2*MAX_SIZE];
	FILE *fpointer;

	printf("***********Entering step4*************\n");

	printf("gxgy: %s\n", gxgy);

        //Sending Nonce|| Challenge to PDM
	strcpy(buffer,"");
	strcat(buffer, gxgy);
	strcat(buffer, ATTESTATION_CHALLENGE);
        printf("@step4@ Nonce||Challenge to be sent: %s\n",buffer);
        //Sending Nonce|| Challenge to PDM
	sendstring(buffer,strlen(buffer),slaveSocket);
	//Calculate hmackM(Nonce|| Challenge)
	HMAC(EVP_sha1(), key,strlen(key),buffer, strlen(buffer), digestLocal, &digestLocalLen);
	printf("@step4@ Hmac(Nonce||Challenge): ");
	for(i=0;i<digestLocalLen;i++)
		printf("%02x ",digestLocal[i]);
	printf("\n");
	//Send hmac(ATTESTATION_CHALLENGE) to PDM
	printf("@step4@ sending attestation challenge...\n");
	ret=sendstring(digestLocal,digestLocalLen,slaveSocket);

	printf("@step4@ receiving attestation verification files...\n");
	//Receive attestation verification files
	if((ck_size=recvFile(CERTIFIED_KEY_cc,fdata, fp))==-1)
	{
		printf("@step4@ error receiving certified key file...\n");
		return -1;
	}

	if((vd_size=recvFile(VERIFICATION_DATA_cc,fdata, fp))==-1){
		printf("@step4@ error receiving verification data file...\n");
		return -1;
	}

	if((pk_size=recvFile(PDM_PUB_KEY_cc,fdata, fp))==-1){
		printf("@step4@ error receiving pdm pub key file...\n");
		return -1;
	}

	//Receive PDM's hmac on received files
	digestInLen=recvstring(digestIn,fp);
	printf("@step4@ HMAC of received files: ");
	for(i=0;i<digestInLen;i++)
		printf("%02x ",digestIn[i]);
	printf("\n");

	//Calculate hmac of received files locally
	fpointer=fopen(PDM_PUB_KEY_cc,"r");
	ret=fread(buffer,1,MAX_SIZE,fpointer);
	fclose(fpointer);
	for(i=0;i<ret;i++){
		fileMac[i]=buffer[i];
	}
	size=size+ret;

	j=0;
	fpointer=fopen(VERIFICATION_DATA_cc,"r");
	ret=fread(buffer,1,MAX_SIZE,fpointer);
	fclose(fpointer);
	for(j=0;j<ret;j++){
		fileMac[i]=buffer[j];
		i++;
	}
	size=size+ret;

	j=0;
	fpointer=fopen(CERTIFIED_KEY_cc,"r");
	ret=fread(buffer,1,MAX_SIZE,fpointer);
	fclose(fpointer);
	for(j=0;j<ret;j++){
		fileMac[i]=buffer[j];
		i++;
	}
	size=size+ret;

	HMAC(EVP_sha1(), key, strlen(key), fileMac, size, digestLocal, &digestLocalLen);

	//and verify with the one that PDM sent
	for(i=0;i<digestInLen;i++)
		if (digestIn[i]!=digestLocal[i]){
			printf("@step4@ invalid HMAC of received files\n");
			return -1;
		}
	printf("@step4@ valid HMAC on received files...\n");

	//verify PDM attestation
	if(verifyAttestaion(vd_size, gxgy)==-1){
		printf("@step4@ verifyAttestaion error\n");
		return -1;
	}

	//send encrypted GROUP secret to PDM
	if(sendFile(ENC_GROUP_KEY, slaveSocket)==-1){
		printf("@step4@ error sending GROUP key...\n");
		return -1;
	}
	else printf("@step4@ sent GROUP key...\n");

	printf("***********Leaving step4*************\n");
	return TSS_SUCCESS;
}

int main(int argc, char *argv[])
{
    int serverSocket = 0,on = 0,port = 0,status = 0,childPid = 0;
    struct hostent *hostPtr = NULL;
    FILE *fp=NULL;
    char hostname[80] = "";
    struct sockaddr_in serverName = { 0 };
    
    unsigned long tempx=0,tempy=0;//tempx-g^xmodp,tempy-g^ymodp
    FILE *fpkey=NULL;
    unsigned char md[160]="";
    int mdlen=0;
    //char signgxgy[2048];
    unsigned char signgxgy[2048];
    unsigned char nonce[2048] = "";
    int signgxgylen=0;
    int siglen=0; 
    int ret=0;
    RSA *rsa_pub,*rsa_priv;
    RSA *rsa_ca_priv,*rsa_ca_pub;
    struct certificate head_certificate={"","",""};
    struct certificate pdm_certificate={"","",""};
    
    const int BACK_LOG = 5;
    unsigned char buffer[2048]="";
    
    char key[2048]="";
    char hmac[2048]="";
    //PARAMETERS OF DIFFIE_HELLMAN
    int g=5;//GENERATOR 
    int p=23;//Prime Number
    int y=3;
    int Ks=0,Km=0;//Ks-Seesion Key;Km-derived from Ks
    int size=0;
    char str_size[2048];
    


 
    rsa_ca_priv=RSA_new();
    rsa_ca_pub=RSA_new();
    fpkey=fopen("capriv.pem","r");
    rsa_ca_priv=(RSA *)PEM_read_RSAPrivateKey(fpkey,NULL,0,NULL);
    fclose(fpkey);
    fpkey=fopen("capub.pem","r");
    rsa_ca_pub=(RSA *)PEM_read_RSA_PUBKEY(fpkey,NULL,0,NULL);
    fclose(fpkey); 
    strcpy(head_certificate.name,"HEADEND");
    fpkey=fopen("headpub.pem","r");
    fread(head_certificate.pubkey,2048,1,fpkey);
    fclose(fpkey);   
    
    strcpy(str_size,"");  
    rsa_pub=RSA_new();
    rsa_priv=RSA_new();
    fpkey=fopen("headpriv.pem","r");
    if(!fpkey)
    {
      printf("File Error\n");
      exit(1);
    }
    rsa_priv=(RSA *)PEM_read_RSAPrivateKey(fpkey,NULL,0,NULL);
    if(!rsa_priv)
    {
       printf("Error Error\n");
       int error=ERR_get_error();
       fprintf(stderr,"RSA Error =%d\n",error);
       fprintf(stderr,"----%s\n",ERR_error_string(error,NULL));
       exit(2);
    }
    fclose(fpkey); 
    fpkey=fopen("headpub.pem","r");
    if(!fpkey)
    {
      printf("File Error\n");
      exit(3);
    }
    rsa_pub=(RSA *)PEM_read_RSA_PUBKEY(fpkey,NULL,0,NULL);
    if(!rsa_pub)
    {
       printf("Error Error\n");
       int error=ERR_get_error();
       fprintf(stderr,"RSA Error =%d\n",error);
       fprintf(stderr,"----%s\n",ERR_error_string(error,NULL));
       exit(2);
    }
    fclose(fpkey);




    if (2 != argc)
    {
        fprintf(stderr, "Usage: %s <port>\n",
		argv[0]);
        exit(1);
    }
    port = atoi(argv[1]);

    serverSocket = socket(PF_INET, SOCK_STREAM, 
		IPPROTO_TCP);
    if (-1 == serverSocket)
    {
        perror("socket()");
        exit(1);
    }

    /*
     * turn off bind address checking, and allow 
     * port numbers to be reused - otherwise
     * the TIME_WAIT phenomenon will prevent 
     * binding to these address.port combinations
     * for (2 * MSL) seconds.
     */

    on = 1;

    status = setsockopt(serverSocket, SOL_SOCKET, 
		SO_REUSEADDR,
        (const char *) &on, sizeof(on));

    if (-1 == status)
    {
        perror("setsockopt(...,SO_REUSEADDR,...)");
    }

    /*
     * when connection is closed, there is a need 
     * to linger to ensure all data is 
     * transmitted, so turn this on also
     */
    {
        struct linger linger = { 0 };

        linger.l_onoff = 1;
        linger.l_linger = 30;
        status = setsockopt(serverSocket, 
		SOL_SOCKET, SO_LINGER,
		(const char *) &linger, 
		sizeof(linger));

        if (-1 == status)
        {
            perror("setsockopt(...,SO_LINGER,...)");
        }
    }

    /*
     * find out who I am
     */

    hostPtr = gethostbyname("127.0.0.1");
//	hostPtr = gethostbyname("129.115.14.21");
//	hostPtr = gethostbyname("10.223.89.44");

    if (NULL == hostPtr)
    {
        perror("gethostbyname()");
        exit(1);
    }
    (void) memset(&serverName, 0, 
		sizeof(serverName));
    (void) memcpy(&serverName.sin_addr, 
		hostPtr->h_addr,
		hostPtr->h_length);

/*
 * to allow server be contactable on any of 
 * its IP addresses, uncomment the following 
 * line of code:
 * serverName.sin_addr.s_addr=htonl(INADDR_ANY);
 */

    serverName.sin_family = AF_INET;
    /* network-order */
    serverName.sin_port = htons(port);

    status = bind(serverSocket, 
	(struct sockaddr *) &serverName,
        sizeof(serverName));
    if (-1 == status)
    {
        perror("bind()");
        exit(1);
    }

    status = listen(serverSocket, BACK_LOG);
    if (-1 == status)
    {
        perror("listen()");
        exit(1);
    }

    for (;;)
    {
        struct sockaddr_in clientName = { 0 };
        int slaveSocket, clientLength = 
		sizeof(clientName);

        (void) memset(&clientName, 0, 
		sizeof(clientName));

        slaveSocket = accept(serverSocket,
		(struct sockaddr *) &clientName, 
		&clientLength);
        if (-1 == slaveSocket)
        {
            perror("accept()");
            exit(1);
        }

        childPid = fork();

        switch (childPid)
        {
        case -1: /* ERROR */
            perror("fork()");
            exit(1);

        case 0: /* child process */

            close(serverSocket);

            if (-1 == getpeername(slaveSocket,
		(struct sockaddr *) &clientName, 
		&clientLength))
            {
                perror("getpeername()");
            }
            else
            {
            printf("Connection request from %s\n",
                    inet_ntoa(clientName.sin_addr));
            }

            /*
             * Server application specific code
             * goes here, e.g. perform some 
             * action, respond to client etc.
             */
            fp=fdopen(slaveSocket,"r");

//PROTOCOL BEGIN
//STEP-1: receive g^x mod p from PDM
          printf("STEP-1 BEGIN\n");          
          printf("-----------------------------------------------------------\n");              
          
          fgets(str_size,512,fp);
          size=atoi(str_size);
          printf("str_Size received=%s\n",str_size);
          printf("Size received=%d\n",size);
          ret=fread(&tempx,1,4,fp); 
          printf("tempx=%d\n",tempx);
          printf("ret=%d\n",ret);
          printf("--------------------------------------------------------\n");                
          printf("STEP-1 END\n");           

//STEP-2
          printf("STEP-2 BEGIN\n");
          //2-A BEGIN
          printf("-----------------------------------------------------------\n");              
          //Calculate Ks=((g^x mod p)^y)modp;
          Ks=((unsigned long)pow(tempx,y))%p;
          //Calculate Km=Ks-1;
          Km=Ks-1;
          printf("Session Key-Ks=%d\n",Ks);                
          //2-A BEGIN Calculate g^y mod p
          tempy=((unsigned long)(pow(g,y)))%p;       
          //Send g^y mod p  to PDM
          size=sizeof(tempy);
          printf("Size=%d\n",size);
          sprintf(str_size,"%d",size);
          str_size[strlen(str_size)]='\n';
          send(slaveSocket,str_size,strlen(str_size),0);
          ret=send(slaveSocket,&tempy,size,0);
          printf("ret=%d\n",ret);
          printf("STEP2-A END----\n");  
          //2-A END


          //2-B BEGIN Send Head End Certificate
          //Send Name in Certificate
          printf("STEP2-B BEGIN----\n");  
          size=strlen(head_certificate.name);
          ret=sendstring(head_certificate.name,size,slaveSocket);
          printf("Sent Bytes for Name=%d\n",ret);
          

          //Send PubKey in Certificate
          size=strlen(head_certificate.pubkey);
          ret=sendstring(head_certificate.pubkey,size,slaveSocket);
          

          //Calculate Signature for Name and PubKey in Certificate
          strcpy(buffer,"");
          strcat(buffer,head_certificate.name);
          strcat(buffer,head_certificate.pubkey);
          strcpy(md,"");
          SHA1(buffer,strlen(buffer),md);
          printf("MD of CERT-NAME+CERT-PUB=%s\n",md);
          printf("Strlen of md=%d\n",strlen(md));
          
         
          ret=RSA_sign(NID_sha1, md, strlen(md),head_certificate.sig,&siglen,rsa_ca_priv);
          printf("Signature Len=%d\n",siglen);
          if(ret!=1)
          {
             printf("STEP 2-B RSA_Sign Error\n",ret);
             exit(10);
          }
          ret=RSA_verify(NID_sha1, md, strlen(md),head_certificate.sig,siglen,rsa_ca_pub);
          if(ret!=1)
          {
             printf("STEP 2-B RSA_Verify Error\n",ret);
             exit(10);
          }
          //Send the Signature part of Certificate  
          size=siglen;//strlen(head_certificate.sig);
          ret=sendstring(head_certificate.sig,size,slaveSocket);
          printf("Stringlen of sig=%d\n",strlen(head_certificate.sig));
          
          
          //2-B END
          printf("STEP2-B END----\n");  
          //while(1); 

          //2-C BEGIN Concatenate tempx,tempy
          printf("STEP2-C BEGIN---\n");  
          strcpy(buffer,"");
          sprintf(buffer,"%d%d\n",tempx,tempy);
          printf("tempxtempy=%s\n",buffer);
          printf("strlenoftempxtempy=%d\n",strlen(buffer));
          //Calculate SHA-1 HASH of Concatenated tempx,tempy
          SHA1( buffer, strlen(buffer),md);
          printf("MD-Begin\n");
          printf("-----------------\n");
//          printf("%s\n",md);      
	int k;
	for(k=0;k<strlen(buffer);k++)
	printf("%x",md[k]);
	printf("\n");
          printf("------------------\n"); 
          //Sign the SHA-1 HASH With Private Key of Head End
          ret=RSA_sign(NID_sha1,md,strlen(md),signgxgy,&signgxgylen,rsa_priv); 
          if(ret==0)
          {
              printf("RSA_SIGN Error \n");
              printf("%d\n",ERR_get_error());
              int error=ERR_get_error();
              fprintf(stderr,"----%s\n",ERR_error_string(error,NULL));
              exit(1);
          }
          printf("SIGNGXGY-Begin\n");
          printf("-----------------\n");
          //printf("%s\n",signgxgy);
          printf("------------------\n");
#if 0
          printf("RSA-PUB\n");
          RSA_print_fp(stdout,rsa_pub,0);
#endif
          printf("-------------------\n");
          //Send the Signed SHA-1 HASH to PDM
          printf("Before Sent =%d %d  \n",strlen(signgxgy),signgxgylen);

          size=signgxgylen;//strlen(signgxgy);
          ret=sendstring(signgxgy,size,slaveSocket);
          
          printf("Sent Bytes=%d\n",ret);
          printf("After Sent =%d %d  \n",strlen(signgxgy),signgxgylen);
          ret=RSA_verify(NID_sha1,md,strlen(md),signgxgy,signgxgylen,rsa_pub);
          if(ret==0)
          { 
              printf("STEP 2-C Verification FAILED\n");
              exit(10);         
          }
          printf("STEP-2C END--------------\n"); 
         //STEP 2-C END
 
          //STEP 2-D BEGIN 
          //Calculate HMACkm(CertCC)
          printf("STEP2-D BEGIN-------\n");  
          printf("-----------------------------------------------------------\n");
          strcpy(buffer,"");
          strcpy(md,"");
          strcpy(buffer,head_certificate.name);
          strcat(buffer,head_certificate.pubkey);
          strcat(buffer,head_certificate.sig);
          sprintf(key,"%d",Km);
          strcpy(md,"");
          HMAC(EVP_sha1(), key,strlen(key),buffer,strlen(buffer), md, &siglen);  
          //Send HMAC
          printf("Strlen of md1=%d\n",strlen(md));


          size=siglen;
          ret=sendstring(md,size,slaveSocket);
          
          
          printf("HMAC=%s\n",md);
          //STEP 2-D END
          printf("STEP2-D END----\n");  
          printf("STEP-2 END\n");  
          printf("-----------------------------------------------------------\n");              
//STEP-3
          //STEP 3-A BEGIN receive CertAik
          struct certificate aik_certificate={"","",""}; 
          RSA *rsa_aik_pub,*rsa_aik_priv;
          rsa_aik_pub=RSA_new();
          rsa_aik_priv=RSA_new();
          unsigned char AIKCertbuffer[2048], AIKpubkey[300], AIKcertsig[200];
		int i,j;
          recvFile(AIK_CERT_cc,AIKCertbuffer, fp);
          printf("AIKCErtBUffer=%s\n",AIKCertbuffer);

	j=0;
	for(i=0;i<284;i++){
		AIKpubkey[j]=AIKCertbuffer[i];
		printf("%x",AIKpubkey[j]);
		j++;
	}
	printf("\n");
	j=0;
	for(i=284;i<412;i++){
		AIKcertsig[j]=AIKCertbuffer[i];
		j++;
	}

        SHA1(AIKpubkey,284,md);
        printf("MD5=%s\n",md);
        printf("strlen of md5=%d\n",strlen(md));
        
	ret=RSA_verify(NID_sha1, md, strlen(md),AIKcertsig,128,rsa_ca_pub); 
         if(ret!=1)
        {
	        printf("STEP 3-A RSA_Verify Error\n",ret);
	        exit(10);
        }  

          
           printf("STEP-3A(sign) Success\n");

	fpkey=fopen(SIGN_KEY_cc,"w+");
	ret=fwrite(AIKpubkey,1,284,fpkey);
	size=ret;
	fclose(fpkey);

//3-B receive sign(Concatenated tempy,tempx)
          printf("3-B BEGIN\n"); 
          ret=recvstring(signgxgy,fp); 
          printf("ret for tempytempx sig=%d\n",ret);
          for(i=0;i<ret;i++)
              printf("%02x",signgxgy[i]);
	printf("\n");
          printf("Read Bytes for tempxtempysign=%d\n",strlen(signgxgy));



	strcpy(buffer,"");
        sprintf(buffer,"%d%d",tempy,tempx);
        ret=quote_verify(signgxgy, ret, buffer, strlen(buffer), SIGN_KEY_cc, size);
         if(ret==-1)
         {
             printf("STEP 3-B Verification FAILED\n");
             exit(10);         
         }
	else printf("Signature Verified on quote info\n");
         
          
         printf("STEP-3B END\n");
        //3-B END
         
         //STEP-3C receive HMAC
         ret=recvstring(hmac,fp); 
         //printf("HMAC received Bytes temp=%d\n",strlen(temp));
         printf("HMAC received Bytes ret=%d\n",ret);
         printf("HMAC received Bytes=%d\n",strlen(hmac));
         printf("received HMAC=%s\n",hmac);

         //Calculate HMACkm(CertCC)
         printf("-----------------------------------------------------------\n");
         
         strcpy(md,"");
         sprintf(key,"%d",Km);
         
     	 HMAC(EVP_sha1(), key,strlen(key),AIKCertbuffer,412, md, &siglen);  
#if 0
           printf("HMAC=%s\n",md);
#endif
    //compare calcuated vs received hmac
         ret=strncmp(hmac,md,20);
         if(ret!=0)
         {
    	      printf("3-C HMAC-ERROR\n");
       	      exit(10);
         } 
         printf("STEP-3 END\n");
         printf("Success\n");
         //STEP-3C END

//STEP 4

	ret=step4(slaveSocket,fp, key, buffer);
	if(ret!=TSS_SUCCESS){
		printf("step4 error\n");
		exit(-1);
	}
	else printf("step4 success\n");
	printf("DONE.\n");


         close(slaveSocket);
         exit(0);
         default: // parent process 
            close(slaveSocket);
        }
    }

    return 0;
}

