#include "blowfish.h"
#include "bf_tab.h"

#define BOXES  3

/* #define S(x,i) (bf_S[i][x.w.byte##i])  */
#define S0(x) (bf_S[0][x.w.byte0])
#define S1(x) (bf_S[1][x.w.byte1])
#define S2(x) (bf_S[2][x.w.byte2])
#define S3(x) (bf_S[3][x.w.byte3])
#define bf_F(x) (((S0(x) + S1(x)) ^ S2(x)) + S3(x))
#define ROUND(a,b,n) (a.word ^= bf_F(b) ^ bf_P[n])

struct box_t {
   UWORD_32bits *P;
   UWORD_32bits **S;
   char key[81];
   char keybytes;
} box[BOXES];


UWORD_32bits *bf_P;

UWORD_32bits **bf_S;



void blowfish_encipher (UWORD_32bits * xl, UWORD_32bits * xr)
{
   union aword Xl;
   union aword Xr;

   Xl.word = *xl;
   Xr.word = *xr;

   Xl.word ^= bf_P[0];
   ROUND(Xr, Xl, 1);
   ROUND(Xl, Xr, 2);
   ROUND(Xr, Xl, 3);
   ROUND(Xl, Xr, 4);
   ROUND(Xr, Xl, 5);
   ROUND(Xl, Xr, 6);
   ROUND(Xr, Xl, 7);
   ROUND(Xl, Xr, 8);
   ROUND(Xr, Xl, 9);
   ROUND(Xl, Xr, 10);
   ROUND(Xr, Xl, 11);
   ROUND(Xl, Xr, 12);
   ROUND(Xr, Xl, 13);
   ROUND(Xl, Xr, 14);
   ROUND(Xr, Xl, 15);
   ROUND(Xl, Xr, 16);
   Xr.word ^= bf_P[17];

   *xr = Xl.word;
   *xl = Xr.word;
}

void blowfish_decipher (UWORD_32bits *xl, UWORD_32bits *xr)
{
   union aword Xl;
   union aword Xr;

   Xl.word = *xl;
   Xr.word = *xr;

   Xl.word ^= bf_P[17];
   ROUND(Xr, Xl, 16);
   ROUND(Xl, Xr, 15);
   ROUND(Xr, Xl, 14);
   ROUND(Xl, Xr, 13);
   ROUND(Xr, Xl, 12);
   ROUND(Xl, Xr, 11);
   ROUND(Xr, Xl, 10);
   ROUND(Xl, Xr, 9);
   ROUND(Xr, Xl, 8);
   ROUND(Xl, Xr, 7);
   ROUND(Xr, Xl, 6);
   ROUND(Xl, Xr, 5);
   ROUND(Xr, Xl, 4);
   ROUND(Xl, Xr, 3);
   ROUND(Xr, Xl, 2);
   ROUND(Xl, Xr, 1);
   Xr.word ^= bf_P[0];

   *xl = Xr.word;
   *xr = Xl.word;
}


