/*
 * Listing 2:
 * An example client for "Hello, World!" server
 * Ivan Griffin (ivan.griffin@ul.ie)
 */

#include <stdio.h>                /* perror() */
#include <stdlib.h>               /* atoi() */
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>               /* read() */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>

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

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

#include "include/trm_helper.h"
#include "include/quote.h"

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

int sendFile(char *filePath, int clientSocket){

	FILE *fileP;
	unsigned char* buffer;
	int i,size;
	char* str_size;

	fileP = fopen(filePath,"rb");
	if(fileP==NULL){
		printf("File to send is NULL: %s\n",filePath);
		return -1;
	}

	printf("Sending file: %s\n",filePath);
	buffer=malloc(4096);
	str_size=calloc(20,1);
	size=fread(buffer,1,4096,fileP);
	fclose(fileP);
	sprintf(str_size,"%d",size);
	str_size[strlen(str_size)]='\n';

	//Send file size
	i=send(clientSocket,str_size,strlen(str_size),0);
	//Send file
	i=send(clientSocket,buffer,size,0); 
	if(i!=size){
		printf("Error sending file: %s\n", filePath);
		return -1;
	}
#if 1
	for(i=0;i<size;i++)
		   printf("%x",buffer[i]);
         printf("\n");
#endif
	printf("Sent.\n");
	free(buffer);
	free(str_size);
	return size;
}

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

	//open file to be received
	fileP=fopen(fpath,"wb+");
	if(fileP==NULL){
		printf("Unable to open file: %s\n",fpath);
		return -1;
	}

	printf("Receiving file: %s\n",fpath);
	str_size=calloc(20,1);

	//Receive file size
	fgets(str_size,512,fp);
	size=atoi(str_size);
	//Receive file
	i=  fread(fdata,1,size,fp);
	if(i!=size)
		return -1;
	printf("Received.\n");

	//Write file
	i=fwrite(fdata,1,size,fileP);
	if(i!=size){
		printf("Error receiving file: %s\n", fpath);
		return -1;
	}
	fclose(fileP);
	printf("Wrote to %s\n",fpath);
	return size;
}

int step4(int clientSocket, FILE* fp, char* key, char* gxgy, char* id1, char* id2){
	unsigned char fdata[500], buffer[500], fileMac[1024];
	int ret, i, j, digestLen;
	int size=0;
	unsigned char challengebuffer[20], challenge[500];
	FILE *fpointer;
	unsigned char digest[25];

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

        //Receive attestation challenge: Nonce|| CHALLENGE from HE
	strcpy(buffer,"");
	ret=recvstring(challengebuffer,fp);
        size=ret;
	printf("@step4@ NONCE||CHALLENGE: ");
	for(i=0;i<ret;i++)
		printf("%c",challengebuffer[i]);
	printf("\n");

	//Receive MAC of attestation challenge using Km from HE
	strcpy(buffer,"");
	ret=recvstring(buffer,fp);
	printf("@step4@ Received Hmac(Nonce||CHALLENGE): ");
	for(i=0;i<ret;i++)
		printf("%02x ",buffer[i]);
	printf("\n");

        //Calculate HMAC of received NONCE||CHALLENGE to verify HMAC from HE
	//gxgy is the nonce for the challenge
	strcpy(challenge,"");
	strcat(challenge,gxgy);
	strcat(challenge,ATTESTATION_CHALLENGE);
        HMAC(EVP_sha1(), key, strlen(key), challenge, size , digest, &digestLen);
	printf("@step4@ Required Hmac(Nonce||CHALLENGE): ");
        for(i=0;i<digestLen;i++)
		printf("%02x ",digest[i]);
	printf("\n");
	printf("@step4@ Authenticating received attestation...\n");
	for(i=0;i<digestLen;i++)
		if (digest[i]!=buffer[i]){
			printf("@step4@ Bad challenge from HE (HMAC error)\n");
			return -1;
		}
	printf("@step4@ Authenticated challenge from HE (valid HMAC)....\n");

	//Prepare challenge response
	//Create TRM key pair
/*
	if(createKey(id1, id2)!=TSS_SUCCESS){
		printf("@step4@ Error while creating trm key pair..exiting...\n");
		return -1;
	}
	else{
		printf("@step4@ TRM key created successfully...\n");
	}
*/
	//Certify TRM public key
	if(certifyKey(gxgy)!=TSS_SUCCESS){
		printf("@step4@ Error while certifying trm key..exiting...\n");
		return -1;
	}
	else{
		printf("@step4@ TRM key certified successfully...\n");
	}

	//Send certified TRM public key to HE	
	if(sendFile(CERTIFIED_KEY_trm, clientSocket)==-1)
	{
		printf("@step4@ Error sending certified key...exiting...\n");
		return -1;
	}

	//Send TRM public key certify info that will be used by HE to verify TRM key certificate
	if(sendFile(VERIFICATION_DATA_trm, clientSocket)==-1){
		printf("@step4@ Error sending verification data...exiting...\n");
		return -1;
	}

	//Send TRM public key to HE
	if(sendFile(TRM_PUB_KEY_trm, clientSocket)==-1){
		printf("@step4@ Error sending trm pub key...exiting...\n");
		return -1;
	}

	//Calculate HMAC of trm pub key, verification data and certified key
        size=0;
	fpointer=fopen(TRM_PUB_KEY_trm,"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_trm,"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_trm,"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, digest, &digestLen);

	printf("@step4@ HMAC of trm pub key, verification data & certified key: ");
	for(i=0;i<digestLen;i++)
		printf("%02x ",digest[i]);
	printf("\n");

	//Send to HMAC HE therby authenticating message source
	ret=sendstring(digest,digestLen,clientSocket);

	//Receive encrypted GROUP key
	if((ret=recvFile(ENC_GROUP_KEY_trm,fdata, fp))==-1){
		printf("@step4@ Error receiving encrypted GROUP key...exiting...\n");
		return -1;
	}

	//Unbind encrypted GROUP key
	if(unBind(ret)!=TSS_SUCCESS){
		printf("@step4@ error from unBind..exiting...\n");
		return -1;
	}
	else{
		printf("@step4@ unBind successful...\n");
	}

	printf("<----\n");	
	printf("*******Leaving step4***********\n");
	return size;

}

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); 
free(str_size);
return ret;
}

