next up previous
Next: About this document ... Up: Steganography Using Computer Images Previous: Bibliography

Appendix A: Code

File 1, stegFr.cpp, is a steganography program for P2 or P3 PGM images.
//Copyright 2002 William Barratt
//stegFr.cpp
/* User-friendly steganography program for .pgm files.  */

#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void handleArgs(int argc, char* argv[],
        FILE *& infile, FILE *& outfile);
void readWriteHeaders(FILE * infile, FILE * outfile, 
        int & h, int & w, int & max);
void readIntoVector(FILE * infile, int* vector);
bool getMessage(char * input, int h, int w);
void modifyPixels(int* vector, char * input);
void writeNewPixels(FILE * outfile, int* vector, int h, int w);

int main(int argc, char* argv[])
{
  FILE * infile;
  FILE * outfile;

  handleArgs(argc, argv, infile, outfile);  //get filenames
  
  int h, w, max;                     //image height, width,
                                     //and max. pixel value

  //read headers of source file and write headers of new file
  readWriteHeaders(infile, outfile, h, w, max);
  
  int* vector = new int[h*w];        //vector for pixel values
  
  readIntoVector(infile, vector);    //read pixels into vector
  
  fclose(infile);                    //close source file
  
  char * input = new char[h*w/7];    //string for message
  if(!getMessage(input, h, w))       //get message, error test
  {
    fclose(outfile);
    system("rm message.pgm");
    exit(2);                        //return code 2
  }
  
  modifyPixels(vector, input);       //put message in pixels
  writeNewPixels(outfile, vector, h, w); //write pixels to file

  fclose(outfile);                   //close output file
  
  return 0;
}

void handleArgs(int argc, char* argv[],
        FILE *& infile, FILE *& outfile)
{
  char* file = new char[80];         //temporary string
    
  if(argc > 3)                       //if 3 filenames entered
  {
    cout << "Too many command-line arguments; aborting." 
         << endl;
    exit(3);
  }
  if(argc == 2)                      //if 1 filename entered
  {
    infile = fopen(argv[1], "r");
    cout << "Name of output image file: ";
    cin >> file;
    outfile = fopen(file, "w");
  }
  else if(argc == 3)                 //if 2 filenames entered
  {
    infile = fopen(argv[1], "r");
    outfile = fopen(argv[2], "w");
  }
  else if(argc == 1)                 //if no filenames entered
  {
    cout << "Name of input image file: ";
    cin >> file;
    infile = fopen(file, "r");
    
    cout << "Name of output image file: ";
    cin >> file;
    outfile = fopen(file, "w");
  }
}
void readWriteHeaders(FILE * infile, FILE * outfile,
        int &h, int &w, int & max)
{
  char* str = new char[80];          //string for reading

  fprintf(outfile,"%s\n", "P2");     //write initial filetype header

  fgets(str, 80, infile);            //reads first line

  if(str[0] != 'P' || str[1] != '2') //check header
  {
    fclose(infile);
    fclose(outfile);
    system("rm message.pgm");
	cout << "File header indicates wrong file type" << endl
	    << "File must be .pgm with 'P2' in first line" << endl;

	exit(1);                         //end with exit code 1
  }

  fgets(str, 80, infile);            //second line (place for comments)
  while(str[0] == '#')               //detects comment line
  {
    fprintf(outfile, "%s", str);     //copy comment line
    fgets(str, 80, infile);          //read next line
  }

    //retrieval and copying of width and height
  char* width = new char[80];
  char* height = new char[80];

  int spaceLoc = strcspn(str, " ");  //finds space between w & h
  for(int x = 0; x < spaceLoc; x++)
  {
	width[x] = str[x];               //copies width from temp string
  }
  w = atoi(width);                   //convert to int
  
  height = strstr(str, " ");         //get substring for height
  h = atoi(height);                  //convert to int

  fprintf(outfile, "%d %d\n", w, h);
    
  fgets(str, 80, infile);            //read final header
  max = atoi(str);                   //copy to max for later use
  fprintf(outfile, "%d\n", max);     //write max to new file
}

void readIntoVector(FILE *infile, int* vector)
{
  char* line = new char[80];         //buffer for reading lines
  char* tmpstr = new char[80];       //temp for manipulation

  int place = 0;                     //vector index
  
  while(!feof(infile))               //while not at file end
  {
    fgets(line, 80, infile);         //get one line
    tmpstr = strtok(line, " ");      //gets pixel value
    
    while(tmpstr != NULL)            //while not at line end
    {
      if(strcspn(tmpstr, "0123456789") != strlen(tmpstr))
      {                              //checks for numbers in substring
        vector[place] = atoi(tmpstr);//places number in vector
        place++;                     //increments index
      }
      tmpstr = strtok(NULL, " ");    //find next value
    }
  }
}

bool getMessage(char * input, int h, int w)
{
  //get text message input from user

  cout << "Enter a text message, up to "<<(h*w/7)-1<<" characters: ";
  cin.getline(input, h*w);
  
  //check to make sure the message is not too long
  if(strlen(input) > ((h * w / 7) - 1))
  {
    cout<<"ERROR: Message is too long. Aborting program.";
    return false;
  }
  else return true;
}

