#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include "daemon.h"
#include "daemon_error.h"

#ifndef DX_DM_USE_CRYPTOREF

#include "CRYS.h"
#include "MNG_Functions.h"

#else

#include "CryptoEngine2.h"

#endif //DX_DM_USE_CRYPTOREF

/*------------------
    GLOBAL variables
---------------------*/

/* application socket */
int           g_AppSockets[DX_DM_MAX_NUM_CONNECTED_APPLICATION];

/* connection socket */
int           g_ConnSocket;

/* file descritpor for SEP driver connection */
int           g_FD;

/* data buffer, used for holding the data that wil be used for crypto with SEP */
unsigned char g_DataBuffer[DX_DM_DATA_BUFFER_SIZE];


/*--------------------------------
    PROTOTYPES
-----------------------------------*/

/*
  This function initializes access to the SEP driver 
*/
static int dx_dm_open_driver(void);

/*
*/
static void dx_dm_handle_caller_id_request(int sock_fd);

/*
*/
static void dx_dm_handle_conn_request();

/*
*/
static void dx_dm_handle_application_request(int sock_fd);

/*
  This function reads the application exe file, parses it, and returns the signature, list of authorized dlls and all the rest of the data of the sign section
*/
static int dx_dm_parse_app_binary(char*                   file_name,
                                  dx_app_sign_section_st* section_data_ptr,
                                  unsigned char**         sign_ptr,
                                  unsigned long*          sign_size_ptr,
                                  dx_pk_st**              dlls_pk_ptr,
                                  unsigned long*          num_dlls_pk_ptr);

/* 
  This function parses the /proc/pid/maps file to get each ddl and then authebnticates them 
*/
static int dx_dm_parse_dlls_binary(char*          file_name,
                                   dx_pk_st*      dll_pks_ptr,
                                   unsigned long  num_dll_pks);
                                   
/*
  This functions parses the elf file of the DDL and returns its signature and public key
*/
static int dx_dm_get_dll_signature_pk(char*           file_name,
                                      unsigned char** sign_ptr,
                                      dx_pk_st*       dll_pk);
                                   
/*
 This function performs verification on the file using the supplied public key and signature
*/
static int dx_dm_verify_elf_file(char*                  file_name,
#ifndef DX_DM_USE_CRYPTOREF
                                 CRYS_RSAUserPubKey_t*  UserPubKey_ptr,
#else
                                 CE2_RSAUserPubKey_t*   UserPubKey_ptr,
#endif //DX_DM_USE_CRYPTOREF
                                 unsigned char*         sign_ptr);

/*
  This function checks if the dlls public is in the list of public keys allowed by application
*/
static int dx_dm_check_dll_pk(dx_pk_st*     dll_pk_ptr,
                              dx_pk_st*     app_pk_list_ptr,
                              unsigned long num_pk_list);
                              
/*
  This functions parses the elf file of the DDL and returns its signature and sign section data, which includes public key
*/
static int dx_dm_get_dll_signature_sct(char*                   file_name,
                                       unsigned char**         sign_ptr,
                                       dx_dll_sign_section_st* dll_sign_section_ptr);
                                       
/*
  This functions hashes the caller id and stores its hash with the pid in the caller id table of the driver
*/                                       
static int dx_dm_store_caller_id(int            pid , 
                                 unsigned long  callerIdSizeInBytes,
                                 unsigned char* callerId_ptr);
                                 
/*
  send the reposnse to the application
*/
static void dx_dm_send_response_to_app(int sockFD , 
                                       int replyId , 
                                       int status , 
                                       void* data , 
                                       int dataSize);

/*---------------------------
    FUNCTIONS
----------------------------*/