int main(int argc, char *argv[])
{
    int clientSocket,remotePort,status = 0;
    struct hostent *hostPtr = NULL;
    struct sockaddr_in serverName = { 0 };
    char buffer[2048] = "";
    unsigned char nonce[2048] = "";
    char *remoteHost = NULL;
    FILE *fp=NULL;
    //PARAMETERS OF DIFFIE_HELLMAN
    int g=5;//GENERATOR 
    int p=23;//Prime Number
    int x=5;//Number chosen by TRM
    int Ks=0,Km=0;//Ks-Session Key;Km-derived from Ks
    char key[2048]="";
    char hmac[2048]="";

    int size=0;
    char str_size[1024]="";
    unsigned long tempx=0,tempy=0;//tempx-g^xmodp,tempy-g^ymodp
    FILE *fpkey=NULL;
    char md[160]="";
    int mdlen;
//    char signgxgy[2048];
	unsigned char signgxgy[2048];
	unsigned char gxgy[500];
    int signgxgylen=0;
    int siglen=0;
    int ret=0;
    RSA *rsa_pub,*rsa_priv;
    RSA *rsa_ca_pub,*rsa_he_pub;
    struct certificate head_certificate={"","",""};
    struct certificate trm_certificate={"","",""}; 
    rsa_pub=RSA_new();
    rsa_priv=RSA_new();
    rsa_ca_pub=RSA_new();
    rsa_he_pub=RSA_new();
    
  
    fpkey=fopen("capub.pem","r");
    if(!fpkey) 
    {
      printf("File Error\n"); 
      exit(3);
    }
    rsa_ca_pub=(RSA *)PEM_read_RSA_PUBKEY(fpkey,NULL,0,NULL);
    if(!rsa_ca_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);
      

    int i=0;
    if (5 != argc)
    {
        fprintf(stderr, "Usage: %s <serverHost>	<serverPort> <key id1> <key id2>\n",argv[0]);
        exit(1);
    }

    remoteHost = argv[1];
    remotePort = atoi(argv[2]);

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

    /*
     * need to resolve the remote server name or 
     * IP address */
    hostPtr = gethostbyname(remoteHost);
    if (NULL == hostPtr)
    {
        hostPtr = gethostbyaddr(remoteHost, 
		strlen(remoteHost), AF_INET);
        if (NULL == hostPtr)
        {
        perror("Error resolving server address");
        exit(1);
        }
    }

    serverName.sin_family = AF_INET;
    serverName.sin_port = htons(remotePort);
    (void) memcpy(&serverName.sin_addr, 
		hostPtr->h_addr,
		hostPtr->h_length);

    status = connect(clientSocket, 
		(struct sockaddr*) &serverName,
        sizeof(serverName));
    if (-1 == status)
    {
        perror("connect()");
        exit(1);
    }
    fp=fdopen(clientSocket,"r");
    
//PROTOCOL BEGIN
//STEP-1: 
    //TRM Calculates g^x mod p
    tempx=((int)(pow(g,x)))%p;
    //TRM sends g^x mod p
    size=sizeof(tempx);
    printf("Size=%d\n",size);
    sprintf(str_size,"%d",size);
    str_size[strlen(str_size)]='\n';
    send(clientSocket,str_size,strlen(str_size),0);
    ret=send(clientSocket,&tempx,size,0);
    printf("ret=%d\n",ret);




//STEP-2

    //2-A Recieve g^ymodp
    fgets(str_size,512,fp);
    size=atoi(str_size);
    printf("str_Size Recieved=%s\n",str_size);
    printf("Size Recieved=%d\n",size);
    strcpy(buffer,""); 
    ret=fread(&tempy,1,4,fp); 
    printf("tempy=%d\n",tempy);
    printf("ret tempy=%d\n",ret);
    printf("Value Recieved in STEP::2-A FROM TRM g^ymodp=%d\n",tempy);
    //Calculate Ks=((g^y mod p)^x)modp;
    Ks=((unsigned long)pow(tempy,x))%p;
    //Calculate Km=Ks-1;
    Km=Ks-1;
    printf("Session Key-Ks=%d\n",Ks);

    //2-B Recieve Certificate
    //Name
    ret=recvstring(head_certificate.name,fp); 
    printf("ret for certificate name=%d\n",ret);
    printf("Certificate Name=%s\n",head_certificate.name);
    //PubKey
    ret=recvstring(head_certificate.pubkey,fp); 
    printf("ret for certificate name=%d\n",ret);
    printf("Certificate Pub=%s\n",head_certificate.pubkey);
    printf("------------\n");
    //Write PubKey to File to temperoary file and read it back again.
    fpkey=fopen("temp.pem","w");
    fwrite(head_certificate.pubkey,strlen(head_certificate.pubkey),1,fpkey);
    fclose(fpkey);
    fpkey=fopen("temp.pem","r");
    if(!fpkey) 
    {
      printf("File Error\n"); 
      exit(3);
    }
    rsa_he_pub=(RSA *)PEM_read_RSA_PUBKEY(fpkey,NULL,0,NULL);
    if(!rsa_he_pub)
    {
       printf("RSA 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);
    }
#if 0
    RSA_print_fp(stdout,rsa_he_pub,0);
#endif    
    fclose(fpkey);
    //Signature 
    ret=recvstring(head_certificate.sig,fp); 

#if 0 
    for(i=0;i<128;i++)
    printf("%c\n",head_certificate.sig[i]);
    printf("ret for certificate sig=%d\n",ret);
    printf("Stringlen of sig=%d\n",strlen(head_certificate.sig));
    printf("Certificate sig=%s\n",head_certificate.sig);    
#endif   
    //Verify Signature on Certificate with rsa_ca_pub
    //calculate sha1 of name||pubkey
    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));
    printf("Strlen of head_certi.sig=%d\n",strlen(head_certificate.sig));    
    ret=RSA_verify(NID_sha1, md, strlen(md),head_certificate.sig,128/*size*/,rsa_ca_pub); 
    if(ret!=1)
    {
        printf("STEP 2-B RSA_Verify Error\n",ret);
        exit(10);
    }  
    //while(1);

    //2-C Recieve sign(Concatenated tempx,tempy)
    printf("2-C BEGIN\n");
    ret=recvstring(signgxgy,fp); 
    printf("ret for certificate sig=%d\n",ret);
    printf("Read Bytes for tempxtempysign=%d\n",strlen(signgxgy));
    //Concatenate tempx,tempy
    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");
	for(i=0;i<strlen(buffer);i++)
		printf("%x",md[i]);
	printf("\n");
