vigenere.c

Download this program

/*****************************************************************************
 * The Vigenere Cipher
 * Copyright Paul Johnston, 2000. See legal.html for details.
 *****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>
#include <assert.h>
#include <errno.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/stat.h>
char *strlwr(char *s) { while(*s) *s++ = tolower(*s); }


/*****************************************************************************
 * Function prototypes
 *****************************************************************************/
void vig_error(int err);
void vig_iencrypt();
void vig_idecrypt();
char *vig_enc_str(char *str, char *key);
char *vig_dec_str(char *str, char *key);
char *vig_tidystr(char *data);
void vig_icrack();
float *vig_shifts(char *data, int *num_shifts);
float vig_cindex(char *data, int shift);
void vig_disp_shifts(float *shifts, int num_shifts);
int vig_keylen(float *shifts, int num_shift);
float *vig_ltrstat(char *data, int key_len, int key_pos);
void vig_disp_ltrstat(float *ltrstat);
float *vig_keycand(float *ltrstat);
void vig_disp_keycand(float *keycand);
char vig_keyltr(float *keycand);
void vig_disp_text(char *data, char *key, int rows);
void *vig_malloc(int size);
void vig_freeall();
void vig_malloc_growlist();
void vig_malloc_init();
void vig_malloc_term();
void vig_keywait();
char *vig_getstr(char *prompt);
char *vig_getstrfile(char *prompt, char **fname);
char *vig_loadfile(char *fname);
char *vig_getstrdef(char *prompt, char *def);
void vig_putstr(char *prompt, char *data, char *fname);
char *vig_strcat(char *s1, char *s2);
char *vig_strcat4(char *s1, char *s2, char *s3, char *s4);
char *vig_strprefix(char *str, int len);
char *vig_strset(char chr, int len);
char *vig_itos(int num);
char *vig_ctos(char chr);


/*****************************************************************************
 * Custom error codes; __ELASTERROR not available on all platforms.
 *****************************************************************************/
#ifdef __ELASTERROR
#define VIG_EBASE __ELASTERROR
#else
#define VIG_EBASE 2000
#endif
#define VIG_ELENGTH VIG_EBASE + 0


/*****************************************************************************
 * This error handling routine longjmp's to the setjmp in main below
 *****************************************************************************/
jmp_buf vig_err_env;
void vig_error(int err)
{
  char *msg;
  switch(err)
  {
    case VIG_ELENGTH: msg = "must not be zero length"; break;
    default:          msg = strerror(err);
  }
  printf("ERROR: %s\n\n", msg);
  longjmp(vig_err_env, err);
}


/*****************************************************************************
 * Main entry point; coordinate main menu
 *****************************************************************************/
int main()
{
  vig_malloc_init();
  for(;;)
  {
    if(setjmp(vig_err_env) == 0)
    {
      printf
      (
        "\n"
        "The Vigenere Cipher\n"
        "Copyright Paul Johnston, 2000. See legal.html for details.\n"
        "\n"
        "1) Encrypt a file/string\n"
        "2) Decrypt a file/string\n"
        "3) Crack a file/string (decrypt with unknown key)\n"
        "4) Quit\n"
        "\n"
      );
      switch(vig_getstr("selection")[0])
      {
        case '1': vig_iencrypt(); break;
        case '2': vig_idecrypt(); break;
        case '3': vig_icrack();   break;
        case '4': exit(0);        break;
      }
    }
    vig_freeall();
    vig_keywait();
  }
  return 0;
}



/*****************************************************************************
 * Interactive routine to enrypt / decrypt a text string or file
 *****************************************************************************/
void vig_iencrypt()
{
  char *key, *data, *fname;
  data = vig_tidystr(vig_getstrfile("plaintext", &fname));
  key  = vig_tidystr(vig_getstrfile("key", NULL));
  vig_putstr("ciphertext", vig_enc_str(data, key), fname);
}

void vig_idecrypt()
{
  char *key, *data, *fname;
  data = vig_tidystr(vig_getstrfile("ciphertext", &fname));
  key  = vig_tidystr(vig_getstrfile("key", NULL));
  vig_putstr("plaintext", vig_dec_str(data, key), fname);
}


/*****************************************************************************
 * Functions to encrypt / decrypt strings
 *****************************************************************************/
char *vig_enc_str(char *str, char *key)
{
  int ii, key_len = strlen(key);
  if(key_len < 1) { vig_error(VIG_ELENGTH); }

#define VIG_ENC_CHAR(C, K)  ('A' + ((C - 'A') + (K - 'A')) % 26)
  for(ii = 0; str[ii]; ii++)
  {
    str[ii] = VIG_ENC_CHAR(str[ii], key[ii % key_len]);
  }
  return str;
}