int main(void) 
{
  /* return value  */
  int     ret_val;

  /* socket address */  
  struct  sockaddr_un local;
  
  /* sockaddr of the application */
  struct  sockaddr_un remote;
  
  /* length */
  int     len;
  
  /* counter */
  int     count;
  
  /* maximum socket */
  int     max_socket;

  /* read fd */
  fd_set  readfd;
  
  /* we assume that the daemon is started by the system. If not, then forking and going under init process is required */ 
  
  /* first of all  - lock the singleton access to SEP driver */
  if(dx_dm_open_driver())
  {
    exit(EXIT_FAILURE);
  }
  
  g_ConnSocket = socket(PF_UNIX, SOCK_STREAM, 0);
  if(g_ConnSocket < 0)
  {
    exit(EXIT_FAILURE);
  }
  
  /* prepare bind parameters */
  local.sun_family = PF_UNIX;  /* local is declared before socket() ^ */
  strcpy(local.sun_path, DX_DM_SOCK_PATH);
  unlink(local.sun_path);
  len = strlen(local.sun_path) + sizeof(local.sun_family);
  
  /* bind */
  ret_val = bind(g_ConnSocket, (struct sockaddr *)&local, len);
  if(ret_val < 0)
  {
    exit(EXIT_FAILURE);
  }
  
  /* listen */
  listen(g_ConnSocket , DX_DM_LISTEN_BACKLOG);
  
  /* main loop */
  while (1) {
   
    /* prepare the FD set */
    FD_ZERO(&readfd);
    
    /* set the listen socket */
    FD_SET(g_ConnSocket , &readfd);
    
    max_socket = g_ConnSocket;
    
    /* set the applications sockets */
    for(count = 0; count < DX_DM_MAX_NUM_CONNECTED_APPLICATION; count++)
    {
      if(g_AppSockets[count])
      {
        FD_SET(g_AppSockets[count] , &readfd);
      }
      
      /* find the maximum */
      if(max_socket < g_AppSockets[count])
      {
        max_socket =  g_AppSockets[count];
      }
    }
    
    /* poll for the events on sockets (aplications) */
    ret_val = select(max_socket + 1 , &readfd , NULL , NULL , NULL);
    if(ret_val < 0)
    {
      exit(EXIT_FAILURE);
    }
    
    /* check which socket is triggered */
    if(FD_ISSET(g_ConnSocket , &readfd))
    {
      /* connection request */
      dx_dm_handle_conn_request();
    }
    
    for(count = 0; count < DX_DM_MAX_NUM_CONNECTED_APPLICATION; count++)
    {
      if(g_AppSockets[count] && FD_ISSET(g_AppSockets[count] , &readfd))
      {
        /* message from application */
        dx_dm_handle_application_request(g_AppSockets[count]);
      }
    }   
  }  
  exit(EXIT_SUCCESS);
}

/*-------------------------------------------
    PRIVATE FUNCTIONS
---------------------------------------------*/

/*
  This function initializes access to the SEP driver 
*/
static int dx_dm_open_driver(void)
{
  int error;
  
  int s_error;
  
  /*--------------------
      CODE
  ---------------------*/
  
  error = 0;
  
  /* open device driver */
  g_FD = open(DX_DM_SEP_DRIVER_PATH , O_RDWR|O_SYNC);
  if(g_FD < 0)
  {
    s_error = errno;
    perror("failed to open:");
    error = DX_DM_SEP_DRIVER_ACCESS_ERR;
  }
  
  return error;
}

/*
  This functions hashes the caller id and stores its hash with the pid in the caller id table of the driver
*/                                       
static int dx_dm_store_caller_id(int            pid , 
                                 unsigned long  callerIdSizeInBytes,
                                 unsigned char* callerId_ptr)
{
  /* error */
  int                 error;
  
  /* hash result buffer */
  CRYS_HASH_Result_t  hashResultBuff;
  
  /*----------------------
      CODE
  ------------------------*/
  
  error = 0;
  
  /* create the hash of caller id */
  error = CRYS_HASH(CRYS_HASH_SHA256_mode , callerId_ptr , callerIdSizeInBytes , hashResultBuff);
  if(error)
  {
    error = DX_DM_CRYS_OPERATION_ERR;
    goto end_function;
  }
  
  error = SEPDriver_SetCallerIdData(pid , hashResultBuff ,CRYS_HASH_SHA256_DIGEST_SIZE_IN_BYTES);
  
end_function:
  
  return error;
}


