/* chat.c

   Shane Eisenman - 11/2002

   This file contains the main chat routine
   and the supporting functions. See the
   Exe_Info() function for details on usage
*/

/*--------------------------------------*/
#include "chat.h"
#include "common.h"
#ifdef VERBOSE
#define DEBUG
#endif
/*======================================*/
int main(int argc, char** argv) {

  unsigned char group[GRP_LEN];
  memset(group,'\0',GRP_LEN);

  if (argc!=2) {
    printf("usage: chat <grp>\n");
    exit(1);
  }

  charncpy(group,argv[1],GRP_LEN);

  StartUp(group);

  if (!gm_flag){

#ifdef DEBUG
    printf("(Joining Group...)\n");
#endif

    Join_Group();
  }
  else {

#ifdef DEBUG    
    printf("(Initializing Key Tree...)\n");
#endif

    GM_Initialize_Key_Tree();
  }

#ifdef DEBUG    
  printf("(Ready to Chat in Group %c%c%c)\n",group[0],group[1],group[2]);
#endif

  Chat(group);

  return 0;

}
/*======================================*/
void StartUp(unsigned char* grp) {

  struct hostent* hostentity;
  struct timeval tv;  
  unsigned char hostname[256];
  unsigned char tmp[4];
  int csock,i;

  memset(gm,'\0',16);
  memset(hostname,'\0',256);
  memset(my_ip,'\0',16);

  /* bootstrap from Admin */
  Set_Alarm_Action();
  Who_Is_GM(&csock,grp);

  if (*gm == '\0') {
    printf("(no GM returned\ngiving up...)\n");
    exit(1);
  }
  else
#ifdef DEBUG    
    printf("(GM IP:%s)\n",gm);
#endif

  /* find local IP address */
  gethostname(hostname,255);
  if ((hostentity=gethostbyname(hostname)) == NULL)
    Err_Exit("gethostbyname");
  for(i=0;i<hostentity->h_length;i++){
    if (i) strcat(my_ip,".");
    sprintf(tmp,"%d",(unsigned char)(hostentity->h_addr_list[0][i]));
    strcat(my_ip,tmp);
  }
#ifdef DEBUG
  printf("(My IP=%s)\n",my_ip);
#endif

  /* test if local node is GM */
  if ((!strncmp(gm,my_ip,ADDR_LEN))||
      (!strncmp(gm,"127.0.0.1",ADDR_LEN)))
    gm_flag=1;

  /* initialize randomness */
  if (gettimeofday(&tv,NULL) == -1) 
    Err_Exit("gettimeofday");
  srand((unsigned int)(0xffffffff & tv.tv_usec));
  if (gettimeofday(&tv,NULL) == -1) 
    Err_Exit("gettimeofday");
  for (i=0;i<((0xff00 & tv.tv_usec)>>8);i++)
    rand();

  if (gm_flag) Add_Active_Member(gm,0); 
  
}
/*--------------------------------------*/
/* note: in a thorough implementation
   I would use RSA to establish a 
   session key, assuming a PKI, authenticating
   the Joiner along the way.  Techniques for
   doing this are well known, and as it is
   not the focus of this project, I will
   defer that part until the next version.  
   Instead, the Joiner will just unicast
   his secret session key to the GM for
   storage.  Of course this is not secure.
   
   Future communication between the client
   and the GM WILL be encrypted with this key.
*/
void Join_Group() {
  
  unsigned char *buf;
  mem_list_msg* m;
  key_list_msg* k;
  int skt,num_msgs=0,i;

  if ((buf=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  m = (mem_list_msg*)buf;
  k = (key_list_msg*)buf;

  memset(skey,'\0',KEY_LEN);
  memset(buf,'\0',MSG_LEN);

  if ((keys=(node*)calloc(DEPTH+1,sizeof(node)))==NULL)
      Err_Exit("calloc keys array");

  /* create Join_Req msg */
  buf[0] = 0x10;//Join_Req msg type
  Generate_Key(skey);
  charncpy(buf+1,skey,KEY_LEN);

#ifdef VEROBSE
  printf("Local user key: "); Debug_Msg(skey,8);
#endif

  /* send Join_Request to GM */
#ifdef DEBUG
  printf("(Sending Join Request to %s)\n",gm);
#endif
  Open_Socket_As_Client(&skt,gm,CHAT_PORT);
  if (send(skt,buf,MSG_LEN,0)==-1)
    Err_Exit("send Join");

  /* get keys from GM and add to list.
     GM should send all but the local key */
  memset(buf,'\0',MSG_LEN);
  if (recv(skt,buf,MSG_LEN,0)==-1){
    close(skt);
    Err_Exit("recv key list");
  }

#ifdef VERBOSE
  printf("Key msg from GM: "); Debug_Msg(buf,100);
#endif

  if (k->type!=JOIN_KEYS)
    Err_Exit("keys not recvd from GM");

  /* load local key into key array */
  keys[DEPTH].desc.kid=k->kid;
  keys[DEPTH].desc.ver=0x01;
  keys[DEPTH].desc.rev=0x01;
  charncpy(keys[DEPTH].key,skey,KEY_LEN);

  /* get rest of keys from message */
  Decrypt(keys[DEPTH].desc.kid,&k->num_keys,DEPTH * sizeof(node)+1);
  Update_Keys(buf);

  /* get active participants from GM */
  do {
    memset(buf,'\0',MSG_LEN);
    if (recv(skt,buf,MSG_LEN,0)==-1){
      close(skt);
      Err_Exit("recv participant list");
    }

    if (m->type!=JOIN_MEMS) 
      Err_Exit("mems not recvd from GM");

    Decrypt(keys[DEPTH].desc.kid,&m->num_users,1+(int)m->entries * ADDR_LEN);

    for (i=0;i<m->entries;i++)
      Add_Active_Member(&m->num_msgs+1+(i*ADDR_LEN),0);

    num_msgs++;
  } while (num_msgs < m->num_msgs);

  free(buf);
  
}  
/*--------------------------------------*/
void Chat(unsigned char* grp) {

  fd_set rdfds,wrtfds,tmprd,tmpwrt;
  struct sockaddr_in requestor_addr;
  static int s_grp,s_ipc,n_s_grp,c_ipc;
  int bytes_read,i,j,event=0,sin_size;
  unsigned char sender[ADDR_LEN],*buf;
  data_msg *d;
  members *tmp;
  int shmid;
  key_t key=777;
  char *shm;

  if ((buf=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  d = (data_msg*)buf;

  if ((pid=fork())==-1)
    Err_Exit("fork");
  if (pid) {//parent - serve incoming msgs
    Reaper();

    /* set up socket for IPC... */
    Open_Socket_As_Server(&s_ipc,IPC_PORT);
    Wait_For_Connect_Request(s_ipc);
    if ((n_s_ipc=accept(s_ipc,(struct sockaddr*)&requestor_addr,&sin_size))<0)
      Err_Exit("accept:ipc");
    /* ...and also a little piece of shared memory */
    if ((shmid = shmget(key, ADDR_LEN, IPC_CREAT | 0666)) < 0)
        Err_Exit("shmget");
    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1)
        Err_Exit("shmat");

    /* this the actual chat port we are serving */
    Open_Socket_As_Server(&s_grp,CHAT_PORT);
    while (1) {
      Wait_For_Connect_Request(s_grp);
      sin_size=sizeof(struct sockaddr_in);
      if ((n_s_grp=accept(s_grp,(struct sockaddr*)\
			  &requestor_addr,&sin_size))==-1){
	perror("accept");
	continue;
      }
      memset(buf,'\0',MSG_LEN);
      bytes_read=recv(n_s_grp,buf,MSG_LEN,MSG_NOSIGNAL);

      /* write sender to shmem... */
      charncpy(sender,(unsigned char*)inet_ntoa(requestor_addr.sin_addr),ADDR_LEN);
      charncpy(shm,sender,ADDR_LEN);
      /* ...and also forward the info to the local client side */
      if (send(n_s_ipc,buf,MSG_LEN,0)==-1)
	Err_Exit("send:forward_to_client");

      /* also handle the message right here */
      Handle_Msg(buf,(unsigned char*)inet_ntoa(requestor_addr.sin_addr),n_s_grp,grp);
      close(n_s_grp);
    }
  } else {//child - handle outgoing msgs

    /* establish IPC socket connection... */
    Open_Socket_As_Client(&c_ipc,"localhost",IPC_PORT);
    /* ...and the shared memory */
    if ((shmid = shmget(key, ADDR_LEN, IPC_CREAT | 0666)) < 0)
        Err_Exit("shmget");
    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1)
        Err_Exit("shmat");

    /* define file descriptors to monitor */
    FD_ZERO(&rdfds);
    FD_ZERO(&wrtfds);
    FD_SET(0,&rdfds);
    FD_SET(c_ipc,&rdfds);

    while (1) {
      /* display prompt */
      printf(">: ");
      fflush(stdout);

      /* wait for something to happen: either a 
	 message from the server side of the 
	 application or an input from the keyboard */
      tmprd=rdfds;
      tmpwrt=wrtfds;
      event=select(c_ipc+1,&tmprd,&tmpwrt,NULL,NULL);

      switch (event) {
      case -1:
	free(buf);
	Err_Exit("select");
	break;
      case 0:
	//printf("<timeout>\n");
	break;
      default:
	/* a message from the server side has arrived */
	if (FD_ISSET(c_ipc,&tmprd)){

	  /* read the msg from the socket...*/
	  memset(buf,'\0',MSG_LEN);
	  bytes_read=recv(c_ipc,buf,MSG_LEN,MSG_NOSIGNAL);
	  if (!bytes_read) {
	    printf("fatal message received!");
	    //exit(99);
	  }

	  /* and get the original sender from
	     the shared memory location */
	  charncpy(sender,shm,ADDR_LEN);

#ifdef VERBOSE
	  printf("msg received from server side\n");
	  printf("sender: ");Debug_Msg(sender,15);
	  printf("mesg: "); Debug_Msg(buf, 280);
#endif

	  /* now handle this new message */
	  Handle_Msg(buf,sender,c_ipc,grp);
	  printf("\n");
	}
	if (FD_ISSET(0,&tmprd)){
	  /* action from the keyboard detected */
	  memset(buf,'\0',MSG_LEN);
	  if (*fgets(d->data,981,stdin)=='\n') continue;

	  if ((!strncmp(d->data,"EVICT",5))&&(!gm_flag))
	    Send_Complaint(d->data);
	  else if ((!strncmp(d->data,"LEAVE",5))&&(!gm_flag))
	    Send_Leave(buf);
	  else if ((!strncmp(d->data,"DESTROY",7))&&(gm_flag))
	    Destroy_Group(grp);
	  else if (!strncmp(d->data,"MEMBERS",7)) {
	    tmp = active;
	    if (active!=NULL){
	      printf("(Active Members are:)\n");
	      while (active != NULL) {
		printf("(");
		for (i=0;i<ADDR_LEN;i++)
		  printf("%c",active->addr[i]);
		printf(")\n");
		active=active->lnk;
	      }
	      active=tmp;
	    } else {
	      printf("<empty list>\n");
	    }
	  }
	  else if (!strncmp(d->data,"KEYS",4)) {
	    if (gm_flag){
	      /* dump 'tree' */
	      for (i=0;i<=DEPTH;i++){
		for (j=0;j<(int)pow(2,i);j++)
		  Debug_Msg((unsigned char*)tree[i] + j*sizeof(node),sizeof(node));
	      }
	    } else {
	      /* dump 'keys' */
	      for (i=0;i<=DEPTH;i++)
		Debug_Msg((unsigned char*)&keys[i],sizeof(node));
	    }
	  }
	  else {//just normal data
	    d->type=CHAT_DATA;
	    if (gm_flag){
	      d->desc.kid=tree[0]->desc.kid;
	      d->desc.ver=tree[0]->desc.ver;
	      d->desc.rev=tree[0]->desc.rev;
	      //Encrypt(tree[0]->desc.kid,d->data,strlen(d->data)-1);
	      Encrypt(tree[0]->desc.kid,d->data,980);
	    } else {
	      d->desc.kid=keys[0].desc.kid;
	      d->desc.ver=keys[0].desc.ver;
	      d->desc.rev=keys[0].desc.rev;
	      Encrypt(keys[0].desc.kid,d->data,980);
	    }

#ifdef VERBOSE
	    printf("chat sending: "); Debug_Msg(buf,20);
#endif

	    Send_To_Group(buf,MSG_LEN,0);

	  }
	}
      }
    }
  }
}
/*--------------------------------------*/
void Who_Is_GM(int* fd,unsigned char* grp) {

  unsigned char *buf;
  grp_query_msg *gq;
  grp_reply_msg *gr;

  if ((buf=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  gq = (grp_query_msg*)buf;
  gr = (grp_reply_msg*)buf;

  gq->type = GRP_QUERY;
  charncpy(gq->grp,grp,GRP_LEN);

  Open_Socket_As_Client(fd,ADMIN_IP,ADMIN_PORT);
  if (send(*fd,buf,MSG_LEN,0)==-1){
    close(*fd);
    Err_Exit("send");
  }
  alarm(5);

  memset(buf,'\0',MSG_LEN);

  if (recv(*fd,buf,MSG_LEN,0)==-1){
    close(*fd);
    Err_Exit("error reading GM");
  }
  alarm(0);

  /* see if this message is concerning
     the query I just just sent out, and 
     if so read the GM address from msg. */
  if (!strncmp(gr->grp,grp,GRP_LEN))
    charncpy(gm,gr->addr,ADDR_LEN);

  close(*fd);
  free(buf);

}
/*--------------------------------------*/
/* This active_member list is necessary because
   multicast is not widely supported.  Therefore
   each client needs to know to which members it
   must unicast each message.  Obviously, this
   implementation scales poorly, but it can be
   replaced by merely sending to a multicast addr */
void Add_Active_Member(unsigned char *new_mem,unsigned char kid) {

  members *tmp = active;
  int i;i=0;

  if (active != NULL) {//add new_mem to end of list
    while (active->lnk!=NULL)
      active = active->lnk;
    if ((active->lnk = (struct list*)malloc(sizeof(members)))==NULL){
      perror("malloc");
      add_fail_flag=1;
      active=tmp;
    }

    active = active->lnk;
    active->lnk = NULL; //terminate list with NULL ptr.
    memset(active->addr,'\0',ADDR_LEN);
    charncpy(active->addr,new_mem,strlen(new_mem));

    if (gm_flag)
      active->kid=kid;
    mem_ctr++;
    active=tmp;  //always points to the first item in the list
  }

  else {// create list
    if ((active=(struct list*)malloc(sizeof(members)))==NULL){
      perror("malloc");
      add_fail_flag=1;
      active=NULL;
    }
    active->lnk = NULL; //terminate list with NULL ptr.
    memset(active->addr,'\0',ADDR_LEN);
    charncpy(active->addr,new_mem,strlen(new_mem));

    if (gm_flag)
      active->kid=kid;
    mem_ctr++;
  }

#ifdef DEBUG
    if (!pid){
      printf("(Adding New Member: ");
      for (i=0;i<ADDR_LEN;i++)
	printf("%c",new_mem[i]);
      printf(")\n");
    }
#endif

}
/*--------------------------------------*/
/* I considered implementing a dynamically
   allocated key tree structure for the GM
   but half way through implementing it, I 
   abandoned the scheme, for the following
   reasons: 1) the framework paper implies
   a statically allocated structure 2) al-
   though dynamic allocation saves memory
   considerably in sparsely populated tree,
   employing this method increased number
   of messages required per revision update
   considerably and also concentrated more
   of the necessary computation at the GM.

   I selected a tree size that I feel will
   adequately serve most group chat sizes
   without being so huge as to waste an 
   inordinate amount of memory - 128 users.
   If more users want to be in the same
   group, two groups can merge, though this
   capability is not yet supported here.
 */
void GM_Initialize_Key_Tree() {

  int i,j;
  node* cur;

#if 0
  if ((tree=(node**)calloc(DEPTH+1,sizeof(node*)))==NULL)
    Err_Exit("calloc key tree"); 
#endif

#ifdef VERBOSE
  printf("(Generating initial keys for tree of depth %d)\n",DEPTH);
#endif

  for (i=0;i<=DEPTH;i++){
    if ((tree[i]=(node*)calloc((int)pow(2,i),sizeof(node)))==NULL)
      Err_Exit("calloc key tree");
    if (i<DEPTH){
      for (j=0;j<(int)pow(2,i);j++){
	cur = (node*)((char*)tree[i] + j*sizeof(node));
	cur->desc.kid=(unsigned char)((pow(2,i)-1)+j);
	cur->desc.ver=0x01;
	cur->desc.rev=0x01;
	Generate_Key(cur->key);

#ifdef DEBUG
	printf(".");
	fflush(stdout);
#endif
      }
    }
#ifdef DEBUG
    printf("\n");
#endif
  }

}
/*--------------------------------------*/
void Generate_Key(unsigned char *skey) {

  int i,secure_key=0;
  des_key_schedule dummy;

  while (!secure_key){
    des_random_key((des_cblock*)skey);
    /* check if secure */
    for(i=0;i<KEY_LEN;i++){
      if(skey[i]){
	secure_key=1;
	break;
      }
    }
    /* check for parity/weakness */
    if (!des_set_key_checked((des_cblock*)skey,dummy))
      break;
  }

}
/*--------------------------------------*/
void Handle_Msg(unsigned char* buf,unsigned char* sender,int skt,unsigned char* grp) {

  key_ver_msg *v = (key_ver_msg*)buf;
  key_rev_msg *r = (key_rev_msg*)buf;
  data_msg *c = (data_msg*)buf;
  unsigned char new_kid;
  chat_msg type=buf[0];
  members *tmp;

  switch (type) {
  case JOIN_REQ:

#ifdef DEBUG    
    if (!pid) printf("(Rcvd JOIN_REQ from %s)\n",sender);
#endif

    if ((gm_flag)&&(Is_Valid_Member(sender,grp))){
      if (pid) {
	Send_Key_Rev_Update(sender);
      }
      /* do key revs for the GM */
      Hash_Keys();
      new_kid=GM_Add_Key(buf+1);//buf+1=start of key
      Add_Active_Member(sender,new_kid);
      if (pid) Send_Join_Keys(sender,new_kid,skt);
      if (pid) Send_Join_Mems(sender,new_kid,skt);
    }
    break;
  case VER_UPDATE:
    /* since there are multiple
       decryption keys for this
       message, Decrypt() is called
       from within Update_Keys()
       for this case. */
    if (gm_flag && pid) break;
    if (!gm_flag) Remove_Member(v->lmem);
    Update_Keys(buf);
    break;
  case REV_UPDATE:
    if (!gm_flag){
      Decrypt(r->desc.kid,r->jmem,ADDR_LEN);
      Update_Keys(buf);
      Add_Active_Member(r->jmem,0);
    }
    break;
  case CHAT_DATA:
    if (pid) {
      if (gm_flag){
	if (c->desc.ver == tree[0]->desc.ver){
	  Decrypt(c->desc.kid,c->data,980);
	  Display(buf,sender);
	}
      } else {
	if (c->desc.ver == keys[0].desc.ver){
	  Decrypt(c->desc.kid,c->data,980);
	  Display(buf,sender);
	}
      }
    }
    break;
  case JOIN_KEYS:
    /* Join()-phase key msg - ignore */
    break;
  case JOIN_MEMS:
    /* Join()-phase member msg - ignore */
   break;
  case COMPLAINT:
    if (gm_flag)
      Handle_Complaint(buf,sender,skt);
    break;
  case LEAVE:
    if (gm_flag)
      GM_Remove_Key(Remove_Member(sender));
    else
      Remove_Member(sender);
    break;
  case DESTROY:
   /* wipe key list */
    memset(keys,'\0',(DEPTH+1)*sizeof(node));
    
    /* free alloc'd memory - member list */
    while(active->lnk!=NULL){
      tmp=active;
      active=active->lnk;
      free(tmp);
    }
    exit(0);
    break;
  default:
    printf("(non-supported message received!) \n");
    //exit(5);
  }

}
/*--------------------------------------*/
void Send_Join_Keys(unsigned char *user,unsigned char kid,int fd) {

  unsigned char *buf;
  key_list_msg* k;
  node *cur;
  int i, ni;

  if ((buf=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  k = (key_list_msg*)buf;

  memset(buf,'\0',MSG_LEN);

  k->type = JOIN_KEYS;
  k->kid = kid;
  k->num_keys = DEPTH;
  
  ni=kid-(MAX_USERS-1);//user node index

  /* add copy necessary keys to the msg buffer.
     The 'lowest' KEK is loaded first, but into
     the last position, while the TEK is loaded
     last, but into the first position. */
  for (i=DEPTH-1;i>=0;i--){
    cur = (node*)(tree[i]+(int)floor((float)ni/(float)2.0));
    charncpy(&k->num_keys+1+i*sizeof(node),(unsigned char*)cur,sizeof(node));
    ni=(int)floor((float)ni/(float)2.0);
  }

  Encrypt(kid,&k->num_keys,DEPTH*sizeof(node)+1);

#ifdef DEBUG
  if (pid) printf("(SENDING JOIN_KEYS)\n");
#endif

  if (send(fd,buf,MSG_LEN,0)==-1)
    Err_Exit("send Join_Keys");

  free(buf);
}
/*--------------------------------------*/
void Send_Join_Mems(unsigned char *sender,unsigned char kid,int fd) {

  unsigned char *buf,entries,mems,msgs,i,j;
  mem_list_msg* m;
  members *tmp = active;

  if ((buf=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  m = (mem_list_msg*)buf;

  memset(buf,'\0',MSG_LEN);
  mems=(char)mem_ctr;

  /* 67 = # of 15-byte addresses that fit in a 
     mem_list_msg after possible block-cipher expansion. */
  msgs=(unsigned char)ceil((float)mem_ctr/(float)67.0);

  for (i=0;i<msgs;i++) {
    m->type = JOIN_MEMS;
    m->num_users = (unsigned char)mem_ctr;
    m->num_msgs = msgs;

    if (mems>=67) entries=67;
    else entries=mems;
    m->entries = entries;
    mems-=entries; //decrement members left to send

    for (j=0;j<entries;j++){
      charncpy(&m->num_msgs+1+j*ADDR_LEN,active->addr,ADDR_LEN);
      active=active->lnk;
    }
    active=tmp;

    Encrypt(kid,&m->num_users,1+entries*ADDR_LEN);

#ifdef DEBUG
    if (pid) printf("(SENDING JOIN_MEMS)\n");
#endif

    if (send(fd,buf,MSG_LEN,0)==-1)
      Err_Exit("send Join_Mems");
  }

  free(buf);

}
/*--------------------------------------*/
void Send_Key_Rev_Update(unsigned char* new) {

  unsigned char *buf;
  key_rev_msg* r;

  if ((buf=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  r = (key_rev_msg*)buf;

  r->type=REV_UPDATE;
  r->desc.kid=tree[0]->desc.kid;
  r->desc.ver=tree[0]->desc.ver;
  r->desc.rev=tree[0]->desc.rev;
  charncpy(r->jmem,new,ADDR_LEN);

  Encrypt(tree[0]->desc.kid,r->jmem,ADDR_LEN);

  /* send to all members in the member list */
  Send_To_Group(buf,MSG_LEN,0);

  free(buf);

}
/*--------------------------------------*/
/* how to handle a complaint is a policy
   decision not tied to the group key
   mgmt. framework. Here we evict the 
   member if anyone complains about him */
void Handle_Complaint(unsigned char* msg,unsigned char* plaintif,int fd) {

  complaint_msg* c = (complaint_msg*)msg;
  int un;//usr leaf num

  Decrypt(c->desc.kid,c->mem,ADDR_LEN);

  /* if complaint is against non-GM then
     evict him */
  if (strncmp(c->mem,gm,ADDR_LEN)){
    un=Remove_Member(c->mem);
    GM_Remove_Key(un);
    if (pid)
      Send_Key_Ver_Update(un,c->mem);
  }
  /* otherwise ignore the complaint. */
}
/*--------------------------------------*/
/* Determine which keys need to change
   based on 'n', generate new keys, and
   send them out. */
void Send_Key_Ver_Update(int n,unsigned char* lmem) {

  unsigned char *buf;
  key_ver_msg *v;
  node *replace,*neighbor,*thisnode;
  int ni,index,i,j;

  /* VER_UPDATE struct: 'desc' is the descriptor 
     of the key that is encrypting <kdesc,key>.
     because of DES expansion I had to make room
     in the message for 16 bytes per new key pair,
     even though a <desc,key> pair only uses 11. */

  typedef struct {
    struct keydesc desc;
    struct keydesc kdesc;
    unsigned char key[KEY_LEN+5];
  } vnode;
  vnode *cur,*next,*startofkeys;

  if ((buf=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  v = (key_ver_msg*)buf;

  v->type = VER_UPDATE;
  charncpy(v->lmem,lmem,ADDR_LEN);
  v->num_keys=(char)2*DEPTH-1;

#ifdef VERBOSE
  printf("Upping version of keys known by:%d: ",n);
  Debug_Msg(lmem,ADDR_LEN);
#endif

  /* generate new keys in the affected 
     positions of the GM key tree.*/
  ni=n-(MAX_USERS-1);
  j=DEPTH-1;

  for (i=DEPTH-1;i>=0;i--){
    index=(int)floor((float)ni/(float)2.0);
    replace=(node*)(tree[i]+index);
    Generate_Key(replace->key);
    replace->desc.ver++;
    replace->desc.rev=1;

    /* copy new keys to message. 2*DEPTH-1 keys
       are sent in the message.  See the program
       documentation for a thorough explanation */
    startofkeys=(vnode*)((unsigned char*)&v->num_keys + 1);
    cur = (vnode*)(startofkeys+2*i);
    next = (vnode*)(cur+1);
    cur->kdesc.kid = replace->desc.kid;
    cur->kdesc.ver = replace->desc.ver;
    cur->kdesc.rev = replace->desc.rev;
    charncpy(cur->key,replace->key,KEY_LEN);

    if (i<DEPTH-1){
      charncpy(next->key,replace->key,KEY_LEN);
      next->kdesc.kid = replace->desc.kid;
      next->kdesc.ver = replace->desc.ver;
      next->kdesc.rev = replace->desc.rev;
    }

    /* copy key descriptors to message. New keys
       need to be encrypted with the keys of their
       node's two children. As we go up the tree 
       fromleaf to root we must encrypt each key we 
       replacetwice since it is a binary tree. The 
       exception is the first iteration (the leaf) 
       where we only encrypt the new key once since 
       one of the node's two children is the one 
       leaving and causing this whole mess. :) */

    if (ni % 2)//odd index
      neighbor=(node*)((unsigned char*)tree[i+1]+(ni-1)*sizeof(node));
    else //even index
      neighbor=(node*)((unsigned char*)tree[i+1]+(ni+1)*sizeof(node));

    cur->desc.kid=neighbor->desc.kid;
    cur->desc.ver=neighbor->desc.ver;
    cur->desc.rev=neighbor->desc.rev;

    /* encrypt each new key with the necessary children's key(s) */
    Encrypt(cur->desc.kid,(unsigned char*)&cur->kdesc,sizeof(node));

    if (i<DEPTH-1){
      thisnode=(node*)((unsigned char*)tree[i+1]+ni*sizeof(node));
      next->desc.kid=thisnode->desc.kid;
      next->desc.ver=thisnode->desc.ver;
      next->desc.rev=thisnode->desc.rev;

      /* encrypt each new key with the necessary children's key(s) */
      Encrypt(next->desc.kid,(unsigned char*)&next->kdesc,sizeof(node));
    }
    
    ni=index;
  }

#ifdef VERBOSE
  printf("key ver update: "); Debug_Msg(buf,280);
#endif

  /* actually send the message */
  Send_To_Group(buf,MSG_LEN,0);

  /* send the info to client side */
  if (send(n_s_ipc,buf,MSG_LEN,0)==-1)
    perror("send to client side");


  free(buf);
}
/*--------------------------------------*/
unsigned char GM_Add_Key(unsigned char* new_key) {

  int i;
  node *cur=tree[DEPTH];

  /* find first empty slot */
  for (i=0;(cur->desc.kid >= 1);i++)
    cur+=1;//sizeof(node);

  /* put new key there */
  if (i<MAX_USERS){
    cur->desc.kid=(unsigned char)((MAX_USERS-1)+i);
    cur->desc.ver=0x01;
    cur->desc.rev=0x01;
    charncpy(cur->key,new_key,KEY_LEN);
    return (unsigned char)((MAX_USERS-1)+i);//kid
  }
  
  return 0; //no room left in group
   
}
/*--------------------------------------*/
void GM_Remove_Key(int unode) {

  node *remove;

  /* reset leaf occupied by user to NULL */
  if ((unode < ((int)pow(2,DEPTH+1)-2)) && (unode >= (MAX_USERS-1))) {
    remove=(node*)(tree[DEPTH]+(unode-(MAX_USERS-1)));
    memset(remove,'\0',sizeof(node));
  }

}
/*--------------------------------------*/
int Is_Valid_Member(unsigned char* addr,unsigned char* grp) {

  unsigned char *buf;
  reg_query_msg* rq; 
  reg_reply_msg* rr;
  int sock;

  if ((buf=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  rq = (reg_query_msg*)buf;
  rr = (reg_reply_msg*)buf;

  rq->type = REG_QUERY;
  charncpy(rq->grp,grp,GRP_LEN);
  charncpy(rq->addr,addr,ADDR_LEN);

  Open_Socket_As_Client(&sock,ADMIN_IP,ADMIN_PORT);
  if (send(sock,buf,MSG_LEN,0)==-1){
    close(sock);
    Err_Exit("send REG_QUERY");
  }

  memset(buf,'\0',MSG_LEN);

  if (recv(sock,buf,MSG_LEN,0)==-1){
    close(sock);
    Err_Exit("recv REG_REPLY");
  }
  close(sock);

  if ((!strncmp(addr,rr->addr,ADDR_LEN))&&(!strncmp(grp,rr->grp,GRP_LEN)))
    return (rr->flag);//'0' if NO, '1' if YES
 
  free(buf);
  return 0;//msg wasn't for us...fail.
}
/*--------------------------------------*/
void Update_Keys(unsigned char* buf) {

  key_list_msg *l = (key_list_msg*)buf;
  key_ver_msg *v = (key_ver_msg*)buf;
  chat_msg type=buf[0];
  node *new_key,*keynode;
  int i,k,m,n,flag;
  unsigned char j;

  /* VER_UPDATE struct: 'desc' is the descriptor 
     of the key that is encrypting <kdesc,key>.
     because of DES expansion I had to make room
     in the message for 16 bytes per new key pair,
     even though a <desc,key> pair only uses 11. */
  typedef struct {
    struct keydesc desc;
    struct keydesc kdesc;
    unsigned char key[KEY_LEN+5];
  } vnode;
  vnode *new_vkey;

  switch (type) {
  case JOIN_KEYS:
    /* add keys to local list */
    new_key = (node*)(&(l->num_keys)+1);
    for (i=0;i<DEPTH;i++){
      keys[i].desc.kid=new_key->desc.kid;
      keys[i].desc.ver=new_key->desc.ver;
      keys[i].desc.rev=new_key->desc.rev;
      charncpy(keys[i].key,new_key->key,KEY_LEN);
      new_key+=1;//sizeof(node);
    }
    break;
    
  case VER_UPDATE:
    /* start at back of message */
    new_vkey = (vnode*)((vnode*)(&(v->num_keys)+1)+2*(DEPTH-1));

    if (gm_flag) {
      
      for (j=0;((j<v->num_keys)&&(j<(2*DEPTH-1)));j++){
      /* look for kid in key tree.  if we find it
	 then decrypt the new key, search for the
	 key it is replacing, and store it there, 
	 replacing the old */

	/* search for the key to unlock the new key */
	flag=0;
	for (i=0;i<=DEPTH;i++){// each level
	  for (k=0;k<(int)pow(2,i);k++){// each node on this level
	    keynode=(node*)(tree[i]+k);
	    if (keynode->desc.kid==new_vkey->desc.kid){

	      /* decrypt the new <desc,key> pair */
	      Decrypt(new_vkey->desc.kid,(unsigned char*)&new_vkey->kdesc,sizeof(node));

	      /* search for the proper slot to install new key */
	      for (m=0;m<=DEPTH;m++){// each level
		for (n=0;n<(int)pow(2,m);n++){// each node on this level
		  keynode=(node*)(tree[m]+n);
		  if (keynode->desc.kid==new_vkey->kdesc.kid){

		    /* install new key */
		    keynode->desc.ver=new_vkey->kdesc.ver;
		    keynode->desc.rev=new_vkey->kdesc.rev;
		    charncpy(keynode->key,new_vkey->key,KEY_LEN);
		    flag=1;
		    break;
		  }
		}
		if (flag) break;
	      }
	      break;
	    }
	  }
	  if (flag) break;
	}
	new_vkey-=1;//sizeof(vnode);
      }
    }

    else {//(!GM)
      for (j=0;((j<v->num_keys)&&(j<(2*DEPTH-1)));j++){
	/* look for kid in key array.  if we find it
	   then decrypt the new key, search for the
	   key it is replacing, and store it there, 
	   replacing the old */

	for (i=0;i<=DEPTH;i++){// find the key to unlock the new key
	  if (keys[i].desc.kid == new_vkey->desc.kid){

	    /* decrypt the new <desc,key> pair */
	    Decrypt(new_vkey->desc.kid,(unsigned char*)&new_vkey->kdesc,sizeof(node));

	    for (k=0;k<=DEPTH;k++){// find the slot to write the new key
	      if (keys[k].desc.kid==new_vkey->kdesc.kid){
		keys[k].desc.ver = new_vkey->kdesc.ver;
		keys[k].desc.rev = new_vkey->kdesc.rev;
		charncpy(keys[k].key,new_vkey->key,KEY_LEN);
		break;
	      }
	    }
	    break;
	  }
	}
	new_vkey-=1;//sizeof(vnode);
      }
    }

    break;
  case REV_UPDATE:
    Hash_Keys();
    break;
  case JOIN_REQ: /* not possible */
  case CHAT_DATA: /* not possible */
  case JOIN_MEMS: /* not possible */
  case COMPLAINT: /* not possible */
  case LEAVE: /* not possible */
  case DESTROY: /* not possible */
  }

}
/*--------------------------------------*/
void Encrypt(unsigned char kid,unsigned char* payload,int length) {

  /* DES encrypt the payload "in place" using
     the key indentified by kid. Selection of 
     the encryption standard used is not at all 
     important to the key management scheme. 
     DES was used because DES libraries are 
     readily available. 

     Important note: the calling function must 
     make sure it has enough room in the buffer 
     it passes to accomodate up to BLK_LEN-1 
     bytes past what it asked to haveencrypted.  
     This is due to possible block-cipher expansion 
     occuring in the case (length % BLK_LEN). */

  des_key_schedule sched;
  unsigned char ivec[8],*tmp,key[8];
  int i,j,tmp_len,flag=0;
  node *keynode;

  if (gm_flag) {
    /* look for kid in key tree */
    for (i=0;i<=DEPTH;i++){
      for (j=0;j<(int)pow(2,i);j++){
	keynode=(node*)((unsigned char*)tree[i]+j*sizeof(node));
	if (keynode->desc.kid==kid){
	  charncpy(key,keynode->key,KEY_LEN);
	  flag=1;
	  break;
	}
      }
      if (flag) break;
    }

#ifdef VERBOSE
    if (flag) {
      printf("key descriptor: ");
      for (i=0;i<sizeof(kd);i++)
	printf("%.2X ",((unsigned char*)keynode)[i]);
    }
#endif

  } else {
    /* look for kid in key array */
    for (i=0;i<=DEPTH;i++){
      if (keys[i].desc.kid==kid){
	charncpy(key,keys[i].key,KEY_LEN);
	break;
      }
    }

#ifdef VERBOSE
    if (flag) {
      printf("key descriptor: ");
      for (j=0;j<sizeof(kd);j++)
	printf("%.2X ",((unsigned char*)&keys[i])[j]);
    }
#endif

  }

  /* create key schedule */
  if (des_set_key_checked((des_cblock*)key,sched)==-1){
    des_set_odd_parity((des_cblock*)key);
    des_set_key_checked((des_cblock*)key,sched);
  }

#ifdef VERBOSE
  printf("(Encrypt key used: ");
  for (i=0;i<KEY_LEN;i++)
    printf("%.2X ",key[i]);
  printf(")\n");
#endif

  /* create initialization vector */
  for (i=0;i<KEY_LEN;i++)
    ivec[i] = ~key[i];
  
  /* force tmp output buf to be integral # of 8 bytes. */
  if (length % BLK_LEN) 
    tmp_len = ((length/BLK_LEN)+1)*BLK_LEN;
  else
    tmp_len = length;
  if ((tmp=calloc(tmp_len,1))==NULL)
    Err_Exit("calloc tmp");

  des_ncbc_encrypt(payload,tmp,length,sched,(des_cblock*)ivec,DES_ENCRYPT);
 
  memset(payload,'\0',tmp_len);
  charncpy(payload,tmp,tmp_len);
  free(tmp);

}
/*--------------------------------------*/
void Decrypt(unsigned char kid,unsigned char* payload,int length) {
  /* DES decrypt the payload in place using
     the 'key' read from the incoming 'msg'.
     Selection of the encryption standard 
     used is not at all important to the key 
     management scheme. DES was used because 
     DES libraries are readily available. */

  des_key_schedule sched;
  unsigned char ivec[8],*tmp,key[8];
  int i,j,tmp_len,flag=0;
  node *keynode;

  if (gm_flag) {
    /* look for kid in key tree */
    for (i=0;i<=DEPTH;i++){
      for (j=0;j<(int)pow(2,i);j++){
	keynode=(node*)((unsigned char*)tree[i]+j*sizeof(node));
	if (keynode->desc.kid==kid){
	  charncpy(key,keynode->key,KEY_LEN);
	  flag=1;
	  break;
	}
      }
      if (flag) break;
    }

#ifdef VERBOSE
    if (flag) {
      printf("key descriptor: ");
      for (i=0;i<sizeof(kd);i++)
	printf("%.2X ",((unsigned char*)keynode)[i]);
    }
#endif

  } else {
    /* look for kid in key array */
    for (i=0;i<=DEPTH;i++){
      if (keys[i].desc.kid==kid){
	charncpy(key,keys[i].key,KEY_LEN);
	flag=1;
	break;
      }
    }

#ifdef VERBOSE
    if (flag){
      printf("key descriptor: ");
      for (j=0;j<sizeof(kd);j++)
	printf("%.2X ",((unsigned char*)&keys[i])[j]);
    }
#endif

  }

  /* don't bother decrypting if the correct key wasn't found */
  if (flag) {

    /* create key schedule */
    if (des_set_key_checked((des_cblock*)key,sched)==-1){
      des_set_odd_parity((des_cblock*)key);
      des_set_key_checked((des_cblock*)key,sched);
    }

#ifdef VERBOSE
    printf("(Decrypt key used: ");
    for (i=0;i<KEY_LEN;i++)
      printf("%.2X ",key[i]);
    printf(")\n");
#endif

    /* create initialization vector */
    for (i=0;i<KEY_LEN;i++)
      ivec[i] = ~key[i];

    /* The length argument passed in the the
       number of bytes the calling function
       expected back in plaintext. However
       because the possibility exists of expansion
       due to the 64 bit block requirement of
       DES, this function knows better :)
       Therefore convert the length arg. passed
       in to force it to be integral # of 8 bytes. */
    if (length % BLK_LEN) 
      tmp_len = ((length/BLK_LEN)+1)*BLK_LEN;
    else
      tmp_len = length;
    
    if ((tmp=calloc(length,1))==NULL)
      Err_Exit("calloc tmp");
    
    des_ncbc_encrypt(payload,tmp,length,sched,(des_cblock*)ivec,DES_DECRYPT);
    
    memset(payload,'\0',tmp_len);
    charncpy(payload,tmp,length);
    free(tmp);

  } else {
#ifdef DEBUG
    printf("could not decrypt this message\n");
#endif
  }

}
/*--------------------------------------*/
int Remove_Member(unsigned char* addr) {

 members *prev = active;
 members *tmp = active;
 int i=0,un;

 while (active!=NULL){

   if (!strncmp(addr,active->addr,ADDR_LEN)){
     if (gm_flag)
       un=(int)active->kid;
     if (!i) {
       active=active->lnk;
       free(tmp);
       mem_ctr--;
       break;
     } else {
       prev->lnk=active->lnk;
       free(active);
       active=tmp;
       mem_ctr--;
       break;
     }
   }
   if (i) prev=prev->lnk;
   active=active->lnk;
   i++;
 }

 if (gm_flag) return (un);
 return 0;

}
/*--------------------------------------*/
void Destroy_Group(unsigned char* grp) {
  /* send a message to all remaining 
     active members.  Then null out the
     key structure, free any alloc'd
     memory, tell the Admin, and then exit */

  unsigned char *buf;
  gm_resign_msg* gr;
  int sock,i;
  members *tmp;

  if ((buf=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  gr = (gm_resign_msg*)buf;

  /* tell the group about resignation */
  buf[0]=DESTROY;
  Send_To_Group(buf,MSG_LEN,0);

  /* wipe key tree, then free memory */
  for (i=0;i<=DEPTH;i++){
    memset((unsigned char*)tree[i],'\0',sizeof(node)*(int)pow(2,i));
    free(tree[i]);
  }

  /* free alloc'd memory - member list */
  if (active!=NULL){
    while(active->lnk!=NULL){
      tmp=active;
      active=active->lnk;
      free(tmp);
    }
  }

  /* assemble and send resignation
     message to the administrator. */
  memset(buf,'\0',MSG_LEN);
  gr->type = GM_RESIGN;
  charncpy(gr->grp,grp,GRP_LEN);

  Open_Socket_As_Client(&sock,ADMIN_IP,ADMIN_PORT);
  if (send(sock,buf,MSG_LEN,0)==-1){
    close(sock);
    Err_Exit("send");
  }
  close(sock);

  printf("exiting...\n");
  free(buf);
  exit(0);
}
/*--------------------------------------*/
void Display(unsigned char* buf,unsigned char* from) {

  data_msg *d = (data_msg*)buf;
  int i,j=0;

  /* just print to the screen - a little formatting */

  printf("\n");
  for (i=0;i<ADDR_LEN;i++)
    printf("%c",from[i]);
  printf("> ");

  i=0;
  while (1){
    if (d->data[i] == '\0') break;
    if (d->data[i] == '\n')
      putchar(0x20);
    else
      putchar(d->data[i]);
    if (j>25){ //wrap after 25
      if(d->data[i]==0x20){
	printf("\n\t\t");
	j=0;
      }
    }
    i++;j++;
  }
  fflush(stdout);
}
/*--------------------------------------*/
void Send_To_Group(unsigned char* buf,int len,int flags) {

  int sock;
  unsigned char host[ADDR_LEN+1];
  members *tmp=active;
  
  while (active!=NULL){
    memset(host,'\0',ADDR_LEN+1);
    charncpy(host,active->addr,ADDR_LEN);

    /* send to all the active members but ourselves */
    if (strncmp(my_ip,active->addr,ADDR_LEN)){

#ifdef VERBOSE
      printf("to host: ");Debug_Msg(host,ADDR_LEN+1);
#endif

      Open_Socket_As_Client(&sock,host,CHAT_PORT);
      if (send(sock,buf,len,flags)==-1)
	perror("send to group");
      close(sock);
    }
    active=active->lnk;
  }
  active=tmp;

}
/*--------------------------------------*/
void Send_Complaint(unsigned char* buf) {

  unsigned char *msg;
  complaint_msg *c;
  int sock,i;

  if ((msg=calloc(MSG_LEN,1))==NULL)
    Err_Exit("calloc buf");

  c = (complaint_msg*)msg;

  memset(msg,'\0',MSG_LEN);

  c->type = COMPLAINT;
  /* specify user secret key for encr. */
  c->desc.kid = keys[DEPTH].desc.kid;
  c->desc.ver = keys[DEPTH].desc.ver;
  c->desc.rev = keys[DEPTH].desc.rev;

  /* complaint message typed in the
     chat window should have the format:
     EVICT=xxx.xxx.xxx.xxx
  */
  charncpy(c->mem,buf+6,ADDR_LEN);
  for (i=0;i<ADDR_LEN;i++){
    if (c->mem[i]=='\n')
      c->mem[i]=0x00;
  }

  Encrypt(c->desc.kid,c->mem,ADDR_LEN);

  /* send complaint to GM */
  Open_Socket_As_Client(&sock,gm,CHAT_PORT);
  if (send(sock,msg,MSG_LEN,0)==-1)
    Err_Exit("send Complaint");

  free(msg);

}
/*--------------------------------------*/
void Hash_Keys() { 

  unsigned char tmp[SHA_DIGEST_LENGTH];
  node *update;
  int i,j;
  
  memset(tmp,'\0',SHA_DIGEST_LENGTH);

  if (gm_flag) {
    /* hash key tree - except leaves */
    for (i=0;i<DEPTH;i++){
      for (j=0;j<(int)pow(2,i);j++){
	update=(node*)((unsigned char*)tree[i]+j*sizeof(node));
	SHA1(update->key,KEY_LEN,tmp);
	charncpy(update->key,tmp,KEY_LEN);//take 1st KEY_LEN bytes
	update->desc.rev++;//incr rev
      }
    }
  } else {
    /* hash key arrary (except local) */
    for (i=0;i<DEPTH;i++){
      SHA1(keys[i].key,(unsigned long)KEY_LEN,tmp);
      charncpy(keys[i].key,tmp,KEY_LEN);//take 1st KEY_LEN bytes
      keys[i].desc.rev++;
    }
  }

}
/*--------------------------------------*/
/* This is a voluntary leave. Since the 
   keys it knows are not considered to be
   compromised, no key changes need to be
   made upon receipt */
void Send_Leave(unsigned char* msg) {

  members *tmp;

  memset(msg,'\0',MSG_LEN);

  msg[0]=0x07;
  Send_To_Group(msg,MSG_LEN,0);

  /* wipe key list */
  memset(keys,'\0',(DEPTH+1)*sizeof(node));

  /* free alloc'd memory - member list */
  while(active->lnk!=NULL){
    tmp=active;
    active=active->lnk;
    free(tmp);
  }
  exit(0);

}
/*--------------------------------------*/
void Debug_Msg(unsigned char* str,int len){

  int i;

  /* for debug purposes */

  for (i=0;i<len;i++)
    printf("%.2X ",str[i]);
  printf("\n");
  fflush(stdout);

}
/*--------------------------------------*/
void charncpy(unsigned char* d,unsigned char* s,int l){

  int i;

  /* created this because strncpy doesn't
     handle the case where the first char
     you want to cpy is 0x00 */

  for (i=0;i<l;i++)
    d[i]=s[i];

}
/*--------------------------------------*/