char *vig_dec_str(char *str, char *key)
{
  int ii, key_len = strlen(key);
  if(key_len < 1) { vig_error(VIG_ELENGTH); }

  /* The check for a key letter being '-' is used in vig_disp_text */
#define VIG_DEC_CHAR(C, K)  (K == '-') ? ' ' : \
                                      ('A' + (26 + (C - 'A') - (K - 'A')) % 26)
  for(ii = 0; str[ii]; ii++)
  {
    str[ii] = VIG_DEC_CHAR(str[ii], key[ii % key_len]);
  }
  return str;
}


/*****************************************************************************
 * Remove all non-alphabetic characters, and convert to upper case
 *****************************************************************************/
char *vig_tidystr(char *data)
{
  int ii, jj = 0;
  for (ii = 0; data[ii]; ii++)
  {
    if(isalpha(data[ii]))
    {
      data[jj++] = toupper(data[ii]);
    }
  }
  data[jj] = 0;
  return data;
}


/*****************************************************************************
 * Interactive routine to decode a ciphertext without the key
 *****************************************************************************/
void vig_icrack()
{
  char *data, *key, *fname, *ltr;
  int num_shift, key_len = 0, ii = -1;
  float *shifts;

  data = vig_tidystr(vig_getstrfile("ciphertext", &fname));
  shifts = vig_shifts(data, &num_shift);

  while(ii <= key_len)
  {
    if(ii == -1)
    { /* determine key length */
      vig_disp_shifts(shifts, num_shift);
      key_len = atoi(vig_getstrdef("key length",
                                     vig_itos(vig_keylen(shifts, num_shift))));
      if(key_len < 1) { vig_error(VIG_ELENGTH); }
      key = vig_strset('-', key_len);
      ii++;
    }
    else if(ii < key_len)
    { /* determine one letter of key */
      ltr = vig_ctos(vig_keyltr(vig_keycand(vig_ltrstat(data, key_len, ii))));
      vig_putstr("key so far", key, NULL);
      vig_disp_text(data, key, 2);
      ltr = vig_getstrdef("key letter or '-' to go back", ltr);
      if(ltr[0] == '-') { key[--ii] = '-'; }
      if(strlen(vig_tidystr(ltr)) > 0) { key[ii++] = ltr[0]; }
    }
    else
    { /* final confirmation screen */
      vig_putstr("key", key, NULL);
      vig_disp_text(data, key, 5);
      ltr = vig_getstr("to confirm or '-' to go back");
      if(ltr[0] == '-') { key[--ii] = '-'; }
      else { ii++; }
    }
  }
  vig_putstr("plaintext", vig_dec_str(data, key), fname);
}


/*****************************************************************************
 * Calculate coincidence indexes for first 256 shifts, unless data is too
 * short.
 *****************************************************************************/
float *vig_shifts(char *data, int *num_shifts)
{
  float *shifts;
  int ii, data_len = strlen(data);
  if(data_len < 1) { vig_error(VIG_ELENGTH); }

#define VIG_MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
  *num_shifts = VIG_MIN(data_len, 256);
  shifts = vig_malloc(*num_shifts * sizeof(float));

  for(ii = 0; ii < *num_shifts; ii++)
  {
    shifts[ii] = vig_cindex(data, ii);
  }

  return shifts;
}


/*****************************************************************************
 * Calculate coincidence index of a particular shift on a ciphertext
 *****************************************************************************/
float vig_cindex(char *data, int shift)
{
  int ii, matches = 0;
  assert(shift < strlen(data));
  for(ii = 0; data[ii + shift]; ii++)
  {
    if(data[ii] == data[ii + shift]) { matches++; }
  }
  return (float) 100 * matches / ii;
}


/*****************************************************************************
 * Display shift information
 *****************************************************************************/
void vig_disp_shifts(float *shifts, int num_shifts)
{
  int shift, ii, jj;
  printf ("Shift / coincidence index:\n\n");
  for(ii = 0; ii < 20; ii++)
  {
    for(jj = 0; jj < 5; jj++)
    {
      shift = 1 + ii + jj * 20;
      if(shift < num_shifts)
      {
        printf("%3d %6.2f%%    ", shift , shifts[shift]);
      }
    }
    printf("\n");
  }
  printf("\n");
}


/*****************************************************************************
 * Determine the suggested key length from the shifts information
 *****************************************************************************/
int vig_keylen(float *shifts, int num_shift)
{
  int ii, jj, cand;
  for(ii = 1; ii < num_shift; ii++)
  {
    cand = 1;
    for(jj = 1; (jj * ii) < num_shift && cand; jj++)
    {
      cand = cand && (shifts[jj * ii] > 6);
    }
    if(cand) { break; }
  }
  return (ii >= num_shift) ? 0 : ii;
}