/*
*/
static void dx_dm_handle_conn_request()
{
  /* counter */
  int                 count;
  
  /* length */
  int                 len;
  
  /* sockaddr of the application */
  struct  sockaddr_un remote;
  
  /*-------------------------
      CODE
  -----------------------------*/

  /* find empty slot */
  for(count = 0; count < DX_DM_MAX_NUM_CONNECTED_APPLICATION; count++)
  {
    if(g_AppSockets[count] == 0)
    {
      break;
    }
  }
  
  if(count == DX_DM_MAX_NUM_CONNECTED_APPLICATION)
  {
    /* no available connection slots */
    return;
    
  }
  
  len = sizeof(struct  sockaddr_un);
  g_AppSockets[count] = accept(g_ConnSocket , (struct sockaddr*)&remote , &len);
  
  
  return;   
}

/*
*/
static void dx_dm_handle_application_request(int sock_fd)
{
  /* command */
  dx_dm_app_request command_arg;
  
  /*-------------------------------
      CODE
  ----------------------------------*/
  
  read(sock_fd , &command_arg , sizeof(dx_dm_app_request));
  
  switch(command_arg.command_id)
  {
    case CALLER_ID_CMD:
      
      /* handle the caller id request */
      dx_dm_handle_caller_id_request(sock_fd);
      
      break;
      
    default:
      break;
  }
}


/*
*/
static void dx_dm_handle_caller_id_request(int sock_fd)
{
  /* process credentials */
  struct ucred            creds;
  
  /* return value */
  int                     ret_val;
  
  /* file name */
  char                    file_name[20];
  
  /* sign section */
  dx_app_sign_section_st  sign_section;
  
  /* pointer to the autorized keys of the DDLs */
  dx_pk_st*               dll_pks_ptr;
  
  /* number of the public keys for dlls */
  unsigned long           num_dll_pks;
  
  /* signature size */
  unsigned long           sign_size;
  
  /* pointer to signature */
  unsigned char*          sign_ptr;
  
  /* length */
  socklen_t               len;
  
#ifndef DX_DM_USE_CRYPTOREF

  /* public key for authentication */
  CRYS_RSAUserPubKey_t    userPubKey; 
  
  /* crys error */
  CRYSError_t             error;   

#else

  CE2_RSAUserPubKey_t     userPubKey;
  
  /* crys error */
  CE2Error_t              error;  

#endif //DX_DM_USE_CRYPTOREF  
 
  /*----------------------
      CODE
  ---------------------------*/
  
  /* get the credentials (pid) of the application through its socket */
  len = sizeof(struct ucred);
  if(getsockopt(sock_fd , SOL_SOCKET , SO_PEERCRED , &creds , &len))
  {
    return;
  }
  
  /* read the proc/pid/exec and authenticate */
  sprintf(file_name , "/proc/%d/exe",creds.pid);
  
  /* parse the file and get the signature */
  ret_val = dx_dm_parse_app_binary(file_name ,
                                   &sign_section, 
                                   &sign_ptr , 
                                   &sign_size,
                                   &dll_pks_ptr,
                                   &num_dll_pks);
  if(ret_val < 0)
  {
    return;
  }
 
#ifndef DX_DM_USE_CRYPTOREF  
  /* build public key for authentication */
#if 0
  error = CRYS_RSA_Build_PubKey(&userPubKey,
                                sign_section.app_pk.pk_exponent,
                                sign_section.app_pk.exponent_size,
                                sign_section.app_pk.modulus,
                                sign_section.app_pk.modulus_size);
#endif
                                
  error = DX_MNG_RSA_BuildPlatformPubKey(1,
                                         0,
                                         &userPubKey,
                                         sign_section.app_pk.pk_exponent,
                                         sign_section.app_pk.exponent_size,
                                         sign_section.app_pk.modulus_size,
                                         sign_section.app_pk.modulus);
#else
  error = CE2_RSA_Build_PubKey(&UserPubKey,
                              sign_section.app_pk.pk_exponent,
                              sign_section.app_pk.exponent_size,
                              sign_section.app_pk.modulus,
                              sign_section.app_pk.modulus_size); 
#endif // DX_DM_USE_CRYPTOREF
  
  if(error)
  {
    return;
  }
  
  /* authenticate file */
  ret_val = dx_dm_verify_elf_file(file_name,
                                  &userPubKey,
                                  sign_ptr);
  if(ret_val < 0)
  {
    return;
  }

#ifndef DX_DM_VERIFY_DDLS_NOT_SUPPORTED

  /* get the list of the DLLs from the /proc/pid/maps */
  sprintf(file_name , "/proc/%d/maps",creds.pid);
  
  ret_val = dx_dm_parse_dlls_binary(file_name , 
                                    dll_pks_ptr,
                                    num_dll_pks);
  if(ret_val < 0)
  {
    return;
  }
  
#endif



  /* set the caller id in the driver */
  ret_val = dx_dm_store_caller_id(creds.pid , 
                                  sign_section.callerIdSizeInBytes,
                                  sign_section.callerId);
                                  
end_function:

  dx_dm_send_response_to_app(sock_fd,
                             CALLER_ID_CMD,
                             ret_val,
                             NULL,
                             0);  
  
                  				                   
}


