#!/usr/bin/tclsh

# -----------------------------------------------------------------
#
# To create a shared lib from this code, obtain a tclkit executable
# (http://www.equi4.com/tclkit/) and critcl2.kit starkit
# (http://www.equi4.com/starkit/critcl.html) and then execute the following
# command in the twofish subdirectory of the gorilla sources (this assumes
# that both the tclkit executable and the critcl2.kit are in the twofish
# subdirectory, adjust paths accordingly if they are stored elsewhere):
#
# tclkit-executable-name critcl2.kit -lib f32-critcl.tcl
#
# This will, if it compiles properly, create a file
# f32-critcl.(shared-libary-extension) in the current directory.  That file
# will need to be renamed to:
#
# f32-(operating system)-(machine).(shared lib extension)
#
# Where (operating system) is the contents of the $tcl_platform(os) variable
# and (machine) is the contents of the $tcl_platform(machine) variable and
# (shared lib extension) is the shared library extension ([info
# sharedlibextension]) without the dot "." that the info command returns.
#
# Note, all 32-bit Intel x86 systems are regularized to use the name "x86"
# only for (machine).

# critcl 2 needs package (zdia)
package provide f32-critcl 1.0

    critcl::ccode {
      /* Generated by critcl on Sat Sep 11 13:49:28 EDT 2010
       * source: /home/work/gorilla-dev/gorilla/sources/twofish/f32-test.tcl
       * binary: /home/work/.critcl/ix86-linux/v20_4809058a5c51d3e2df9f94bac9f5d5d0_pic.o
       */
      #include "tcl.h"

      /* ---------------------------------------------------------------------- */

          static unsigned char P8x80[256] = {
              0xA9,0x67,0xB3,0xE8,0x04,0xFD,0xA3,0x76,
              0x9A,0x92,0x80,0x78,0xE4,0xDD,0xD1,0x38,
              0x0D,0xC6,0x35,0x98,0x18,0xF7,0xEC,0x6C,
              0x43,0x75,0x37,0x26,0xFA,0x13,0x94,0x48,
              0xF2,0xD0,0x8B,0x30,0x84,0x54,0xDF,0x23,
              0x19,0x5B,0x3D,0x59,0xF3,0xAE,0xA2,0x82,
              0x63,0x01,0x83,0x2E,0xD9,0x51,0x9B,0x7C,
              0xA6,0xEB,0xA5,0xBE,0x16,0x0C,0xE3,0x61,
              0xC0,0x8C,0x3A,0xF5,0x73,0x2C,0x25,0x0B,
              0xBB,0x4E,0x89,0x6B,0x53,0x6A,0xB4,0xF1,
              0xE1,0xE6,0xBD,0x45,0xE2,0xF4,0xB6,0x66,
              0xCC,0x95,0x03,0x56,0xD4,0x1C,0x1E,0xD7,
              0xFB,0xC3,0x8E,0xB5,0xE9,0xCF,0xBF,0xBA,
              0xEA,0x77,0x39,0xAF,0x33,0xC9,0x62,0x71,
              0x81,0x79,0x09,0xAD,0x24,0xCD,0xF9,0xD8,
              0xE5,0xC5,0xB9,0x4D,0x44,0x08,0x86,0xE7,
              0xA1,0x1D,0xAA,0xED,0x06,0x70,0xB2,0xD2,
              0x41,0x7B,0xA0,0x11,0x31,0xC2,0x27,0x90,
              0x20,0xF6,0x60,0xFF,0x96,0x5C,0xB1,0xAB,
              0x9E,0x9C,0x52,0x1B,0x5F,0x93,0x0A,0xEF,
              0x91,0x85,0x49,0xEE,0x2D,0x4F,0x8F,0x3B,
              0x47,0x87,0x6D,0x46,0xD6,0x3E,0x69,0x64,
              0x2A,0xCE,0xCB,0x2F,0xFC,0x97,0x05,0x7A,
              0xAC,0x7F,0xD5,0x1A,0x4B,0x0E,0xA7,0x5A,
              0x28,0x14,0x3F,0x29,0x88,0x3C,0x4C,0x02,
              0xB8,0xDA,0xB0,0x17,0x55,0x1F,0x8A,0x7D,
              0x57,0xC7,0x8D,0x74,0xB7,0xC4,0x9F,0x72,
              0x7E,0x15,0x22,0x12,0x58,0x07,0x99,0x34,
              0x6E,0x50,0xDE,0x68,0x65,0xBC,0xDB,0xF8,
              0xC8,0xA8,0x2B,0x40,0xDC,0xFE,0x32,0xA4,
              0xCA,0x10,0x21,0xF0,0xD3,0x5D,0x0F,0x00,
              0x6F,0x9D,0x36,0x42,0x4A,0x5E,0xC1,0xE0
          };

          static unsigned char P8x81[256] = {
              0x75,0xF3,0xC6,0xF4,0xDB,0x7B,0xFB,0xC8,
              0x4A,0xD3,0xE6,0x6B,0x45,0x7D,0xE8,0x4B,
              0xD6,0x32,0xD8,0xFD,0x37,0x71,0xF1,0xE1,
              0x30,0x0F,0xF8,0x1B,0x87,0xFA,0x06,0x3F,
              0x5E,0xBA,0xAE,0x5B,0x8A,0x00,0xBC,0x9D,
              0x6D,0xC1,0xB1,0x0E,0x80,0x5D,0xD2,0xD5,
              0xA0,0x84,0x07,0x14,0xB5,0x90,0x2C,0xA3,
              0xB2,0x73,0x4C,0x54,0x92,0x74,0x36,0x51,
              0x38,0xB0,0xBD,0x5A,0xFC,0x60,0x62,0x96,
              0x6C,0x42,0xF7,0x10,0x7C,0x28,0x27,0x8C,
              0x13,0x95,0x9C,0xC7,0x24,0x46,0x3B,0x70,
              0xCA,0xE3,0x85,0xCB,0x11,0xD0,0x93,0xB8,
              0xA6,0x83,0x20,0xFF,0x9F,0x77,0xC3,0xCC,
              0x03,0x6F,0x08,0xBF,0x40,0xE7,0x2B,0xE2,
              0x79,0x0C,0xAA,0x82,0x41,0x3A,0xEA,0xB9,
              0xE4,0x9A,0xA4,0x97,0x7E,0xDA,0x7A,0x17,
              0x66,0x94,0xA1,0x1D,0x3D,0xF0,0xDE,0xB3,
              0x0B,0x72,0xA7,0x1C,0xEF,0xD1,0x53,0x3E,
              0x8F,0x33,0x26,0x5F,0xEC,0x76,0x2A,0x49,
              0x81,0x88,0xEE,0x21,0xC4,0x1A,0xEB,0xD9,
              0xC5,0x39,0x99,0xCD,0xAD,0x31,0x8B,0x01,
              0x18,0x23,0xDD,0x1F,0x4E,0x2D,0xF9,0x48,
              0x4F,0xF2,0x65,0x8E,0x78,0x5C,0x58,0x19,
              0x8D,0xE5,0x98,0x57,0x67,0x7F,0x05,0x64,
              0xAF,0x63,0xB6,0xFE,0xF5,0xB7,0x3C,0xA5,
              0xCE,0xE9,0x68,0x44,0xE0,0x4D,0x43,0x69,
              0x29,0x2E,0xAC,0x15,0x59,0xA8,0x0A,0x9E,
              0x6E,0x47,0xDF,0x34,0x35,0x6A,0xCF,0xDC,
              0x22,0xC9,0xC0,0x9B,0x89,0xD4,0xED,0xAB,
              0x12,0xA2,0x0D,0x52,0xBB,0x02,0x2F,0xA9,
              0xD7,0x61,0x1E,0xB4,0x50,0x04,0xF6,0xC2,
              0x16,0x25,0x86,0x56,0x55,0x09,0xBE,0x91
          };
          

      /* ---------------------------------------------------------------------- */

      /* This C code is as close to a "literal" translation of the Tcl code
         for f32 into C as I could achieve.  This part, except for some of
         the function setup and the function name, was written (well
         translated) by me, Richard Ellis.

         I realize that this is not the optimal solution to converting
         twofish into a C extension.  However, this much is just about at
         the limits of my ability to code in C and interface to the C side
         of Tcl, so it is the best I can do.  If anyone wishes to volunteer
         to write a full, proper, complete critcl version of Frank's twofish
         code, they are welcome to submit the work.
         
         However, as f32 is called 32 times per block in the inner loop of
         both encrypt and decrypt, any speedup achieved by having f32
         implemented in C is multiplied by 32 times as a result of the
         encrypt/decrypt loop calls.
         
      */

     static unsigned int
      c_f32_critcl(unsigned int x, unsigned int * k32, int keyLen)
      {

              unsigned char b0 =  x & 255;
              unsigned char b1 = (x >> 8) & 255;
              unsigned char b2 = (x >> 16) & 255;
              unsigned char b3 = (x >> 24) & 255;

              int kl = ((keyLen + 63) / 64) & 3; 

              if (kl == 0) {
                  b0 = P8x81[b0] ^ ( k32[3] & 255);
                  b1 = P8x80[b1] ^ ((k32[3] >> 8)  & 255);
                  b2 = P8x80[b2] ^ ((k32[3] >> 16) & 255);
                  b3 = P8x81[b3] ^ ((k32[3] >> 24) & 255);
              };

              if (kl == 0 || kl == 3) {
                  b0 = P8x81[b0] ^ ( k32[2] & 255);
                  b1 = P8x81[b1] ^ ((k32[2] >> 8)  & 255);
                  b2 = P8x80[b2] ^ ((k32[2] >> 16) & 255);
                  b3 = P8x80[b3] ^ ((k32[2] >> 24) & 255);
              };

              if (kl == 0 || kl == 3 || kl == 2) {

                  unsigned char t0 = P8x80[b0] ^ ( k32[1] & 255);
                  unsigned char t1 = P8x81[b1] ^ ((k32[1] >> 8)  & 255);
                  unsigned char t2 = P8x80[b2] ^ ((k32[1] >> 16) & 255);
                  unsigned char t3 = P8x81[b3] ^ ((k32[1] >> 24) & 255);

                  t0 = P8x80[t0] ^ ( k32[0] & 255);
                  t1 = P8x80[t1] ^ ((k32[0] >> 8)  & 255);
                  t2 = P8x81[t2] ^ ((k32[0] >> 16) & 255);
                  t3 = P8x81[t3] ^ ((k32[0] >> 24) & 255);

                  b0 = P8x81[t0];
                  b1 = P8x80[t1];
                  b2 = P8x81[t2];
                  b3 = P8x80[t3];
              };

      /*
              #
              # MDS_GF_FDBK/2 == 180
              # MDS_GF_FDBK/4 == 90
              #
      */

              unsigned char b0x = b0  ^ ((b0 >> 2) ^ ((b0 & 2) ? 180 : 0)) ^ ((b0 & 1) ? 90 : 0);
              unsigned char b0y = b0x ^ ((b0 >> 1) ^ ((b0 & 1) ? 180 : 0));

              unsigned char b1x = b1  ^ ((b1 >> 2) ^ ((b1 & 2) ? 180 : 0)) ^ ((b1 & 1) ? 90 : 0);
              unsigned char b1y = b1x ^ ((b1 >> 1) ^ ((b1 & 1) ? 180 : 0));

              unsigned char b2x = b2  ^ ((b2 >> 2) ^ ((b2 & 2) ? 180 : 0)) ^ ((b2 & 1) ? 90 : 0);
              unsigned char b2y = b2x ^ ((b2 >> 1) ^ ((b2 & 1) ? 180 : 0));

              unsigned char b3x = b3  ^ ((b3 >> 2) ^ ((b3 & 2) ? 180 : 0)) ^ ((b3 & 1) ? 90 : 0);
              unsigned char b3y = b3x ^ ((b3 >> 1) ^ ((b3 & 1) ? 180 : 0));

              return (((b0  ^ b1y ^ b2x ^ b3x) |  \
                      ((b0x ^ b1y ^ b2y ^ b3 )  << 8) |  \
                      ((b0y ^ b1x ^ b2  ^ b3y) << 16) |  \
                      ((b0y ^ b1  ^ b2y ^ b3x) << 24)) + 0x100000000) %  0x100000000; 

          }

    } ; # end critcl::ccode


    critcl::ccommand f32_critcl { cd interp objc objv } {

      /* Most of the C in this critcl::ccommand was "written" by critcl when
         compiling an earlier critcl::cproc variant of this code.  I simply
         edited the critcl created C to extract the list items from the
         second parameter and stuff them into an array, and then pass the
         address of the array on to c_f32_critcl function.
      */

        Tcl_WideInt temp;

        unsigned int _x;
        unsigned int _k32[4] = { 0, 0, 0, 0 };
        Tcl_Obj **data;
        int data_len;
        int _keyLen;
        unsigned int rv;

        if (objc != 4) {
          Tcl_WrongNumArgs(interp, 1, objv, "x k32 keyLen");
          return TCL_ERROR;
        }

        if (Tcl_GetWideIntFromObj(interp, objv[1], &temp) != TCL_OK)
          return TCL_ERROR;
        _x = temp & 0xffffffff;
        if (Tcl_ListObjGetElements(interp, objv[2], &data_len, &data) != TCL_OK)
          return TCL_ERROR;
        if (data_len > 4)
          return TCL_ERROR;
        int i;
        for (i=0 ; i < data_len ; i++ ) {
          if (Tcl_GetWideIntFromObj(interp, data[i], &temp) != TCL_OK)
            return TCL_ERROR;
          _k32[i] = temp & 0xffffffff;
        }
        if (Tcl_GetIntFromObj(interp, objv[3], &_keyLen) != TCL_OK)
          return TCL_ERROR;

        rv = c_f32_critcl(_x, _k32, _keyLen);

        Tcl_SetObjResult(interp, Tcl_NewWideIntObj(rv & 0xffffffff));
        return TCL_OK;

    } ; # end critcl::ccommand