//    printf("%s\n",md);
    printf("------------------\n"); 
    printf("SIGNGXGY-Begin\n");
    printf("-----------------\n");
    //printf("%s\n",signgxgy);
#if 0   
    printf("------------------\n"); 
    printf("RSA-HE-PUB\n");
    RSA_print_fp(stdout,rsa_he_pub,0);
    printf("--------------\n");
#endif
    ret=RSA_verify(NID_sha1,md,strlen(md),signgxgy,128,rsa_he_pub);
    if(ret==0)
    {
          printf("STEP 2-C Verification FAILED\n");
          exit(10);         
    }
    printf("STEP-2C END\n");
    //2-C END

    //STEP-2D Recieve HMAC
    ret=recvstring(hmac,fp); 
    //printf("HMAC Recieved Bytes temp=%d\n",strlen(temp));
    printf("HMAC Recieved Bytes ret=%d\n",ret);
    printf("HMAC Recieved Bytes=%d\n",strlen(hmac));
    printf("Recieved HMAC=%s\n",hmac);
    //Calculate HMACkm(CertHE)
    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);
    HMAC(EVP_sha1(), key,strlen(key),buffer,strlen(buffer), md, &siglen);  
#if 0
    printf("HMAC=%s\n",md);
#endif
    //compare calcuated vs recieved hmac
    ret=strncmp(hmac,md,20);///*strlen(hmac)-1*/);
    if(ret!=0)
    {
       printf("2-D HMAC-ERROR\n");
       exit(10);
    } 
   
    
