/* enigma.c implements an Enigma-like encryption/decryption for passwords */
/* markus@mhoenicka.de 6-22-01 */

/*
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, see <http://www.gnu.org/licenses/>

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "enigma.h"

/* This encryption/decryption algorithm is one of the most secure that
   human ingenuity could ever conceive: everyone trying to crack the
   algorithm will invariably drop to the floor laughing 'till he or
   she passes away. */

/* In other words: while this encryption discourages casual packet
   sniffers, it is not as secure as e.g. public key encryptions. */

/* The code is a modification of the Enigma mechanism which is based on
   sequential permutation enhanced by a change of the encoding scheme
   after each character (see:
   http://www.cs.miami.edu/~harald/enigma/enigma.html
   for an introduction. As we try to encrypt passwords <= 16 chars,
   the original mechanism (advancing wheel 1 until a full cycle is
   completed, then advancing wheel 2 one position etc) does not make
   much sense: the password length is far less than the number of
   characters on the wheels. We'll simply rotate each wheel after each
   character. We also skip the steckerboard and use only 3 wheels */

/* rotor wirings. We have to work around a little problem: The original
   Enigma used 26 characters on each wheel. We don't want to arbitrarily
   limit the available characters, so we allow all printable characters.
   Unfortunately there are 95 printable characters, but we need an even
   number for the whole thing to work. We cannot include ASCII chars 127
   or 31 as these are control characters with strange effects. We have
   to exclude the space as this is the easiest to skip and should not
   be used in passwords anyway */
char rotor[3][95] = {
  /* Input "!\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" */
  /* 0: */ "0uV`$/zG73La*N[;UFZ^ltm{]\"s=bQ+?OAgwT5cWv#~})oMS\'_\\@K(dDp|y8B%YxJ2:9&r4!kf-j.h16ICi,Rne<EPq>XH",
  /* 1: */ "EPlb(SO\'Dp|y8BxtJ2:j.hf-ICi,Rne0G\"169&%Y_\\k73uV$/zN@FZ^m{]swXUT5cWv#=Hqr4![;<>g~})Q+La*d?AK`oM",
  /* 2: */ "r4![;</z$s`wXUTq{]>S5)@FZQDM|y8cW0G\"uvRY_\\#=pa*d?AK+LEPlb(\'H16tJ2:j.ne9&%hf-ICimoBx,k7O3Vg~}N^"
};

/* the reflector array must be symmetrical, e.g. if ! maps to /, / must also map to ! */
char reflector[95] = {
  "/-Q=~E1.642H\"(!?\'+X*>)Scv@<;$50:RunZ&r{,YMspJ^zq#A7g\\fh3IDeU}Nywdo8a[VTW|mxtjCbLPFKlB9`k_OGi]%"};


