/* 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>

// default block size

void btea(uint32_t *v, int n, uint32_t const k[4]);
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 */
  uint32_t *buf;

  /* Default values */
  char verbose=0;	// be silent
  k[0]=0; k[1]=0; k[2]=0; k[3]=0;
  char block=16;
  char *outfile=NULL; // outfile == infile
  int decrypt=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, "vhdk:b:o:")) != EOF)
	  switch (c) {
		  case 'v':
			  verbose++;
			  break;

		  case 'd':
			  decrypt=1;
			  break;

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

		  case 'b':
			  block=atoi(optarg);
			  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 (instead of encrypt)\n"
"-o file      Output to <file>. (Default: overwrite input file)\n"
"-k key       128bit hex key.\n"
"-b block     Set blocksize. (Default: file size)\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(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;
  };

  if(block==0){
      fseek(fp, 0L, SEEK_END);
      block = ftell(fp)/sizeof(*buf); // XXX: padding!
      fseek(fp, 0L, SEEK_SET);
  };
  buf=malloc(sizeof(*buf)*block);

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

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

  int cnt;
  if (verbose)
      fprintf(stderr,"Encrypting: ");

  do{
	  cnt=fread(buf,sizeof(*buf),block,fp); // XXX: deal with non-block-sized?

      if(cnt<0){
		  fprintf(stderr, "Error: read failed\n");
		  exit(253);
      };

      if(verbose)
          fprintf(stderr,"cnt=%d:",cnt);
/*      if(cnt%sizeof(*buf)!=0){
          fprintf(stderr,"Whoops. needs padding: cnt=%d, mod=%d\n",cnt,cnt%sizeof(*buf));
      }; */

      btea(buf, decrypt?-cnt:cnt, k);

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

      if (fwrite(buf,sizeof(*buf),cnt,ofp) != cnt){
          fprintf(stderr, "Error: CRC write failed\n");
          exit(253);
      }
      if(verbose) fprintf(stderr,".\n");
  }while(cnt==block);

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

  fclose(fp);

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

#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (k[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const k[4]) {
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1) {          /* Coding Part */
        rounds = 6 + 52/n;
        sum = 0;
        z = v[n-1];
        do {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p=0; p<n-1; p++) {
                y = v[p+1]; 
                z = v[p] += MX;
            }
            y = v[0];
            z = v[n-1] += MX;
        } while (--rounds);
    } else if (n < -1) {  /* Decoding Part */
        n = -n;
        rounds = 6 + 52/n;
        sum = rounds*DELTA;
        y = v[0];
        do {
            e = (sum >> 2) & 3;
            for (p=n-1; p>0; p--) {
                z = v[p-1];
                y = v[p] -= MX;
            }
            z = v[n-1];
            y = v[0] -= MX;
        } while ((sum -= DELTA) != 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;
        };
    };
}
