Kjetil's Information Center: A Blog About My Projects

Shift Encryption Filter

Here is a re-implementation of a couple of programs I made around 10 years ago. It's a standard in/out filter that performs simple shift encryption, of the Caesar or Vigenere variants.

It supports both text and binary mode. Text mode only operates on A to Z, while binary mode operates on the whole 8-bit range of a character byte.

Take a look at the code and compile it:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

typedef enum {
  CIPHER_NONE,
  CIPHER_CAESAR,
  CIPHER_VIGENERE,
} cipher_t;

typedef enum {
  MODE_NONE,
  MODE_TEXT,
  MODE_BINARY,
} mode_t;

typedef enum {
  DIRECTION_NONE,
  DIRECTION_ENCRYPT,
  DIRECTION_DECRYPT,
} direction_t;

static inline int binary_shift(int c, int n)
{
  c += n;
  if (c > 255) {
    c -= 256;
  }
  return c;
}

static inline int binary_unshift(int c, int n)
{
  c -= n;
  if (c < 0) {
    c += 256;
  }
  return c;
}

static inline int text_shift(int c, int n)
{
  if (c >= 65 && c <= 90) {
    c += n;
    if (c > 90)
      c -= 26;
  }
  if (c >= 97 && c <= 122) {
    c += n;
    if (c > 122)
      c -= 26;
  }
  return c;
}

static inline int text_unshift(int c, int n)
{
  if (c >= 65 && c <= 90) {
    c -= n;
    if (c < 65) 
      c += 26;
  }
  if (c >= 97 && c <= 122) {
    c -= n;
    if (c < 97) 
      c += 26;
  }
  return c;
}

static void caesar_filter(int shift_amount, mode_t mode, direction_t direction)
{
  int c;

  while ((c = fgetc(stdin)) != EOF) {
    if (mode == MODE_TEXT) {
      if (direction == DIRECTION_ENCRYPT) {
        c = text_shift(c, shift_amount);
        
      } else { /* DIRECTION_DECRYPT */
        c = text_unshift(c, shift_amount);
      }

    } else { /* MODE_BINARY */
      if (direction == DIRECTION_ENCRYPT) {
        c = binary_shift(c, shift_amount);

      } else { /* DIRECTION_DECRYPT */
        c = binary_unshift(c, shift_amount);
      }
    }
    fputc(c, stdout);
  }
}

static void vigenere_filter(char *key, mode_t mode, direction_t direction)
{
  int c;
  char *p;

  p = &key[0];

  while ((c = fgetc(stdin)) != EOF) {
    if (mode == MODE_TEXT) {
      if (direction == DIRECTION_ENCRYPT) {
        c = text_shift(c, *p - 97);
        
      } else { /* DIRECTION_DECRYPT */
        c = text_unshift(c, *p - 97);
      }

    } else { /* MODE_BINARY */
      if (direction == DIRECTION_ENCRYPT) {
        c = binary_shift(c, *p - 97);

      } else { /* DIRECTION_DECRYPT */
        c = binary_unshift(c, *p - 97);
      }
    }
    fputc(c, stdout);

    p++;
    if (*p == '\0')
      p = &key[0];
  }
}

static void display_help(char *progname)
{
  fprintf(stderr, "Usage: %s <options>\n", progname);
  fprintf(stderr, "Options:\n"
     "  -h        Display this help and exit.\n"
     "  -c SHIFT  Use Caesar cipher, with SHIFT.\n"
     "  -v KEY    Use Vigenere cipher, with KEY.\n"
     "  -t        Text mode. (Operate on 'a-z' and 'A-Z' only.)\n"
     "  -b        Binary mode. (Operate on 0-255 byte range.)\n"
     "  -e        Encrypt. (Forward shift.)\n"
     "  -d        Decrypt. (Reverse shift.)\n"
     "\n");
}

int main(int argc, char *argv[])
{
  int c;
  mode_t mode = MODE_NONE;
  cipher_t cipher = CIPHER_NONE;
  direction_t direction = DIRECTION_NONE;
  char *vigenere_key = NULL;
  int caesar_shift = 0;

  while ((c = getopt(argc, argv, "hc:v:tbed")) != -1) {
    switch (c) {
    case 'h':
      display_help(argv[0]);
      return EXIT_SUCCESS;

    case 'c':
      cipher = CIPHER_CAESAR;
      caesar_shift = atoi(optarg);
      break;

    case 'v':
      cipher = CIPHER_VIGENERE;
      vigenere_key = optarg;
      break;

    case 't':
      mode = MODE_TEXT;
      break;

    case 'b':
      mode = MODE_BINARY;
      break;

    case 'e':
      direction = DIRECTION_ENCRYPT;
      break;

    case 'd':
      direction = DIRECTION_DECRYPT;
      break;

    case '?':
    default:
      display_help(argv[0]);
      return EXIT_FAILURE;
    }
  }

  if (mode == MODE_NONE) {
    fprintf(stderr, "Error: Specify text or binary mode.\n");
    display_help(argv[0]);
    return EXIT_FAILURE;
  }

  if (direction == DIRECTION_NONE) {
    fprintf(stderr, "Error: Specify encryption or decryption.\n");
    display_help(argv[0]);
    return EXIT_FAILURE;
  }

  switch (cipher) {
  case CIPHER_CAESAR:
    caesar_filter(caesar_shift, mode, direction);
    break;

  case CIPHER_VIGENERE:
    vigenere_filter(vigenere_key, mode, direction);
    break;

  default:
    fprintf(stderr, "Error: Specify a cipher to use.\n");
    display_help(argv[0]);
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}
          


Topic: Scripts and Code, by Kjetil @ 03/05-2015, Article Link