void blowfish_init (UBYTE_08bits * key, short keybytes, int bxtouse)
{
   int i, j, bx;
   time_t lowest;
   UWORD_32bits data;
   UWORD_32bits datal;
   UWORD_32bits datar;
   union aword temp;

   for (i = 0; i < BOXES; i++)
      if (box[i].P != NULL) {
	 if ((box[i].keybytes == keybytes) &&
	 (strncmp((char *) (box[i].key), (char *) key, keybytes) == 0)) {
	    bf_P = box[i].P;
	    bf_S = box[i].S;
	    return;
	 }
      }
   bx = (-1);
   for (i = 0; i < BOXES; i++) {
      if (box[i].P == NULL) {
	 bx = i;
	 i = BOXES + 1;
      }
   }
   if (bx < 0) {

bx = bxtouse;
      free(box[bx].P);
      for (i = 0; i < 4; i++)
	 free(box[bx].S[i]);
      free(box[bx].S);
   }
   box[bx].P = (UWORD_32bits *) malloc((bf_N + 2) * sizeof(UWORD_32bits));
   box[bx].S = (UWORD_32bits **) malloc(4 * sizeof(UWORD_32bits *));
   for (i = 0; i < 4; i++)
      box[bx].S[i] = (UWORD_32bits *) malloc(256 * sizeof(UWORD_32bits));
   bf_P = box[bx].P;
   bf_S = box[bx].S;
   box[bx].keybytes = keybytes;
   strncpy(box[bx].key, key, keybytes);
   for (i = 0; i < bf_N + 2; i++)
      bf_P[i] = initbf_P[i];
   for (i = 0; i < 4; i++)
      for (j = 0; j < 256; j++)
	 bf_S[i][j] = initbf_S[i][j];

   j = 0;
   for (i = 0; i < bf_N + 2; ++i) {
      temp.word = 0;
      temp.w.byte0 = key[j];
      temp.w.byte1 = key[(j + 1) % keybytes];
      temp.w.byte2 = key[(j + 2) % keybytes];
      temp.w.byte3 = key[(j + 3) % keybytes];
      data = temp.word;
      bf_P[i] = bf_P[i] ^ data;
      j = (j + 4) % keybytes;
   }

   datal = 0x00000000;
   datar = 0x00000000;

   for (i = 0; i < bf_N + 2; i += 2) {
      blowfish_encipher(&datal, &datar);

      bf_P[i] = datal;
      bf_P[i + 1] = datar;
   }

   for (i = 0; i < 4; ++i) {
      for (j = 0; j < 256; j += 2) {

	 blowfish_encipher(&datal, &datar);

	 bf_S[i][j] = datal;
	 bf_S[i][j + 1] = datar;
      }
   }
}


#define SALT1  0xdeadd061
#define SALT2  0x23f6b095

char *base64 = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

int base64dec (char c)
{
   int i;
   for (i = 0; i < 64; i++)
      if (base64[i] == c)
	 return i;
   return 0;
}


char *encrypt_string (char *key, char *str)
{
   UWORD_32bits left, right;
   char *p, *s, *dest, *d;
   int i;
   dest = (char *) malloc((strlen(str) + 9) * 2);
   s = (char *) malloc(strlen(str) + 9);
   strcpy(s, str);
   p = s;
   while (*p)
      p++;
   for (i = 0; i < 8; i++)
      *p++ = 0;
   blowfish_init(key, strlen(key), 0);
   p = s;
   d = dest;
   while (*p) {
      left = ((*p++) << 24);
      left += ((*p++) << 16);
      left += ((*p++) << 8);
      left += (*p++);
      right = ((*p++) << 24);
      right += ((*p++) << 16);
      right += ((*p++) << 8);
      right += (*p++);
      blowfish_encipher(&left, &right);
      for (i = 0; i < 6; i++) {
         *d++ = base64[right & 0x3f];
         right = (right >> 6);
      }
      for (i = 0; i < 6; i++) {
         *d++ = base64[left & 0x3f];
         left = (left >> 6);
      }
   }
   *d = 0;
   free(s);
   return dest;
}

char *decrypt_string (char *key, char *str)
{
   UWORD_32bits left, right;
   char *p, *s, *dest, *d;
   int i;
   dest = (char *) malloc(strlen(str) + 12);
   s = (char *) malloc(strlen(str) + 12);
   strcpy(s, str);
   p = s;
   while (*p)
      p++;
   for (i = 0; i < 12; i++)
      *p++ = 0;
   blowfish_init(key, strlen(key), 0);
   p = s;
   d = dest;
   while (*p) {
      right = 0L;
      left = 0L;
      for (i = 0; i < 6; i++)
         right |= (base64dec(*p++)) << (i * 6);
      for (i = 0; i < 6; i++)
         left |= (base64dec(*p++)) << (i * 6);
      blowfish_decipher(&left, &right);
      for (i = 0; i < 4; i++)
         *d++ = (left & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8);
      for (i = 0; i < 4; i++)
         *d++ = (right & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8);
   }
   *d = 0;
   free(s);
   return dest;
}