/* forward declarations of local functions */
static void move_wheels(struct wheel_pos* pos);
static char reflect(char inchar);
static char wheel(char inchar, int pos, int the_wheel);
static char rwheel(char inchar, int pos, int the_wheel);
static int set_wheels(struct wheel_pos* pos, char* wheelpos);

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  enigma_encrypt(): encrypts a password

  int enigma_encrypt returns 0 if ok, 1 if error

  char* passwd ptr to a string containing the original password

  char* scrambled_passwd ptr to a string that receives the encrypted
        password. This buffer must be at least as long as passwd

  char* wheelpos ptr to a string that encodes the wheel sequence and
        wheel positions

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int enigma_encrypt(char* passwd, char* scrambled_passwd, char* wheelpos) {
  int i;
  int passwd_len;
  struct wheel_pos pos;

  if (set_wheels(&pos, wheelpos)) {
/*      fprintf(stderr, "could not set wheel positions\n"); */
    return 1;
  }

  passwd_len = strlen(passwd);

  for (i = 0; i < passwd_len; i++) {
    /* using functions isn't as bad as it seems for passwd_len <= 16 */
    scrambled_passwd[i] = wheel(passwd[i], pos.pos[0], pos.rotor[0]);
    scrambled_passwd[i] = wheel(scrambled_passwd[i], pos.pos[1], pos.rotor[1]);
    scrambled_passwd[i] = wheel(scrambled_passwd[i], pos.pos[2], pos.rotor[2]);
    scrambled_passwd[i] = reflect(scrambled_passwd[i]);
    scrambled_passwd[i] = rwheel(scrambled_passwd[i], pos.pos[2], pos.rotor[2]);
    scrambled_passwd[i] = rwheel(scrambled_passwd[i], pos.pos[1], pos.rotor[1]);
    scrambled_passwd[i] = rwheel(scrambled_passwd[i], pos.pos[0], pos.rotor[0]);
/*      printf("\n"); */

    move_wheels(&pos);
  }
  scrambled_passwd[i] = '\0'; /* terminate string */
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  wheel(): performs character permutation on a wheel in the forward
           direction

  static char wheel returns the permuted character

  char inchar the original character

  int pos position of the wheel

  int the_wheel number of the wheel to use

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char wheel(char inchar, int pos, int the_wheel) {
  char outchar;
  outchar = rotor[the_wheel][((((int)inchar)-33)+pos)%94];
/*    printf("%c:%d:%d:%c\n", inchar, (int)inchar, ((((int)inchar)-33)+pos)%94, outchar); */
  return outchar;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  rwheel(): performs character permutation on a wheel in the reverse
            direction

  static char rwheel returns the permuted character

  char inchar the original character

  int pos position of the wheel

  int the_wheel number of the wheel to use

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char rwheel(char inchar, int pos, int the_wheel) {
  signed char outchar;
  int i;

  /* find position of char on wheel */
  for (i = 0; i < 94; i++) {
    if (rotor[the_wheel][i] == inchar) {
      break;
    }
  }

  /* the next block of code will fail if char is unsigned (ppc), thus
     we explicitly use signed char */
  outchar = (signed char)(i+33-pos);
  while (outchar < 33) {
    outchar += 94;
  }

  return (char)outchar; /* return signed or unsigned,
			   whatever the default is */
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  reflect(): performs character permutation on the reflector

  static char reflect returns the permuted character

  char inchar the original character

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static char reflect(char inchar) {
  char outchar;

  outchar = reflector[((int)inchar)-33];
/*    printf("%c:%d:%c\n", inchar, ((int)inchar)-33, outchar); */
  return outchar;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  set_wheels(): set the wheel sequence and position

  static int set_wheels returns 0 if ok, 1 if error

  struct wheel_pos* pos ptr to a struct that holds the wheel sequence
         and position information

  char* wheelpos ptr to a string that holds the sequence and position
        information. The format is:
	ABC-DE-FG-HI
	with: ABC: wheel sequence: 012, 021, 120, 102, 210, 201
	      DE: position of wheel in position 0; 0<DE<94
	      FG: position of wheel in position 1; 0<DE<94
	      HI: position of wheel in position 2; 0<DE<94

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int set_wheels(struct wheel_pos* pos, char* wheelpos) {
  char the_pos[3] = {'\0', '\0', '\0'};
  int i;
  int n_pos;

  /* the formatting string must be at least 12 chars long. It may well
     be longer, but only the first 12 chars are used */
  if (strlen(wheelpos) < 12) {
/*      fprintf(stderr, "wheelpos string too short\n"); */
    return 1; /* string is incomplete */
  }

  pos->rotor[0] = (int)(wheelpos[0]) - 48;
  pos->rotor[1] = (int)(wheelpos[1]) - 48;
  pos->rotor[2] = (int)(wheelpos[2]) - 48;

  /* position values must be between 0 and 2 and we don't use
     a wheel twice */
  if (pos->rotor[0] == pos->rotor[1] || pos->rotor[1] == pos->rotor[2] ||
      pos->rotor[0] == pos->rotor[2] ||
      pos->rotor[0] < 0 || pos->rotor[0] > 2 ||
      pos->rotor[1] < 0 || pos->rotor[1] > 2 ||
      pos->rotor[2] < 0 || pos->rotor[2] > 2) {
/*      fprintf(stderr, "duplicate or out of range entries:pos0:%d:pos1:%d:pos2:%d\n", pos->rotor[0], pos->rotor[1], pos->rotor[2]); */
    return 1;
  }

  for (i = 0; i < 3; i++) {
    the_pos[0] = wheelpos[(i*3)+4];
    the_pos[1] = wheelpos[(i*3)+5];
    n_pos = atoi(the_pos);
    if (n_pos < 0 || n_pos > 94) {
/*        fprintf(stderr, "n_pos out of range:%d<<\n", n_pos); */
      return 1; /* out of range */
    }
    pos->pos[i] = n_pos;
  }
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  move_wheels(): advances the wheel positions

  void move_wheels does not return anything

  struct wheel_pos* pos ptr to a struct that holds the wheel sequence
         and position information

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void move_wheels(struct wheel_pos* pos) {
  (pos->pos)[0] = ((pos->pos)[0]+1)%94;
  (pos->pos)[1] = ((pos->pos)[1]+1)%94;
  (pos->pos)[2] = ((pos->pos)[2]+1)%94;
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  randomize_wheels(): generates a pseudo-random wheel sequence and
                     position string

  void randomize_wheels does not return anything

  char* wheelpos ptr to a buffer of at least 13 chars which receives
        the position string

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void randomize_wheels(char* wheelpos) {
  int i, j;

  /* seed the random functions with the time */
  srand((unsigned int)time((time_t*)NULL));

  /* create the wheel sequence */
  j = (int)(6.0*rand()/RAND_MAX);

  if (!j) {
    strcpy(wheelpos, "012");
  }
  else if (j == 1) {
    strcpy(wheelpos, "021");
  }
  else if (j == 2) {
    strcpy(wheelpos, "120");
  }
  else if (j == 3) {
    strcpy(wheelpos, "102");
  }
  else if (j == 4) {
    strcpy(wheelpos, "201");
  }
  else if (j == 5) {
    strcpy(wheelpos, "210");
  }
  
  /* generate pseudo-random positions for the wheels */
  for (i = 0; i < 3; i++) {
    j = (int)(93.0*rand()/RAND_MAX);
    sprintf(&wheelpos[3+(3*i)], "-%02d", j);
  }
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  numberize_passwd(): transforms a password string into a numeric
                      string representation

  void numberize_passwd does not return anything

  char* numberpasswd ptr to a buffer that will receive the numeric
                      string representation. Must be at least three
		      times the size of passwd

  char* passwd ptr to the original password string

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void numberize_passwd(char* numberpasswd, char* passwd) {
  size_t i;

  for (i = 0; i < strlen(passwd); i++) {
    sprintf(numberpasswd+(3*i), "%03d", (int)passwd[i]);
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  denumberize_passwd(): transforms a numeric string representation of
                        a password back into the password string

  void denumberize_passwd does not return anything

  char* passwd ptr to the buffer that receives the password string
                      the size must be at least one-third of
		      numberpasswd

  char* numberpasswd ptr to a buffer that contains the numeric
                      string representation.

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void denumberize_passwd(char* passwd, char* numberpasswd) {
  size_t i;
  char buffer[4] = {'\0','\0','\0','\0'};

  for (i = 0; i < strlen(numberpasswd)/3; i++) {
    strncpy(buffer, &numberpasswd[i*3], 3);
    passwd[i] = (char)atoi(buffer);
  }
  passwd[i] = '\0';
}

