diff --git a/CVS/Entries b/CVS/Entries new file mode 100644 index 0000000..48dc61c --- /dev/null +++ b/CVS/Entries @@ -0,0 +1,30 @@ +/STIRMARK-README/1.1.1.1/Mon May 1 19:57:38 2000// +/TODO/1.2/Mon May 1 20:09:05 2000// +/arc.c/1.2/Mon May 1 21:47:58 2000// +/arc.h/1.2/Mon May 1 21:47:58 2000// +/config.h.bot/1.1.1.1/Mon May 1 19:57:38 2000// +/fourier.c/1.2/Mon May 1 21:01:06 2000// +/fourier.h/1.2/Mon May 1 21:01:06 2000// +/golay.c/1.1.1.1/Mon May 1 19:57:38 2000// +/golay.h/1.1.1.1/Mon May 1 19:57:38 2000// +/install-sh/1.1.1.1/Mon May 1 19:57:38 2000// +/jpeg-6b-steg.diff/1.1.1.1/Mon May 1 19:57:38 2000// +/seek_script/1.1.1.1/Mon May 1 19:57:38 2000// +D/jpeg-6b-steg//// +/jpg.h/1.2/Wed Jan 17 23:47:37 2001// +/ChangeLog/1.3/Sat Jan 20 23:14:06 2001// +/README/1.8/Tue Feb 13 00:05:25 2001// +/iterator.c/1.4/Tue Feb 13 00:29:48 2001// +/iterator.h/1.4/Tue Feb 13 00:30:08 2001// +/jpg.c/1.9/Tue Feb 13 00:29:07 2001// +/outguess.h/1.11/Tue Feb 13 00:29:19 2001// +/pnm.c/1.4/Tue Feb 13 00:29:33 2001// +/pnm.h/1.3/Tue Feb 13 00:29:28 2001// +/outguess.1/1.7/Tue Feb 13 00:42:15 2001// +D/missing//// +/config.h.in/1.3/Tue Feb 13 19:26:18 2001// +/histogram.c/1.2/Tue Feb 13 19:57:38 2001// +/outguess.c/1.16/Tue Feb 13 19:19:23 2001// +/Makefile.in/1.5/Tue Feb 13 21:03:31 2001// +/configure/1.4/Tue Feb 13 21:05:08 2001// +/configure.in/1.4/Tue Feb 13 21:05:02 2001// diff --git a/CVS/Repository b/CVS/Repository new file mode 100644 index 0000000..c5006fe --- /dev/null +++ b/CVS/Repository @@ -0,0 +1 @@ +outguess diff --git a/CVS/Root b/CVS/Root new file mode 100644 index 0000000..bd2f612 --- /dev/null +++ b/CVS/Root @@ -0,0 +1 @@ +provos@monkey.org:/cvs diff --git a/ChangeLog b/ChangeLog index 0a9f311..7bf5437 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +OutGuess 0.2 +-------------- +2000-01-20 - Niels Provos + - Use statistical corrections to defend against steganalysis. + +2000-04-01 - Niels Provos + - A lot of cleanup. + - Use all DCT coefficients for JPG now. This version is not any more + compatible with the previous versions. + OutGuess 0.13b -------------- 1999-08-06 - Niels Provos diff --git a/Makefile.in b/Makefile.in index c3e9111..c911a5a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -27,12 +27,22 @@ JPEGDEP = $(JPEGDIR)/libjpeg.a # CFLAGS += -DFOURIER -I/usr/local/include # LIBS += -L/usr/local/lib -lrfftw -lfftw -OBJ = outguess.o golay.o arc.o pnm.o jpg.o @MD5OBJ@ +MISSING = @LIBMISSING@ +OBJ = outguess.o golay.o arc.o pnm.o jpg.o iterator.o -all: outguess +all: outguess extract histogram -outguess: $(JPEGDEP) $(OBJ) - $(CC) $(CFLAGS) $(INCS) -o $@ $(OBJ) $(LDFLAGS) $(LIBS) +$(MISSING): + $(CC) $(CFLAGS) $(INCS) -c @MISSINGOBJ@ + +outguess: $(JPEGDEP) $(OBJ) $(MISSING) + $(CC) $(CFLAGS) $(INCS) -o $@ $(OBJ) $(MISSING) $(LDFLAGS) $(LIBS) + +histogram: histogram.o $(MISSING) + $(CC) $(CFLAGS) $(INCS) -o $@ histogram.o $(MISSING) + +extract: outguess + ln -sf outguess $@ $(JPEGDEP): cd $(JPEGDIR); $(MAKE) libjpeg.a @@ -42,7 +52,7 @@ install: all $(INSTALL_DATA) outguess.1 $(install_prefix)$(mandir) clean: - rm -f outguess *~ $(OBJ) + rm -f outguess extract histogram histogram.o *~ $(OBJ) $(MISSING) distclean: clean cd $(JPEGDIR); $(MAKE) $@ diff --git a/README b/README index 65e8bce..4ffe5a5 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ -OutGuess 0.13b +OutGuess 0.2 -------------- -1999-08-06 - Niels Provos +2001-02-12 - Niels Provos OutGuess is a universal steganographic tool that allows the insertion of hidden information into the redundant bits of data sources. The @@ -11,24 +11,29 @@ and JPEG image formats are supported. In the next paragraphs, images will be used as concrete example of data objects, though OutGuess can use any kind of data, as long as a handler is provided. -OutGuess is available under the BSD software license. Please see each -source file for its respective license. OutGuess was written in -Germany, and CONTAINS CRYPTOGRAPHIC FUNCTIONS. - -Steganography is the art of hiding the fact that communication is -happening. Classical steganography systems depend on keeping the -encoding system secret, but modern steganography should only be -detectable if a secret information, e.g. a secret key is known. But -because of their invasive nature they leave detectable traces within -an image's characteristics, e.g. its Fourier signature, and hence -allow an eavesdropper to detect images which have been modified, and -thus giving away the fact that secret communication is happening. The -secret of the information is not degraded, its hidden nature is -revealed, defeating the whole purpose of Steganography. - -A more processor- and space-intensive alternative is to match existing -images against the source data in order to minimize the number of -modifications required to express the original message. +OutGuess is available under the BSD software license. It is +completely free for any use including commercial. + +Please see each source file for its respective license. OutGuess was +developed in Germany. + +Steganography is the art and sience of hiding that communication is +happening. Classical steganography systems depend on keeping the +encoding system secret, but modern steganography are detectable only +if secret information is known, e.g. a secret key. Because of their +invasive nature steganography systems leave detectable traces within a +medium's characteristics. This allows an eavesdropper to detect media +that has been modified, revealing that secret communication is taking +place. Although the secrecy of the information is not degraded, its +hidden nature is revealed, defeating the main purpose of +Steganography. + +For JPEG images, OutGuess preserves statistics based on frequency +counts. As a result, no known statistical test is able to detect +the presence of steganographic content. Before embedding data +into an image, the OutGuess system can determine the maximum +message size that can be hidden while still being able to maintain +statistics based on frequency counts. OutGuess uses a generic iterator object to select which bits in the data should be modified. A seed can be used to modify the behavior @@ -37,110 +42,43 @@ message. By altering the seed, OutGuess tries to find a sequence of bits that minimizes the number of changes in the data that have to be made. -A bias is introduced that favors the modification of bits that were -extracted from a high value, and tries to avoid the modification of -bits that were extracted from a low value. - A sample output from OutGuess is as follows: -Reading .... -Extracting usable bits ... -JPEG compression quality set to 91 -Encoded data: 1178 +Reading dscf0001.jpg.... +JPEG compression quality set to 75 +Extracting usable bits: 40059 bits +Correctable message size: 21194 bits, 52.91% +Encoded 'snark.bz2': 14712 bits, 1839 bytes Finding best embedding... -New best: 0: 4709(49.8%), bias 4332(0.92), saved: 3 -New best: 5: 4700(49.7%), bias 4290(0.91), saved: 12 -New best: 7: 4663(49.3%), bias 4217(0.90), saved: 49 -New best: 17: 4644(49.1%), bias 4188(0.90), saved: 68 -New best: 121: 4660(49.3%), bias 4169(0.89), saved: 52 -121, 8829: Embedding data: 9424 in 81406 -Bits embedded: 9456, changed: 4660(49.3%), bias: 4169, tot: 81320, skip: 71864 -Total bits changed: 8829 (changed 4660 + bias 4169) + 0: 7467(50.6%)[50.8%], bias 8137(1.09), saved: -13, total: 18.64% + 1: 7311(49.6%)[49.7%], bias 8079(1.11), saved: 5, total: 18.25% + 4: 7250(49.2%)[49.3%], bias 7906(1.09), saved: 13, total: 18.10% + 59: 7225(49.0%)[49.1%], bias 7889(1.09), saved: 16, total: 18.04% +59, 7225: Embedding data: 14712 in 40059 +Bits embedded: 14744, changed: 7225(49.0%)[49.1%], bias: 7889, tot: 40032, skip: 25288 +Foiling statistics: corrections: 2590, failed: 1, offset: 122.585494 +- 239.664983 +Total bits changed: 15114 (change 7225 + bias 7889) Storing bitmap into data... -Writing .... +Writing foil/dscf0001.jpg.... The simple example script "seek_script" uses OutGuess to select an image that fits the data we want to hide the best, yielding the lowest number of changed bits. Because we do not care about the actual content of the cover data we send, this is a very viable approach. -Additionally, OutGuess allows to hide an arbitrary number of messages -in the data. Thus it also provides plausible deniablity. It keeps -track of the bits that have been modified previously and locks them. -A (23,12,7) Golay code is used for error correction to tolerate -collisions on locked bits. Artifical errors are introduced to avoid -modifying bits that have a hight bias. - -IN THIS VERSION, OUTGUESS ONLY INSERTS TWO DIFFERENT MESSAGES. THE -RELEASE VERSION WILL HANDLE AN ARBITRARY NUMBER. - -The available command line options are, capital letters specify options -for the second message, - - -k The secret key used to encrypt and hide the message in - the provided data. - - -d The filename specifying the message to be hidden in the - data. - - -i The upper limit in finding an optimal iterator seed. The - maximum value for the limit is 65536. - - -s The initial seed the iterator object uses for - selecting bits in the redundant data. If no upper limit - is specified, the iterator will use this seed without - searching for a more optimal embedding. - - -e Use error correction for data encoding and decoding. - -Other options that apply to the general executions of OutGuess are - - -r Retrieve a message from a data object. If this option - is not specified, OutGuess will embed messages. - - -x If the second key does not create an iterator object - that is successful in embedding the data, the program - will derive up to new keys. - - -p "param" Passes a string as parameter to the destination data - handler. For the JPEG image format, this is the - compression quality, it can take values between 75 and - 100. The higher the quality the more bits to hide - a message in the data are available. - - -t Collect statistics about redundant bit usage. Repeated - 't's increase output level. Probably meaningless to - most. - -For embedding messages, you need to specify a source and a destination -filename. OutGuess determines the data format by the filename extension. -If no filenames are specified OutGuess operates as filter and assumes -the PPM data format. - -For example - - outguess -k "my secret pass phrase" -d hidden.txt monkey.jpg out.jpg - -embeds the message 'hidden.txt' into the 'monkey.jpg' image. In the -other direction - - outguess -k "my secret pass phrase" -r out.jpg message.txt - -will retrieve the hidden message from the image. - -If you want to embed a second message, use - - outguess -k "secret1" -d hide1.txt -E -K "secret2" -D hide2.txt \ - monkey.jpg out.jpg - -OutGuess will first embed "hide1.txt" and then "hide2.txt" on top of -it, using error correcting codes. The second message "hide2.txt" can -be retrieved with - - outguess -k "secret2" -e -r out.jpg message.txt +Additionally, OutGuess allows to hide multiple messages in the data. +Thus, it also provides plausible deniablity. It keeps track of the +bits that have been modified previously and locks them. A (23,12,7) +Golay code is used for error correction to tolerate collisions on +locked bits. Artifical errors are introduced to avoid modifying bits +that have a hight bias. +This version of OutGuess can insert only two different messages. As this is a BETA version, I would like you to give me feedback on -the usefulness of OutGuess. +the usefulness of OutGuess. And if you like it, you can support +me via the links on + + http://www.outguess.org/ Installation ------------ @@ -149,6 +87,9 @@ should require only 1. ./configure && make +There is an optimization bug in gcc, you might have to compile +with -O0. + If your system is not supported, trying building by hand as follows 1. Install the JPEG-6b libjpeg.a library and patch it with @@ -162,13 +103,7 @@ OutGuess has only been tested on OpenBSD, Linux, Solaris and AIX. BUGS: ----- -Not all the redundant data available in the JPEG encoding is used, -this is due to a problem when reconstructing the Huffman coefficients. -I have seen cases when two MCU blocks have only the first coefficient -assigned, that the coefficents are the same and do not represent -the Huffman coefficients that were used when the image was created, -and thus getting the bit stream out of sync. So I just ignore the -first coefficent always. +None known at the moment. Acknowledgments: ---------------- diff --git a/README-FIRST b/README-FIRST deleted file mode 100644 index 266aeaf..0000000 --- a/README-FIRST +++ /dev/null @@ -1,8 +0,0 @@ -Originally, Niels Provos wrote outguess and released its versions in -the following order: 0.1, 0.11, 0.12, 0.13, 0.13b and 0.2. - -I could find 0.13b and 0.2 versions only in Internet. Note that 0.2 is -the latest version but 0.2 < 0.13b. So, I tagged 0.13b as 0.0.13b to -avoid conflicts. - - -- Joao Eriberto Mota Filho Thu, 08 Nov 2018 21:17:11 -0200 diff --git a/TODO b/TODO index 85ca22b..e69de29 100644 --- a/TODO +++ b/TODO @@ -1 +0,0 @@ -save detecability of bits for bitmaps, as weight diff --git a/arc.c b/arc.c index 0b93b57..8f7c43c 100644 --- a/arc.c +++ b/arc.c @@ -104,13 +104,14 @@ arc4_addrandom(struct arc4_stream *as, u_char *dat, int datlen) } void -arc4_initkey(struct arc4_stream *as, u_char *key, int keylen) +arc4_initkey(struct arc4_stream *as, char *type, u_char *key, int keylen) { MD5_CTX ctx; u_char digest[16]; /* Bah, we want bcrypt */ MD5Init(&ctx); + MD5Update(&ctx, type, strlen(type)); MD5Update(&ctx, key, keylen); MD5Final(digest, &ctx); diff --git a/arc.h b/arc.h index df8bc86..d298a43 100644 --- a/arc.h +++ b/arc.h @@ -53,6 +53,6 @@ void arc4_init(struct arc4_stream *as); u_int8_t arc4_getbyte(struct arc4_stream *as); u_int32_t arc4_getword(struct arc4_stream *as); void arc4_addrandom(struct arc4_stream *as, u_char *dat, int datlen); -void arc4_initkey(struct arc4_stream *as, u_char *key, int keylen); +void arc4_initkey(struct arc4_stream *as, char *type, u_char *key, int keylen); #endif /* _ARC_H */ diff --git a/config.h.in b/config.h.in index 4d168d3..ea14af5 100644 --- a/config.h.in +++ b/config.h.in @@ -1,5 +1,11 @@ /* config.h.in. Generated automatically from configure.in by autoheader. */ +/* Define if you have strcasecmp, as a function or macro. */ +#undef HAVE_STRCASECMP + +/* Define if you have snprintf, as a function or macro. */ +#undef HAVE_SNPRINTF + /* Define if you have a working `mmap' system call. */ #undef HAVE_MMAP @@ -36,6 +42,9 @@ /* Define if you have the header file. */ #undef HAVE_MD5_H +/* Define if you have the header file. */ +#undef HAVE_ERR_H + /* Define if you have the header file. */ #undef HAVE_UNISTD_H diff --git a/configure b/configure index d360c2f..46fe3e5 100755 --- a/configure +++ b/configure @@ -1067,7 +1067,7 @@ EOF fi -for ac_hdr in fcntl.h unistd.h md5.h +for ac_hdr in fcntl.h unistd.h md5.h err.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 @@ -1639,7 +1639,10 @@ fi if eval "test \"`echo '$ac_cv_func_'strcasecmp`\" = yes"; then echo "$ac_t""yes" 1>&6 - : + cat >> confdefs.h <<\EOF +#define HAVE_STRCASECMP 1 +EOF + else echo "$ac_t""no" 1>&6 echo "$ac_t""File extensions will be case sensitive!" 1>&6 @@ -1648,12 +1651,12 @@ fi echo $ac_n "checking for snprintf""... $ac_c" 1>&6 -echo "configure:1652: checking for snprintf" >&5 +echo "configure:1655: checking for snprintf" >&5 if eval "test \"`echo '$''{'ac_cv_func_snprintf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:1683: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_snprintf=yes" else @@ -1690,64 +1693,147 @@ fi if eval "test \"`echo '$ac_cv_func_'snprintf`\" = yes"; then echo "$ac_t""yes" 1>&6 - : + cat >> confdefs.h <<\EOF +#define HAVE_SNPRINTF 1 +EOF + else echo "$ac_t""no" 1>&6 -echo "$ac_t""will use sprintf instead you have a bad system" 1>&6 +echo "$ac_t""will use sprintf instead" 1>&6 fi -echo $ac_n "checking for MD5Init""... $ac_c" 1>&6 -echo "configure:1704: checking for MD5Init" >&5 -if eval "test \"`echo '$''{'ac_cv_func_MD5Init'+set}'`\" = set"; then + + +needmd5=no +needincmiss=no +for ac_func in MD5Update +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1716: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ -char MD5Init(); +char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ -#if defined (__stub_MD5Init) || defined (__stub___MD5Init) +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else -MD5Init(); +$ac_func(); #endif ; return 0; } EOF -if { (eval echo configure:1732: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:1744: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* - eval "ac_cv_func_MD5Init=yes" + eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* - eval "ac_cv_func_MD5Init=no" + eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi -if eval "test \"`echo '$ac_cv_func_'MD5Init`\" = yes"; then +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 - : + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +needmd5=yes; needincmiss=yes +fi +done + +if test $needmd5 = yes; then + LIBMISSING="$LIBMISSING md5.o" + MISSINGOBJ="$MISSINGOBJ missing/md5.c" +fi +neederr=no +for ac_func in warnx +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1777: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1805: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 -CFLAGS="$CFLAGS -I."; MD5OBJ="md5c.o" +neederr=yes; needincmiss=yes fi +done +if test $neederr = yes; then + LIBMISSING="$LIBMISSING err.o" + MISSINGOBJ="$MISSINGOBJ missing/err.c" +fi + +if test $needincmiss = yes; then + CFLAGS="$CFLAGS -Imissing" +fi subdirs="jpeg-6b-steg" @@ -1891,7 +1977,8 @@ s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g s%@INSTALL_DATA@%$INSTALL_DATA%g s%@SET_MAKE@%$SET_MAKE%g s%@CPP@%$CPP%g -s%@MD5OBJ@%$MD5OBJ%g +s%@LIBMISSING@%$LIBMISSING%g +s%@MISSINGOBJ@%$MISSINGOBJ%g s%@subdirs@%$subdirs%g CEOF diff --git a/configure.in b/configure.in index d8cf2f3..a70c3c1 100644 --- a/configure.in +++ b/configure.in @@ -24,7 +24,7 @@ dnl Checks for libraries. dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS(fcntl.h unistd.h md5.h) +AC_CHECK_HEADERS(fcntl.h unistd.h md5.h err.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T @@ -37,17 +37,38 @@ dnl Checks for library functions. AC_FUNC_MMAP AC_CHECK_FUNCS(strrchr memcpy) -AC_CHECK_FUNC(strcasecmp, , +AC_CHECK_FUNC(strcasecmp, + AC_DEFINE(HAVE_STRCASECMP), AC_MSG_RESULT(File extensions will be case sensitive!) ) -AC_CHECK_FUNC(snprintf, , - AC_MSG_RESULT(will use sprintf instead you have a bad system) +AC_CHECK_FUNC(snprintf, + AC_DEFINE(HAVE_SNPRINTF), + AC_MSG_RESULT(will use sprintf instead, you have a bad system) ) -dnl Check if we need to compile md5c -AC_SUBST(MD5OBJ) -AC_CHECK_FUNC(MD5Init, , CFLAGS="$CFLAGS -I."; MD5OBJ="md5c.o") + +AC_SUBST(LIBMISSING) +AC_SUBST(MISSINGOBJ) +dnl Check if we need to compile md5 +needmd5=no +needincmiss=no +AC_CHECK_FUNCS(MD5Update, , [needmd5=yes; needincmiss=yes]) +if test $needmd5 = yes; then + LIBMISSING="$LIBMISSING md5.o" + MISSINGOBJ="$MISSINGOBJ missing/md5.c" +fi +dnl Check if there is err() +neederr=no +AC_CHECK_FUNCS(warnx, , [neederr=yes; needincmiss=yes]) +if test $neederr = yes; then + LIBMISSING="$LIBMISSING err.o" + MISSINGOBJ="$MISSINGOBJ missing/err.c" +fi + +if test $needincmiss = yes; then + CFLAGS="$CFLAGS -Imissing" +fi AC_CONFIG_SUBDIRS(jpeg-6b-steg) diff --git a/fourier.c b/fourier.c new file mode 100644 index 0000000..fe66acb --- /dev/null +++ b/fourier.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 1999, 2000 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "fourier.h" + +/* if depth > 1 */ +int +split_colors(u_char **pred, u_char **pgreen, u_char **pblue, + u_char *img, + int xdim, int ydim, int depth) +{ + int i, j; + u_char *red, *green, *blue; + + /* Split to red - blue */ + red = checkedmalloc(ydim*xdim); + green = checkedmalloc(ydim*xdim); + blue = checkedmalloc(ydim*xdim); + + for (i = 0; i < ydim; i++) { + for (j = 0; j < xdim; j++) { + red[j + xdim*i] = img[0 + depth*(j + xdim*i)]; + green[j + xdim*i] = img[1 + depth*(j + xdim*i)]; + blue[j + xdim*i] = img[2 + depth*(j + xdim*i)]; + } + } + + *pred = red; + *pgreen = green; + *pblue = blue; + + return 1; +} + +void +fft_image(int xdim, int ydim, int depth, u_char *img) +{ + int i,j; + u_char *red, *green, *blue; + fftw_complex *c, *d, *e; + double maxre, maxim, maxmod; + + split_colors(&red, &green, &blue, img, xdim, ydim, depth); + + maxre = maxim = maxmod = 0; + c = fft_transform(xdim, ydim, red, &maxre, &maxim, &maxmod); + d = fft_transform(xdim, ydim, green, &maxre, &maxim, &maxmod); + e = fft_transform(xdim, ydim, blue, &maxre, &maxim, &maxmod); + fft_visible(xdim, ydim, c, red, maxre, maxim, maxmod); + free(c); + fft_visible(xdim, ydim, d, green, maxre, maxim, maxmod); + free(d); + fft_visible(xdim, ydim, e, blue, maxre, maxim, maxmod); + free(e); + + for (i = 0; i < ydim; i++) { + for (j = 0; j < xdim; j++) { + img[0 + depth*(j + xdim*i)] = red[j + xdim*i]; + img[1 + depth*(j + xdim*i)] = green[j + xdim*i]; + img[2 + depth*(j + xdim*i)] = blue[j + xdim*i]; + } + } + + free(red); free(green); free(blue); +} + +void +fft_visible(int xdim, int ydim, fftw_complex *c, u_char *img, + double maxre, double maxim, double maxmod) +{ + int i, j, ind; + double contrast, scale, factor, total, val; + + contrast = exp((-100.5) * log(xdim*ydim) / 256); + factor = - 255.0; + total = 0; + scale = factor / log(maxmod * contrast * contrast + 1.0); + + printf("Visible: max (%f/%f/%f), contrast: %f, scale: %f\n", + maxre, maxim, maxmod, contrast, scale); + + for (i = 0; i < xdim ; i++) + for (j = 0; j < ydim; j++) { + ind = j*xdim + i; + val = total - scale*log(contrast*contrast* + (c[ind].re*c[ind].re + + c[ind].im*c[ind].im) + 1); + img[ind] = val; + } +} + +fftw_complex * +fft_transform(int xdim, int ydim, unsigned char *data, + double *mre, double *mim, double *mmod) +{ + rfftwnd_plan p; + int i,j, ind, di, dj; + double maxre, maxim, maxmod, val; + fftw_complex *a; + + fprintf(stderr, "Starting complex 2d FFT\n"); + + a = checkedmalloc(xdim * ydim * sizeof(fftw_complex)); + + p = fftw2d_create_plan(ydim, xdim, FFTW_FORWARD, + FFTW_ESTIMATE | FFTW_IN_PLACE); + + di = 1; + for (j = 0; j < ydim; j++) { + dj = di; + for (i = 0; i < xdim ; i++) { + ind = i + j * xdim; + a[ind].re = data[ind] * dj; + a[ind].im = 0.0; + dj = -dj; + } + di = -di; + } + + fftwnd_one(p, a, NULL); + + maxim = *mim; + maxre = *mre; + maxmod = *mmod; + for (i = 0; i < xdim ; i++) + for (j = 0; j < ydim; j++) { + ind = j*xdim + i; + + /* Update Stats */ + if (fabs(a[ind].re) > maxre) maxre = fabs(a[ind].re); + if (fabs(a[ind].im) > maxim) maxim = fabs(a[ind].im); + val = a[ind].re * a[ind].re + a[ind].im * a[ind].im; + if (val > maxmod) maxmod = val; + } + *mim = maxim; + *mre = maxre; + *mmod = maxmod; + + fftwnd_destroy_plan(p); + + return a; +} + +void +fft_filter(int xdim, int ydim, fftw_complex *data) +{ + int i, j, ind; + double val; + + fprintf(stderr, "Starting complex filtering\n"); + + for (j = 0; j < ydim; j++) { + for (i = 0; i < xdim ; i++) { + ind = i + j * xdim; + val = sqrt((xdim/2-i)*(xdim/2-i) + + (ydim/2-j)*(ydim/2-j)); + if (val > 15) { + data[ind].re = 2*data[ind].re; + data[ind].im = 2*data[ind].im; + } else { + data[ind].re = 0.2*data[ind].re; + data[ind].im = 0.2*data[ind].im; + } + } + } +} + +u_char * +fft_transform_back(int xdim, int ydim, fftw_complex *data) +{ + rfftwnd_plan p; + int i,j, ind; + fftw_real dj, val; + u_char *a; + + fprintf(stderr, "Starting complex 2d FFT-back\n"); + + a = checkedmalloc(xdim * ydim * sizeof(u_char)); + + p = fftw2d_create_plan(ydim, xdim, FFTW_BACKWARD, + FFTW_ESTIMATE | FFTW_IN_PLACE); + + fftwnd_one(p, data, NULL); + + dj = (xdim * ydim); + for (j = 0; j < ydim; j++) { + for (i = 0; i < xdim ; i++) { + ind = i + j * xdim; + val = sqrt(data[ind].re * data[ind].re + + data[ind].im * data[ind].im)/dj; + a[ind] = val; + } + } + + return a; +} diff --git a/fourier.h b/fourier.h new file mode 100644 index 0000000..bdb0dee --- /dev/null +++ b/fourier.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1999, 2000 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FOURIER_H +#define _FOURIER_H + +int split_colors(u_char **pred, u_char **pgreen, u_char **pblue, + u_char *img, int xdim, int ydim, int depth); + +void fft_visible(int xdim, int ydim, fftw_complex *c, u_char *img, + double maxre, double maxim, double maxmod); +fftw_complex *fft_transform(int xdim, int ydim, unsigned char *data, + double *mre, double *mim, double *mmod); +void fft_filter(int xdim, int ydim, fftw_complex *data); +u_char *fft_transform_back(int xdim, int ydim, fftw_complex *data); +#endif + diff --git a/histogram.c b/histogram.c new file mode 100644 index 0000000..caa71c1 --- /dev/null +++ b/histogram.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include + +#include "config.h" +#include "outguess.h" + +char *progname; + +void +usage(void) +{ + + fprintf(stderr, "%s: \n", progname); +} + +void +histogram_simple(u_char *data, int bits) +{ + int i; + int one = 0, zero = 0; + + for (i = 0; i < bits; i++) + if (TEST_BIT(data, i)) + one++; + else + zero++; + + fprintf(stdout, "Bits: %6d\n", bits); + fprintf(stdout, "One: %6d, %f\n", one, (float)one/bits); + fprintf(stdout, "Zero: %6d, %f\n", zero, (float)zero/bits); +} + +#define MAXRUNLEN 25 + +void +histogram_runlen(u_char *data, int bits) +{ + int buckets[MAXRUNLEN]; + int what, count, i; + + memset(buckets, 0, sizeof(buckets)); + what = TEST_BIT(data, 0); + count = 1; + for (i = 1; i < bits; i++) { + if (TEST_BIT(data, i) != what) { + if (count >= MAXRUNLEN) + count = MAXRUNLEN - 1; + buckets[count]++; + + what = TEST_BIT(data, i); + count = 1; + } else + count++; + } + if (count >= MAXRUNLEN) + count = MAXRUNLEN - 1; + buckets[count]++; + + for (i = 1; i < MAXRUNLEN; i++) { + fprintf(stdout, "%3d: %6d, %f\n", + i, buckets[i], (float)buckets[i]/bits); + } +} + +int +main(int argc, char *argv[]) +{ + FILE *fin; + char *data; + int bits, bytes, res; + + progname = argv[0]; + + argv++; + argc--; + + if (argc != 1) { + usage(); + exit(1); + } + + if ((fin = fopen(argv[0], "r")) == NULL) + err(1, "fopen"); + + if ((res = fread(&bits, sizeof(int), 1, fin)) != 1) + err(1, "fread(1): %d", res); + + bits = ntohl(bits); + bytes = (bits + 7) / 8; + if ((data = malloc(bytes)) == NULL) + err(1, "malloc"); + + if ((res = fread(data, sizeof(char), bytes, fin)) != bytes) + err(1, "fread(2): %d", res); + + histogram_simple(data, bits); + histogram_runlen(data, bits); + + exit(0); +} diff --git a/iterator.c b/iterator.c new file mode 100644 index 0000000..7cbed9e --- /dev/null +++ b/iterator.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 1999-2001 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "config.h" + +#include "outguess.h" +#include "arc.h" +#include "iterator.h" + +/* Initalize the iterator */ + +void +iterator_init(iterator *iter, bitmap *bitmap, u_char *key, u_int klen) +{ + iter->skipmod = INIT_SKIPMOD; + + arc4_initkey(&iter->as, "Seeding", key, klen); + + iter->off = arc4_getword(&iter->as) % iter->skipmod; +} + +/* The next bit in the bitmap we should embed data into */ + +int +iterator_next(iterator *iter, bitmap *bitmap) +{ + iter->off += (arc4_getword(&iter->as) % iter->skipmod) + 1; + + return iter->off; +} + +void +iterator_seed(iterator *iter, bitmap *bitmap, u_int16_t seed) +{ + u_int8_t reseed[2]; + + reseed[0] = seed; + reseed[1] = seed >> 8; + + arc4_addrandom(&iter->as, reseed, 2); +} + +void +iterator_adapt(iterator *iter, bitmap *bitmap, int datalen) +{ + iter->skipmod = SKIPADJ(bitmap->bits, bitmap->bits - iter->off) * + (bitmap->bits - iter->off)/(8 * datalen); +} diff --git a/iterator.h b/iterator.h new file mode 100644 index 0000000..1b48206 --- /dev/null +++ b/iterator.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 1999-2001 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ITERATOR_H +#define _ITERATOR_H + +#define INIT_SKIPMOD 32 +#define DEFAULT_ITER 256 + +/* Slowly fade skip out at the end of the picture */ +#define SKIPADJ(x,y) ((y) > (x)/32 ? 2 : 2 - ((x/32) - (y))/(float)(x/32)) + +/* + * The generic iterator + */ + +typedef struct _iterator { + struct arc4_stream as; + u_int32_t skipmod; + int off; /* Current bit position */ +} iterator; + +struct _bitmap; + +void iterator_init(iterator *, struct _bitmap *, u_char *key, u_int klen); +int iterator_next(iterator *, struct _bitmap *); + +#define ITERATOR_CURRENT(x) (x)->off + +void iterator_seed(iterator *, struct _bitmap *, u_int16_t); +void iterator_adapt(iterator *, struct _bitmap *, int); + +#endif diff --git a/jpeg-6b-steg/CVS/Entries b/jpeg-6b-steg/CVS/Entries new file mode 100644 index 0000000..96c6ef6 --- /dev/null +++ b/jpeg-6b-steg/CVS/Entries @@ -0,0 +1,133 @@ +/README/1.1.1.1/Mon May 1 19:57:38 2000// +/ansi2knr.1/1.1.1.1/Mon May 1 19:57:39 2000// +/ansi2knr.c/1.1.1.1/Mon May 1 19:57:39 2000// +/cderror.h/1.1.1.1/Mon May 1 19:57:39 2000// +/cdjpeg.c/1.1.1.1/Mon May 1 19:57:39 2000// +/cdjpeg.h/1.1.1.1/Mon May 1 19:57:39 2000// +/change.log/1.1.1.1/Mon May 1 19:57:38 2000// +/cjpeg.1/1.1.1.1/Mon May 1 19:57:38 2000// +/cjpeg.c/1.1.1.1/Mon May 1 19:57:39 2000// +/ckconfig.c/1.1.1.1/Mon May 1 19:57:39 2000// +/config.guess/1.1.1.1/Mon May 1 19:57:39 2000// +/config.sub/1.1.1.1/Mon May 1 19:57:39 2000// +/configure/1.1.1.1/Mon May 1 19:57:38 2000// +/djpeg.1/1.1.1.1/Mon May 1 19:57:38 2000// +/djpeg.c/1.1.1.1/Mon May 1 19:57:39 2000// +/example.c/1.1.1.1/Mon May 1 19:57:38 2000// +/install-sh/1.1.1.1/Mon May 1 19:57:39 2000// +/jcapimin.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jcapistd.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jccoefct.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jccolor.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jcdctmgr.c/1.2/Mon May 1 20:12:19 2000// +/jchuff.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jchuff.h/1.1.1.1/Mon May 1 19:57:39 2000// +/jcinit.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jcmainct.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jcmarker.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jcmaster.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jcomapi.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.bcc/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.cfg/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.dj/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.doc/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.mac/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.manx/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.mc6/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.sas/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.st/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.vc/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.vms/1.1.1.1/Mon May 1 19:57:38 2000// +/jconfig.wat/1.1.1.1/Mon May 1 19:57:38 2000// +/jcparam.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jcphuff.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jcprepct.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jcsample.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jctrans.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdapimin.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdapistd.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdatadst.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdatasrc.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdcoefct.c/1.2/Mon May 1 20:12:19 2000// +/jdcolor.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdct.h/1.1.1.1/Mon May 1 19:57:39 2000// +/jddctmgr.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdhuff.c/1.2/Mon May 1 20:12:19 2000// +/jdhuff.h/1.1.1.1/Mon May 1 19:57:39 2000// +/jdinput.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdmainct.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdmarker.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdmaster.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdmerge.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdphuff.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdpostct.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdsample.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jdtrans.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jerror.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jerror.h/1.1.1.1/Mon May 1 19:57:39 2000// +/jfdctflt.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jfdctfst.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jfdctint.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jidctflt.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jidctfst.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jidctint.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jidctred.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jinclude.h/1.1.1.1/Mon May 1 19:57:39 2000// +/jmemansi.c/1.1.1.1/Mon May 1 19:57:39 2000// +/jmemdos.c/1.1.1.1/Mon May 1 19:57:39 2000// +/jmemdosa.asm/1.1.1.1/Mon May 1 19:57:39 2000// +/jmemmac.c/1.1.1.1/Mon May 1 19:57:39 2000// +/jmemmgr.c/1.1.1.1/Mon May 1 19:57:39 2000// +/jmemname.c/1.1.1.1/Mon May 1 19:57:39 2000// +/jmemnobs.c/1.1.1.1/Mon May 1 19:57:39 2000// +/jmemsys.h/1.1.1.1/Mon May 1 19:57:39 2000// +/jmorecfg.h/1.1.1.1/Mon May 1 19:57:39 2000// +/jpegint.h/1.1.1.1/Mon May 1 19:57:39 2000// +/jpeglib.h/1.1.1.1/Mon May 1 19:57:39 2000// +/jpegtran.1/1.1.1.1/Mon May 1 19:57:38 2000// +/jpegtran.c/1.1.1.1/Mon May 1 19:57:39 2000// +/jquant1.c/1.1.1.1/Mon May 1 19:57:38 2000// +/jquant2.c/1.1.1.1/Mon May 1 19:57:39 2000// +/jutils.c/1.1.1.1/Mon May 1 19:57:39 2000// +/jversion.h/1.1.1.1/Mon May 1 19:57:39 2000// +/ltconfig/1.1.1.1/Mon May 1 19:57:39 2000// +/ltmain.sh/1.1.1.1/Mon May 1 19:57:39 2000// +/makcjpeg.st/1.1.1.1/Mon May 1 19:57:38 2000// +/makdjpeg.st/1.1.1.1/Mon May 1 19:57:38 2000// +/makeapps.ds/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.ansi/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.bcc/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.cfg/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.dj/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.manx/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.mc6/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.mms/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.sas/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.unix/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.vc/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.vms/1.1.1.1/Mon May 1 19:57:38 2000// +/makefile.wat/1.1.1.1/Mon May 1 19:57:38 2000// +/makelib.ds/1.1.1.1/Mon May 1 19:57:38 2000// +/makeproj.mac/1.1.1.1/Mon May 1 19:57:38 2000// +/makljpeg.st/1.1.1.1/Mon May 1 19:57:38 2000// +/maktjpeg.st/1.1.1.1/Mon May 1 19:57:38 2000// +/makvms.opt/1.1.1.1/Mon May 1 19:57:38 2000// +/rdbmp.c/1.1.1.1/Mon May 1 19:57:39 2000// +/rdcolmap.c/1.1.1.1/Mon May 1 19:57:39 2000// +/rdgif.c/1.1.1.1/Mon May 1 19:57:39 2000// +/rdjpgcom.1/1.1.1.1/Mon May 1 19:57:38 2000// +/rdjpgcom.c/1.1.1.1/Mon May 1 19:57:39 2000// +/rdppm.c/1.1.1.1/Mon May 1 19:57:39 2000// +/rdrle.c/1.1.1.1/Mon May 1 19:57:39 2000// +/rdswitch.c/1.1.1.1/Mon May 1 19:57:39 2000// +/rdtarga.c/1.1.1.1/Mon May 1 19:57:39 2000// +/transupp.c/1.1.1.1/Mon May 1 19:57:39 2000// +/transupp.h/1.1.1.1/Mon May 1 19:57:39 2000// +/wrbmp.c/1.1.1.1/Mon May 1 19:57:39 2000// +/wrgif.c/1.1.1.1/Mon May 1 19:57:39 2000// +/wrjpgcom.1/1.1.1.1/Mon May 1 19:57:38 2000// +/wrjpgcom.c/1.1.1.1/Mon May 1 19:57:39 2000// +/wrppm.c/1.1.1.1/Mon May 1 19:57:39 2000// +/wrrle.c/1.1.1.1/Mon May 1 19:57:39 2000// +/wrtarga.c/1.1.1.1/Mon May 1 19:57:39 2000// +D diff --git a/jpeg-6b-steg/CVS/Repository b/jpeg-6b-steg/CVS/Repository new file mode 100644 index 0000000..a0aa01c --- /dev/null +++ b/jpeg-6b-steg/CVS/Repository @@ -0,0 +1 @@ +outguess/jpeg-6b-steg diff --git a/jpeg-6b-steg/CVS/Root b/jpeg-6b-steg/CVS/Root new file mode 100644 index 0000000..bd2f612 --- /dev/null +++ b/jpeg-6b-steg/CVS/Root @@ -0,0 +1 @@ +provos@monkey.org:/cvs diff --git a/jpeg-6b-steg/jcdctmgr.c b/jpeg-6b-steg/jcdctmgr.c index 8818f7f..292648d 100644 --- a/jpeg-6b-steg/jcdctmgr.c +++ b/jpeg-6b-steg/jcdctmgr.c @@ -257,7 +257,7 @@ forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, temp += qval>>1; /* for rounding */ DIVIDE_BY(temp, qval); } - output_ptr[i] = (JCOEF) i ? steg_use_bit(temp) : temp; + output_ptr[i] = steg_use_bit(temp); } } } diff --git a/jpeg-6b-steg/jdcoefct.c b/jpeg-6b-steg/jdcoefct.c index 4938d20..6ffe53f 100644 --- a/jpeg-6b-steg/jdcoefct.c +++ b/jpeg-6b-steg/jdcoefct.c @@ -194,6 +194,13 @@ decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) yoffset+yindex < compptr->last_row_height) { output_col = start_col; for (xindex = 0; xindex < useful_width; xindex++) { + { + /* Retrieve LSB from DCT coefficient */ + JBLOCKROW block = coef->MCU_buffer[blkn + xindex]; + int k; + for (k = 0; k < DCTSIZE2; k++) + steg_use_bit((JCOEF) (*block)[k]); + } (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) coef->MCU_buffer[blkn+xindex], output_ptr, output_col); diff --git a/jpeg-6b-steg/jdhuff.c b/jpeg-6b-steg/jdhuff.c index 184f535..4ac4beb 100644 --- a/jpeg-6b-steg/jdhuff.c +++ b/jpeg-6b-steg/jdhuff.c @@ -590,9 +590,6 @@ decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) k += 15; } } - for (k = 1; k < DCTSIZE2; k++) - steg_use_bit((JCOEF) (*block)[k]); - } else { /* Section F.2.2.2: decode the AC coefficients */ diff --git a/jpg.c b/jpg.c index ed65531..dea6230 100644 --- a/jpg.c +++ b/jpg.c @@ -1,5 +1,5 @@ /* - * Copyright 1999 Niels Provos + * Copyright 1999-2001 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,7 +54,8 @@ handler jpg_handler = { read_JPEG_file, write_JPEG_file, bitmap_from_jpg, - bitmap_to_jpg + bitmap_to_jpg, + preserve_jpg }; static int jpeg_state; @@ -64,6 +65,22 @@ static int quality = 75; static int jpeg_eval; static int eval_cnt; +static int dctmin; +static int dctmax; + +extern int steg_foil; /* Statistics keps in main program */ +extern int steg_foilfail; + +#define DCTMIN 100 +#define DCTENTRIES 256 +static int dctadjust[DCTENTRIES]; + +#define DCTFREQRANGE 5000 /* Number of bits for which the below holds */ +#define DCTFREQREDUCE 33 /* Threshold is /REDUCE, 1% for 100 */ +#define DCTFREQMIN 2 /* At least 5 coeff in cache */ +static int dctfreq[DCTENTRIES]; +static int dctpending; + void init_state(int state, int eval, bitmap *bitmap) { @@ -71,21 +88,164 @@ init_state(int state, int eval, bitmap *bitmap) jpeg_eval = eval; eval_cnt = 0; + dctmin = 127; + dctmax = -127; + off = 0; if (state == JPEG_READING) { memset(&tbitmap, 0, sizeof(tbitmap)); tbitmap.bytes = 256; tbitmap.bits = tbitmap.bytes * 8; tbitmap.bitmap = checkedmalloc(tbitmap.bytes); - tbitmap.detect = checkedmalloc(tbitmap.bits); + tbitmap.locked = checkedmalloc(tbitmap.bytes); + memset(tbitmap.locked, 0, tbitmap.bytes); + tbitmap.data = checkedmalloc(tbitmap.bits); } else if (bitmap) { memcpy(&tbitmap, bitmap, sizeof(tbitmap)); } } +int +preserve_single(bitmap *bitmap, int off, char coeff) +{ + int i; + char *data = bitmap->data; + char *pbits = bitmap->bitmap; + char *plock = bitmap->locked; + char *pmetalock = bitmap->metalock; + + for (i = off - 1; i >= 0; i--) { + if (TEST_BIT(plock, i)) + continue; + if (TEST_BIT(pmetalock, i)) + continue; + + /* Switch the coefficient to the value that we just replaced */ + if (data[i] == coeff) { + char cbit; + + data[i] = coeff ^ 0x01; + + cbit = (unsigned char)coeff & 0x01; + WRITE_BIT(pbits, i, cbit ^ 0x01); + + WRITE_BIT(pmetalock, i, 1); + + if (jpeg_eval) + fprintf(stderr, + "off: %d, i: %d, coeff: %d, data: %d\n", + off, i, coeff, data[i]); + + return (i); + } + } + + return (-1); +} + + +int +preserve_jpg(bitmap *bitmap, int off) +{ + char coeff; + int i, a, b; + char *data = bitmap->data; + + if (off == -1) { + int res; + + if (jpeg_eval) + fprintf(stderr, "DCT: %d<->%d\n", dctmin, dctmax); + + bitmap->preserve = preserve_jpg; + memset(bitmap->metalock, 0, bitmap->bytes); + + memset(dctadjust, 0, sizeof(dctadjust)); + memset(dctfreq, 0, sizeof(dctfreq)); + dctpending = 0; + + /* Calculate coefficent frequencies */ + for (i = 0; i < bitmap->bits; i++) { + dctfreq[data[i] + 127]++; + } + + a = dctfreq[-1 + 127]; + b = dctfreq[-2 + 127]; + + if (a < b) { + fprintf(stderr, "Can not calculate estimate\n"); + res = -1; + } else + res = 2*bitmap->bits*b/(a + b); + + /* Pending threshold based on frequencies */ + for (i = 0; i < DCTENTRIES; i++) { + dctfreq[i] = dctfreq[i] / + ((float)bitmap->bits / DCTFREQRANGE); + dctfreq[i] /= DCTFREQREDUCE; + if (dctfreq[i] < DCTFREQMIN) + dctfreq[i] = DCTFREQMIN; + + if (jpeg_eval) + fprintf(stderr, "Foil: %d :< %d\n", + i - 127, dctfreq[i]); + } + + bitmap->maxcorrect = res; + return (res); + } else if (off >= bitmap->bits) { + /* Reached end of image */ + for (i = 0; i < DCTENTRIES; i++) { + while (dctadjust[i]) { + dctadjust[i]--; + + coeff = i - 127; + + if (preserve_single(bitmap, bitmap->bits - 1, + coeff) != -1) + steg_foil++; + else + steg_foilfail++; + } + } + + return(0); + } + + /* We need to find this coefficient, and change it to data[off] */ + coeff = data[off] ^ 0x01; + + if (dctadjust[data[off] + 127]) { + /* But we are still missing compensation for the opposite */ + dctadjust[data[off] + 127]--; + dctpending--; + return (0); + } + + if (dctadjust[coeff + 127] < dctfreq[coeff + 127]) { + dctadjust[coeff + 127]++; + dctpending++; + return (0); + } + + i = preserve_single(bitmap, off, coeff); + + if (i != -1) { + steg_foil++; + return (i); + } + + /* We have one too many of this */ + dctadjust[coeff + 127]++; + dctpending++; + + return (-1); +} + bitmap * finish_state(void) { + int i; bitmap *pbitmap; if (jpeg_eval) @@ -94,13 +254,25 @@ finish_state(void) if (jpeg_state != JPEG_READING) return NULL; - pbitmap = checkedmalloc(sizeof(bitmap)); - tbitmap.bits = off; tbitmap.bytes = (off + 7) / 8; - tbitmap.locked = checkedmalloc(tbitmap.bytes); - memset(tbitmap.locked, 0, tbitmap.bytes); + tbitmap.detect = checkedmalloc(tbitmap.bits); + tbitmap.metalock = checkedmalloc(tbitmap.bytes); + + for (i = 0; i < off; i++) { + char temp = abs(tbitmap.data[i]); + if (temp >= JPG_THRES_MAX) + tbitmap.detect[i] = -1; + else if (temp >= JPG_THRES_LOW) + tbitmap.detect[i] = 0; + else if (temp >= JPG_THRES_MIN) + tbitmap.detect[i] = 1; + else + tbitmap.detect[i] = 2; + } + + pbitmap = checkedmalloc(sizeof(bitmap)); memcpy(pbitmap, &tbitmap, sizeof(tbitmap)); @@ -110,20 +282,18 @@ finish_state(void) short steg_use_bit (unsigned short temp) { - if ((temp & 0x1) == temp) + if ((temp & 0x1) == temp) goto steg_end; switch (jpeg_state) { case JPEG_READING: WRITE_BIT(tbitmap.bitmap, off, temp & 0x1); - if (abs((short) temp) >= JPG_THRES_MAX) - tbitmap.detect[off] = -1; - else if (abs((short) temp) >= JPG_THRES_LOW) - tbitmap.detect[off] = 0; - else if (abs((short) temp) >= JPG_THRES_MIN) - tbitmap.detect[off] = 1; - else - tbitmap.detect[off] = 2; + tbitmap.data[off] = temp; + + if ((short)temp < dctmin) + dctmin = (short)temp; + if ((short)temp > dctmax) + dctmax = (short)temp; off++; @@ -137,11 +307,17 @@ steg_use_bit (unsigned short temp) exit(1); } tbitmap.bitmap = buf; - if (!(buf = realloc(tbitmap.detect, tbitmap.bits))) { + if (!(buf = realloc(tbitmap.locked, tbitmap.bytes))) { + fprintf(stderr, "steg_use_bit: realloc()\n"); + exit(1); + } + tbitmap.locked = buf; + memset(tbitmap.locked + tbitmap.bytes - 256, 0, 256); + if (!(buf = realloc(tbitmap.data, tbitmap.bits))) { fprintf(stderr, "steg_use_bit: realloc()\n"); exit(1); } - tbitmap.detect = buf; + tbitmap.data = buf; } break; default: @@ -154,8 +330,9 @@ steg_use_bit (unsigned short temp) steg_end: if (jpeg_eval) { if (eval_cnt % DCTSIZE2 == 0) - fprintf(stderr, "\n%.7d: ", eval_cnt); - fprintf(stderr, "% .3d,", (short) temp); + fprintf(stderr, "\n[%d]%.7d: ", jpeg_state, eval_cnt); + if ((temp & 0x1) != temp) + fprintf(stderr, "% .3d,", (short) temp); eval_cnt++; } diff --git a/jpg.h b/jpg.h index dfcc8d7..b099643 100644 --- a/jpg.h +++ b/jpg.h @@ -37,6 +37,8 @@ void init_JPEG_handler(char *parameters); +int preserve_jpg(bitmap *, int); + void write_JPEG_file (FILE *outfile, image *image); image *read_JPEG_file (FILE *infile); diff --git a/missing/CVS/Entries b/missing/CVS/Entries new file mode 100644 index 0000000..6987c1d --- /dev/null +++ b/missing/CVS/Entries @@ -0,0 +1,5 @@ +/err.c/1.1/Tue Feb 13 19:41:15 2001// +/err.h/1.1/Tue Feb 13 19:41:15 2001// +/md5.c/1.1/Mon May 1 19:57:38 2000// +/md5.h/1.1/Mon May 1 19:57:38 2000// +D diff --git a/missing/CVS/Repository b/missing/CVS/Repository new file mode 100644 index 0000000..f7a9774 --- /dev/null +++ b/missing/CVS/Repository @@ -0,0 +1 @@ +outguess/missing diff --git a/missing/CVS/Root b/missing/CVS/Root new file mode 100644 index 0000000..bd2f612 --- /dev/null +++ b/missing/CVS/Root @@ -0,0 +1 @@ +provos@monkey.org:/cvs diff --git a/missing/err.c b/missing/err.c new file mode 100644 index 0000000..0c6793a --- /dev/null +++ b/missing/err.c @@ -0,0 +1,99 @@ +/* + * err.c + * + * Adapted from OpenBSD libc *err* *warn* code. + * + * Copyright (c) 2000 Dug Song + * + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +void +err(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + va_end(ap); + (void)fprintf(stderr, "%s\n", strerror(errno)); + exit(eval); +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + va_end(ap); + (void)fprintf(stderr, "%s\n", strerror(errno)); +} + +void +errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + va_end(ap); + exit(eval); +} + +void +warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + va_end(ap); +} + diff --git a/missing/err.h b/missing/err.h new file mode 100644 index 0000000..c78389a --- /dev/null +++ b/missing/err.h @@ -0,0 +1,50 @@ +/* + * err.h + * + * Adapted from OpenBSD libc *err* *warn* code. + * + * Copyright (c) 2000 Dug Song + * + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)err.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _ERR_H_ +#define _ERR_H_ + +void err(int eval, const char *fmt, ...); +void warn(const char *fmt, ...); +void errx(int eval, const char *fmt, ...); +void warnx(const char *fmt, ...); + +#endif /* !_ERR_H_ */ diff --git a/md5c.c b/missing/md5.c similarity index 100% rename from md5c.c rename to missing/md5.c diff --git a/md5.h b/missing/md5.h similarity index 100% rename from md5.h rename to missing/md5.h diff --git a/outguess.1 b/outguess.1 new file mode 100644 index 0000000..3564e60 --- /dev/null +++ b/outguess.1 @@ -0,0 +1,187 @@ +.\" outguess manpage, converted from README +.\" dugsong@monkey.org +.TH OUTGUESS 1 "1 May 2000" +.SH NAME +outguess - universal steganographic tool +.SH SYNOPSIS +.B outguess +[ +.B \-emt +] [ +.B \-r +] [ +.B \-k +.I key +] [ +.B \-F +[+-] +] [ +.B \-d +.I datafile +] [ +.B \-s +.I seed +] [ +.B \-i +.I limit +] [ +.B \-x +.I maxkeys +] [ +.B \-p +.I param +] [ +.I inputfile +[ +.I outputfile +]] +.LP +.SH DESCRIPTION +.LP +.I Outguess +is a universal steganographic tool that allows the insertion +of hidden information into the redundant bits of data sources. The +nature of the data source is irrelevant to the core of +.IR outguess . +The program relies on data specific handlers that will extract +redundant bits and write them back after modification. Currently only +the PPM, PNM, and JPEG image formats are supported, although +.I outguess +could use any kind of data, as long as a handler were provided. +.PP +.I Outguess +uses a generic iterator object to select which bits in the data should +be modified. A seed can be used to modify the behavior of the +iterator. It is embedded in the data along with the rest of the +message. By altering the seed, +.I outguess +tries to find a sequence of bits that minimizes the number of changes +in the data that have to be made. +.PP +A bias is introduced that favors the modification of bits that were +extracted from a high value, and tries to avoid the modification of +bits that were extracted from a low value. +.PP +Additionally, +.I Outguess +allows for the hiding of two distinct messages in the data, thus +providing plausible deniablity. It keeps track of the bits that have +been modified previously and locks them. A (23,12,7) Golay code is +used for error correction to tolerate collisions on locked bits. +Artifical errors are introduced to avoid modifying bits that have a +high bias. +.SH OPTIONS +.LP +The following command line options, when specified as capital letters, +indicate options for the second message. +.TP +.B \-F \fI[+-]\fR +Specifies that OutGuess should preserve statistics based on frequency +counts. As a result, no statistical test that is based on frequency +counts will be able to detect steganographic content. This option is +on by default. +.TP +.B \-kK \fIkey\fR +Specify the secret key used to encrypt and hide the message in the +provided data. +.TP +.B \-dD \fIdatafile\fR +Specify the filename containing a message to be hidden in the data. +.TP +.B \-sS \fIseed\fR +Specify the initial seed the iterator object uses for selecting bits +in the redundant data. If no upper limit is specified, the iterator +will use this seed without searching for a more optimal embedding. +.TP +.B \-iI \fIlimit\fR +Specify the upper limit for finding an optimal iterator seed. The +maximum value for the limit is 65535. +.TP +.B \-eE +Use error correction for data encoding and decoding. +.PP +Other options that apply to the general execution of +.IR outguess : +.TP +.B \-r +Retrieve a message from a data object. If this option is not +specified, +.I outguess +will embed messages. +.TP +.B \-x \fImaxkeys\fR +If the second key does not create an iterator object +that is successful in embedding the data, the program +will derive up to specified number of new keys. +.TP +.B \-p \fIparam\fR +Passes a string as parameter to the destination data handler. For the +JPEG image format, this is the compression quality, it can take values +between 75 and 100. The higher the quality the more bits to hide a +message in the data are available. +.TP +.B \-m +Mark pixels that have been modified. +.TP +.B \-t +Collect statistics about redundant bit usage. Repeated use increases +output level. +.PP +For embedding messages, you need to specify a source and a destination +filename. +.I Outguess +determines the data format by the filename extension. If no filenames +are specified +.I outguess +operates as a filter and assumes the PPM data format. +.SH EXAMPLES +.LP +To embed the message +.I hidden.txt +into the +.I monkey.jpg +image: +.IP +.B outguess \-k +"my secret pass phrase" +.B \-d +.I hidden.txt monkey.jpg out.jpg +.PP +And in the other direction: +.IP +.B outguess \-k +"my secret pass phrase" +.B \-r +.I out.jpg message.txt +.PP +will retrieve the hidden message from the image. +.PP +If you want to embed a second message, use: +.IP +.B outguess \-k +"secret1" +.B \-d +.I hide1.txt +.B \-E \-K +"secret2" +.B \-D +.I hide2.txt monkey.jpg out.jpg +.PP +.I Outguess +will first embed +.I hide1.txt +and then +.I hide2.txt +on top of it, using error correcting codes. The second message +.I hide2.txt +can be retrieved with +.IP +.B outguess \-k +"secret2" +.B \-e \-r +.I out.jpg message.txt +.LP +.SH "SEE ALSO" +cjpeg(1), djpeg(1), pnm(5), stirmark(1) +.SH AUTHOR +Niels Provos diff --git a/outguess.c b/outguess.c index f638308..557be9e 100644 --- a/outguess.c +++ b/outguess.c @@ -1,8 +1,9 @@ /* * Outguess - a Universal Steganograpy Tool for - * Steganographic Experiments and Fourier Transformation -.* (c) 1999 Niels Provos + * + * Copyright (c) 1999-2001 Niels Provos * Features + * - preserves frequency count based statistics * - multiple data embedding * - PRNG driven selection of bits * - error correcting encoding @@ -10,7 +11,7 @@ */ /* - * Copyright 1999 Niels Provos + * Copyright (c) 1999-2001 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -56,19 +58,7 @@ #include "golay.h" #include "pnm.h" #include "jpg.h" - -#ifdef FOURIER -#include -#include - -void fft_visible(int xdim, int ydim, fftw_complex *c, u_char *img, - double maxre, double maxim, double maxmod); -fftw_complex *fft_transform(int xdim, int ydim, unsigned char *data, - double *mre, double *mim, double *mmod); -void fft_filter(int xdim, int ydim, fftw_complex *data); -u_char *fft_transform_back(int xdim, int ydim, fftw_complex *data); - -#endif /* FOURIER */ +#include "iterator.h" #ifndef MAP_FAILED /* Some Linux systems are missing this */ @@ -80,9 +70,14 @@ static int steg_err_cnt; static int steg_errors; static int steg_encoded; +static int steg_offset[MAX_SEEK]; +int steg_foil; +int steg_foilfail; + static int steg_count; static int steg_mis; static int steg_mod; +static int steg_data; /* Exported variables */ @@ -124,256 +119,6 @@ checkedmalloc(size_t n) return p; } -#ifdef FOURIER - -/* if depth > 1 */ -int -split_colors(u_char **pred, u_char **pgreen, u_char **pblue, - u_char *img, - int xdim, int ydim, int depth) -{ - int i, j; - u_char *red, *green, *blue; - - /* Split to red - blue */ - red = checkedmalloc(ydim*xdim); - green = checkedmalloc(ydim*xdim); - blue = checkedmalloc(ydim*xdim); - - for (i = 0; i < ydim; i++) { - for (j = 0; j < xdim; j++) { - red[j + xdim*i] = img[0 + depth*(j + xdim*i)]; - green[j + xdim*i] = img[1 + depth*(j + xdim*i)]; - blue[j + xdim*i] = img[2 + depth*(j + xdim*i)]; - } - } - - *pred = red; - *pgreen = green; - *pblue = blue; - - return 1; -} - -void -fft_image(int xdim, int ydim, int depth, u_char *img) -{ - int i,j; - u_char *red, *green, *blue; - fftw_complex *c, *d, *e; - double maxre, maxim, maxmod; - - split_colors(&red, &green, &blue, img, xdim, ydim, depth); - - maxre = maxim = maxmod = 0; - c = fft_transform(xdim, ydim, red, &maxre, &maxim, &maxmod); - d = fft_transform(xdim, ydim, green, &maxre, &maxim, &maxmod); - e = fft_transform(xdim, ydim, blue, &maxre, &maxim, &maxmod); - fft_visible(xdim, ydim, c, red, maxre, maxim, maxmod); - free(c); - fft_visible(xdim, ydim, d, green, maxre, maxim, maxmod); - free(d); - fft_visible(xdim, ydim, e, blue, maxre, maxim, maxmod); - free(e); - - for (i = 0; i < ydim; i++) { - for (j = 0; j < xdim; j++) { - img[0 + depth*(j + xdim*i)] = red[j + xdim*i]; - img[1 + depth*(j + xdim*i)] = green[j + xdim*i]; - img[2 + depth*(j + xdim*i)] = blue[j + xdim*i]; - } - } - - free(red); free(green); free(blue); -} - -void -fft_visible(int xdim, int ydim, fftw_complex *c, u_char *img, - double maxre, double maxim, double maxmod) -{ - int i, j, ind; - double contrast, scale, factor, total, val; - - contrast = exp((-100.5) * log(xdim*ydim) / 256); - factor = - 255.0; - total = 0; - scale = factor / log(maxmod * contrast * contrast + 1.0); - - printf("Visible: max (%f/%f/%f), contrast: %f, scale: %f\n", - maxre, maxim, maxmod, contrast, scale); - - for (i = 0; i < xdim ; i++) - for (j = 0; j < ydim; j++) { - ind = j*xdim + i; - val = total - scale*log(contrast*contrast* - (c[ind].re*c[ind].re + - c[ind].im*c[ind].im) + 1); - img[ind] = val; - } -} - -fftw_complex * -fft_transform(int xdim, int ydim, unsigned char *data, - double *mre, double *mim, double *mmod) -{ - rfftwnd_plan p; - int i,j, ind, di, dj; - double maxre, maxim, maxmod, val; - fftw_complex *a; - - fprintf(stderr, "Starting complex 2d FFT\n"); - - a = checkedmalloc(xdim * ydim * sizeof(fftw_complex)); - - p = fftw2d_create_plan(ydim, xdim, FFTW_FORWARD, - FFTW_ESTIMATE | FFTW_IN_PLACE); - - di = 1; - for (j = 0; j < ydim; j++) { - dj = di; - for (i = 0; i < xdim ; i++) { - ind = i + j * xdim; - a[ind].re = data[ind] * dj; - a[ind].im = 0.0; - dj = -dj; - } - di = -di; - } - - fftwnd_one(p, a, NULL); - - maxim = *mim; - maxre = *mre; - maxmod = *mmod; - for (i = 0; i < xdim ; i++) - for (j = 0; j < ydim; j++) { - ind = j*xdim + i; - - /* Update Stats */ - if (fabs(a[ind].re) > maxre) maxre = fabs(a[ind].re); - if (fabs(a[ind].im) > maxim) maxim = fabs(a[ind].im); - val = a[ind].re * a[ind].re + a[ind].im * a[ind].im; - if (val > maxmod) maxmod = val; - } - *mim = maxim; - *mre = maxre; - *mmod = maxmod; - - fftwnd_destroy_plan(p); - - return a; -} - -void -fft_filter(int xdim, int ydim, fftw_complex *data) -{ - int i, j, ind; - double val; - - fprintf(stderr, "Starting complex filtering\n"); - - for (j = 0; j < ydim; j++) { - for (i = 0; i < xdim ; i++) { - ind = i + j * xdim; - val = sqrt((xdim/2-i)*(xdim/2-i) + - (ydim/2-j)*(ydim/2-j)); - if (val > 15) { - data[ind].re = 2*data[ind].re; - data[ind].im = 2*data[ind].im; - } else { - data[ind].re = 0.2*data[ind].re; - data[ind].im = 0.2*data[ind].im; - } - } - } -} - -u_char * -fft_transform_back(int xdim, int ydim, fftw_complex *data) -{ - rfftwnd_plan p; - int i,j, ind; - fftw_real dj, val; - u_char *a; - - fprintf(stderr, "Starting complex 2d FFT-back\n"); - - a = checkedmalloc(xdim * ydim * sizeof(u_char)); - - p = fftw2d_create_plan(ydim, xdim, FFTW_BACKWARD, - FFTW_ESTIMATE | FFTW_IN_PLACE); - - fftwnd_one(p, data, NULL); - - dj = (xdim * ydim); - for (j = 0; j < ydim; j++) { - for (i = 0; i < xdim ; i++) { - ind = i + j * xdim; - val = sqrt(data[ind].re * data[ind].re + - data[ind].im * data[ind].im)/dj; - a[ind] = val; - } - } - - return a; -} -#endif /* FOURIER */ - -/* Initalize the iterator */ - -void -iterator_init(iterator *iter, bitmap *bitmap, struct arc4_stream *as) -{ - int i; - char derive[16]; - - iter->skipmod = INIT_SKIPMOD; - iter->as = *as; - - /* Put the PRNG in different state, using key dependant data - * provided by the PRNG itself. - */ - for (i = 0; i < sizeof(derive); i++) - derive[i] = arc4_getbyte(&iter->as); - arc4_addrandom(&iter->as, derive, sizeof(derive)); - - iter->off = arc4_getword(&iter->as) % iter->skipmod; -} - -/* The next bit in the bitmap we should embed data into */ - -int -iterator_next(iterator *iter, bitmap *bitmap) -{ - iter->off += (arc4_getword(&iter->as) % iter->skipmod) + 1; - - return iter->off; -} - -static __inline int -iterator_current(iterator *iter) -{ - return iter->off; -} - -void -iterator_seed(iterator *iter, bitmap *bitmap, u_int16_t seed) -{ - u_int8_t reseed[2]; - - reseed[0] = seed; - reseed[1] = seed >> 8; - - arc4_addrandom(&iter->as, reseed, 2); -} - -void -iterator_adapt(iterator *iter, bitmap *bitmap, int datalen) -{ - iter->skipmod = SKIPADJ(bitmap->bits, bitmap->bits - iter->off) * - (bitmap->bits - iter->off)/(8 * datalen); -} - /* * The error correction might allow us to introduce extra errors to * avoid modifying data. Choose to leave bits with high detectability @@ -420,6 +165,7 @@ steg_adjust_errors(bitmap *bitmap, int flags) for (i = 0; i < j; i++) { if (flags & STEG_EMBED) { + WRITE_BIT(bitmap->locked, i, 0); if (TEST_BIT(bitmap->bitmap, priority[i])) WRITE_BIT(bitmap->bitmap, i, 0); else @@ -434,10 +180,17 @@ int steg_embedchunk(bitmap *bitmap, iterator *iter, u_int32_t data, int bits, int embed) { - int i = iterator_current(iter); + int i = ITERATOR_CURRENT(iter); + u_int8_t bit; u_int32_t val; + u_char *pbits, *plocked; + int nbits; + + pbits = bitmap->bitmap; + plocked = bitmap->locked; + nbits = bitmap->bits; - while (i < bitmap->bits && bits) { + while (i < nbits && bits) { if ((embed & STEG_ERROR) && !steg_encoded) { if (steg_err_cnt > 0) steg_adjust_errors(bitmap, embed); @@ -448,7 +201,8 @@ steg_embedchunk(bitmap *bitmap, iterator *iter, } steg_encoded--; - val = (TEST_BIT(bitmap->bitmap, i) ? 1 : 0) ^ (data & 1); + bit = TEST_BIT(pbits, i) ? 1 : 0; + val = bit ^ (data & 1); steg_count++; if (val == 1) { steg_mod += bitmap->detect[i]; @@ -456,8 +210,7 @@ steg_embedchunk(bitmap *bitmap, iterator *iter, } /* Check if we are allowed to change a bit here */ - if ((embed & STEG_FORBID) && (val == 1) && - (TEST_BIT(bitmap->locked, i))) { + if ((val == 1) && TEST_BIT(plocked, i)) { if (!(embed & STEG_ERROR) || (++steg_errors > 3)) return 0; val = 2; @@ -468,10 +221,8 @@ steg_embedchunk(bitmap *bitmap, iterator *iter, steg_err_buf[steg_err_cnt++] = i; if (val != 2 && (embed & STEG_EMBED)) { - if (embed & (STEG_FORBID|STEG_MARK)) - WRITE_BIT(bitmap->locked, i, 1); - - WRITE_BIT(bitmap->bitmap, i, data & 1); + WRITE_BIT(plocked, i, 1); + WRITE_BIT(pbits, i, data & 1); } data >>= 1; @@ -497,7 +248,7 @@ steg_embed(bitmap *bitmap, iterator *iter, struct arc4_stream *as, memset(&result, 0, sizeof(result)); if (bitmap->bits / (datalen * 8) < 2) { - fprintf(stderr, "steb_embed: not enough bits in bitmap " + fprintf(stderr, "steg_embed: not enough bits in bitmap " "for embedding: %d > %d/2\n", datalen * 8, bitmap->bits); exit (1); @@ -542,7 +293,7 @@ steg_embed(bitmap *bitmap, iterator *iter, struct arc4_stream *as, iterator_seed(iter, bitmap, seed); - while (iterator_current(iter) < bitmap->bits && datalen > 0) { + while (ITERATOR_CURRENT(iter) < bitmap->bits && datalen > 0) { iterator_adapt(iter, bitmap, datalen); tmp = *data++; @@ -558,14 +309,17 @@ steg_embed(bitmap *bitmap, iterator *iter, struct arc4_stream *as, if ((embed & STEG_ERROR) && steg_err_cnt > 0) steg_adjust_errors(bitmap, embed); - if (embed & STEG_EMBED) - fprintf(stderr, "Bits embedded: %d, changed: %d(%2.1f%%), " + if (embed & STEG_EMBED) { + fprintf(stderr, "Bits embedded: %d, " + "changed: %d(%2.1f%%)[%2.1f%%], " "bias: %d, tot: %d, skip: %d\n", steg_count, steg_mis, (float) 100 * steg_mis/steg_count, + (float) 100 * steg_mis/steg_data, /* normalized */ steg_mod, - iterator_current(iter), - iterator_current(iter) - steg_count); + ITERATOR_CURRENT(iter), + ITERATOR_CURRENT(iter) - steg_count); + } result.changed = steg_mis; result.bias = steg_mod; @@ -576,7 +330,7 @@ steg_embed(bitmap *bitmap, iterator *iter, struct arc4_stream *as, u_int32_t steg_retrbyte(bitmap *bitmap, int bits, iterator *iter) { - u_int32_t i = iterator_current(iter); + u_int32_t i = ITERATOR_CURRENT(iter); int where; u_int32_t tmp = 0; @@ -686,6 +440,10 @@ steg_find(bitmap *bitmap, iterator *iter, struct arc4_stream *as, else if (result.error) continue; + /* + * Only count bias, if we do not modifiy many + * extra bits for statistical foiling. + */ tch = result.changed + result.bias; if (steg_stat) @@ -699,12 +457,14 @@ steg_find(bitmap *bitmap, iterator *iter, struct arc4_stream *as, if (changed == -1 || tch < changed) { changed = tch; j = i; - fprintf(stderr, "New best: %5u: %5u(%2.1f%%), bias %5u(%1.2f), saved: % 5d\n", + fprintf(stderr, "%5u: %5u(%3.1f%%)[%3.1f%%], bias %5d(%1.2f), saved: % 5d, total: %5.2f%%\n", j, result.changed, (float) 100 * steg_mis / steg_count, + (float) 100 * steg_mis / steg_data, result.bias, (float)result.bias / steg_mis, - (half - result.changed) / 8); + (half - result.changed) / 8, + (float) 100 * steg_mis / bitmap->bits); } } @@ -899,6 +659,62 @@ decode_data(u_char *encdata, int *len, struct arc4_stream *as, int flags) return data; } +int +do_embed(bitmap *bitmap, u_char *filename, u_char *key, u_int klen, + config *cfg, stegres *result) +{ + iterator iter; + struct arc4_stream as, tas; + u_char *encdata, *data; + u_int datalen, enclen; + size_t correctlen; + int j; + + /* Initialize random data stream */ + arc4_initkey(&as, "Encryption", key, klen); + tas = as; + + iterator_init(&iter, bitmap, key, klen); + + /* Encode the data for us */ + mmap_file(filename, &data, &datalen); + steg_data = datalen * 8; + enclen = datalen; + encdata = encode_data(data, &enclen, &tas, cfg->flags); + if (cfg->flags & STEG_ERROR) { + fprintf(stderr, "Encoded '%s' with ECC: %d bits, %d bytes\n", + filename, enclen * 8, enclen); + correctlen = enclen / 2 * 8; + } else { + fprintf(stderr, "Encoded '%s': %d bits, %d bytes\n", + filename, enclen * 8, enclen); + correctlen = enclen * 8; + } + if (bitmap->maxcorrect && correctlen > bitmap->maxcorrect) { + fprintf(stderr, "steg_embed: " + "message larger than correctable size %d > %d\n", + correctlen, bitmap->maxcorrect); + exit(1); + } + + munmap_file(data, datalen); + + j = steg_find(bitmap, &iter, &as, cfg->siter, cfg->siterstart, + encdata, enclen, cfg->flags); + if (j < 0) { + fprintf(stderr, "Failed to find embedding.\n"); + goto out; + } + + *result = steg_embed(bitmap, &iter, &as, encdata, enclen, j, + cfg->flags | STEG_EMBED); + + out: + free(encdata); + + return (j); +} + void mmap_file(char *name, u_char **data, int *size) { @@ -950,7 +766,7 @@ munmap_file(u_char *data, int len) int main(int argc, char **argv) { - char version[] = "OutGuess 0.13b Universal Stego (c) 1999 Niels Provos"; + char version[] = "OutGuess 0.2 Universal Stego (c) 1999-2001 Niels Provos"; char usage[] = "%s\n\n%s [options] [ []]\n" "\t-[sS] iteration start, capital letter for 2nd dataset\n" "\t-[iI] iteration limit\n" @@ -962,42 +778,66 @@ main(int argc, char **argv) "\t-x number of key derivations to be tried\n" "\t-m mark pixels that have been modified\n" "\t-t collect statistic information\n" + "\t-F[+-] turns statistical steganalysis foiling on/off.\n" + "\t The default is on.\n" #ifdef FOURIER "\t-f fourier transform modified image\n" #endif /* FOURIER */ ; + char *progname; FILE *fin = stdin, *fout = stdout; image *image; handler *srch = NULL, *dsth = NULL; char *param = NULL; unsigned char *encdata; bitmap bitmap; /* Extracted bits that we may modify */ - iterator iter, titer; + iterator iter; int j, ch, derive = 0; stegres cumres, tmpres; - u_int16_t siter = 0, siterstart = 0, siter2 = 0, siterstart2 = 0; + config cfg1, cfg2; u_char *data = NULL, *data2 = NULL; - int enclen, datalen, flags = 0; + int datalen; char *key = "Default key", *key2 = NULL; struct arc4_stream as, tas; - char mark = 0, useforbidden = 0, doretrieve = 0; + char mark = 0, doretrieve = 0; char doerror = 0, doerror2 = 0; + char *cp; + int extractonly = 0, foil = 1; #ifdef FOURIER char dofourier = 0; #endif /* FOURIER */ + progname = argv[0]; steg_stat = 0; + memset(&cfg1, 0, sizeof(cfg1)); + memset(&cfg2, 0, sizeof(cfg2)); + + if (strchr(argv[0], '/')) + cp = strrchr(argv[0], '/') + 1; + else + cp = argv[0]; + if (!strcmp("extract", cp)) { + extractonly = 1; + doretrieve = 1; + argv++; + argc--; + goto aftergetop; + } + /* read command line arguments */ - while ((ch = getopt(argc, argv, "eErmftp:s:S:i:I:k:d:D:K:x:")) != -1) + while ((ch = getopt(argc, argv, "eErmftp:s:S:i:I:k:d:D:K:x:F:")) != -1) switch((char)ch) { + case 'F': + if (optarg[0] == '-') + foil = 0; + break; case 'k': key = optarg; break; case 'K': key2 = optarg; - useforbidden = 1; break; case 'p': param = optarg; @@ -1006,10 +846,10 @@ main(int argc, char **argv) derive = atoi(optarg); break; case 'i': - siter = atoi(optarg); + cfg1.siter = atoi(optarg); break; case 'I': - siter2 = atoi(optarg); + cfg2.siter = atoi(optarg); break; case 'r': doretrieve = 1; @@ -1018,10 +858,10 @@ main(int argc, char **argv) steg_stat++; break; case 's': - siterstart = atoi(optarg); + cfg1.siterstart = atoi(optarg); break; case 'S': - siterstart2 = atoi(optarg); + cfg2.siterstart = atoi(optarg); break; #ifdef FOURIER case 'f': @@ -1049,15 +889,16 @@ main(int argc, char **argv) } argc -= optind; + argv += optind; + aftergetop: if ((argc != 2 && argc != 0) || - (!doretrieve && data == NULL)) { - fprintf(stderr, usage, version, argv[0]); + (extractonly && argc != 2) || + (!doretrieve && !extractonly && data == NULL)) { + fprintf(stderr, usage, version, progname); exit(1); } - argv += optind; - if (argc == 2) { srch = get_handler(argv[0]); if (srch == NULL) { @@ -1099,13 +940,20 @@ main(int argc, char **argv) init_golay(); } - if (doerror) - flags |= STEG_ERROR; - fprintf(stderr, "Reading %s....\n", argv[0]); image = srch->read(fin); - fprintf(stderr, "Extracting usable bits ...\n"); - if (doretrieve) + + if (extractonly) { + int bits; + /* Wen extracting get the bitmap from the source handler */ + srch->get_bitmap(&bitmap, image, STEG_RETRIEVE); + + fprintf(stderr, "Writing %d bits\n", bitmap.bits); + bits = htonl(bitmap.bits); + fwrite(&bits, 1, sizeof(int), fout); + fwrite(bitmap.bitmap, bitmap.bytes, sizeof(char), fout); + exit (1); + } else if (doretrieve) /* Wen extracting get the bitmap from the source handler */ srch->get_bitmap(&bitmap, image, STEG_RETRIEVE); else { @@ -1114,50 +962,36 @@ main(int argc, char **argv) /* When embedding the destination format determines the bits */ dsth->get_bitmap(&bitmap, image, 0); } + fprintf(stderr, "Extracting usable bits: %d bits\n", bitmap.bits); - /* Initialize random data stream */ - arc4_initkey(&as, key, strlen(key)); - tas = as; - - iterator_init(&iter, &bitmap, &as); - + if (doerror) + cfg1.flags |= STEG_ERROR; + if (!doretrieve) { - if (mark) - flags |= STEG_MARK; - - if (useforbidden) - flags |= STEG_FORBID; - - /* Encode the data for us */ - mmap_file(data, &data, &datalen); - enclen = datalen; - encdata = encode_data(data, &enclen, &tas, flags); - if (flags & STEG_ERROR) - fprintf(stderr, "Encoded data with ECC: %d\n", enclen); - else - fprintf(stderr, "Encoded data: %d\n", datalen); - - munmap_file(data, datalen); - - j = steg_find(&bitmap, &iter, &as, siter, siterstart, - encdata, enclen, flags); - - cumres = steg_embed(&bitmap, &iter, &as, encdata, enclen, j, flags | STEG_EMBED); + cfg1.flags |= STEG_MARK; + if (foil) { + dsth->preserve(&bitmap, -1); + if (bitmap.maxcorrect) + fprintf(stderr, + "Correctable message size: %d bits, %0.2f%%\n", + bitmap.maxcorrect, + (float)100*bitmap.maxcorrect/bitmap.bits); + } - free(encdata); + do_embed(&bitmap, data, key, strlen(key), &cfg1, &cumres); if (key2 && data2) { char derivekey[128]; - struct arc4_stream tas; int i; + /* Flags from first configuration are being copied */ + cfg2.flags = cfg1.flags; if (doerror2) - flags |= STEG_ERROR; + cfg2.flags |= STEG_ERROR; else - flags &= ~STEG_ERROR; + cfg2.flags &= ~STEG_ERROR; - encdata = NULL; for (j = -1, i = 0; i <= derive && j < 0; i++) { #ifdef HAVE_SNPRINTF snprintf(derivekey, 128, "%s%d", key2, i); @@ -1166,30 +1000,10 @@ main(int argc, char **argv) #endif /* HAVE_SNPRINTF */ if (i == 0) derivekey[strlen(key2)] = '\0'; - arc4_initkey(&as, derivekey, strlen(derivekey)); - iterator_init(&iter, &bitmap, &as); - - tas = as; - titer = iter; - - if (encdata) - free (encdata); - /* Map the file and encode its data */ - mmap_file(data2, &data, &datalen); - enclen = datalen; - encdata = encode_data(data, &enclen, &tas, flags); - if (flags & STEG_ERROR) - fprintf(stderr, "Encoded data with ECC: %d\n", enclen); - else - fprintf(stderr, "Encoded data: %d\n", datalen); - munmap_file(data, datalen); - - fprintf(stderr, "2nd Embedding key: %s, %x\n", - derivekey, arc4_getword(&tas)); - - j = steg_find(&bitmap, &titer, &as, siter2, siterstart2, - encdata, enclen, flags); + j = do_embed(&bitmap, data2, + derivekey, strlen(derivekey), + &cfg2, &tmpres); } if (j < 0) { @@ -1197,16 +1011,72 @@ main(int argc, char **argv) exit (1); } - tmpres = steg_embed(&bitmap, &iter, &as, encdata, enclen, j, flags | STEG_EMBED); cumres.changed += tmpres.changed; cumres.bias += tmpres.bias; } + if (foil) { + int i, count; + double mean, dev, sq; + int n; + u_char cbit; + u_char *pbits = bitmap.bitmap; + u_char *data = bitmap.data; + u_char *plocked = bitmap.locked; + + memset(steg_offset, 0, sizeof(steg_offset)); + steg_foil = steg_foilfail = 0; + + for (i = 0; i < bitmap.bits; i++) { + if (!TEST_BIT(plocked, i)) + continue; + + cbit = TEST_BIT(pbits, i) ? 1 : 0; + + if (cbit == (data[i] & 0x01)) + continue; + + n = bitmap.preserve(&bitmap, i); + if (n > 0) { + /* Actual modificaton */ + n = abs(n - i); + if (n > MAX_SEEK) + n = MAX_SEEK; + + steg_offset[n - 1]++; + } + } + + /* Indicates that we are done with the image */ + bitmap.preserve(&bitmap, bitmap.bits); + + /* Calculate statistics */ + count = 0; + mean = 0; + for (i = 0; i < MAX_SEEK; i++) { + count += steg_offset[i]; + mean += steg_offset[i] * (i + 1); + } + mean /= count; + + dev = 0; + for (i = 0; i < MAX_SEEK; i++) { + sq = (i + 1 - mean) * (i + 1 - mean); + dev += steg_offset[i] * sq; + } + + fprintf(stderr, "Foiling statistics: " + "corrections: %d, failed: %d, " + "offset: %f +- %f\n", + steg_foil, steg_foilfail, + mean, sqrt(dev / (count - 1))); + } + fprintf(stderr, "Total bits changed: %d (change %d + bias %d)\n", cumres.changed + cumres.bias, cumres.changed, cumres.bias); fprintf(stderr, "Storing bitmap into data...\n"); - dsth->put_bitmap (image, &bitmap, flags); + dsth->put_bitmap (image, &bitmap, cfg1.flags); #ifdef FOURIER if (dofourier) @@ -1217,9 +1087,16 @@ main(int argc, char **argv) fprintf(stderr, "Writing %s....\n", argv[1]); dsth->write(fout, image); } else { - encdata = steg_retrieve(&datalen, &bitmap, &iter, &as, flags); + /* Initialize random data stream */ + arc4_initkey(&as, "Encryption", key, strlen(key)); + tas = as; + + iterator_init(&iter, &bitmap, key, strlen(key)); + + encdata = steg_retrieve(&datalen, &bitmap, &iter, &as, + cfg1.flags); - data = decode_data(encdata, &datalen, &tas, flags); + data = decode_data(encdata, &datalen, &tas, cfg1.flags); free(encdata); fwrite(data, datalen, sizeof(u_char), fout); diff --git a/outguess.h b/outguess.h index bb60f48..b0e6ebb 100644 --- a/outguess.h +++ b/outguess.h @@ -1,5 +1,5 @@ /* - * Copyright 1999 Niels Provos + * Copyright (C) 1999-2001 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,22 +36,16 @@ #define BITSHIFT 0 /* which bit in the byte the data is in */ #define MAX_DEPTH 3 /* maximum number of bytes per pixel */ - -#define INIT_SKIPMOD 32 -#define DEFAULT_ITER 256 +#define MAX_SEEK 1024 /* maximum number of pixels for foil stats */ #define STEG_EMBED 0x01 #define STEG_MARK 0x02 -#define STEG_FORBID 0x04 #define STEG_ERROR 0x08 #define STEG_RETRIEVE 0x10 #define STEG_STATS 0x20 -/* Slowly fade skip out at the end of the picture */ -#define SKIPADJ(x,y) ((y) > (x)/32 ? 2 : 2 - ((x/32) - (y))/(float)(x/32)) - extern int steg_stat; /* @@ -64,9 +58,15 @@ extern int steg_stat; typedef struct _bitmap { u_char *bitmap; /* the bitmap */ u_char *locked; /* bits that may not be modified */ + u_char *metalock; /* bits that have been used for foil */ char *detect; /* relative detectability of changes */ + char *data; /* data associated with the bit */ int bytes; /* allocated bytes */ int bits; /* number of bits in here */ + + /* function to call for preserve stats */ + int (*preserve)(struct _bitmap *, int); + size_t maxcorrect; } bitmap; #define STEG_ERR_HEADER 1 @@ -79,15 +79,11 @@ typedef struct _stegres { int bias; /* Accumulated bias of changed bits */ } stegres; -/* - * The generic iterator - */ - -typedef struct _iterator { - struct arc4_stream as; - u_int32_t skipmod; - int off; /* Current bit position */ -} iterator; +typedef struct _config { + int flags; + int siter; + int siterstart; +} config; #define TEST_BIT(x,y) ((x)[(y) / 8] & (1 << ((y) & 7))) #define WRITE_BIT(x,y,what) ((x)[(y) / 8] = ((x)[(y) / 8] & \ @@ -100,14 +96,17 @@ void *checkedmalloc(size_t n); u_char *encode_data(u_char *, int *, struct arc4_stream *, int); u_char *decode_data(u_char *, int *, struct arc4_stream *, int); -int split_colors(u_char **pred, u_char **pgreen, u_char **pblue, - u_char *img, int xdim, int ydim, int depth); +struct _iterator; -stegres steg_embed(bitmap *bitmap, iterator *iter, +stegres steg_embed(bitmap *bitmap, struct _iterator *iter, struct arc4_stream *as, u_char *data, u_int datalen, u_int16_t seed, int embed); -u_int32_t steg_retrbyte(bitmap *bitmap, int bits, iterator *iter); +u_int32_t steg_retrbyte(bitmap *bitmap, int bits, struct _iterator *iter); -char *steg_retrieve(int *len, bitmap *bitmap, iterator *iter, +char *steg_retrieve(int *len, bitmap *bitmap, struct _iterator *iter, struct arc4_stream *as, int); + +void mmap_file(char *name, u_char **data, int *size); +void munmap_file(u_char *data, int len); + #endif /* _OUTGUESS_H */ diff --git a/pnm.c b/pnm.c index 5cea188..3bfc330 100644 --- a/pnm.c +++ b/pnm.c @@ -1,5 +1,5 @@ /* - * Copyright 1999 Niels Provos + * Copyright 1999-2001 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,7 +51,8 @@ handler pnm_handler = { read_pnm, write_pnm, bitmap_from_pnm, - bitmap_to_pnm + bitmap_to_pnm, + preserve_pnm, }; void @@ -59,6 +60,13 @@ init_pnm(char *parameter) { } +int +preserve_pnm(bitmap *bitmap, int off) +{ + + return (-1); +} + /* skip whitespace and comments in PGM/PPM headers */ void skip_white(FILE *f) @@ -110,11 +118,15 @@ bitmap_from_pnm(bitmap *bitmap, image *image, int flags) y = image->y; depth = image->depth; + memset(bitmap, 0, sizeof(*bitmap)); + bitmap->bits = x * y * depth; bitmap->bytes = (bitmap->bits + 7) / 8; bitmap->bitmap = checkedmalloc(bitmap->bytes); bitmap->locked = checkedmalloc(bitmap->bytes); + bitmap->metalock = checkedmalloc(bitmap->bytes); bitmap->detect = checkedmalloc(bitmap->bits); + bitmap->data = checkedmalloc(bitmap->bits); memset (bitmap->locked, 0, bitmap->bytes); @@ -130,6 +142,8 @@ bitmap_from_pnm(bitmap *bitmap, image *image, int flags) else bitmap->detect[i] = 0; + bitmap->data[i] = img[i]; + tmp |= ((img[i++] & (1 << BITSHIFT)) >> BITSHIFT) << j; } bitmap->bitmap[off++] = tmp; diff --git a/pnm.h b/pnm.h index 0aada64..2aaf6be 100644 --- a/pnm.h +++ b/pnm.h @@ -1,5 +1,5 @@ /* - * Copyright 1999 Niels Provos + * Copyright 1999-2001 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,6 +57,7 @@ typedef struct _handler { void (*write)(FILE *, image *); void (*get_bitmap)(bitmap *, image *, int); void (*put_bitmap)(image *, bitmap *, int); + int (*preserve)(bitmap *, int); } handler; extern handler pnm_handler; @@ -65,6 +66,8 @@ void skip_white(FILE *f); void init_pnm(char *); +int preserve_pnm(bitmap *, int); + void bitmap_to_pnm(image *img, bitmap *bitmap, int flags); void bitmap_from_pnm(bitmap *bitmap, image *image, int flags);