void modifyPixels(int* vector, char * input)
{
  char * tmpstr = new char[1];       //temporary string
  tmpstr[0] = char(7);               //beep char. denotes msg end
  strcat(input, tmpstr);             //add message end character
  
  int length = strlen(input), strIndex, shiftNum, vectorIndex = 0;

  for(strIndex = 0; strIndex < length; strIndex++)
  {
    for(shiftNum = 6; shiftNum >= 0; shiftNum--)
    {
      //bitwise ops. mask set low order bit to desired value
      vector[vectorIndex] = (vector[vectorIndex] & 0xFE) ^ 
          ((input[strIndex] >> shiftNum) & 1);
      vectorIndex++;                 //increment vector position
    }
    if(input[strIndex] == char(7))   //end loop if end reached
      break;
  }
}

void writeNewPixels(FILE * outfile, int* vector, int h, int w)
{
  int elementCount = 17;             //for formatting purposes
  for(int x = 0; x < (h * w); x++)   //runs through all pixels
  {
    for(int y = 100; y > 1; y /= 10) //formatting
    {
      if(vector[x] / y == 0)
        fprintf(outfile, " ");
    }
    
    fprintf(outfile, "%i ", vector[x]); //write pixel
    
    elementCount--;
    
    if(elementCount == 0)            //detect when to end line
    {
      fprintf(outfile, "\n");
      elementCount = 17;
    }
  }
}
File 2, destegFr.cpp, is an extraction program for P2 or P3 PGM files with data hidden by stegFr.cpp.
//William Barratt
//Copyright 2002 William Barratt
//destegFr.cpp
/* User-friendly steganography decoding program for .pgm files
   Companion to stegFr.cpp */

#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void handleArgs(int argc, char* argv[], FILE *& infile);
void readThroughHeaders(FILE * infile, int & h, int & w);
void readIntoVector(FILE * infile, int* vector);
void interpretVector(int* vector, char * msg);
void outputMessage(char * msg);

int main(int argc, char* argv[])
{
  FILE * infile;                     //source file
  
  handleArgs(argc, argv, infile);    //get filename
  
  int h, w;                          //image height, width
  
  readThroughHeaders(infile, h, w);  //deal with headers
  
  int * vector = new int[h*w];       //vector for pixel values
    
  readIntoVector(infile, vector);    //read pixel values
  
  fclose(infile);                    //close source file
  
  char * msg = new char[h*w/7];      //string for message
  
  interpretVector(vector, msg);      //decode message
  
  outputMessage(msg);                //print message to screen

  return 0;
}

void handleArgs(int argc, char * argv[], FILE *& infile)
{
  char* file = new char[80];         //temporary string

  if(argc > 2)                       //if user enters 2 things
  {
    cout << "Too many command-line arguments; aborting."
         << endl;
    exit(3);
  }
  else if(argc == 2)                 //if user enters 1 filename
  {
    infile = fopen(argv[1], "r");
  }
  else if(argc == 1)                 //if user enters no name
  {
    cout << "Name of source image file: ";
    cin >> file;
    infile = fopen(file, "r");
  }
}

void readThroughHeaders(FILE * infile, int & h, int & w)
{
  char* str = new char[80];          //string for reading

  fgets(str, 80, infile);            //reads first line
  
  if(str[0] != 'P' || str[1] != '2') //check header
  {
    fclose(infile);
    
    cout << "File header indicates wrong file type" << endl
         << "File must be .pgm with 'P2' in first line" << endl;

    exit(1);
  }

  fgets(str, 80, infile);            //second line (comments)
  while(str[0] == '#')
  {
    fgets(str, 80, infile);          //read through comments
  }

    //retrieval of width and height
  char* width = new char[80];
  char* height = new char[80];

  int spaceLoc = strcspn(str, " ");  //finds space between w & h
  for(int x = 0; x < spaceLoc; x++)
  {
    width[x] = str[x];
  }
  w = atoi(width);                   //convert to int

  height = strstr(str, " ");         //gets height substring
  h = atoi(height);                  //convert to int

  fgets(str, 80, infile);            //line with max value
}

void readIntoVector(FILE *infile, int* vector)
{
    //identical to like-named function of steg.cpp
    //
  char* line = new char[80];         //buffer for reading lines
  char* tmpstr = new char[80];       //temp for manipulation

  int place = 0;                     //vector index
  
  while(!feof(infile))               //while not at file end
  {
    fgets(line, 80, infile);         //get one line
    tmpstr = strtok(line, " ");      //gets pixel value
    
    while(tmpstr != NULL)            //while not at line end
    {
      if(strcspn(tmpstr, "0123456789") != strlen(tmpstr))
      {                              //checks for numbers in substring
        vector[place] = atoi(tmpstr);//places number in vector
        place++;                     //increments index
      }
      tmpstr = strtok(NULL, " ");    //find next value
    }
  }
}

void interpretVector(int * vector, char * msg)
{
  char tmp = 0;                      //buffer char.

  int vectorIndex = 0, strIndex = 0, shiftNum;
  
  while(tmp != char(7))              //while end not reached
  {
    tmp = 0;                         //reset buffer
    
      //runs through, bit-masking etc.
    for(shiftNum = 6; shiftNum >= 0; shiftNum--)
    {
      tmp = tmp ^ ((vector[vectorIndex] & 1) << shiftNum);
      vectorIndex++;                 //increment place in vector
    }
    if(tmp != char(7))
      msg[strIndex] = tmp;           //set output char (if not 7)
    strIndex++;                      //increment place in string
  }
}

void outputMessage(char * msg)
{
  cout << "The message extracted from the file is:" 
       << endl << msg << endl;
}


William Barratt 2003-06-13