/* 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 #include #include #include #include #include #include #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 \n"); printf("stego -s \n"); printf("\n"); printf("Note that and 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 touretzky-testimony.txt.mistake