#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <time.h>

#include <rand.h>

#include "libpgp5.h"
#include "athens.h"

void newpad(void);
void sendpad(unsigned char *);
void htofn(unsigned char *ap, int len, unsigned char *hnp);
int dohash(unsigned char *, unsigned char *, unsigned int, unsigned char *);
int xorbuf(unsigned char *c, unsigned char *d);

#define SHOWTIMES
/*--------------------------------------------------*/
/* decode message or pad and handle simple things */
static unsigned char nexthost[50];

/*----- Helpers to check if decrypt succeeded -----*/
int getnexthop(unsigned char *bp, unsigned char *scbpt)
{
  unsigned char nexthtmp[2048], *cp;
  int i, len, ll, j;

  i = *bp++;
  ll = 0;
  while (i--) {
    bp += ll;
    ll = *bp++ * 256;
    ll += *bp++;
    if (pkedec5(&bp[3], nexthtmp) ||
        nexthtmp[0] >= 50 || nexthtmp[nexthtmp[0] + 1])
      continue;
    memmove(nexthtmp, &nexthtmp[1], nexthtmp[0] + 3);
    for (len = 0, j = 0; nexthtmp[j]; j++)
      len += nexthtmp[j];       /* compute checksum */
    if (((len >> 8) != nexthtmp[j + 1]) || ((len & 0xff) != nexthtmp[j + 2]))
      continue;

    cp = &nexthtmp[j + 4];
    cfbinit(&cp[1], &cp[17], *cp);
    memset(auxpad, 0, DSIZE);
    docfb(auxpad, DSIZE, 1);
    xorbuf(scbpt, auxpad);

    /* nuke this path in pad (for multiple keys) */
    RAND_bytes(&bp[ll / 2 - 4], 8);
    strcpy(nexthost, nexthtmp);

    return 0;
  }
  return -1;
}

/*-----*/
int checkcfb(unsigned char *bp)
{
  int i, j;

  pkedec5(&bp[3], auxpad);
  if (auxpad[0] != CPKE)
    return -1;
  for (i = 0, j = 0; i < 26; i++)
    j += auxpad[i];
  if (auxpad[26] != (j & 0xff))
    return -1;
  return 0;
}

/*-----*/
void decpad()
{
  FILE *fp;
  unsigned char *bp, *scbpt;
  int len;
  unsigned char hashname[40];
  unsigned char savename[40];

#ifdef SHOWTIMES
  struct stat s;

#endif
  setkeyring5("./secring.skr");
/* check for the simple types - write something out */
  if (msg[0] >= 'a') {
    sprintf(savename, "UNKNOWN.%d", getpid());
    if (msg[0] == RESULT) {
      htofn(&msg[1], msglen - 1, hashname);
#ifdef SHOWTIMES
      if (!stat(hashname, &s))
        fprintf(stderr, "Msg %s lasted %ld sec\n",
                hashname, time(NULL) - s.st_ctime);
#endif
      unlink(hashname);
      sprintf(savename, "RESULT.%d", getpid());
      if (msg[1] == PTYPEPK || msg[1] == MESSAGE || msg[1] == INTERN
          || msg[1] == LOCMSG)
        memmove(msg, &msg[1], --msglen);  /* promote */
    }
    if (msg[0] == PTYPEPK)
      sprintf(savename, "PUBKEY.%d", getpid());
    if (msg[0] == MESSAGE || msg[0] == INTERN || msg[0] == LOCMSG) {
      /* cyphercast if not from result */
      if (msg[0] == LOCMSG)
        sprintf(savename, "LOCAL.");
      else if (msg[0] == MESSAGE)
        sprintf(savename, "INBOX.%d", getpid());
      else
        sprintf(savename, "INTERN.%d", getpid());
      bp = &msg[3];
      len = msg[1] * 256 + msg[2];
      msglen -= 3;
      if (msglen < len)
        exit(-1);
      if (checkcfb(bp))
        exit(0);

      cfbinit(&auxpad[2], &auxpad[18], auxpad[1]);
      msglen -= len;
      bp += len;
      docfb(bp, DSIZE, 0);
      msglen = *bp * 256 + bp[1] + 1;
      if (msg[0] == INTERN) {
        strcpy(hashname, savename);
        strcat(hashname, ".tag");
        fp = fopen(hashname, "wb");
        fwrite(&bp[msglen + 1], 1, 4, fp);
        fclose(fp);
        strcpy(hashname, savename);
        strcat(hashname, ".key");
        fp = fopen(hashname, "wb");
        fprintf(fp, "%02X%02X%02X%02X%02X%02X%02X%02X\n",
                bp[msglen + 5], bp[msglen + 6], bp[msglen + 7], bp[msglen + 8],
           bp[msglen + 9], bp[msglen + 10], bp[msglen + 11], bp[msglen + 12]);
        fclose(fp);
      } else if (msg[0] == LOCMSG) {
        sprintf(&savename[6], "%02X%02X%02X%02X", bp[2], bp[3], bp[4], bp[5]);
        bp += 4;
        msglen -= 4;
      }
      memmove(&msg[1], &bp[2], msglen);
    }
    fp = fopen(savename, "wb");
    fwrite(&msg[1], 1, msglen - 1, fp);
    fclose(fp);
    fprintf(keyf, "%s\n", savename);  /* send to keymgr */
    exit(0);
  }
  if (msg[0] != CPKE)
    exit(1);
  /* pad - returned or to be forwarded */
  bp = &msg[3];
  len = msg[1] * 256 + msg[2];
  msglen -= 3;
  if (msglen < len)
    exit(-1);
  if (checkcfb(bp))
    exit(0);
  cfbinit(&auxpad[2], &auxpad[18], auxpad[1]);
  msglen -= len;
  bp += len;
  docfb(bp, msglen, 0);
  scbpt = bp;
  bp += DSIZE;
  pblen = msglen - DSIZE;
  /* look for next hop - put in nexthost */
  if (getnexthop(bp, scbpt))
    exit(-7);
  for (len = 0; len < 8; len++) /* END will be all hexdigits */
    if (!isxdigit(nexthost[len])) {
      memcpy(scribpad, scbpt, DSIZE);
      memcpy(pathbuf, bp, pblen);
      sendpad(nexthost);
    }
  /* returned pad - new pads are encrypted to someone else */
#ifdef SHOWTIMES
  if (!stat(nexthost, &s))
    fprintf(stderr, "Pad %s lasted %ld sec\n",
            nexthost, time(NULL) - s.st_ctime);
#endif
  if (unlink(nexthost))         /* delete pad marker */
    fprintf(stderr, "Missing Pad returned: %s\n", nexthost);
  len = 256 * *scbpt++;
  len += *scbpt;
  *scbpt++ = RESULT;
  if (len && len < DSIZE - 22 && !dohash(nexthost, scbpt, len, &scbpt[len]))
    /* hash matches */
    if (len + 1 !=
        sendto(sockfd, --scbpt, len + 1, 0, SADDR & remote_addr, cl))
      fprintf(stderr, "result broadcast sendto failed\n");
  newpad();
}