/*****************************************************************************
 * Calculate letter frequency information for one key position
 *****************************************************************************/
float *vig_ltrstat(char *data, int key_len, int key_pos)
{
  float num_chr = 0, *ltrstat = vig_malloc(26 * sizeof(float));
  int ii, data_len = strlen(data);

  for(ii = 0; ii < 26; ii++) { ltrstat[ii] = 0; }
  for(ii = key_pos; ii < data_len; ii += key_len)
  {
    ltrstat[data[ii] - 'A']++;
    num_chr++;
  }
  for(ii = 0; ii < 26; ii++) { ltrstat[ii] *= 100 / num_chr; }

  vig_disp_ltrstat(ltrstat);
  return ltrstat;
}


/*****************************************************************************
 * Display letter frequencies
 *****************************************************************************/
void vig_disp_ltrstat(float *ltrstat)
{
  int ii;
  printf("Letter frequencies:\n");
  for(ii = 0; ii < 26; ii++)
  {
    printf("%c %6.2f%%    ", 'A' + ii, ltrstat[ii]);
    if(ii % 6 == 5) { printf("\n"); }
  }
  printf("\n\n");
}


/*****************************************************************************
 * Calculate the squared deviation from standard letter frequencies for
 * English for each possible key letter.
 *****************************************************************************/
float english_freq[] =
{  /* A */
  7.81, 1.28, 2.93, 4.11, 13.05, 2.88, 1.39,
  5.85, 6.77, 0.23, 0.42, 3.60, 2.62, 7.28,
  8.21, 2.15, 0.14, 6.64, 6.46, 9.02, 2.77,
  1.00, 1.49, 0.30, 1.51, 0.09
}; /* Z */

float *vig_keycand(float *ltrstat)
{
  float dev, *keycand = vig_malloc(26 * sizeof(float));
  int ii, jj;

  for(ii = 0; ii < 26; ii++)
  {
    keycand[ii] = 0;
    for(jj = 0; jj < 26; jj++)
    {
      dev = english_freq[(26 + jj - ii) % 26] - ltrstat[jj];
      keycand[ii] += (dev * dev);
    }
  }
  vig_disp_keycand(keycand);

  return keycand;
}


/*****************************************************************************
 * Display key candidates
 *****************************************************************************/
void vig_disp_keycand(float *keycand)
{
  int ii;
  printf("Key candidates (most likely has smallest number):\n");
  for(ii = 0; ii < 26; ii++)
  {
    printf("%c  %5.0f     ", 'A' + ii, keycand[ii]);
    if(ii % 6 == 5) { printf("\n"); }
  }
  printf("\n\n");
}


/*****************************************************************************
 * Find letter with least deviation
 *****************************************************************************/
char vig_keyltr(float *keycand)
{
  int ii, min = 0;
  for(ii = 1; ii < 26; ii++)
  {
    if(keycand[ii] < keycand[min]) { min = ii; }
  }
  return 'A' + min;
}


/*****************************************************************************
 * Display a sample of the cipher and plaintext
 *****************************************************************************/
void vig_disp_text(char *data, char *key, int rows)
{
  char *cipher = strlwr(vig_strprefix(data, rows * 79));
  char *plain  = vig_dec_str(vig_strprefix(data, rows * 79), key);
  int ii;
  for(ii = 0; (ii < rows) && cipher[ii * 79]; ii++)
  {
    printf("%.79s\n%.79s\n", cipher + (ii * 79), plain + (ii * 79));
  }
  printf("\n");
}


/*****************************************************************************
 * This is a customised version of malloc, which calls vig_error instead of
 * returning NULL, and keeps a list of allocated memory. Calling vig_freeall
 * frees everything.
 *****************************************************************************/
void **vig_mem_list;
int vig_mem_idx;
int vig_mem_llen;

void *vig_malloc(int size)
{
  if(vig_mem_idx >= vig_mem_llen) { vig_malloc_growlist(); }
  vig_mem_list[vig_mem_idx] = malloc(size);
  if(vig_mem_list[vig_mem_idx] == NULL) { vig_error(ENOMEM); }
  return vig_mem_list[vig_mem_idx++];;
}

void vig_freeall()
{
  while(vig_mem_idx > 0) { free(vig_mem_list[--vig_mem_idx]); }
}


/*****************************************************************************
 * Grow the memory block used for the list of allocated blocks.
 *****************************************************************************/
void vig_malloc_growlist()
{
  int new_llen = (vig_mem_llen == 0) ? 256 : 2 * vig_mem_llen;
  void *new_list = realloc(vig_mem_list, new_llen * sizeof(void *));
  if(new_list == NULL) { vig_error(ENOMEM); }
  vig_mem_list = new_list;
  vig_mem_llen = new_llen;
}