//STEP-3 BEGIN //Cert(Aik) || SignAik(gx,gy) || MacKm(Cert)

//STEP 3-A BEGIN
    RSA* rsa_aik_pub;
    struct certificate aik_certificate={"","",""}; 
    printf("STEP3-A BEGIN----\n");  
    sendFile(AIK_CERT_trm, clientSocket);
    printf("STEP3-A END----\n");  
//3-A END

//STEP 3-B BEGIN - SignAik(gy,gx)
    //3-B BEGIN Concatenate tempy,tempx
    printf("STEP3-B BEGIN---\n");
    strcpy(nonce,"");
    sprintf(nonce,"%d%d",tempy,tempx);
	strcpy(gxgy,nonce);
    printf("tempytempx=%s\n",nonce);
    printf("strlenoftempytempx=%d\n",strlen(nonce));

//send gx||gy as the nonce to tpm_quote.
//tpm_quote internally pads additional bytes to make it 20 bytes.
//tpm_quote is used to get the AIK signature on gx||gy
//HE should disregard the nonce padding while verifying signed nonce it receives

    signgxgylen=_quote_Main(nonce, strlen(nonce), signgxgy);
    if(signgxgylen==-1){
	printf("Error in _quote_Main...exiting...\n");
	exit(1);
    }
    printf("\nsignature: %d\n", signgxgylen);
    //Signed quote info

    for(i=0;i<signgxgylen;i++)
	printf("%x", signgxgy[i]);
	printf("\n");

    size=signgxgylen;
    ret=sendstring(signgxgy,size,clientSocket);
    printf("Sent Bytes=%d\n",ret);

//   printf("signature: %s\n",_quote_Main(buffer));	
//   strcpy(signgxgy,_quote_Main(buffer));
//   printf("signature: %s\n",signgxgy);
//STEP 3-B END


//STEP 3-C BEGIN - MacKm(Cert)
   //Calculate HMACkm(CertHE)
    printf("STEP3-C BEGIN-------\n");  
    printf("-----------------------------------------------------------\n");
    strcpy(buffer,"");
    strcpy(md,"");
    sprintf(key,"%d",Km);
    //printf("%s\n",key);
    fpkey=fopen(AIK_CERT_trm,"r");
    ret=fread(buffer,1,2048,fpkey);
    fclose(fpkey);
    HMAC(EVP_sha1(), key,strlen(key),buffer,ret, md, &siglen);
    //Send HMAC
    printf("Strlen of md=%d\n",strlen(md));
    size=siglen;
    ret=sendstring(md,size,clientSocket);
    printf("HMAC=%s\n",md);
    //STEP 3-C END
    printf("STEP3-C END----\n");  
    printf("STEP-3 END\n");  
    printf("-----------------------------------------------------------\n");    


//STEP-3 END
//STEP 4

	ret=step4(clientSocket,fp, key, gxgy, argv[3], argv[4]);
	
	if(ret==-1){
		printf("@trm@ step4 error\n");
		exit(-1);
	}
	else printf("@trm@ step4 success; received GROUP secret\n");
	
    close(clientSocket);

    return 0;
}

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",string);
#endif
free(str_size);
return ret;
}