/*
  This function reads the application exe file, parses it, and returns the signature, list of authorized dlls and all the rest of the data of the sign section
*/
static int dx_dm_parse_app_binary(char*                   file_name,
                                  dx_app_sign_section_st* section_data_ptr,
                                  unsigned char**         sign_ptr,
                                  unsigned long*          sign_size_ptr,
                                  dx_pk_st**              dlls_pk_ptr,
                                  unsigned long*          num_dlls_pk_ptr)
{
  /* return value */
  int                 ret_val;
  
  /* error */
  int                 error;
  
  /* elf header */
  elf_hdr_st          elf_header;
  
  /* elf section header entry */
  elf32_shdr_st       elf_section_hdr_ent;
  
  /* file */
  FILE*               file;
  
  /* counter */
  int                 count;
  
  /*--------------------------------
      CODE
  ------------------------------------*/
  
  error = 0;
  
  file = fopen(file_name , "r");
  if(file == NULL)
  {
    error = DX_DM_OPEN_EXE_FILE_ERR;
    goto end_function;
  }
  
  /* read the ELF header */
  ret_val = fread((void*)&elf_header , 1 , sizeof(elf_hdr_st) , file);
  if(ret_val != sizeof(elf_hdr_st))
  {
    error = DX_DM_ELF_HDR_READ_ERR;
    goto end_function_close_file;
  }
  
  /* goto to the section header start */
  ret_val = fseek(file , elf_header.e_shoff , SEEK_SET);
  if(ret_val < 0)
  {
    error = DX_DM_ELF_SECTION_SEEK_ERR;
    goto end_function_close_file;
  }
  
  for(count = 0; count < elf_header.e_shnum; count++)
  {
    /* read each section header entry until reaching the signature section */
    ret_val = fread(&elf_section_hdr_ent , 1 , elf_header.e_shentsize , file);
    if(ret_val != elf_header.e_shentsize)
    {
      error = DX_DM_ELF_SCT_HDR_READ_ERR;
      goto end_function_close_file;
    }
  
    if(elf_section_hdr_ent.sh_type == DX_SIGN_SECTION_TYPE)
    {
      break;
    }
  }
  
  /* check if sign section was found */
  if(count == elf_header.e_shnum)
  {
    error = DX_DM_NO_SIGN_SECTION_ERR;
    goto end_function_close_file;
  }
  
  /* go to the beggining of the section */
  ret_val = fseek(file , elf_section_hdr_ent.sh_offset , SEEK_SET);
  if(ret_val < 0)
  {
    error = DX_DM_ELF_SECTION_SEEK_ERR;
    goto end_function_close_file;
  }
  
  ret_val = fread(section_data_ptr , 1 , sizeof(dx_app_sign_section_st) , file);
  if(ret_val != sizeof(dx_app_sign_section_st))
  {
    error = DX_DM_ELF_SIGN_SCT_READ_ERR;
    goto end_function_close_file;
  }
  
  /* set the number of the authorized pki */
  *num_dlls_pk_ptr = section_data_ptr->numDLLPks;
  
  /* allocate memory for the the public keys of the DDLs */
  *dlls_pk_ptr = (dx_pk_st*)malloc(sizeof(dx_pk_st) * section_data_ptr->numDLLPks);
  if(*dlls_pk_ptr == NULL)
  {
    error = DX_DM_MALLOC_FAIL_ERR;
    goto end_function_close_file;
  }
  
  ret_val = fread(*dlls_pk_ptr , 1 , (sizeof(dx_pk_st) * section_data_ptr->numDLLPks) , file);
  if(ret_val != (sizeof(dx_pk_st) * section_data_ptr->numDLLPks))
  {
    error = DX_DM_SIGN_DATA_READ_ERR;
    goto end_function_dealloc1;
  }
  
  /* allocate buffer for signature */
  *sign_ptr = (unsigned char*)malloc(DX_DM_MAX_SIGN_LEN_IN_BYTES);
  if(*sign_ptr == NULL)
  {
    error = DX_DM_MALLOC_FAIL_ERR;
    goto end_function_dealloc1;
  }
  
  /* read all the signature */
  ret_val = fread(*sign_ptr , 1 , DX_DM_MAX_SIGN_LEN_IN_BYTES , file);
  if(ret_val !=  DX_DM_MAX_SIGN_LEN_IN_BYTES)
  {
    error = DX_DM_SIGN_DATA_READ_ERR;
    goto end_function_dealloc2;
  }
  
  goto end_function_close_file;
  
end_function_dealloc2:

  /* free signature array */
  free(*sign_ptr);

end_function_dealloc1:

  /* free authorised dlls */
  free(*dlls_pk_ptr);

end_function_close_file:

  /* close file */
  fclose(file);  
  
end_function:
  
  return error;
}

