Information wants to be free...

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