Kjetil's Information Center: A Blog About My Projects

Plaintext Steganography

The most common form of Steganography is to hide messages inside images, but what about hiding messages inside (plaintext) messages? This is the question I asked myself before creating this set of programs to do exactly that.

There are several ways to do this, but the approach I used was to utilize the whitespace (spaces and newlines) to hide the message. Basically, the amount of words (odd or even) on a line will indicate if the hidden bit is a one or zero. This works on all kind of text, but unfortunately requires a lot of it to hide even the smallest amounts of data. The only practical usage I can think of right now, is to hide passwords.

Here is the encoder:

#include <stdio.h>

#define WRAP 80

static int getbit(FILE *fh)
{
  static int x = 7;
  static int c = '\0';

  if (x == -1)
    x = 7;
  if (x == 7) {
    c = fgetc(fh);
    if (c == EOF)
      return -1;
  }

  return (c >> x--) & 0x1;
}

int main(int argc, char *argv[])
{
  int c, n, spaces, bit, index, had_space, new_index;
  unsigned char buffer[WRAP];
  FILE *fh;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s <text file>\n", argv[0]);
    return 1;
  }

  fh = fopen(argv[1], "r");
  if (fh == NULL) {
    fprintf(stderr, "Error: Unable to open text file for reading.\n");
    return 1;
  }

  n = spaces = index = had_space = 0;
  while (1) {
    bit = getbit(stdin);

    while ((c = fgetc(fh)) != EOF) {
      if (c == '\n' || c == '\r' || c == '\t')
        c = ' '; /* Convert. */

      if (c == ' ') {
        if (had_space)
          continue;
        had_space = 1;
        spaces++;
        if (bit == -1) {
          if ((spaces % 2) == 0) /* Pad output with zeros. */
            index = n;
        } else {
          if ((spaces % 2) == bit)
            index = n;
        }
      } else {
        had_space = 0;
      }

      buffer[n] = c;
      n++;

      if (n >= WRAP) {
        if (index == 0) {
          fprintf(stderr, "Error: Words in text are too large.\n");
          fclose(fh);
          return 1;
        }

        for (n = 0; n < index; n++) {
          fputc(buffer[n], stdout);
        }
        fputc('\n', stdout);

        spaces = new_index = had_space = 0;
        index++;
        for (n = index; n < WRAP; n++) {
          buffer[n - index] = buffer[n];
          if (buffer[n] == ' ') {
            spaces++;
            new_index = n;
          }
        }
        n = WRAP - index;
        index = new_index;

        break;
      }
    }

    if (feof(fh)) {
      fclose(fh);
      if (bit == -1) {
        return 0;
      } else {
        fprintf(stderr, "Error: Not enough text to cover data.\n");
        return 1;
      }
    }
  }

  return 0;
}
          


And here is the decoder:

#include <stdio.h>

static int power_of_two(int n)
{
  if (n == 0)
    return 1;
  else
    return 2 * power_of_two(n - 1);
}

int main(int argc, char *argv[])
{
  int n, c, spaces, value;

  n = 7;
  spaces = value = 0;
  while ((c = fgetc(stdin)) != EOF) {
    if (c == '\n') {
      if ((spaces % 2) == 0)
        value += power_of_two(n);
      n--;

      if (n < 0) {
        fputc(value, stdout);
        value = 0;
        n = 7;
      }

      spaces = 0;
    } else if (c == ' ') {
      spaces++;
    }
  }

  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 17/07-2011, Article Link