/* 
  This function parses the /proc/pid/maps file to get each ddl and then authebnticates them 
*/
static int dx_dm_parse_dlls_binary(char*          file_name,
                                   dx_pk_st*      dll_pks_ptr,
                                   unsigned long  num_dll_pks)
{
  /* file */
  FILE*                   file;
  
  /* return value */
  int                     ret_val;
  
  /* error */
  int                     error;
  
  /* parameter of the line in the maps file */
  char*                   param_ptr;
  
  /* permission string */
  char*                   permission_ptr;
  
  /* inode string */
  char*                   inode_ptr;
  
  /* line in the maps file */
  char*                   line_ptr;
  
  /* dll signature pointer */
  unsigned char*          sign_ptr;
  
  /* sign section of the DLL */
  dx_dll_sign_section_st  dll_sign_section;
  
  /* size */
  int                     size;
  
  /* counter */
  int                     count;
 
#ifndef DX_DM_USE_CRYPTOREF

  /* public key for authentication */
  CRYS_RSAUserPubKey_t    userPubKey; 

#else

  CE2_RSAUserPubKey_t     userPubKey;

#endif //DX_DM_USE_CRYPTOREF
  
  /*--------------------------------
      CODE
  ------------------------------------*/
  
  error = 0;
  
  /* open maps file */
  file = fopen(file_name , "r");
  if(file == NULL)
  {
    error = DX_DM_OPEN_EXE_FILE_ERR;
    goto end_function;
  }
  
  /* read the first 2 lines - they define the mapping of the executable code and data */
  line_ptr = NULL;
  
  /* read text mapping */
  if(getline(&line_ptr , &size , file) < 0)
  {
    error = DX_DM_INVALID_MAPS_FILE_ERR;
    goto end_function;
  }
  
  /* read data mapping */
  if(getline(&line_ptr , &size , file) < 0)
  {
    error = DX_DM_INVALID_MAPS_FILE_ERR;
    goto end_function;
  }
  
  free(line_ptr);
  
  /* loop over the lines and authenticate each dll */
  while(!feof(file))
  {
    line_ptr = NULL;
    
    /* read mapping */
    if(getline(&line_ptr , &size , file) < 0)
    {
      break;
    }
    
    /* get mapping area */
    param_ptr = strtok(line_ptr , " \n\r");
    if(param_ptr == NULL)
    {
      error = DX_DM_MAPS_FILE_PARSE_ERR;
      goto end_function;
    }
    
    /* get permissions */
    permission_ptr = strtok(NULL , " \n\r");
    if(permission_ptr == NULL)
    {
      error = DX_DM_MAPS_FILE_PARSE_ERR;
      goto end_function;
    }
    
    /* check if there is an executable permision */
    if(strchr(permission_ptr , 'x') == NULL)
    {
      /* this is not an executable dll mapping */
      free(line_ptr);
      continue;
    }
    
    /* get offset */
    param_ptr = strtok(NULL , " \n\r");
    if(param_ptr == NULL)
    {
      error = DX_DM_MAPS_FILE_PARSE_ERR;
      goto end_function;
    }
    
    /* get time */
    param_ptr = strtok(NULL , " \n\r");
    if(param_ptr == NULL)
    {
      error = DX_DM_MAPS_FILE_PARSE_ERR;
      goto end_function;
    }
    
    /* get inode */
    inode_ptr = strtok(NULL , " \n\r");
    if(inode_ptr == NULL)
    {
      error = DX_DM_MAPS_FILE_PARSE_ERR;
      goto end_function;
    }
    
    /* get the executable path and name */
    param_ptr = strtok(NULL , " \n\r");
    
    /* if we reached this far, then the permission is executable */ 
    if( param_ptr[0] == '[' )
    {
      /* this is not a dll mapping */
      free(line_ptr);
      continue;
    }
    
    if(inode_ptr[0] == '0')
    {
      /* illegal dll mapping */
      error = DX_DM_ILLEGAL_INODE_DLL_ERR;
      goto end_function;
    }
    
    /* parse and verify the ddls */
    ret_val = dx_dm_get_dll_signature_sct(param_ptr , &sign_ptr , &dll_sign_section);
    
    if(ret_val < 0)
    {
      error = ret_val;
      break;
    }
    
    /* check if the public key is inside the list of application's public keys */
    ret_val = dx_dm_check_dll_pk(&dll_sign_section.dll_pk , dll_pks_ptr , num_dll_pks);
    if(ret_val == 0)
    {
      /* the key of DDL is not in the allowed keys of the application */
      break;
    }
#ifndef DX_DM_USE_CRYPTOREF   
    /* build public key for authentication */
    error = CRYS_RSA_Build_PubKey(&userPubKey,
                                  dll_sign_section.dll_pk.pk_exponent,
                                  dll_sign_section.dll_pk.exponent_size,
                                  dll_sign_section.dll_pk.modulus,
                                  dll_sign_section.dll_pk.modulus_size);
#else
    error = CE2_RSA_Build_PubKey(&UserPubKey,
                                dll_sign_section.dll_pk.pk_exponent,
                                dll_sign_section.dll_pk.exponent_size,
                                dll_sign_section.dll_pk.modulus,
                                dll_sign_section.dll_pk.modulus_size);

#endif //DX_DM_USE_CRYPTOREF                              
    
    if(error)
    {
      return;
    }
  
    /* authenticate file */
    ret_val = dx_dm_verify_elf_file(param_ptr,
                                    &userPubKey,
                                    sign_ptr);
    if(ret_val < 0)
    {
      break;
    }
    
  }
  
  fclose(file);
  
end_function:

  if(line_ptr)
  {
    free(line_ptr);
  }

  return error;
}

