gifshuffle/0040755000076400007640000000000007613231201012223 5ustar mkwanmkwangifshuffle/Makefile0100644000076400007640000000043207606151734013675 0ustar mkwanmkwan# # Rudimentary makefile for the gifshuffle steganography program. # Written by Matthew Kwan - January 1998 # CC = gcc CFLAGS = -O -Wall OBJ = main.o encrypt.o ice.o compress.o encode.o epi.o gif.o gifshuffle: $(OBJ) $(CC) -o $@ $(OBJ) clean: /bin/rm -f $(OBJ) gifshuffle gifshuffle/gshuf.txt0100644000076400007640000001605107606206466014121 0ustar mkwanmkwan gifshuffle(1) gifshuffle(1) NAME gifshuffle - GIF colourmap steganography program SYNOPSIS gifshuffle [ -CQS1 ] [ -p passwd ] [ -f file | -m message ] [ infile.gif [ outfile.gif ]] DESCRIPTION gifshuffle is a program for concealing messages in GIF images by shuffling the colourmap. A shuffled image is visibly indistinguishable from the original. gifshuffle works with all GIF images, including those with trans- parency and animation. Consider a pack of 52 cards. There are 52 factorial ways to sort the pack, which means that any particular ordering of the cards can represent a number in the range [0, 52!-1]. In other words, given n cards, you can store approximately log2(n!) bits of information based on their ordering. GIF images contain a colourmap with up to 256 entries, resulting in a maximum storage capacity of 1675 bits. The image itself consists of a compressed array of indices into this colourmap. To conceal a message within a GIF image the following steps take place. 1. Start with the message you want to conceal, speci- fied on the command line or in a file. Optionally compress and/or encrypt this message. You are then left with a sequence of 1's and 0's. 2. Prepend a 1 to this sequence, giving you a binary number m (probably quite large). 3. Next take a look at the GIF image that you want to conceal the message in. Count the number of unique colours in the image, and call the value n. If m > n!-1 then the message is too large, and the proce- dure will be aborted. 4. The colours in the colourmap are first sorted into their "natural" order (except when encryption is being used - see details below). Each RGB colour is assigned the value (red * 65536 + green * 256 + blue), and the colours are sorted according to these values. Any duplicate colours are removed. 5. Iterate i through the values 1 .. n. Each colour i-1 is allocated a target position (m mod i), then m is divided by i. 6. Each colour is then in turn inserted into a new colourmap at its target position. Colours previ- ously occupying the target position and above are moved up one place. 7. If the size of the colourmap is greater than the number of unique colours, then the colourmap will be padded with the last colour from the original colourmap. 8. The image component of the GIF is then uncom- pressed, the colour indices are re-mapped to the new colourmap, and the image is re-compressed. For animated GIFs this is repeated for each image. Extracting a hidden message follows a similar procedure, but in reverse. The ordering of the colourmap is used to construct a binary number, which is then optionally decrypted and uncompressed before being output. gifshuffle provides rudimentary compression, using Huffman tables optimised for English text. However, if the data is not text, or if there is a lot of data, the use of the built-in compression is not recommended, since an external compression program such as compress or gzip will do a much better job. Encryption is also provided, using the ICE encryption algorithm in 1-bit cipher-feedback (CFB) mode to encrypt the data (after compression). Because of ICE's arbitrary key size, passwords of any length up to 1170 characters are supported (since only 7 bits of each character are used, this means keys up to 1024-bytes are supported). As of gifshuffle 2.0 encryption is also applied to the ordering of the colours in the colourmap. Instead of using their "natural" ordering, the ordering of their encrypted hash is used. This has the benefit of making colourmaps look random even when there is only a small message con- cealed. To disable this feature (which is incompatible with version 1 of gifshuffle) use the -1 option. If a message string or message file are specified on the command-line, gifshuffle will attempt to conceal the mes- sage in the file infile.gif if specified, or standard input otherwise. The resulting file will be written to outfile.gif if specified, or standard output if not. If no message string is provided, gifshuffle attempts to extract a message from the input file. The result is writ- ten to the output file or standard output. OPTIONS -C Compress the data if concealing, or uncompress it if extracting. -Q Quiet mode. If not set, the program reports statis- tics such as compression percentages and amount of available storage space used. -S Report on the amount of space available for hidden message in the GIF colourmap. This is calculated from the number of unique colours in the image. -1 Retain compatibility with version 1 of gifshuffle by ordering colours using their "natural" ordering, rather than their encrypted ordering. This is only relevant if a password is specified. -p password If this is set, the data will be encrypted with this password during concealment, or decrypted dur- ing extraction. -f message-file The contents of this file will be concealed in the input GIF image. -m message-string The contents of this string will be concealed in the input GIF image. Note that, unless a newline is somehow included in the string, a newline will not be printed when the message is extracted. EXAMPLES The following command will conceal the message "Meet me at 6" in the file infile.gif, with compression, and encrypted with the password "hello world". The resulting text will be stored in outfile.gif. gifshuffle -C -m "Meet me at 6" -p "hello world" infile.gif outfile.gif To extract the message, the command would be gifshuffle -C -p "hello world" outfile.gif Note that the resulting message will not be terminated by a newline. The storage capacity of a file can be determined with the -S option. gifshuffle -S infile.gif AUTHOR This application was written by Matthew Kwan, who can be reached at mkwan@darkside.com.au gifshuffle/compress.c0100644000076400007640000001015107605542665014241 0ustar mkwanmkwan/* * Compression routines for the gifshuffle steganography program. * Uses simple Huffman coding. * * Written by Matthew Kwan - December 1996 */ #include "gifshuf.h" #include /* * The Huffman codes. */ static const char *huffcodes[256] = { #include "huffcode.h" }; /* * Local variables used for compression. */ static int compress_bit_count; static int compress_value; static int compress_bits_in; static int compress_bits_out; /* * Initialize the compression routines. */ void compress_init (void) { compress_bit_count = 0; compress_value = 0; compress_bits_in = 0; compress_bits_out = 0; encrypt_init (); } /* * Compress a single bit. */ BOOL compress_bit ( int bit, FILE *inf, FILE *outf ) { if (!compress_flag) return (encrypt_bit (bit, inf, outf)); compress_bits_in++; compress_value = (compress_value << 1) | bit; if (++compress_bit_count == 8) { const char *s; for (s = huffcodes[compress_value]; *s != '\0'; s++) { int bit; if (*s == '1') bit = 1; else if (*s == '0') bit = 0; else { fprintf (stderr, "Illegal Huffman character '%c'\n", *s); return (FALSE); } if (!encrypt_bit (bit, inf, outf)) return (FALSE); compress_bits_out++; } compress_value = 0; compress_bit_count = 0; } return (TRUE); } /* * Flush the contents of the compression routines. */ BOOL compress_flush ( FILE *inf, FILE *outf ) { if (compress_bit_count != 0 && !quiet_flag) fprintf (stderr, "Warning: residual of %d bits not compressed\n", compress_bit_count); if (compress_bits_out > 0 && !quiet_flag) { double cpc = (double) (compress_bits_in - compress_bits_out) / (double) compress_bits_in * 100.0; if (cpc < 0.0) fprintf (stderr, "Compression enlarged data by %.2f%% - recommend not using compression\n", -cpc); else fprintf (stderr, "Compressed by %.2f%%\n", cpc); } return (encrypt_flush (inf, outf)); } /* * Local variables used for output. */ static int output_bit_count; static int output_value; /* * Initialize the output variables. */ static void output_init (void) { output_bit_count = 0; output_value = 0; } /* * Output a single bit. */ static BOOL output_bit ( int bit, FILE *outf ) { output_value = (output_value << 1) | bit; if (++output_bit_count == 8) { if (fputc (output_value, outf) == EOF) { perror ("Output file"); return (FALSE); } output_value = 0; output_bit_count = 0; } return (TRUE); } /* * Flush the contents of the output routines. */ static BOOL output_flush ( FILE *outf ) { if (output_bit_count > 2 && !quiet_flag) fprintf (stderr, "Warning: residual of %d bits not output\n", output_bit_count); return (TRUE); } /* * Local variables used for uncompression. */ static int uncompress_bit_count; static char uncompress_value[256]; /* * Initialize the uncompression routines. */ void uncompress_init (void) { uncompress_bit_count = 0; output_init (); } /* * Find the Huffman code string that matches. */ static int huffcode_find ( const char *str ) { int i; for (i=0; i<256; i++) if (strcmp (str, huffcodes[i]) == 0) return (i); return (-1); } /* * Uncompress a single bit. */ BOOL uncompress_bit ( int bit, FILE *outf ) { int code; if (!compress_flag) return (output_bit (bit, outf)); uncompress_value[uncompress_bit_count++] = bit ? '1' : '0'; uncompress_value[uncompress_bit_count] = '\0'; if ((code = huffcode_find (uncompress_value)) >= 0) { int i; for (i=0; i<8; i++) { int b = ((code & (128 >> i)) != 0) ? 1 : 0; if (!output_bit (b, outf)) return (FALSE); } uncompress_bit_count = 0; } if (uncompress_bit_count >= 255) { fprintf (stderr, "Error: Huffman uncompress buffer overflow\n"); return (FALSE); } return (TRUE); } /* * Flush the contents of the uncompression routines. */ BOOL uncompress_flush ( FILE *outf ) { if (uncompress_bit_count > 2 && !quiet_flag) fprintf (stderr, "Warning: residual of %d bits not uncompressed\n", uncompress_bit_count); return (output_flush (outf)); } gifshuffle/encode.c0100644000076400007640000001503207606172256013642 0ustar mkwanmkwan/* * GIF colourmap encoding routines for the gifshuffle steganography program. * * Written by Matthew Kwan - January 1998 */ #include "gifshuf.h" #include "epi.h" #include "gif.h" #include /* * An RGB with position information and possibly its encrypted ciphertext. */ typedef struct { int pos; RGB rgb; unsigned char ctext[8]; } CMAP_INFO; /* * Local variables used for encoding. */ static int encode_bit_count; static EPI encode_bits; /* * Compare two RGB values. */ static int rgb_cmp ( const RGB *r1, const RGB *r2 ) { int v1 = (r1->r << 16) | (r1->g << 8) | r1->b; int v2 = (r2->r << 16) | (r2->g << 8) | r2->b; return (v1 - v2); } /* * Compare two colourmap info values. */ static int cmap_cmp ( const void *p1, const void *p2 ) { return (rgb_cmp (&((CMAP_INFO *) p1)->rgb, &((CMAP_INFO *) p2)->rgb)); } /* * Compare two colourmap info values by their encrypted values. */ static int cmap_encrypt_cmp ( const void *p1, const void *p2 ) { const CMAP_INFO *c1 = (const CMAP_INFO *) p1; const CMAP_INFO *c2 = (const CMAP_INFO *) p2; unsigned int i; for (i=0; i<8; i++) if (c1->ctext[i] != c2->ctext[i]) return ((int) c1->ctext[i] - (int) c2->ctext[i]); return (0); } /* * Return the unique colours from a colourmap. * Put the non-unique colours at the top of the array. */ static int unique_colours ( const RGB *cols, int ncols, CMAP_INFO *ci_array ) { int i, n = 0, top = 0; for (i=0; igi_colours, gi->gi_num_colours, dummy); epi_set (epi, 1); for (i=2; i<=ncols; i++) epi_multiply (epi, i); epi_decrement (epi); } /* * Encode a colourmap with the specified value. */ static BOOL colourmap_encode ( GIFINFO *gi, EPI *epi ) { int i, ncols = 0; CMAP_INFO ci_array[256]; ncols = unique_colours (gi->gi_colours, gi->gi_num_colours, ci_array); if (encrypting_colourmap ()) { for (i=0; irgb.r, ci->rgb.g, ci->rgb.b, ci->ctext); } qsort (ci_array, ncols, sizeof (CMAP_INFO), cmap_encrypt_cmp); } else qsort (ci_array, ncols, sizeof (CMAP_INFO), cmap_cmp); for (i=0; iepi_high_bit > 0) { fprintf (stderr, "Error: remainder of %d bits.\n", epi->epi_high_bit); return (FALSE); } for (i=0; ipos; for (j=i; j>pos; j--) gi->gi_colours[j] = gi->gi_colours[j - 1]; gi->gi_colours[pos] = ci->rgb; } /* Pad out the rest of the colourmap. */ for (; igi_num_colours; i++) gi->gi_colours[i] = gi->gi_colours[gi->gi_num_colours - 1]; return (TRUE); } /* * Initialize the encoding routines. */ void encode_init (void) { encode_bit_count = 0; epi_init (&encode_bits); } /* * Encode a single bit. */ BOOL encode_bit ( int bit, FILE *inf, FILE *outf ) { encode_bit_count++; if (encode_bits.epi_high_bit < EPI_MAX_BITS) encode_bits.epi_bits[encode_bits.epi_high_bit++] = bit; return (TRUE); } /* * Flush the contents of the encoding routines. */ BOOL encode_flush ( FILE *inf, FILE *outf ) { GIFINFO gi; EPI max_epi; int max_bits; encode_bit_count++; if (encode_bits.epi_high_bit < EPI_MAX_BITS) encode_bits.epi_bits[encode_bits.epi_high_bit++] = 1; if (!gif_header_load (&gi, inf)) { fprintf (stderr, "Input file is not in GIF format.\n"); return (FALSE); } colourmap_max_storage (&gi, &max_epi); max_bits = max_epi.epi_high_bit; if (encode_bit_count > max_epi.epi_high_bit || epi_cmp (&encode_bits, &max_epi) > 0) { if (max_bits == 0) fprintf (stderr, "GIF file has no storage space.\n"); else fprintf (stderr, "Message exceeded available space by approximately %.2f%%.\n", ((double) encode_bit_count / max_bits - 1.0) * 100.0); return (FALSE); } if (!colourmap_encode (&gi, &encode_bits)) return (FALSE); if (!gif_filter_save (&gi, inf, outf)) return (FALSE); if (!quiet_flag) fprintf (stderr, "Message used approximately %.2f%% of available space.\n", (double) encode_bit_count / max_bits * 100.0); return (TRUE); } /* * Decode a value from a colourmap. */ static void colourmap_decode ( const GIFINFO *gi, EPI *epi ) { int i, ncols = 0; CMAP_INFO ci_array[256]; ncols = unique_colours (gi->gi_colours, gi->gi_num_colours, ci_array); for (i=0; irgb.r, ci->rgb.g, ci->rgb.b, ci->ctext); } qsort (ci_array, ncols, sizeof (CMAP_INFO), cmap_encrypt_cmp); } else qsort (ci_array, ncols, sizeof (CMAP_INFO), cmap_cmp); epi_init (epi); for (i = 0; i < ncols - 1; i++) { int j, pos = ci_array[i].pos; EPI epi_pos; epi_multiply (epi, ncols - i); epi_set (&epi_pos, pos); epi_add (epi, &epi_pos); for (j = i + 1; j < ncols; j++) if (ci_array[j].pos > pos) ci_array[j].pos--; } } /* * Extract a message from the input stream. */ BOOL message_extract ( FILE *inf, FILE *outf ) { GIFINFO gi; EPI epi; int i; decrypt_init (); if (!gif_header_load (&gi, inf)) { fprintf (stderr, "Input file is not in GIF format.\n"); return (FALSE); } colourmap_decode (&gi, &epi); for (i = 0; i < epi.epi_high_bit - 1; i++) if (!decrypt_bit (epi.epi_bits[i], outf)) return (FALSE); return (decrypt_flush (outf)); } /* * Calculate the amount of covert information that can be stored * in the file. */ void space_calculate ( FILE *fp ) { GIFINFO gi; EPI max_epi; int max_bits; if (!gif_header_load (&gi, fp)) { fprintf (stderr, "Input file is not in GIF format.\n"); return; } colourmap_max_storage (&gi, &max_epi); max_bits = max_epi.epi_high_bit - 1; if (max_bits < 0) max_bits = 0; printf ("File has storage capacity of %d bits (%d bytes)\n", max_bits, max_bits / 8); } gifshuffle/encrypt.c0100644000076400007640000000707107605546407014077 0ustar mkwanmkwan/* * Encryption routines for the gifshuffle steganography program. * Uses the ICE encryption algorithm in 1-bit cipher-feedback (CFB) mode. * * Written by Matthew Kwan - December 1996 */ #include "gifshuf.h" #include "ice.h" #include /* * The key to use for encryption/decryption. */ static ICE_KEY *ice_key = NULL; static unsigned char encrypt_iv_block[8]; /* * Build the ICE key from the supplied password. * Only uses the lower 7 bits from each character. */ void password_set ( const char *passwd ) { int i, level; unsigned char buf[1024]; level = (strlen (passwd) * 7 + 63) / 64; if (level == 0) { if (!quiet_flag) fprintf (stderr, "Warning: an empty password is being used\n"); level = 1; } else if (level > 128) { if (!quiet_flag) fprintf (stderr, "Warning: password truncated to 1170 chars\n"); level = 128; } if ((ice_key = ice_key_create (level)) == NULL) { if (!quiet_flag) fprintf (stderr, "Warning: failed to set password\n"); return; } for (i=0; i<1024; i++) buf[i] = 0; i = 0; while (*passwd != '\0') { unsigned char c = *passwd & 0x7f; int idx = i / 8; int bit = i & 7; if (bit == 0) { buf[idx] = (c << 1); } else if (bit == 1) { buf[idx] |= c; } else { buf[idx] |= (c >> (bit - 1)); buf[idx + 1] = (c << (9 - bit)); } i += 7; passwd++; if (i > 8184) break; } ice_key_set (ice_key, buf); /* Set the initialization vector with the key * with itself. */ ice_key_encrypt (ice_key, buf, encrypt_iv_block); } /* * Are we doing colourmap ordering encryption? */ BOOL encrypting_colourmap (void) { return (!v1_flag && ice_key != NULL); } /* * Encrypt a colour. */ void encrypt_colour ( unsigned char r, unsigned char g, unsigned char b, unsigned char *ctext ) { unsigned char ptext[8]; int i; if (ice_key == NULL) return; ptext[0] = r; ptext[1] = g; ptext[2] = b; for (i=3; i<8; i++) ptext[i] = 0; ice_key_encrypt (ice_key, ptext, ctext); } /* * Initialize the encryption routines. */ void encrypt_init (void) { encode_init (); } /* * Encrypt a single bit. */ BOOL encrypt_bit ( int bit, FILE *inf, FILE *outf ) { int i; unsigned char buf[8]; if (ice_key == NULL) return (encode_bit (bit, inf, outf)); ice_key_encrypt (ice_key, encrypt_iv_block, buf); if ((buf[0] & 128) != 0) bit = !bit; /* Rotate the IV block one bit left */ for (i=0; i<8; i++) { encrypt_iv_block[i] <<= 1; if (i < 7 && (encrypt_iv_block[i+1] & 128) != 0) encrypt_iv_block[i] |= 1; } encrypt_iv_block[7] |= bit; return (encode_bit (bit, inf, outf)); } /* * Flush the contents of the encryption routines. */ BOOL encrypt_flush ( FILE *inf, FILE *outf ) { return (encode_flush (inf, outf)); } /* * Initialize the decryption routines. */ void decrypt_init (void) { uncompress_init (); } /* * Decrypt a single bit. */ BOOL decrypt_bit ( int bit, FILE *outf ) { int i; int nbit; unsigned char buf[8]; if (ice_key == NULL) return (uncompress_bit (bit, outf)); ice_key_encrypt (ice_key, encrypt_iv_block, buf); if ((buf[0] & 128) != 0) nbit = !bit; else nbit = bit; /* Rotate the IV block one bit left */ for (i=0; i<8; i++) { encrypt_iv_block[i] <<= 1; if (i < 7 && (encrypt_iv_block[i+1] & 128) != 0) encrypt_iv_block[i] |= 1; } encrypt_iv_block[7] |= bit; return (uncompress_bit (nbit, outf)); } /* * Flush the contents of the decryption routines. */ BOOL decrypt_flush ( FILE *outf ) { return (uncompress_flush (outf)); } gifshuffle/epi.c0100644000076400007640000001032006476511637013161 0ustar mkwanmkwan/* * Extended-precision integers. * * Written by Matthew Kwan - January 1998 */ #include "epi.h" #include #include #include /* * Initialize an EPI. */ void epi_init ( EPI *epi ) { epi->epi_high_bit = 0; memset (epi->epi_bits, 0, EPI_MAX_BITS * sizeof (char)); } /* * Convert an integer to an EPI. */ void epi_set ( EPI *epi, int n ) { int i = 0; epi_init (epi); while (n != 0) { epi->epi_bits[i++] = n & 1; n >>= 1; } epi->epi_high_bit = i; } /* * Return the integer equivalent of an EPI. */ static int epi_as_int ( const EPI *epi ) { int i, n = 0; for (i = epi->epi_high_bit - 1; i >= 0; i--) { n <<= 1; n += epi->epi_bits[i]; } return (n); } /* * Assign an EPI to another. * * epi1 = epi2; */ static void epi_copy ( EPI *epi1, const EPI *epi2 ) { int i; epi_init (epi1); epi1->epi_high_bit = epi2->epi_high_bit; for (i=0; iepi_high_bit; i++) epi1->epi_bits[i] = epi2->epi_bits[i]; } /* * Compare two EPIs. */ int epi_cmp ( const EPI *epi1, const EPI *epi2 ) { int i, hd = epi1->epi_high_bit - epi2->epi_high_bit; if (hd != 0) return (hd); for (i = epi1->epi_high_bit - 1; i >= 0; i--) if ((hd = epi1->epi_bits[i] - epi2->epi_bits[i]) != 0) return (hd); return (0); } /* * Add two EPIs together. * * epi1 += epi2; */ void epi_add ( EPI *epi1, const EPI *epi2 ) { int i, hb = epi1->epi_high_bit; int v = 0; if (epi2->epi_high_bit > hb) hb = epi2->epi_high_bit; for (i=0; iepi_bits[i] + epi2->epi_bits[i]; epi1->epi_bits[i] = v & 1; v >>= 1; } if (v != 0) epi1->epi_bits[i++] = v; epi1->epi_high_bit = i; } /* * Shift an EPI n bits to the left. * Negative n values are OK. * * epi <<= n; */ static void epi_shift ( EPI *epi, int n ) { int hb = epi->epi_high_bit; if (n == 0 || hb == 0) return; if (hb + n <= 0) { epi_init (epi); return; } if (n < 0) { int i; epi->epi_high_bit += n; for (i=0; iepi_high_bit; i++) epi->epi_bits[i] = epi->epi_bits[i - n]; for (i = 0; i < -n; i++) epi->epi_bits[epi->epi_high_bit + i] = 0; } else { int i; for (i = epi->epi_high_bit - 1; i >= 0; i--) epi->epi_bits[i + n] = epi->epi_bits[i]; for (i=0; iepi_bits[i] = 0; epi->epi_high_bit += n; } } /* * Multiply an EPI by an integer. * * epi *= n; */ void epi_multiply ( EPI *epi, int n ) { EPI epiret; epi_init (&epiret); while (n != 0) { if ((n & 1) != 0) epi_add (&epiret, epi); n >>= 1; epi_shift (epi, 1); } epi_copy (epi, &epiret); } /* * Subtract one EPI from another. * Assumes that epi1 > epi2. * * epi1 -= epi2; */ static void epi_subtract ( EPI *epi1, const EPI *epi2 ) { int i; int v = 0; for (i=0; iepi_high_bit; i++) { v = epi1->epi_bits[i] - epi2->epi_bits[i] - v; epi1->epi_bits[i] = v & 1; v >>= 1; v &= 1; } i = epi1->epi_high_bit - 1; epi1->epi_high_bit = 0; while (i >= 0) { if (epi1->epi_bits[i] == 1) { epi1->epi_high_bit = i + 1; break; } i--; } } /* * Decrement an EPI. * * epi--; */ void epi_decrement ( EPI *epi ) { int i; int v = 1; for (i=0; iepi_high_bit; i++) { v = epi->epi_bits[i] - v; epi->epi_bits[i] = v & 1; v >>= 1; v &= 1; if (v == 0) break; } i = epi->epi_high_bit - 1; epi->epi_high_bit = 0; while (i >= 0) { if (epi->epi_bits[i] == 1) { epi->epi_high_bit = i + 1; break; } i--; } } /* * Divide an EPI by an integer. * Return the remainder. * * x = epi % n; * epi /= n; * return x; */ int epi_divide ( EPI *epi, int n ) { EPI epires, epidiv; int rem; epi_set (&epidiv, n); epi_init (&epires); if (epi_cmp (epi, &epidiv) >= 0) { int i, hb = 1; epi_shift (&epidiv, 1); while (epi_cmp (&epidiv, epi) <= 0) { hb++; epi_shift (&epidiv, 1); } for (i=0; i /* * Local variables. */ static int block_size = 0; static int clear_code_index = 0; static int max_code = 0; static BOOL use_end_code = FALSE; /* * Load the header of a GIF image. * Returns FALSE if the file is not a GIF. */ BOOL gif_header_load ( GIFINFO *gi, FILE *fp ) { unsigned char *bp = gi->gi_header; unsigned char buf[768]; int i, n; if (fread (bp, sizeof (char), 13, fp) != 13) { fprintf (stderr, "Error: could not read header information.\n"); return (FALSE); } if (bp[0] != 'G' && bp[1] != 'I' && bp[2] != 'F') { fprintf (stderr, "Error: not a GIF file.\n"); return (FALSE); } if ((bp[10] & 0x80) == 0) { fprintf (stderr, "Error: GIF file does not have a global colourmap.\n"); return (FALSE); } gi->gi_bits_per_pixel = (bp[10] & 7) + 1; n = gi->gi_num_colours = 1 << gi->gi_bits_per_pixel; if (fread (buf, sizeof (char), n * 3, fp) != n * 3) { fprintf (stderr, "Error: could not read colourmap.\n"); return (FALSE); } for (i=0; igi_num_colours; i++) { gi->gi_colours[i].r = gi->gi_orig_colours[i].r = buf[i*3]; gi->gi_colours[i].g = gi->gi_orig_colours[i].g = buf[i*3 + 1]; gi->gi_colours[i].b = gi->gi_orig_colours[i].b = buf[i*3 + 2]; } return (TRUE); } /* * The GIF decoding code is based on giftoppm.c * As a result, I must include the following message. */ /* +-------------------------------------------------------------------+ */ /* | Copyright 1990, David Koblas. | */ /* | Permission to use, copy, modify, and distribute this software | */ /* | and its documentation for any purpose and without fee is hereby | */ /* | granted, provided that the above copyright notice appear in all | */ /* | copies and that both that copyright notice and this permission | */ /* | notice appear in supporting documentation. This software is | */ /* | provided "as is" without express or implied warranty. | */ /* +-------------------------------------------------------------------+ */ /* * Filter a GIF extension. * This involves passing it straight through, except for GIF89 * extensions, where the transparency index must be re-mapped. */ static BOOL filter_extension ( const int *imap, FILE *infp, FILE *outfp ) { unsigned char c; BOOL gif89_flag = FALSE; if (fread (&c, sizeof (char), 1, infp) != 1) { fprintf (stderr, "Error: could not read extension code.\n"); return (FALSE); } if (fwrite (&c, sizeof (char), 1, outfp) != 1) { perror (NULL); return (FALSE); } if (c == 0xf9) gif89_flag = TRUE; if (fread (&c, sizeof (char), 1, infp) != 1) { fprintf (stderr, "Error: could not read data block size.\n"); return (FALSE); } if (fwrite (&c, sizeof (char), 1, outfp) != 1) { perror (NULL); return (FALSE); } while (c != 0) { unsigned char buf[256]; if (fread (buf, sizeof (char), c, infp) != c) { fprintf (stderr, "Error: could not read data block.\n"); return (FALSE); } if (gif89_flag && (buf[0] & 1) != 0) buf[3] = imap[buf[3]]; if (fwrite (buf, sizeof (char), c, outfp) != c) { perror (NULL); return (FALSE); } if (fread (&c, sizeof (char), 1, infp) != 1) { fprintf (stderr, "Error: could not read data block size.\n"); return (FALSE); } if (fwrite (&c, sizeof (char), 1, outfp) != 1) { perror (NULL); return (FALSE); } } return (TRUE); } /* * Get a data block. */ static int get_data_block ( FILE *fp, unsigned char *buf ) { unsigned char count; if (fread (&count, sizeof (char), 1, fp) != 1) { fprintf (stderr, "Error: could not read data block size.\n"); return (-1); } if (count > 0 && fread (buf, sizeof (char), count, fp) != count) { fprintf (stderr, "Error: could not read data block.\n"); return (-1); } if (block_size == 0) block_size = count; return (count); } /* * Structures for storing LWZ parameters. */ #define MAX_LWZ_BITS 12 typedef struct { BOOL lp_fresh; int lp_code_size; int lp_set_code_size; int lp_max_code; int lp_max_code_size; int lp_first_code; int lp_old_code; int lp_clear_code; int lp_end_code; int lp_table[2][1 << MAX_LWZ_BITS]; int lp_stack[2 << MAX_LWZ_BITS]; int *lp_sp; } LWZ_PARAMS; typedef struct { unsigned char lb_buffer[280]; int lb_curr_bit; int lb_last_bit; int lb_last_byte; BOOL lb_done; BOOL lb_zero_data_block; } LWZ_BUFFER; /* * Get an LWZ code. */ static int get_lwz_code ( FILE *fp, int code_size, LWZ_BUFFER *lb ) { int i, j, ret; if (lb->lb_curr_bit + code_size >= lb->lb_last_bit) { int count; if (lb->lb_done) { if (lb->lb_curr_bit >= lb->lb_last_bit) { fprintf (stderr, "Error: uncompression exceeded end.\n"); return (-2); } return (-1); } if (lb->lb_last_byte >= 2) { lb->lb_buffer[0] = lb->lb_buffer[lb->lb_last_byte - 2]; lb->lb_buffer[1] = lb->lb_buffer[lb->lb_last_byte - 1]; } count = get_data_block (fp, &lb->lb_buffer[2]); lb->lb_zero_data_block = (count == 0); if (count < 0) return (-2); else if (count == 0) lb->lb_done = TRUE; lb->lb_last_byte = count + 2; lb->lb_curr_bit += 16 - lb->lb_last_bit; lb->lb_last_bit = lb->lb_last_byte * 8; } ret = 0; i = lb->lb_curr_bit; for (j = 0; j < code_size; i++, j++) if ((lb->lb_buffer[i / 8] & (1 << (i % 8))) != 0) ret |= (1 << j); lb->lb_curr_bit += code_size; return (ret); } /* * Uncompress a byte from the image. */ static int lwz_read_byte ( FILE *fp, int input_code_size, LWZ_PARAMS *lp, LWZ_BUFFER *lb ) { int code, incode; int i; if (lp->lp_fresh) { lp->lp_fresh = FALSE; do { lp->lp_old_code = get_lwz_code (fp, lp->lp_code_size, lb); lp->lp_first_code = lp->lp_old_code; } while (lp->lp_first_code == lp->lp_clear_code); return (lp->lp_first_code); } if (lp->lp_sp > lp->lp_stack) return (*--lp->lp_sp); while ((code = get_lwz_code (fp, lp->lp_code_size, lb)) >= 0) { if (code == lp->lp_clear_code) { if (max_code >= clear_code_index) clear_code_index = max_code + 1; for (i=0; ilp_table[0][i] = 0; lp->lp_table[1][i] = i; } for (; i < (1 << MAX_LWZ_BITS); i++) lp->lp_table[0][i] = lp->lp_table[1][i] = 0; lp->lp_code_size = lp->lp_set_code_size + 1; lp->lp_max_code_size = lp->lp_clear_code * 2; lp->lp_max_code = lp->lp_clear_code + 2; lp->lp_sp = lp->lp_stack; lp->lp_old_code = get_lwz_code (fp, lp->lp_code_size, lb); lp->lp_first_code = lp->lp_old_code; return (lp->lp_first_code); } else if (code == lp->lp_end_code) { int count; unsigned char buf[260]; use_end_code = TRUE; if (lb->lb_zero_data_block) return (-2); while ((count = get_data_block (fp, buf)) > 0) ; if (count != 0) fprintf (stderr, "Error: missing EOD in data stream.\n"); return (-2); } incode = code; if (code >= lp->lp_max_code) { *lp->lp_sp++ = lp->lp_first_code; code = lp->lp_old_code; } while (code >= lp->lp_clear_code) { *lp->lp_sp++ = lp->lp_table[1][code]; if (code == lp->lp_table[0][code]) { fprintf (stderr, "Error: circular table entry.\n"); return (-2); } code = lp->lp_table[0][code]; } *lp->lp_sp++ = lp->lp_first_code = lp->lp_table[1][code]; if ((code = lp->lp_max_code) < (1 << MAX_LWZ_BITS)) { lp->lp_table[0][code] = lp->lp_old_code; lp->lp_table[1][code] = lp->lp_first_code; lp->lp_max_code++; if (lp->lp_max_code >= lp->lp_max_code_size && lp->lp_max_code_size < (1 << MAX_LWZ_BITS)) { lp->lp_max_code_size *= 2; lp->lp_code_size++; } if (code > max_code) max_code = code; } lp->lp_old_code = incode; if (lp->lp_sp > lp->lp_stack) return (*--lp->lp_sp); } return (code); } /* * Initialize the LWZ structure and buffer. */ static void lwz_init ( int input_code_size, LWZ_PARAMS *lp, LWZ_BUFFER *lb ) { int i; lp->lp_set_code_size = input_code_size; lp->lp_code_size = lp->lp_set_code_size + 1; lp->lp_clear_code = 1 << lp->lp_set_code_size; lp->lp_end_code = lp->lp_clear_code + 1; lp->lp_max_code_size = 2 * lp->lp_clear_code; lp->lp_max_code = lp->lp_clear_code + 2; lp->lp_fresh = TRUE; for (i=0; ilp_clear_code; i++) { lp->lp_table[0][i] = 0; lp->lp_table[1][i] = i; } for (; i < (1 << MAX_LWZ_BITS); i++) lp->lp_table[0][i] = lp->lp_table[1][0] = 0; lp->lp_sp = lp->lp_stack; lb->lb_curr_bit = 0; lb->lb_last_bit = 0; lb->lb_last_byte = 0; lb->lb_done = FALSE; lb->lb_zero_data_block = FALSE; } /* * Load an image into the provided buffer. */ static BOOL load_image ( unsigned char *image, int size, FILE *fp ) { unsigned char c; int i, v; LWZ_PARAMS lwz; LWZ_BUFFER lb; /* Initialise the compression routines */ if (fread (&c, sizeof (char), 1, fp) != 1) { fprintf (stderr, "Error: could not read image data.\n"); return (FALSE); } lwz_init (c, &lwz, &lb); /* Uncompress the image */ i = 0; while ((v = lwz_read_byte (fp, c, &lwz, &lb)) >= 0) { if (i >= size) { fprintf (stderr, "Error: too much image data.\n"); return (FALSE); } image[i++] = v; } if (i < size) { fprintf (stderr, "Error: incomplete image data.\n"); return (FALSE); } return (TRUE); } /* * The GIF encoding code is based on ppmtogif.c * As a result, I must include the following message. */ /* ppmtogif.c - read a portable pixmap and produce a GIF file ** ** Based on GIFENCOD by David Rowley .A ** Lempel-Zim compression based on "compress". ** ** Copyright (C) 1989 by Jef Poskanzer. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. ** ** The Graphics Interchange Format(c) is the Copyright property of ** CompuServe Incorporated. GIF(sm) is a Service Mark property of ** CompuServe Incorporated. */ #define HSIZE 5003 /* 80% occupancy */ /* * Structure for handling compression params. */ typedef struct { int cp_n_bits; /* number of bits/code */ long int cp_maxcode; /* maximum code, given n_bits */ int cp_free_ent; /* first unused entry */ BOOL cp_clear_flag; int cp_init_bits; } COMPRESS_PARAMS; /* * Structure for handling a 'packet'. */ typedef struct { int p_count; unsigned char p_buffer[256]; } PACKET; /* * Structure for handling accumulated bits. */ typedef struct { unsigned long ab_accum; int ab_num_bits; } ACCUM_BITS; /* * Flush the packet to disk, and reset the accumulator */ static BOOL packet_flush ( PACKET *p, FILE *fp ) { if (p->p_count == 0) return (TRUE); if (fputc (p->p_count, fp) < 0 || fwrite (p->p_buffer, sizeof (char), p->p_count, fp) != p->p_count) { perror (NULL); return (FALSE); } p->p_count = 0; return (TRUE); } /* * Add a character to the end of the current packet, and if it has block * size characters, flush the packet to disk. */ static BOOL packet_write_char ( PACKET *p, int block_size, unsigned char c, FILE *fp ) { p->p_buffer[p->p_count++] = c; if (p->p_count >= block_size) return (packet_flush (p, fp)); return (TRUE); } /* * Output the given code. */ static BOOL output_code ( int code, ACCUM_BITS *ab, PACKET *p, COMPRESS_PARAMS *cp, FILE *fp ) { static const unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff}; ab->ab_accum &= masks[ab->ab_num_bits]; if (ab->ab_num_bits > 0) ab->ab_accum |= code << ab->ab_num_bits; else ab->ab_accum = code; ab->ab_num_bits += cp->cp_n_bits; while (ab->ab_num_bits >= 8) { if (!packet_write_char (p, block_size, ab->ab_accum & 0xff, fp)) return (FALSE); ab->ab_accum >>= 8; ab->ab_num_bits -= 8; } /* * If the next entry is going to be too big for the code size, * then increase it, if possible. */ if (cp->cp_free_ent > cp->cp_maxcode || cp->cp_clear_flag) { if (cp->cp_clear_flag) { cp->cp_n_bits = cp->cp_init_bits; cp->cp_maxcode = (1 << cp->cp_n_bits) - 1; cp->cp_clear_flag = FALSE; } else { cp->cp_n_bits++; if (cp->cp_n_bits == MAX_LWZ_BITS) cp->cp_maxcode = 1 << MAX_LWZ_BITS; else cp->cp_maxcode = (1 << cp->cp_n_bits) - 1; } } return (TRUE); } /* * Reset code table. */ static void clear_hash ( long int *tab ) { int i; for (i=0; i= 0) { /* Occupied slot */ int disp = HSIZE - i; /* Secondary hash */ if (i == 0) disp = 1; do { i -= disp; if (i < 0) i += HSIZE; if (htab[i] == fcode) break; } while (htab[i] > 0); if (htab[i] == fcode) { ent = codetab[i]; continue; } } if (!output_code (ent, &ab, &p, &cp, fp)) return (FALSE); ent = c; if (cp.cp_free_ent < clear_code_index) { codetab[i] = cp.cp_free_ent++; /* Add code to hashtable */ htab[i] = fcode; } else { /* Clear the hashtable */ clear_hash (htab); cp.cp_free_ent = clear_code + 2; cp.cp_clear_flag = TRUE; if (!output_code (clear_code, &ab, &p, &cp, fp)) return (FALSE); } } /* Put out the final code */ if (!output_code (ent, &ab, &p, &cp, fp)) return (FALSE); if (use_end_code && !output_code (eof_code, &ab, &p, &cp, fp)) return (FALSE); /* At EOF, write the rest of the buffer */ while (ab.ab_num_bits > 0) { if (!packet_write_char (&p, block_size, ab.ab_accum & 0xff, fp)) return (FALSE); ab.ab_accum >>= 8; ab.ab_num_bits -= 8; } if (!packet_flush (&p, fp)) return (FALSE); return (TRUE); } /* * Compress and save the image. */ static BOOL save_image ( const unsigned char *image, int bpp, long int size, FILE *fp ) { unsigned char init_code_size; if (bpp < 2) init_code_size = 2; else init_code_size = bpp; if (fputc (init_code_size, fp) < 0) { perror (NULL); return (FALSE); } if (!save_compressed_image (init_code_size + 1, image, size, fp)) return (FALSE); /* Write out a zero-length packet (to end the series) */ if (fputc (0, fp) < 0) { perror (NULL); return (FALSE); } return (TRUE); } /* * Filter a GIF image. * This involves expanding it, re-mapping the colours, then compressing it. */ static BOOL filter_image ( const GIFINFO *gi, const int *imap, FILE *infp, FILE *outfp ) { unsigned char buf[9], *image; BOOL local_cmap; int width, height; long int i, size; int bpp = gi->gi_bits_per_pixel; if (fread (buf, sizeof (char), 9, infp) != 9) { fprintf (stderr, "Error: could not read image header.\n"); return (FALSE); } if (fwrite (buf, sizeof (char), 9, outfp) != 9) { perror (NULL); return (FALSE); } local_cmap = ((buf[8] & 0x80) != 0); if (local_cmap) { int n; unsigned char cmbuf[768]; bpp = (buf[8] & 7) + 1; n = 1 << bpp; if (fread (cmbuf, sizeof (char), n * 3, infp) != n * 3) { fprintf (stderr, "Error: could not read local colourmap.\n"); return (FALSE); } if (fwrite (cmbuf, sizeof (char), n * 3, outfp) != n * 3) { perror (NULL); return (FALSE); } } width = buf[4] | (buf[5] << 8); height = buf[6] | (buf[7] << 8); if (width == 0 || height == 0) { fprintf (stderr, "Error: illegal image dimensions (%d x %d).\n", width, height); return (FALSE); } size = width * height; if ((image = (unsigned char *) malloc (size)) == NULL) { fprintf (stderr, "Error: memory allocation failure.\n"); return (FALSE); } if (!load_image (image, size, infp)) { free (image); return (FALSE); } if (!local_cmap) { for (i=0; ir == v2->r && v1->g == v2->g && v1->b == v2->b); } /* * Save a GIF image, filtering the image from the input file. */ BOOL gif_filter_save ( const GIFINFO *gi, FILE *infp, FILE *outfp ) { int i, n = gi->gi_num_colours; int cidx[256]; unsigned char buf[768]; /* Create a cross-mapping table for the colours */ for (i=0; igi_orig_colours[i]; int j; for (j=0; jgi_colours[j])) { cidx[i] = j; break; } } /* Write the header and new colourmap */ for (i=0; i<13; i++) buf[i] = gi->gi_header[i]; buf[11] = cidx[buf[11]]; /* Re-map the background */ if (fwrite (buf, sizeof (char), 13, outfp) != 13) { perror (NULL); return (FALSE); } for (i=0; igi_colours[i].r; buf[i*3 + 1] = gi->gi_colours[i].g; buf[i*3 + 2] = gi->gi_colours[i].b; } if (fwrite (buf, sizeof (char), n * 3, outfp) != n * 3) { perror (NULL); return (FALSE); } /* Filter through the image data */ for (;;) { if (fread (buf, sizeof (char), 1, infp) != 1) { fprintf (stderr, "Error: could not read image data.\n"); return (FALSE); } if (fwrite (buf, sizeof (char), 1, outfp) != 1) { perror (NULL); return (FALSE); } if (buf[0] == ';') break; switch (buf[0]) { case '!': if (!filter_extension (cidx, infp, outfp)) return (FALSE); break; case ',': if (!filter_image (gi, cidx, infp, outfp)) return (FALSE); break; default: fprintf (stderr, "Error: unknown start character 0x%02x\n", (int) buf[0]); return (FALSE); } } return (TRUE); } gifshuffle/gif.h0100644000076400007640000000102306476513366013157 0ustar mkwanmkwan/* * GIF routines. * * Written by Matthew Kwan - January 1998 */ #ifndef _GIF_H #define _GIF_H #include typedef struct { unsigned char r; unsigned char g; unsigned char b; } RGB; typedef struct { int gi_bits_per_pixel; int gi_num_colours; RGB gi_colours[256]; RGB gi_orig_colours[256]; unsigned char gi_header[13]; } GIFINFO; /* * Define external functions. */ extern BOOL gif_header_load (GIFINFO *gi, FILE *fp); extern BOOL gif_filter_save (const GIFINFO *gi, FILE *infp, FILE *outfp); #endif gifshuffle/gifshuffle.10100644000076400007640000001403607606203070014435 0ustar mkwanmkwan.TH gifshuffle 1 "5 Jan 2003" "Version 2.0" .SH NAME gifshuffle \- GIF colourmap steganography program .SH SYNOPSIS .B gifshuffle [ .B -CQS1 ] [ .B -p .I passwd ] [ .B -f .I file | .B -m .I message ] [ .I infile.gif [ .I outfile.gif ]] .SH DESCRIPTION \fBgifshuffle\fP is a program for concealing messages in GIF images by shuffling the colourmap. A shuffled image is visibly indistinguishable from the original. \fBgifshuffle\fP works with all GIF images, including those with transparency and animation. .PP Consider a pack of 52 cards. There are 52 factorial ways to sort the pack, which means that any particular ordering of the cards can represent a number in the range [0, 52!-1]. In other words, given \fIn\fP cards, you can store approximately log2(n!) bits of information based on their ordering. .PP GIF images contain a colourmap with up to 256 entries, resulting in a maximum storage capacity of 1675 bits. The image itself consists of a compressed array of indices into this colourmap. To conceal a message within a GIF image the following steps take place. .IP 1. Start with the message you want to conceal, specified on the command line or in a file. Optionally compress and/or encrypt this message. You are then left with a sequence of \fI1\fP's and \fI0\fP's. .IP 2. Prepend a \fI1\fP to this sequence, giving you a binary number \fIm\fP (probably quite large). .IP 3. Next take a look at the GIF image that you want to conceal the message in. Count the number of unique colours in the image, and call the value \fIn\fP. If \fIm\fP > \fIn\fP!-1 then the message is too large, and the procedure will be aborted. .IP 4. The colours in the colourmap are first sorted into their "natural" order (except when encryption is being used - see details below). Each RGB colour is assigned the value (red * 65536 + green * 256 + blue), and the colours are sorted according to these values. Any duplicate colours are removed. .IP 5. Iterate \fIi\fP through the values 1 .. \fIn\fP. Each colour \fIi\fP-1 is allocated a target position (\fIm\fP mod \fIi\fP), then \fIm\fP is divided by \fIi\fP. .IP 6. Each colour is then in turn inserted into a new colourmap at its target position. Colours previously occupying the target position and above are moved up one place. .IP 7. If the size of the colourmap is greater than the number of unique colours, then the colourmap will be padded with the last colour from the original colourmap. .IP 8. The image component of the GIF is then uncompressed, the colour indices are re-mapped to the new colourmap, and the image is re-compressed. For animated GIFs this is repeated for each image. .PP Extracting a hidden message follows a similar procedure, but in reverse. The ordering of the colourmap is used to construct a binary number, which is then optionally decrypted and uncompressed before being output. .PP \fBgifshuffle\fP provides rudimentary compression, using Huffman tables optimised for English text. However, if the data is not text, or if there is a lot of data, the use of the built-in compression is not recommended, since an external compression program such as \fBcompress\fP or \fBgzip\fP will do a much better job. .PP Encryption is also provided, using the ICE encryption algorithm in 1-bit cipher-feedback (CFB) mode to encrypt the data (after compression). Because of ICE's arbitrary key size, passwords of any length up to 1170 characters are supported (since only 7 bits of each character are used, this means keys up to 1024-bytes are supported). .PP As of \fBgifshuffle 2.0\fP encryption is also applied to the ordering of the colours in the colourmap. Instead of using their "natural" ordering, the ordering of their encrypted hash is used. This has the benefit of making colourmaps look random even when there is only a small message concealed. To disable this feature (which is incompatible with version 1 of \fBgifshuffle\fP) use the \fB-1\fP option. .PP If a message string or message file are specified on the command-line, \fBgifshuffle\fP will attempt to conceal the message in the file \fIinfile.gif\fP if specified, or standard input otherwise. The resulting file will be written to \fIoutfile.gif\fP if specified, or standard output if not. .PP If no message string is provided, \fBgifshuffle\fP attempts to extract a message from the input file. The result is written to the output file or standard output. .SH OPTIONS .TP .B -C Compress the data if concealing, or uncompress it if extracting. .TP .B -Q Quiet mode. If not set, the program reports statistics such as compression percentages and amount of available storage space used. .TP .B -S Report on the amount of space available for hidden message in the GIF colourmap. This is calculated from the number of unique colours in the image. .TP .B -1 Retain compatibility with version 1 of \fBgifshuffle\fP by ordering colours using their "natural" ordering, rather than their encrypted ordering. This is only relevant if a password is specified. .TP \fB-p\fP \fIpassword\fP If this is set, the data will be encrypted with this password during concealment, or decrypted during extraction. .TP \fB-f\fP \fImessage-file\fP The contents of this file will be concealed in the input GIF image. .TP \fB-m\fP \fImessage-string\fP The contents of this string will be concealed in the input GIF image. Note that, unless a newline is somehow included in the string, a newline will not be printed when the message is extracted. .SH EXAMPLES The following command will conceal the message "Meet me at 6" in the file \fIinfile.gif\fP, with compression, and encrypted with the password "hello world". The resulting text will be stored in \fIoutfile.gif\fP. .PP .RS \fBgifshuffle -C -m "Meet me at 6" -p "hello world" infile.gif outfile.gif\fP .RE .PP To extract the message, the command would be .PP .RS \fBgifshuffle -C -p "hello world" outfile.gif\fP .RE .PP Note that the resulting message will not be terminated by a newline. .PP The storage capacity of a file can be determined with the \fB-S\fP option. .PP .RS \fBgifshuffle -S infile.gif\fP .RE .SH AUTHOR This application was written by Matthew Kwan, who can be reached at mkwan@darkside.com.au gifshuffle/gifshuf.h0100644000076400007640000000261707605535653014055 0ustar mkwanmkwan/* * Header file for the gifshuffle steganography program. * * Written by Matthew Kwan - January 1998 */ #ifndef _GIFSHUF_H #define _GIFSHUF_H #include /* * Define boolean types. */ typedef int BOOL; #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif /* * Define global variables. */ extern BOOL compress_flag; extern BOOL quiet_flag; extern BOOL v1_flag; /* * Define external functions. */ extern void password_set (const char *passwd); extern BOOL encrypting_colourmap (); extern void encrypt_colour (unsigned char r, unsigned char g, unsigned char b, unsigned char *ctext); extern BOOL message_extract (FILE *inf, FILE *outf); extern void space_calculate (FILE *inf); extern void compress_init (void); extern BOOL compress_bit (int bit, FILE *inf, FILE *outf); extern BOOL compress_flush (FILE *inf, FILE *outf); extern void uncompress_init (void); extern BOOL uncompress_bit (int bit, FILE *outf); extern BOOL uncompress_flush (FILE *outf); extern void encrypt_init (void); extern BOOL encrypt_bit (int bit, FILE *inf, FILE *outf); extern BOOL encrypt_flush (FILE *inf, FILE *outf); extern void decrypt_init (void); extern BOOL decrypt_bit (int bit, FILE *outf); extern BOOL decrypt_flush (FILE *outf); extern void encode_init (void); extern BOOL encode_bit (int bit, FILE *inf, FILE *outf); extern BOOL encode_flush (FILE *inf, FILE *outf); #endif gifshuffle/huffcode.h0100644000076400007640000002320606476507603014201 0ustar mkwanmkwan"010011101110011001000", /* 0 - 0.00% */ "010011101110011001001", /* 1 - 0.00% */ "010011101110011001010", /* 2 - 0.00% */ "010011101110011001011", /* 3 - 0.00% */ "010011101110011001100", /* 4 - 0.00% */ "010011101110011001101", /* 5 - 0.00% */ "010011101110011001110", /* 6 - 0.00% */ "010011101110011001111", /* 7 - 0.00% */ "101100010101", /* 8 - 0.03% */ "0100100", /* 9 - 0.62% */ "101101", /* 10 - 1.93% */ "010011101110011010000", /* 11 - 0.00% */ "0100111011100111", /* 12 - 0.00% */ "010011101110011010001", /* 13 - 0.00% */ "010011101110011010010", /* 14 - 0.00% */ "010011101110011010011", /* 15 - 0.00% */ "010011101110011010100", /* 16 - 0.00% */ "010011101110011010101", /* 17 - 0.00% */ "010011101110011010110", /* 18 - 0.00% */ "010011101110011010111", /* 19 - 0.00% */ "010011101110011011000", /* 20 - 0.00% */ "010011101110011011001", /* 21 - 0.00% */ "010011101110011011010", /* 22 - 0.00% */ "010011101110011011011", /* 23 - 0.00% */ "010011101110011011100", /* 24 - 0.00% */ "010011101110011011101", /* 25 - 0.00% */ "010011101110011011110", /* 26 - 0.00% */ "010011101110001", /* 27 - 0.00% */ "010011101110011011111", /* 28 - 0.00% */ "01001110111000000000", /* 29 - 0.00% */ "01001110111000000001", /* 30 - 0.00% */ "01001110111000000010", /* 31 - 0.00% */ "111", /* ' ' - 17.61% */ "0100101000", /* '!' - 0.08% */ "101100100", /* '"' - 0.23% */ "10111111111", /* '#' - 0.07% */ "101111010010", /* '$' - 0.03% */ "1011000101000", /* '%' - 0.01% */ "0010100010101", /* '&' - 0.01% */ "00101011", /* ''' - 0.31% */ "101111110", /* '(' - 0.26% */ "00100011", /* ')' - 0.28% */ "010010101", /* '*' - 0.16% */ "101111010011", /* '+' - 0.03% */ "1010110", /* ',' - 0.80% */ "10111110", /* '-' - 0.49% */ "101000", /* '.' - 1.51% */ "101111001", /* '/' - 0.24% */ "0010000", /* '0' - 0.53% */ "01001011", /* '1' - 0.32% */ "101100101", /* '2' - 0.23% */ "001010101", /* '3' - 0.15% */ "001010011", /* '4' - 0.15% */ "1011110111", /* '5' - 0.12% */ "1011001100", /* '6' - 0.11% */ "0100101001", /* '7' - 0.08% */ "1010011001", /* '8' - 0.10% */ "001010000", /* '9' - 0.14% */ "101111000", /* ':' - 0.24% */ "10111111110", /* ';' - 0.07% */ "01001110110", /* '<' - 0.04% */ "10100101010", /* '=' - 0.04% */ "10111101000", /* '>' - 0.06% */ "1010010100", /* '?' - 0.09% */ "0010100011", /* '@' - 0.07% */ "01001111", /* 'A' - 0.34% */ "1011110110", /* 'B' - 0.12% */ "101100011", /* 'C' - 0.22% */ "101001101", /* 'D' - 0.20% */ "00100010", /* 'E' - 0.27% */ "001010010", /* 'F' - 0.14% */ "1011000000", /* 'G' - 0.10% */ "1011001101", /* 'H' - 0.12% */ "0111000", /* 'I' - 0.68% */ "10110000011", /* 'J' - 0.05% */ "10110001011", /* 'K' - 0.06% */ "001010100", /* 'L' - 0.15% */ "101100111", /* 'M' - 0.23% */ "101001011", /* 'N' - 0.19% */ "101100001", /* 'O' - 0.22% */ "010011100", /* 'P' - 0.16% */ "1010011110001", /* 'Q' - 0.01% */ "101001110", /* 'R' - 0.20% */ "10100100", /* 'S' - 0.35% */ "10101110", /* 'T' - 0.41% */ "1011110101", /* 'U' - 0.12% */ "10100111101", /* 'V' - 0.05% */ "1011000100", /* 'W' - 0.11% */ "10110000010", /* 'X' - 0.05% */ "0100111010", /* 'Y' - 0.08% */ "010011101111", /* 'Z' - 0.02% */ "101001111001", /* '[' - 0.03% */ "001010001011", /* '\' - 0.02% */ "101001010111", /* ']' - 0.03% */ "1011000101001", /* '^' - 0.01% */ "10111111100", /* '_' - 0.06% */ "00101000100", /* '`' - 0.03% */ "0101", /* 'a' - 5.17% */ "001001", /* 'b' - 1.16% */ "110110", /* 'c' - 2.14% */ "01000", /* 'd' - 2.46% */ "1100", /* 'e' - 8.17% */ "101010", /* 'f' - 1.58% */ "011101", /* 'g' - 1.42% */ "10001", /* 'h' - 2.92% */ "0011", /* 'i' - 4.84% */ "1010011111", /* 'j' - 0.10% */ "0100110", /* 'k' - 0.67% */ "01111", /* 'l' - 2.90% */ "101110", /* 'm' - 1.94% */ "0001", /* 'n' - 4.66% */ "0110", /* 'o' - 5.51% */ "100001", /* 'p' - 1.47% */ "10111111101", /* 'q' - 0.07% */ "11010", /* 'r' - 4.10% */ "0000", /* 's' - 4.36% */ "1001", /* 't' - 6.15% */ "110111", /* 'u' - 2.21% */ "0111001", /* 'v' - 0.70% */ "001011", /* 'w' - 1.25% */ "10101111", /* 'x' - 0.42% */ "100000", /* 'y' - 1.43% */ "1010011000", /* 'z' - 0.10% */ "1010011110000", /* '{' - 0.01% */ "101001010110", /* '|' - 0.02% */ "0100111011101", /* '}' - 0.01% */ "0010100010100", /* '~' - 0.01% */ "01001110111000000011", /* 127 - 0.00% */ "010011101110000001000", /* 128 - 0.00% */ "010011101110000001001", /* 129 - 0.00% */ "010011101110000001010", /* 130 - 0.00% */ "010011101110000001011", /* 131 - 0.00% */ "010011101110000001100", /* 132 - 0.00% */ "010011101110000001101", /* 133 - 0.00% */ "010011101110000001110", /* 134 - 0.00% */ "010011101110000001111", /* 135 - 0.00% */ "010011101110000010000", /* 136 - 0.00% */ "010011101110000010001", /* 137 - 0.00% */ "010011101110000010010", /* 138 - 0.00% */ "010011101110000010011", /* 139 - 0.00% */ "010011101110000010100", /* 140 - 0.00% */ "010011101110000010101", /* 141 - 0.00% */ "010011101110000010110", /* 142 - 0.00% */ "010011101110000010111", /* 143 - 0.00% */ "010011101110000011000", /* 144 - 0.00% */ "010011101110000011001", /* 145 - 0.00% */ "010011101110000011010", /* 146 - 0.00% */ "010011101110000011011", /* 147 - 0.00% */ "010011101110000011100", /* 148 - 0.00% */ "010011101110000011101", /* 149 - 0.00% */ "010011101110000011110", /* 150 - 0.00% */ "010011101110000011111", /* 151 - 0.00% */ "010011101110000100000", /* 152 - 0.00% */ "010011101110000100001", /* 153 - 0.00% */ "010011101110000100010", /* 154 - 0.00% */ "010011101110000100011", /* 155 - 0.00% */ "010011101110000100100", /* 156 - 0.00% */ "010011101110000100101", /* 157 - 0.00% */ "010011101110000100110", /* 158 - 0.00% */ "010011101110000100111", /* 159 - 0.00% */ "010011101110000101000", /* 160 - 0.00% */ "010011101110000101001", /* 161 - 0.00% */ "010011101110000101010", /* 162 - 0.00% */ "010011101110000101011", /* 163 - 0.00% */ "010011101110000101100", /* 164 - 0.00% */ "010011101110000101101", /* 165 - 0.00% */ "010011101110000101110", /* 166 - 0.00% */ "010011101110000101111", /* 167 - 0.00% */ "010011101110000110000", /* 168 - 0.00% */ "010011101110000110001", /* 169 - 0.00% */ "010011101110000110010", /* 170 - 0.00% */ "010011101110000110011", /* 171 - 0.00% */ "010011101110000110100", /* 172 - 0.00% */ "010011101110000110101", /* 173 - 0.00% */ "010011101110000110110", /* 174 - 0.00% */ "010011101110000110111", /* 175 - 0.00% */ "010011101110000111000", /* 176 - 0.00% */ "010011101110000111001", /* 177 - 0.00% */ "010011101110000111010", /* 178 - 0.00% */ "010011101110000111011", /* 179 - 0.00% */ "010011101110000111100", /* 180 - 0.00% */ "010011101110000111101", /* 181 - 0.00% */ "010011101110000111110", /* 182 - 0.00% */ "010011101110000111111", /* 183 - 0.00% */ "010011101110010000000", /* 184 - 0.00% */ "010011101110010000001", /* 185 - 0.00% */ "010011101110010000010", /* 186 - 0.00% */ "010011101110010000011", /* 187 - 0.00% */ "010011101110010000100", /* 188 - 0.00% */ "010011101110010000101", /* 189 - 0.00% */ "010011101110010000110", /* 190 - 0.00% */ "010011101110010000111", /* 191 - 0.00% */ "010011101110010001000", /* 192 - 0.00% */ "010011101110010001001", /* 193 - 0.00% */ "010011101110010001010", /* 194 - 0.00% */ "010011101110010001011", /* 195 - 0.00% */ "010011101110010001100", /* 196 - 0.00% */ "010011101110010001101", /* 197 - 0.00% */ "010011101110010001110", /* 198 - 0.00% */ "010011101110010001111", /* 199 - 0.00% */ "010011101110010010000", /* 200 - 0.00% */ "010011101110010010001", /* 201 - 0.00% */ "010011101110010010010", /* 202 - 0.00% */ "010011101110010010011", /* 203 - 0.00% */ "010011101110010010100", /* 204 - 0.00% */ "010011101110010010101", /* 205 - 0.00% */ "010011101110010010110", /* 206 - 0.00% */ "010011101110010010111", /* 207 - 0.00% */ "010011101110010011000", /* 208 - 0.00% */ "010011101110010011001", /* 209 - 0.00% */ "010011101110010011010", /* 210 - 0.00% */ "010011101110010011011", /* 211 - 0.00% */ "010011101110010011100", /* 212 - 0.00% */ "010011101110010011101", /* 213 - 0.00% */ "010011101110010011110", /* 214 - 0.00% */ "010011101110010011111", /* 215 - 0.00% */ "010011101110010100000", /* 216 - 0.00% */ "010011101110010100001", /* 217 - 0.00% */ "010011101110010100010", /* 218 - 0.00% */ "010011101110010100011", /* 219 - 0.00% */ "010011101110010100100", /* 220 - 0.00% */ "010011101110010100101", /* 221 - 0.00% */ "010011101110010100110", /* 222 - 0.00% */ "010011101110010100111", /* 223 - 0.00% */ "010011101110010101000", /* 224 - 0.00% */ "010011101110010101001", /* 225 - 0.00% */ "010011101110010101010", /* 226 - 0.00% */ "010011101110010101011", /* 227 - 0.00% */ "010011101110010101100", /* 228 - 0.00% */ "010011101110010101101", /* 229 - 0.00% */ "010011101110010101110", /* 230 - 0.00% */ "010011101110010101111", /* 231 - 0.00% */ "010011101110010110000", /* 232 - 0.00% */ "010011101110010110001", /* 233 - 0.00% */ "010011101110010110010", /* 234 - 0.00% */ "010011101110010110011", /* 235 - 0.00% */ "010011101110010110100", /* 236 - 0.00% */ "010011101110010110101", /* 237 - 0.00% */ "010011101110010110110", /* 238 - 0.00% */ "010011101110010110111", /* 239 - 0.00% */ "010011101110010111000", /* 240 - 0.00% */ "010011101110010111001", /* 241 - 0.00% */ "010011101110010111010", /* 242 - 0.00% */ "010011101110010111011", /* 243 - 0.00% */ "010011101110010111100", /* 244 - 0.00% */ "010011101110010111101", /* 245 - 0.00% */ "010011101110010111110", /* 246 - 0.00% */ "010011101110010111111", /* 247 - 0.00% */ "010011101110011000000", /* 248 - 0.00% */ "010011101110011000001", /* 249 - 0.00% */ "010011101110011000010", /* 250 - 0.00% */ "010011101110011000011", /* 251 - 0.00% */ "010011101110011000100", /* 252 - 0.00% */ "010011101110011000101", /* 253 - 0.00% */ "010011101110011000110", /* 254 - 0.00% */ "010011101110011000111" /* 255 - 0.00% */ gifshuffle/ice.c0100644000076400007640000001701506625725411013145 0ustar mkwanmkwan/* * Implementation of the ICE encryption algorithm. * * Written by Matthew Kwan - July 1996 */ #include "ice.h" #include #include /* Structure of a single round subkey */ typedef unsigned long ICE_SUBKEY[3]; /* Internal structure of the ICE_KEY structure */ struct ice_key_struct { int ik_size; int ik_rounds; ICE_SUBKEY *ik_keysched; }; /* The S-boxes */ static unsigned long ice_sbox[4][1024]; static int ice_sboxes_initialised = 0; /* Modulo values for the S-boxes */ static const int ice_smod[4][4] = { {333, 313, 505, 369}, {379, 375, 319, 391}, {361, 445, 451, 397}, {397, 425, 395, 505}}; /* XOR values for the S-boxes */ static const int ice_sxor[4][4] = { {0x83, 0x85, 0x9b, 0xcd}, {0xcc, 0xa7, 0xad, 0x41}, {0x4b, 0x2e, 0xd4, 0x33}, {0xea, 0xcb, 0x2e, 0x04}}; /* Expanded permutation values for the P-box */ static const unsigned long ice_pbox[32] = { 0x00000001, 0x00000080, 0x00000400, 0x00002000, 0x00080000, 0x00200000, 0x01000000, 0x40000000, 0x00000008, 0x00000020, 0x00000100, 0x00004000, 0x00010000, 0x00800000, 0x04000000, 0x20000000, 0x00000004, 0x00000010, 0x00000200, 0x00008000, 0x00020000, 0x00400000, 0x08000000, 0x10000000, 0x00000002, 0x00000040, 0x00000800, 0x00001000, 0x00040000, 0x00100000, 0x02000000, 0x80000000}; /* The key rotation schedule */ static const int ice_keyrot[16] = { 0, 1, 2, 3, 2, 1, 3, 0, 1, 3, 2, 0, 3, 1, 0, 2}; /* * Galois Field multiplication of a by b, modulo m. * Just like arithmetic multiplication, except that additions and * subtractions are replaced by XOR. */ static unsigned int gf_mult ( register unsigned int a, register unsigned int b, register unsigned int m ) { register unsigned int res = 0; while (b) { if (b & 1) res ^= a; a <<= 1; b >>= 1; if (a >= 256) a ^= m; } return (res); } /* * Galois Field exponentiation. * Raise the base to the power of 7, modulo m. */ static unsigned long gf_exp7 ( register unsigned int b, unsigned int m ) { register unsigned int x; if (b == 0) return (0); x = gf_mult (b, b, m); x = gf_mult (b, x, m); x = gf_mult (x, x, m); return (gf_mult (b, x, m)); } /* * Carry out the ICE 32-bit P-box permutation. */ static unsigned long ice_perm32 ( register unsigned long x ) { register unsigned long res = 0; register const unsigned long *pbox = ice_pbox; while (x) { if (x & 1) res |= *pbox; pbox++; x >>= 1; } return (res); } /* * Initialise the ICE S-boxes. * This only has to be done once. */ static void ice_sboxes_init (void) { register int i; for (i=0; i<1024; i++) { int col = (i >> 1) & 0xff; int row = (i & 0x1) | ((i & 0x200) >> 8); unsigned long x; x = gf_exp7 (col ^ ice_sxor[0][row], ice_smod[0][row]) << 24; ice_sbox[0][i] = ice_perm32 (x); x = gf_exp7 (col ^ ice_sxor[1][row], ice_smod[1][row]) << 16; ice_sbox[1][i] = ice_perm32 (x); x = gf_exp7 (col ^ ice_sxor[2][row], ice_smod[2][row]) << 8; ice_sbox[2][i] = ice_perm32 (x); x = gf_exp7 (col ^ ice_sxor[3][row], ice_smod[3][row]); ice_sbox[3][i] = ice_perm32 (x); } } /* * Create a new ICE key. */ ICE_KEY * ice_key_create ( int n ) { ICE_KEY *ik; if (!ice_sboxes_initialised) { ice_sboxes_init (); ice_sboxes_initialised = 1; } if ((ik = (ICE_KEY *) malloc (sizeof (ICE_KEY))) == NULL) return (NULL); if (n < 1) { ik->ik_size = 1; ik->ik_rounds = 8; } else { ik->ik_size = n; ik->ik_rounds = n * 16; } if ((ik->ik_keysched = (ICE_SUBKEY *) malloc (ik->ik_rounds * sizeof (ICE_SUBKEY))) == NULL) { free (ik); return (NULL); } return (ik); } /* * Destroy an ICE key. * Zero out the memory to prevent snooping. */ void ice_key_destroy ( ICE_KEY *ik ) { int i, j; if (ik == NULL) return; for (i=0; iik_rounds; i++) for (j=0; j<3; j++) ik->ik_keysched[i][j] = 0; ik->ik_rounds = ik->ik_size = 0; if (ik->ik_keysched != NULL) free (ik->ik_keysched); free (ik); } /* * The single round ICE f function. */ static unsigned long ice_f ( register unsigned long p, const ICE_SUBKEY sk ) { unsigned long tl, tr; /* Expanded 40-bit values */ unsigned long al, ar; /* Salted expanded 40-bit values */ /* Left half expansion */ tl = ((p >> 16) & 0x3ff) | (((p >> 14) | (p << 18)) & 0xffc00); /* Right half expansion */ tr = (p & 0x3ff) | ((p << 2) & 0xffc00); /* Perform the salt permutation */ /* al = (tr & sk[2]) | (tl & ~sk[2]); */ /* ar = (tl & sk[2]) | (tr & ~sk[2]); */ al = sk[2] & (tl ^ tr); ar = al ^ tr; al ^= tl; al ^= sk[0]; /* XOR with the subkey */ ar ^= sk[1]; /* S-box lookup and permutation */ return (ice_sbox[0][al >> 10] | ice_sbox[1][al & 0x3ff] | ice_sbox[2][ar >> 10] | ice_sbox[3][ar & 0x3ff]); } /* * Encrypt a block of 8 bytes of data with the given ICE key. */ void ice_key_encrypt ( const ICE_KEY *ik, const unsigned char *ptext, unsigned char *ctext ) { register int i; register unsigned long l, r; l = (((unsigned long) ptext[0]) << 24) | (((unsigned long) ptext[1]) << 16) | (((unsigned long) ptext[2]) << 8) | ptext[3]; r = (((unsigned long) ptext[4]) << 24) | (((unsigned long) ptext[5]) << 16) | (((unsigned long) ptext[6]) << 8) | ptext[7]; for (i = 0; i < ik->ik_rounds; i += 2) { l ^= ice_f (r, ik->ik_keysched[i]); r ^= ice_f (l, ik->ik_keysched[i + 1]); } for (i = 0; i < 4; i++) { ctext[3 - i] = r & 0xff; ctext[7 - i] = l & 0xff; r >>= 8; l >>= 8; } } /* * Decrypt a block of 8 bytes of data with the given ICE key. */ void ice_key_decrypt ( const ICE_KEY *ik, const unsigned char *ctext, unsigned char *ptext ) { register int i; register unsigned long l, r; l = (((unsigned long) ctext[0]) << 24) | (((unsigned long) ctext[1]) << 16) | (((unsigned long) ctext[2]) << 8) | ctext[3]; r = (((unsigned long) ctext[4]) << 24) | (((unsigned long) ctext[5]) << 16) | (((unsigned long) ctext[6]) << 8) | ctext[7]; for (i = ik->ik_rounds - 1; i > 0; i -= 2) { l ^= ice_f (r, ik->ik_keysched[i]); r ^= ice_f (l, ik->ik_keysched[i - 1]); } for (i = 0; i < 4; i++) { ptext[3 - i] = r & 0xff; ptext[7 - i] = l & 0xff; r >>= 8; l >>= 8; } } /* * Set 8 rounds [n, n+7] of the key schedule of an ICE key. */ static void ice_key_sched_build ( ICE_KEY *ik, unsigned short *kb, int n, const int *keyrot ) { int i; for (i=0; i<8; i++) { register int j; register int kr = keyrot[i]; ICE_SUBKEY *isk = &ik->ik_keysched[n + i]; for (j=0; j<3; j++) (*isk)[j] = 0; for (j=0; j<15; j++) { register int k; unsigned long *curr_sk = &(*isk)[j % 3]; for (k=0; k<4; k++) { unsigned short *curr_kb = &kb[(kr + k) & 3]; register int bit = *curr_kb & 1; *curr_sk = (*curr_sk << 1) | bit; *curr_kb = (*curr_kb >> 1) | ((bit ^ 1) << 15); } } } } /* * Set the key schedule of an ICE key. */ void ice_key_set ( ICE_KEY *ik, const unsigned char *key ) { int i; if (ik->ik_rounds == 8) { unsigned short kb[4]; for (i=0; i<4; i++) kb[3 - i] = (key[i*2] << 8) | key[i*2 + 1]; ice_key_sched_build (ik, kb, 0, ice_keyrot); return; } for (i = 0; i < ik->ik_size; i++) { int j; unsigned short kb[4]; for (j=0; j<4; j++) kb[3 - j] = (key[i*8 + j*2] << 8) | key[i*8 + j*2 + 1]; ice_key_sched_build (ik, kb, i*8, ice_keyrot); ice_key_sched_build (ik, kb, ik->ik_rounds - 8 - i*8, &ice_keyrot[8]); } } gifshuffle/ice.h0100644000076400007640000000114506625723434013152 0ustar mkwanmkwan/* * Header file for the ICE encryption library. * * Written by Matthew Kwan - July 1996 */ #ifndef _ICE_H #define _ICE_H typedef struct ice_key_struct ICE_KEY; #if __STDC__ #define P_(x) x #else #define P_(x) () #endif extern ICE_KEY *ice_key_create P_((int n)); extern void ice_key_destroy P_((ICE_KEY *ik)); extern void ice_key_set P_((ICE_KEY *ik, const unsigned char *k)); extern void ice_key_encrypt P_((const ICE_KEY *ik, const unsigned char *ptxt, unsigned char *ctxt)); extern void ice_key_decrypt P_((const ICE_KEY *ik, const unsigned char *ctxt, unsigned char *ptxt)); #undef P_ #endif gifshuffle/main.c0100644000076400007640000001121407605542610013321 0ustar mkwanmkwan/* * Command-line program for hiding and extracting messages within * the colourmap of GIF images. * * Usage: gifshuffle [-C][-Q][-S][-1][-p passwd] [-f file | -m message] * [infile [outfile]] * * -C : Use compression * -Q : Be quiet * -S : Calculate the space available in the file * -1 : Use the old Gifshuffle 1.0 concealment algorithm * -p : Specify the password to encrypt the message * * -f : Insert the message contained in the file * -m : Insert the message given * * If the program is executed without either of the -f or -m options * then the program will attempt to extract a concealed message. * The output will go to outfile if specified, stdout otherwise. * * Written by Matthew Kwan - January 1998 */ #include "gifshuf.h" /* * Declaration of global variables. */ BOOL compress_flag = FALSE; BOOL quiet_flag = FALSE; BOOL v1_flag = FALSE; /* * Encode a single character. */ static BOOL character_encode ( unsigned char c, FILE *infile, FILE *outfile ) { int i; for (i=0; i<8; i++) { int bit = ((c & (128 >> i)) != 0) ? 1 : 0; if (!compress_bit (bit, infile, outfile)) return (FALSE); } return (TRUE); } /* * Encode a string of characters. */ static BOOL message_string_encode ( const char *msg, FILE *infile, FILE *outfile ) { compress_init (); while (*msg != '\0') { if (!character_encode (*msg, infile, outfile)) return (FALSE); msg++; } return (compress_flush (infile, outfile)); } /* * Encode the contents of a file. */ static BOOL message_fp_encode ( FILE *msg_fp, FILE *infile, FILE *outfile ) { int c; compress_init (); while ((c = fgetc (msg_fp)) != EOF) if (!character_encode (c, infile, outfile)) return (FALSE); if (ferror (msg_fp) != 0) { perror ("Message file"); return (FALSE); } return (compress_flush (infile, outfile)); } /* * Program's starting point. * Processes command-line args and starts things running. */ int main ( int argc, char *argv[] ) { int optind; BOOL errflag = FALSE; BOOL space_flag = FALSE; char *passwd = NULL; char *message_string = NULL; FILE *message_fp = NULL; FILE *infile = stdin; FILE *outfile = stdout; optind = 1; for (optind = 1; optind < argc #ifdef unix && argv[optind][0] == '-'; #else && (argv[optind][0] == '-' || argv[optind][0] == '/'); #endif optind++) { char c = argv[optind][1]; char *optarg; switch (c) { case 'C': compress_flag = TRUE; break; case 'Q': quiet_flag = TRUE; break; case 'S': space_flag = TRUE; break; case '1': v1_flag = TRUE; break; case 'f': if (argv[optind][2] != '\0') optarg = &argv[optind][2]; else if (++optind == argc) { errflag = TRUE; break; } else optarg = argv[optind]; if ((message_fp = fopen (optarg, "rb")) == NULL) { perror (optarg); errflag = TRUE; } break; case 'm': if (argv[optind][2] != '\0') optarg = &argv[optind][2]; else if (++optind == argc) { errflag = TRUE; break; } else optarg = argv[optind]; message_string = optarg; break; case 'p': if (argv[optind][2] != '\0') optarg = &argv[optind][2]; else if (++optind == argc) { errflag = TRUE; break; } else optarg = argv[optind]; passwd = optarg; break; default: fprintf (stderr, "Illegal option '%s'\n", argv[optind]); errflag = TRUE; break; } if (errflag) break; } if (message_string != NULL && message_fp != NULL) { fprintf (stderr, "Cannot specify both message string and file\n"); errflag = TRUE; } if (errflag || optind < argc - 2) { fprintf (stderr, "Usage: %s [-C][-Q][-S][-1] ", argv[0]); fprintf (stderr, "[-p passwd] [-f file | -m message]\n"); fprintf (stderr, "\t\t\t\t\t[infile [outfile]]\n"); return (1); } if (passwd != NULL) password_set (passwd); if (optind < argc) { if ((infile = fopen (argv[optind], "rb")) == NULL) { perror (argv[optind]); return (1); } } if (optind + 1 < argc) { if ((outfile = fopen (argv[optind + 1], "wb")) == NULL) { perror (argv[optind + 1]); return (1); } } if (space_flag) { space_calculate (infile); } else if (message_string != NULL) { if (!message_string_encode (message_string, infile, outfile)) return (1); } else if (message_fp != NULL) { if (!message_fp_encode (message_fp, infile, outfile)) return (1); fclose (message_fp); } else { if (!message_extract (infile, outfile)) return (1); } if (outfile != stdout) fclose (outfile); if (infile != stdout) fclose (infile); return (0); }