/*****************************************************************************
 * Initialise/cleanup vig_malloc routines
 *****************************************************************************/
void vig_malloc_init()
{
  vig_mem_idx = 0;
  vig_mem_llen = 0;
  vig_mem_list = NULL;
  atexit(vig_malloc_term);
}

void vig_malloc_term()
{
  vig_freeall();
  free(vig_mem_list);
}


/*****************************************************************************
 * Wait for user to press enter
 *****************************************************************************/
void vig_keywait()
{
  char input[4];
  printf("[Press Enter]\n");
  fgets(input, 3, stdin);
}

/*****************************************************************************
 * Input a string from the user
 *****************************************************************************/
char *vig_getstr(char *prompt)
{
  char *input = vig_malloc(256);
  printf("Enter %s\n: ", prompt);
  fgets(input, 255, stdin);
  input[strlen(input) - 1] = 0;
  printf("\n");
  return input;
}


/*****************************************************************************
 * Input a string from the user; load a file if user types !filename.
 *****************************************************************************/
char *vig_getstrfile(char *prompt, char **fname)
{
  char *input = vig_getstr(vig_strcat(prompt, " (or !filename)"));
  if(fname != NULL) { (*fname) = NULL; }
  if(input[0] == '!')
  {
    if(fname != NULL) { (*fname) = vig_strcat(input + 1, ".vig"); }
    input = vig_loadfile(input + 1);
  }
  return input;
}


/*****************************************************************************
 * Load a complete file into memory
 *****************************************************************************/
char *vig_loadfile(char *fname)
{
  FILE *fhandle;
  char *data;
  struct stat finfo;

  if(stat(fname, &finfo) != 0) { vig_error(errno); }
  data = vig_malloc(finfo.st_size + 1);

  fhandle = fopen(fname, "rt");
  if(fhandle == NULL) { vig_error(errno); }

  fread(data, finfo.st_size, 1, fhandle);
  data[finfo.st_size] = 0;
  fclose(fhandle);

  return data;
}


/*****************************************************************************
 * Input a string from the user; use given default value if user just
 * presses enter
 *****************************************************************************/
char *vig_getstrdef(char *prompt, char *def)
{
  char *str = vig_getstr(vig_strcat4(prompt, " (suggest ", def, ")"));
  return (strlen(str) == 0) ? def : str;
}


/*****************************************************************************
 * Write data to the named file, or stdout if fname is null
 *****************************************************************************/
void vig_putstr(char *prompt, char *data, char *fname)
{
  FILE *fhandle;
  printf("Generated %s\n: ", prompt);
  if(fname != NULL)
  {
    fhandle = fopen(fname, "wt");
    if(fhandle == NULL) { vig_error(errno); }
    fwrite(data, strlen(data), 1, fhandle);
    fclose(fhandle);
    printf("written to %s\n\n", fname);
  }
  else
  {
    printf("%s\n\n", data);
  }
}


/*****************************************************************************
 * Concatonate strings
 *****************************************************************************/
char *vig_strcat(char *s1, char *s2)
{
  char *str = vig_malloc(strlen(s1) + strlen(s2) + 1);
  strcpy(str, s1);
  strcat(str, s2);
  return str;
}

char *vig_strcat4(char *s1, char *s2, char *s3, char *s4)
{
  char *str = vig_malloc(strlen(s1) + strlen(s2) + strlen(s3) + strlen(s4) +1);
  strcpy(str, s1);
  strcat(str, s2);
  strcat(str, s3);
  strcat(str, s4);
  return str;
}


/*****************************************************************************
 * Extract the beginning of a string into a new block of memory
 *****************************************************************************/
char *vig_strprefix(char *str, int len)
{
  char *sub = vig_malloc(len + 1);
  memset(sub, 0, len + 1);
  strncpy(sub, str, len);
  return sub;
}


/*****************************************************************************
 * Create a string of length len, containing all 'chr's
 *****************************************************************************/
char *vig_strset(char chr, int len)
{
  char *str = vig_malloc(len + 1);
  memset(str, chr, len);
  str[len] = 0;
  return str;
}


/*****************************************************************************
 * Convert an integer/character to a string
 *****************************************************************************/
char *vig_itos(int num)
{
  char *str = vig_malloc(10);
  sprintf(str, "%d", num);
  return str;
}

char *vig_ctos(char chr)
{
  char *str = vig_malloc(2);
  str[0] = chr;
  str[1] = 0;
  return str;
}
© 1998 - 2012 Paul Johnston, distributed under the BSD License   Updated:10 Jun 2009