/*
  This functions parses the elf file of the DDL and returns its signature and sign section data, which includes public key
*/
static int dx_dm_get_dll_signature_sct(char*                   file_name,
                                       unsigned char**         sign_ptr,
                                       dx_dll_sign_section_st* dll_sign_section_ptr)
{
  /* file */
  FILE*                   file;
  
  /* return value */
  int                     ret_val;
  
  /* error */
  int                     error;
  
  /* elf header */
  elf_hdr_st              elf_header;
  
  /* elf section header entry */
  elf32_shdr_st           elf_section_hdr_ent;
  
  /* counter */
  int                     count;
  
  /*--------------------------------
      CODE
  ------------------------------------*/
  
  error = 0;
  
  file = fopen(file_name , "r");
  if(file == NULL)
  {
    error = DX_DM_OPEN_EXE_FILE_ERR;
    goto end_function;
  }
  
  /* read the ELF header */
  ret_val = sizeof(elf_hdr_st);
  ret_val = fread((void*)&elf_header , 1 , sizeof(elf_hdr_st) , file);
  if(ret_val != sizeof(elf_hdr_st))
  {
    error = DX_DM_ELF_HDR_READ_ERR;
    goto end_function_close_file;
  }
  
  /* goto to the section header start */
  ret_val = fseek(file , elf_header.e_shoff , SEEK_SET);
  if(ret_val < 0)
  {
    error = DX_DM_ELF_SECTION_SEEK_ERR;
    goto end_function_close_file;
  }
  
  for(count = 0; count < elf_header.e_shnum; count++)
  {
    /* read each section header entry until reaching the signature section */
    ret_val = fread(&elf_section_hdr_ent , 1 , elf_header.e_shentsize , file);
    if(ret_val != elf_header.e_shentsize)
    {
      error = DX_DM_ELF_SCT_HDR_READ_ERR;
      goto end_function_close_file;
    }
  
    if(elf_section_hdr_ent.sh_type == DX_SIGN_SECTION_TYPE)
    {
      break;
    }
  }
  
  /* check if sign section was found */
  if(count == elf_header.e_shnum)
  {
    error = DX_DM_NO_SIGN_SECTION_ERR;
    goto end_function_close_file;
  }
  
  /* go to the beginning of the section */
  ret_val = fseek(file , elf_section_hdr_ent.sh_offset , SEEK_SET);
  if(ret_val < 0)
  {
    error = DX_DM_ELF_SECTION_SEEK_ERR;
    goto end_function_close_file;
  }
  
  ret_val = fread(dll_sign_section_ptr , 1 , sizeof(dx_dll_sign_section_st) , file);
  if(ret_val != sizeof(dx_dll_sign_section_st))
  {
    error = DX_DM_ELF_SIGN_SCT_READ_ERR;
    goto end_function_close_file;
  }
  
  /* allocate buffer for signature */
  *sign_ptr = (unsigned char*)malloc(DX_DM_MAX_SIGN_LEN_IN_BYTES);
  if(*sign_ptr == NULL)
  {
    error = DX_DM_MALLOC_FAIL_ERR;
    goto end_function_close_file;
  }
  
  /* read all the signature */
  ret_val = fread(*sign_ptr , 1 , DX_DM_MAX_SIGN_LEN_IN_BYTES , file);
  if(ret_val !=  DX_DM_MAX_SIGN_LEN_IN_BYTES)
  {
    error = DX_DM_SIGN_DATA_READ_ERR;
    goto end_function_dealloc;
  }
  
  goto end_function_close_file;

end_function_dealloc:

  free(*sign_ptr);
  
end_function_close_file:
  
  fclose(file);
  
end_function:

  return error;
}

