/* This code was written by Scott A Crosby   crosby@qwes.math.cmu.edu
   and is hereby put in the public domain.
   
   It may be freely used by anyone for any purpose.

   Though I warn you: This code does do steganography, but it is not secure and does not offer plausible deniability. The placement algorithm must be altered for that.. IE: Don't use it if your words could get you jailed or shot.
*/





#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define UN_STEGO 1
#define STEGO 2
#define TEXT 3


int stego_fd;
int plain_fd;
int skip = 17;


/* Randomly compute a gap to skip. */
int get_gap_length(char key[16]) {
  return skip;
}

/* Is this a good character for stego'ing of this type? */
int good_char(char c,int type) {
  switch (type) {
    case TEXT:
      return (c >='a' && c <= 'z') || (c >='A' && c <= 'Z');
  }
}



char safe_read_plain() {
  char c;
  
  if (read(plain_fd,&c,1) <1) {
    fprintf(stderr,"Ran out of plaintext for stego/unstego.\n");
    exit(1);
  }
  return c;  
}  


char safe_read_stego() {
  char c;
  if (read(stego_fd,&c,1) <1) {
    fprintf(stderr,"Ran out of stegotext for unstego\n");
    exit(1);
  }
  return c;
}


char safe_write_stego(char c) {
  if (write(stego_fd,&c,1) <1) {
    fprintf(stderr,"Unable to write stegotext. \n");
    exit(1);
  }
}

void skip_gap(int skip,int mode) {
  char c;
  for (int j = 0; j < skip ; j++) {
    c = safe_read_plain();
      
      if (mode == UN_STEGO) {
	/* Consume a character of unaltered stego-text */
	c = safe_read_stego();
      } else {
	/* STEGO'ing, so copy it over. */
	safe_write_stego(c);
      }
  }
}

void stego_rest() {
  char buffer[4096];
  int used;
  while ((used = read(plain_fd,buffer,4096))) 
    write(stego_fd,buffer,used);
}




/* mode == STEGO  || UN_STEGO */
/* type == TEXT   || BINARY */
int stego_it (char *message, int message_length, 
	      int mode, int type, char key[16]) {
  char c;
  for (int i = 0 ; i< 2*message_length ; i++) {

    do {
      /* Skip over the gap between 'typos' */
      skip_gap(get_gap_length(key),mode);

      /* Read a character */
      c = safe_read_plain();
      if (!good_char(c,type)) {
	if (mode == UN_STEGO) {
	  /* Consume a character of unaltered stego-text */
	  c = safe_read_stego();
	} else {
	  /* STEGO'ing, so copy it over. */
	  safe_write_stego(c);
	}
      }
      /* Repeat skipping till we find a character we like. */
    } while (!good_char(c,type));
    

    if (mode == UN_STEGO) {
      /* Excellent.. pull out the stego character */
      char c = safe_read_stego()-1;
      if (i%2)
	message[i/2] <<= 4;
      message[i/2] += c%16;
      //fprintf(stderr,"outnibble1 %x\n", c%16);

    } else {
      /* STEGO'ing, so copy it over. */
      int outnibble = message[i/2];
      if (i%2 == 0)
	outnibble >>= 4;
      outnibble &= 0x0F;
      //fprintf(stderr,"outnibble2 %i\n", outnibble);
      safe_write_stego(random() %2 ? outnibble + 'A' : outnibble + 'a');
    }
  }
  if (mode == STEGO) {
    stego_rest();
  }

}

#define MAX_LENGTH 1000000

void stego_test (char *plain_filename, int length) {
  char *buffer=(char *) memset(malloc(MAX_LENGTH),0,MAX_LENGTH);

  plain_fd = open(plain_filename,O_RDONLY);
  stego_fd = 1;
  
  read (0,buffer,MAX_LENGTH);

  stego_it(buffer,length,STEGO,TEXT,0);

}

void unstego_test(char *plain_filename, char *stego_filename, int length) {
  char *buffer=(char *) malloc(MAX_LENGTH);

  plain_fd = open(plain_filename,O_RDONLY);
  stego_fd = open(stego_filename,O_RDONLY);

  stego_it(buffer,length,UN_STEGO,TEXT,0);
  write(1,buffer,length);
}

main(int argc, char *argv[]) {
  //  fprintf(stderr,"argc = %i\n",argc);
  //  exit(0);

  if (argv[1][0] == '-' && argv[1][1] == 'u' && argc == 4)
    unstego_test(argv[2],argv[3],atoi(&argv[1][2]));
  else if (argv[1][0] == '-' && argv[1][1] == 's' && argc == 3)
    stego_test(argv[2],atoi(&argv[1][2]));
  else {
    printf("Stego as mistakes: \n");
    printf("stego -u<length> <origional-file> <mistake-file>\n");
    printf("stego -s<length> <origional>\n");
    printf("\n");
    printf("Note that <origional-file> and <mistake-file> can both be the mistake file\n");
    printf("\n");
    printf("Input and output of the secret will be through stdin/stdout\n");
  }
}



/*
./typo-stego -s19 touretzky-testimony.txt > touretzky-testimony.txt.mistake <test
./typo-stego -u20 touretzky-testimony.txt touretzky-testimony.txt.mistake

./typo-stego -s3149 touretzky-testimony.txt.gz >touretzky-testimony.txt.mistake <css-descramble.c

./typo-stego -u3149 touretzky-testimony.txt.gz touretzky-testimony.txt.mistake 





*/
