/* FILE        : mmpc_rec.c
** AUTHOR      : Jim Shapiro
** DATE        : 19 March, 1999
** DISCLAIMER  : No liability is assumed by the author for any use
**               made of this program.
** DISTRIBUTION: Any use may be made of this program, as long as the
**               clear acknowledgment is made to the author in code
**               and runtime executables.  The author retains
**               copyright.
*/

#define THIS_IS_MAIN
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/stat.h>

#include "msg_dgst_rsa.h"
/* must come before package.h for typedef of ARRAYS */
#include "blowfish.h"
#include "package.h"
#include "hash.h"

enum { KEEP_INDICES, CB_HASH_COUNT };

static void help(char *program);

/* (m)ulti-(m)essage (p)ackage (c)haffing - receiver */

/* MD5 (the default) or SHA */
#define SHA

#ifndef SHA
const int Digest_bytes = 16;
const int Block_bytes  = 24;
#else
const int Digest_bytes = 20;
const int Block_bytes  = 28;
#endif

int main(int argc, char **argv)
{
  char buf[11];
  char byte_s[3] = { '\0', '\0', '\0' };
  unsigned char *key_p;
  unsigned char block_p[8];
  unsigned char previous_block_p[8];
  unsigned char build_p[8]; /* to build key via xor's  */
  unsigned char plain_p[8]; /* plaintext  */
  unsigned char act_digest_p[Digest_bytes];
  unsigned char file_digest_p[Digest_bytes];
  int i;
  int j;
  int len;                  /* key length when in hex */
  int key_len;              /* key length when in bytes */
  int rv = EXIT_FAILURE;
  unsigned long ul;
  unsigned long matches;
  unsigned long data_bytes;
  /* initialized to suppress benign warning */
  unsigned long next_to_last_match_index = 0;
  /* initialized to suppress benign warning */
  unsigned long last_match_index         = 0;
  unsigned long total_blocks;
  FILE *fin_p;
  struct stat stat_struct;
  ARRAYS r_array;           /* for the random key */
  
  do {
    if((argc == 2) && (strcmp(argv[1], "-h") == 0)) {
      help(argv[0]);
      break;
    }
    if(argc != 3) {
      (void)printf("Usage: %s <HMAC key> <chaff file>\n", argv[0]);
      break;
    }
#ifndef SHA
    (void)fputs("Expecting hmac_md5 for digest.\n", stderr);
#else
    (void)fputs("Expecting hmac_sha for digest.\n", stderr);
#endif
    len = strlen(argv[1]);
    if((len % 2) != 0) {
      (void)puts("Sorry, you did not enter an even number of "
                 "(hex) characters for the key!...");
      exit(EXIT_FAILURE);
    }
    if((fin_p = fopen(argv[2], "rb")) == NULL) {
      (void)printf("Cannot open chaff file \"%s\"!...\n", argv[2]);
      break;
    }
    if(stat(argv[2], &stat_struct) == -1) {
      (void)printf("Cannot stat \"%s\"!...\n", argv[2]);
      break;
    }
    key_len = len >> 1; /* convert from hex to byte count */
    if((key_p = (unsigned char *)malloc(key_len *
                                   sizeof(unsigned char))) == NULL) {
    (void)fprintf(stderr, "Could not malloc %d bytes for key!...\n",
                  len);
    exit(EXIT_FAILURE);
  }
    for(i = j = 0; i < len; i += 2) {
      bcopy(argv[1] + i, byte_s, 2);
      key_p[j++] = (unsigned char)strtol(byte_s, (char **)NULL, 16);
    }
    Endian = check_byte_order();
    data_bytes = stat_struct.st_size;
    if((data_bytes % Block_bytes) != 0) {
      (void)printf("Chaffed file size is not n * %u !...\n",
                   Block_bytes);
      break;
    }
    total_blocks = data_bytes / Block_bytes;
    initialize_cb_hash_count(CB_HASH_COUNT);
    initialize_cb_hash_size(KEEP_INDICES, Primes[SMALL_HASH]);
    initialize_cb_hashes(CB_HASH_COUNT);
    (void)fprintf(stderr,
                  "There are %lu blocks each containing 8 bytes of "
                  "data and a %d byte HMAC.\n",
                  total_blocks, Digest_bytes);
    /* set (internal) public key arrays */
    package_one_time_set(123450, 678900);
    rev_package_initialize(build_p); /* zero out for xor's */
    for(ul = matches = 0lu; ul < total_blocks; ul++) { /* 1st pass */
      if(fread(block_p, sizeof(unsigned char), 8, fin_p) != 8) {
        (void)printf("Error 0 reading block %lu!...\n", ul);
        break;
      }
#ifndef SHA
      hmac_md5_string(block_p, 8, key_p, key_len, act_digest_p);
#else
      hmac_sha_string(block_p, 8, key_p, key_len, act_digest_p);
#endif
      if(fread(file_digest_p, sizeof(unsigned char), Digest_bytes,
               fin_p) != Digest_bytes) {
         (void)puts("Could not read HMAC from chaff file!...");
         exit(EXIT_FAILURE);
      }
      for(i = 0; i < Digest_bytes; i++) {
        if(act_digest_p[i] != file_digest_p[i]) {
          break; /* HMACs do not match */
        }
      }
      if(i == Digest_bytes) {
        (void)sprintf(buf, "%lu", ul);
        if(cb_new_pair(KEEP_INDICES, buf, TRUE) != INSERT_OK) {
          (void)printf("Cannot insert key %s into hash!...\n", buf);
          exit(EXIT_FAILURE);
        }
        if(matches > 0lu) {
          rev_package_build_kp(build_p, matches, previous_block_p);
        }
        next_to_last_match_index = last_match_index;
        last_match_index         = ul;
        matches++;
        bcopy(block_p, previous_block_p, 8);
      }
    }
    if(last_match_index == 0lu) {
      (void)printf(
            "Sorry, there is no match for \"%s\" in file \"%s\".\n",
                   argv[1], argv[2]);
      break;
    }
    rev_package_set_key(build_p, previous_block_p, &r_array);
    rewind(fin_p);
    /* 2nd pass -- note that we quit after index of NEXT to last
       match */
    for(ul = matches = 0lu; ul <= next_to_last_match_index; ul++) {
      (void)sprintf(buf, "%lu", ul);
      if(cb_exists(KEEP_INDICES, buf) == FALSE) {
        fseek(fin_p, Block_bytes, SEEK_CUR); /* skip data and HMAC */
      } else {
        if(fread(block_p, sizeof(unsigned char), 8, fin_p) != 8) {
          (void)printf("Error 1 reading block %lu!...\n", ul);
          break;
        }
        rev_package_finalize(&r_array, ++matches, block_p, plain_p);
        if(ul < next_to_last_match_index) { /* data block */
          j = 8;
        } else { /* last data block with sentinel */
          j = 8 - (int)plain_p[7]; /* number of bytes to print */
        }
        for(i = 0; i < j; i++) {
          (void)putchar(plain_p[i]);
        }
        fseek(fin_p, Digest_bytes, SEEK_CUR); /* skip HMAC */
      }
    }
    (void)fclose(fin_p);
    rv = EXIT_SUCCESS;
  } while(0);
  return rv;
}

static void help(char *program)
{
  (void)printf(
"\n\"%s\" is a companion program to \"mmpc_snd\", the latter of which\n"
"package-chaffs any number of files using a different HMAC for each\n"
"file and optionally \"randomly\" adds chaff blocks of \"random\" bytes.\n"
"This program is executed by supplying the key entered as a string of\n"
"hexadecimal characters followed by the name of a package-chaffed file\n"
"from mmpc_snd.\n\n", program);
}