/*
 This function performs verification on the file using the supplied public key and signature
*/
static int dx_dm_verify_elf_file(char*                  file_name,
#ifndef DX_DM_USE_CRYPTOREF
                                 CRYS_RSAUserPubKey_t*  UserPubKey_ptr,
#else
                                 CE2_RSAUserPubKey_t*   UserPubKey_ptr,
#endif //DX_DM_USE_CRYPTOREF
                                 unsigned char*         sign_ptr)
{
  /* file */
  FILE*                     file;
  
  /* return value */
  int                       ret_val;
  
  /* total bytes read */
  int                       numBytesLeft;
  
  /* number of bytes to read */
  int                       numBytesToRead;
  
  /* data on the file */
  struct stat               statData;

#ifndef DX_DM_USE_CRYPTOREF 
  
  /* user context */
  CRYS_RSAPubUserContext_t  userContext;
  
  /* hash context */
  CRYS_HASHUserContext_t    hashContext;
  
  /* hash result */
  CRYS_HASH_Result_t        hashResult;
  
  /* error */
  int                       error;
  
#else  
  CE2_HASHUserContext_t     hashContext;
  
  CE2_HASH_Result_t         hashResult;
  
  /* error */
  CE2Error_t                error;

#endif //DX_DM_USE_CRYPTOREF  
  /*-----------------------------
      CODE
  -------------------------------*/
  
  error = 0;
  ret_val = 0;
  
  /* open file */
  file = fopen(file_name , "r");
  if(file == NULL)
  {
    ret_val = DX_DM_OPEN_EXE_FILE_ERR;
    goto end_function;
  }
  
  if(stat(file_name , &statData) < 0)
  {
    ret_val = DX_DM_OPEN_EXE_FILE_ERR;
    goto end_function;
  }
  
#ifdef DX_DM_USE_CRYPTOREF  
  error = CE2_HASH_Init(&hashContext , CE2_HASH_SHA1_mode);
#else
  error = CRYS_HASH_Init(&hashContext , CRYS_HASH_SHA1_mode);
#endif
  if(error)
  {
    ret_val = DX_DM_AUTHENTICATE_FAILED_ERR;
    goto end_function;
  }
  
  numBytesLeft = (statData.st_size - DX_DM_MAX_SIGN_LEN_IN_BYTES);
  while( numBytesLeft )
  {
    numBytesToRead = DX_DM_DATA_BUFFER_SIZE;
    if(numBytesLeft < DX_DM_DATA_BUFFER_SIZE)
    {
      numBytesToRead = numBytesLeft;
    }
   
    ret_val = fread(g_DataBuffer , 1 , numBytesToRead , file);
    if(ret_val < 0)
    {
      ret_val = DX_DM_AUTHENTICATE_FAILED_ERR;
      goto end_function;
    }
    
    numBytesLeft -= ret_val;
#ifdef DX_DM_USE_CRYPTOREF    
    error = CE2_HASH_Update(&hashContext , g_DataBuffer , ret_val);
#else
    error = CRYS_HASH_Update(&hashContext , g_DataBuffer , ret_val);
#endif
    if(error)
    {
      ret_val = DX_DM_AUTHENTICATE_FAILED_ERR;
      goto end_function;
      
    }
  }

#ifdef DX_DM_USE_CRYPTOREF  
  error = CE2_HASH_Finish(&hashContext , hashResult);
#else  
  error = CRYS_HASH_Finish(&hashContext , hashResult);
#endif  
  if(error)
  {
    ret_val = DX_DM_AUTHENTICATE_FAILED_ERR;
    goto end_function;
      
  }
  
#ifdef DX_DM_USE_CRYPTOREF  
  error = CE2_DX_RSA_Verify(UserPubKey_ptr,
                            CE2_RSA_After_SHA1_mode,
                            CE2_PKCS1_MGF1,
                            20,
                            hashResult,
                            CE2_HASH_SHA256_DIGEST_SIZE_IN_BYTES,
                            sign_ptr,
                            CE2_PKCS1_VER21);
  
#else
  error = CRYS_RSA_PSS_SHA1_Verify(&userContext,
                                   UserPubKey_ptr,
                                   CRYS_PKCS1_MGF1,
                                   20,
                                   (DxUint8_t*)hashResult,
                                   sign_ptr);
#endif
  if(error)
  {
    ret_val = DX_DM_AUTHENTICATE_FAILED_ERR;
    goto end_function;
    
  }
  
  ret_val = error;
  
end_function:

  return ret_val;
}

