/* simple XXTEA en/decrypt utility
 * 
 * BSD Licence
 *
 * btea function is from 
 * <https://secure.wikimedia.org/wikipedia/en/wiki/XXTEA#Reference_code>
 *
 * (c) by Sec <sec@42.org> 6/2011
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <stdint.h>
#include <getopt.h>
#include "xxtea.h"

// default block size

void hexkey(char *string, uint32_t k[4]);

int main(int argc, char *argv[]) {
  FILE *fp;
  FILE *ofp;

  char *prog;
  char c;			/* for getopt */
  uint32_t k[4];    /* key */
  uint8_t *buf;

  /* Default values */
  char verbose=0;	// be silent
  k[0]=0; k[1]=0; k[2]=0; k[3]=0;
  char *outfile=NULL; // outfile == infile
  int decrypt=0;
  int encrypt=0;
  int sign=0;

  /* init section */
  prog=argv[0];
  if(!prog)prog="xxtea";
  if(strrchr(prog,'/')){
	  prog=strrchr(argv[0],'/');
	  prog++;
  }

  /* The big getopt loop */
  while ((c = getopt(argc, argv, "vhesdk:o:")) != EOF)
	  switch (c) {
		  case 'v':
			  verbose++;
			  break;

		  case 'd':
			  decrypt=1;
			  break;

          case 'e':
              encrypt=1;
              break;

          case 's':
              sign=1;
              break;

		  case 'k':
              hexkey(optarg, k);
			  break;

		  case 'o':
			  outfile=optarg;
			  break;

		  case 'h':
		  default:
			  fprintf(stderr, "Usage: %s [options] filename\n\n"
"This program en/decrypts a file with the XXTEA algorithm\n"
"\n\n"
"-v           Be verbose (-v -v = even more)\n"
"-d           Decrypt file\n"
"-e           Encrypt file\n"
"-s           Sign file\n\n"
"-o file      Output to <file>. (Default: overwrite input file)\n"
"-k key       128bit hex key.\n"
"-h           This help\n\n"
"\n",prog);
			  exit(255);

	  }

  argc -= optind; argv += optind;

  if (argc !=1){
	  fprintf(stderr,"Error: No filename given!\n");
	  exit(254);
  };

  if( !(decrypt||encrypt||sign) ){
      fprintf(stderr, "No operation specified!\n");
	  exit(254);
  }

  if( decrypt && (encrypt || sign) ){
      fprintf(stderr, "Can not decrypt and sign or decrypt and encrypt!\n");
	  exit(254);
  }
  if(outfile){
      if ((fp = fopen(argv[0],"rb")) == NULL){
          fprintf(stderr,"Error: Can't open file %s\n",argv[0]);
          exit(253);
      }
      if ((ofp = fopen(outfile,"wb")) == NULL){
          fprintf(stderr,"Error: Can't open file %s\n",argv[0]);
          exit(253);
      }
  }else{
      if ((fp = fopen(argv[0],"r+b")) == NULL){
          fprintf(stderr,"Error: Can't open file %s\n",argv[0]);
          exit(253);
      }
      ofp=fp;
  };

  fseek(fp, 0L, SEEK_END);
  int filesize = ftell(fp);
  fseek(fp, 0L, SEEK_SET);

  if (verbose)
    fprintf(stderr,"file size=%d\n", filesize);
  
  int words = (((filesize+3)/sizeof(uint32_t)+3)/4)*4;
  int bytes = sizeof(uint32_t)*words;

  if (verbose)
    fprintf(stderr,"byte count=%d word count=%d\n", bytes, words);
  
  if( sign )
      buf=malloc(bytes+sizeof(uint32_t)*4);
  else
      buf=malloc(bytes);

  if(!buf){
      fprintf(stderr,"Error: malloc() failed.\n");
      exit(253);
  };

  if (verbose)
      fprintf(stderr,"Key: %08x %08x %08x %08x\n",k[0],k[1],k[2],k[3]);

  memset(buf, 0, bytes);
  if( fread(buf,sizeof(*buf),filesize,fp) != filesize ){
      fprintf(stderr, "Error: read failed\n");
      exit(253);
  };

  if( encrypt ){
      if (verbose)
          fprintf(stderr,"Encrypting: ");

      xxtea_encode_words((uint32_t*)buf, words, k);
      if(verbose) fprintf(stderr,".\n");
  }

  if( sign ){
      if (verbose)
          fprintf(stderr,"Signing: ");
      uint32_t mac[4];
      xxtea_cbcmac(mac, (uint32_t*)buf, words, k);

      if (verbose)
        fprintf(stderr,"MAC: %08x %08x %08x %08x\n",mac[0],mac[1],mac[2],mac[3]);
      memcpy(buf+bytes, mac, sizeof(*mac)*4);
      bytes += sizeof(*mac)*4;
      if(verbose) fprintf(stderr,".\n");
  }

  if(!outfile) // in-place crypting...
      if (fseek(fp,0,SEEK_SET) != 0){
          fprintf(stderr, "Error: Seek failed\n");
          exit(253);
      }

  if (fwrite(buf,sizeof(*buf),bytes,ofp) != bytes){
      fprintf(stderr, "Error: CRC write failed\n");
      exit(253);
  }
  if( fseek(ofp, 0L, SEEK_SET) != 0){
      fprintf(stderr, "Error: Seek failed\n");
      exit(253);
  }

  if(verbose)
      fprintf(stderr,"done\n");

  fclose(fp);

  if(outfile)
      fclose(ofp);
  
  return 0;
}

void hexkey(char *string, uint32_t k[4]){
    int idx=0;
    int kidx=0;
    int kctr=0;
    int value;
    char ch;

    while ((ch=string[idx++])!=0){
        if(ch == ' ')
            continue;
        if(ch == '\t')
            continue;

        if (ch >= '0' && ch <= '9')
            value = (ch - '0');
        else if (ch >= 'A' && ch <= 'F')
            value = (ch - 'A' + 10);
        else if (ch >= 'a' && ch <= 'f')
            value = (ch - 'a' + 10);
        else
            continue;


        k[kidx]=(k[kidx]<<4)+value;
        kctr++;
        if(kctr>=8){
            kctr=0;
            kidx++;
            if(kidx>4)
                return;
        };
    };
}