/* pnmdestega.c - extract a steganographic message from a portable anymap ** ** Copyright (C) 1994 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. */ #include "pnm.h" static int get_bit( unsigned long loc ); static int comp_r, comp_g, comp_b, num_comps, comp_map[3]; static unsigned long isize; static int format, cols; static xel** xels; #define LENLEN 32 /* length of the length field */ int main( int argc, char* argv[] ) { FILE* ifp; long seed; unsigned long fsize, psize, plen, loc; char* cp; int argn, len, rows, byte, bitnum; xelval maxval; unsigned char c; static unsigned char bitmask[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; char* usage = "[-[no]length] [-seed n] [-components rgb] [pnmfile]"; pnm_init( &argc, argv ); argn = 1; len = 0; seed = 0; comp_r = comp_g = comp_b = 1; num_comps = 3; while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) { if ( pm_keymatch( argv[argn], "-length", 2 ) ) len = 1; else if ( pm_keymatch( argv[argn], "-nolength", 2 ) ) len = 0; else if ( pm_keymatch( argv[argn], "-seed", 2 ) ) { ++argn; if ( argn == argc || sscanf( argv[argn], "%ld", &seed ) != 1 ) pm_usage( usage ); } else if ( pm_keymatch( argv[argn], "-components", 2 ) ) { ++argn; if ( argn == argc ) pm_usage( usage ); comp_r = comp_g = comp_b = 0; for ( cp = argv[argn]; *cp != '\0'; ++cp ) switch ( *cp ) { case 'r': case 'R': comp_r = 1; break; case 'g': case 'G': comp_g = 1; break; case 'b': case 'B': comp_b = 1; break; default: pm_usage( usage ); } num_comps = 0; if ( comp_r ) ++num_comps; if ( comp_g ) ++num_comps; if ( comp_b ) ++num_comps; } else pm_usage( usage ); ++argn; } if ( seed < 0 ) pm_error( "seed must be positive" ); if ( seed != 0 ) pm_srandom( seed ); else if ( len ) pm_message( "warning, using the default seed with a length field is insecure" ); if ( argn != argc ) { ifp = pm_openr( argv[argn] ); ++argn; } else ifp = stdin; if ( argn != argc ) pm_usage( usage ); xels = pnm_readpnm( ifp, &cols, &rows, &maxval, &format ); pm_closer( ifp ); if ( PNM_FORMAT_TYPE(format) == PBM_TYPE ) pm_error( "can't stega from a PBM file - use PGM or PPM" ); if ( PNM_FORMAT_TYPE(format) == PGM_TYPE ) num_comps = 1; isize = rows * cols; psize = isize * num_comps; if ( seed == 0 ) loc = 0; else pm_init_perm( psize, psize ); /* ask for the full permutation */ pm_map_comps( comp_r, comp_g, comp_b, num_comps, comp_map ); if ( len ) { fsize = 0; for ( bitnum = 0; bitnum < LENLEN; ++bitnum ) if ( seed == 0 ) { if ( get_bit( loc++ ) ) fsize |= 1 << bitnum; } else { if ( get_bit( pm_next_perm() ) ) fsize |= 1 << bitnum; } plen = fsize * 8 + LENLEN; if ( plen > psize ) { pm_message( "file length field is %d - that's too big", fsize ); pm_error( "perhaps you got the length/seed/components options wrong?" ); } pm_message( "extracting %d bits from %d bit locations", plen, psize ); } else { fsize = psize / 8; pm_message( "extracting from %d bit locations", psize ); } for ( byte = 0; byte < fsize; ++byte ) { c = 0; for ( bitnum = 0; bitnum < 8; ++bitnum ) if ( seed == 0 ) { if ( get_bit( loc++ ) ) c |= bitmask[bitnum]; } else { if ( get_bit( pm_next_perm() ) ) c |= bitmask[bitnum]; } putchar( c ); } pm_closew( stdout ); exit( 0 ); } static int get_bit( unsigned long loc ) { register int comp, row, col; register xel* xP; register xelval r, g, b; comp = loc / isize; row = ( loc % isize ) / cols; col = loc % cols; xP = &(xels[row][col]); if ( PNM_FORMAT_TYPE(format) == PGM_TYPE ) { g = PNM_GET1( *xP ); return g & 1; } else { r = PPM_GETR( *xP ); g = PPM_GETG( *xP ); b = PPM_GETB( *xP ); switch ( comp_map[comp] ) { case 0: return r & 1; case 1: return g & 1; case 2: return b & 1; default: pm_error( "bogus comp_map entry - shouldn't happen" ); } } }