/*
  This function checks if the dlls public is in the list of public keys allowed by application
*/
static int dx_dm_check_dll_pk(dx_pk_st*     dll_pk_ptr,
                              dx_pk_st*     app_pk_list_ptr,
                              unsigned long num_pk_list)
{
  /* count */
  int count;
  
  /*--------------------------
      CODE
  -----------------------------*/
  
  for(count = 0; count < num_pk_list; count++)
  {
    /* check the sizes of exponent and modulus */
    if( (dll_pk_ptr->modulus_size != app_pk_list_ptr[count].modulus_size) ||
        (dll_pk_ptr->exponent_size != app_pk_list_ptr[count].exponent_size) )
    {
      continue;
    }
    
    /* check the value of exponent */
    if(memcmp(dll_pk_ptr->modulus , app_pk_list_ptr[count].modulus , dll_pk_ptr->modulus_size ))
    {
      continue;
    }
    
    /* check the value of exponent */
    if(memcmp(dll_pk_ptr->pk_exponent , app_pk_list_ptr[count].pk_exponent , dll_pk_ptr->exponent_size ))
    {
      continue;
    }
    
    /* if we reached this point - the key is equal */
    break;
  }
  
  if(count == num_pk_list)
  {
    /* no matching key was found */
    return 0;
  }
  
  return 1;
  
}


/*
  send the reposnse to the application
*/
static void dx_dm_send_response_to_app(int sockFD , 
                                       int replyId , 
                                       int status , 
                                       void* data , 
                                       int dataSize)
{
  dx_dm_app_reply   replyCmd;
  
  /*------------------------
      CODE
  ----------------------------*/
  
  replyCmd.replyId = replyId;
  replyCmd.status = status;
  
  if(data != NULL && dataSize != 0)
  {
    memcpy(&replyCmd.data , data , dataSize);
  }
  
  /* send reply to application via socket */
  write(sockFD , &replyCmd , sizeof(dx_dm_app_reply));
  
  return;
  
}
