context: //#define		FEISTEL	 
  
  using System;
  using System.Diagnostics;
  using System.Security.Cryptography;
  
  /*
   * This implementation of Twofish has been published here:
   * http://www.codetools.com/csharp/twofish_csharp.asp
   */
  
  namespace Scripts.Engines.Encryption
  {
  
  	/// <summary>
  	/// Summary description for TwofishBase.
  	/// </summary>
  	internal class TwofishBase
  	{
  		public enum EncryptionDirection
  		{
  			Encrypting,
  			Decrypting
  		}
  
  		public TwofishBase()
  		{
  		}
  
  		protected int inputBlockSize = BLOCK_SIZE/8;
  		protected int outputBlockSize = BLOCK_SIZE/8;
  
  		/*
  		+*****************************************************************************
  		*
  		* Function Name:	f32
  		*
  		* Function:			Run four bytes through keyed S-boxes and apply MDS matrix
  		*
  		* Arguments:		x			=	input to f function
  		*					k32			=	pointer to key dwords
  		*					keyLen		=	total key length (k32 --> keyLey/2 bits)
  		*
  		* Return:			The output of the keyed permutation applied to x.
  		*
  		* Notes:
  		*	This function is a keyed 32-bit permutation.  It is the major building
  		*	block for the Twofish round function, including the four keyed 8x8 
  		*	permutations and the 4x4 MDS matrix multiply.  This function is used
  		*	both for generating round subkeys and within the round function on the
  		*	block being encrypted.  
  		*
  		*	This version is fairly slow and pedagogical, although a smartcard would
  		*	probably perform the operation exactly this way in firmware.   For
  		*	ultimate performance, the entire operation can be completed with four
  		*	lookups into four 256x32-bit tables, with three dword xors.
  		*
  		*	The MDS matrix is defined in TABLE.H.  To multiply by Mij, just use the
  		*	macro Mij(x).
  		*
  		-****************************************************************************/
  		private static uint f32(uint x,ref uint[] k32,int keyLen)
  		{
  			byte[]  b = {b0(x),b1(x),b2(x),b3(x)};
  		
  			/* Run each byte thru 8x8 S-boxes, xoring with key byte at each stage. */
  			/* Note that each byte goes through a different combination of S-boxes.*/
  
  			//*((DWORD *)b) = Bswap(x);	/* make b[0] = LSB, b[3] = MSB */
  			switch (((keyLen + 63)/64) & 3)
  			{
  				case 0:		/* 256 bits of key */
  				b[0] = (byte)(P8x8[P_04,b[0]] ^ b0(k32[3]));
  				b[1] = (byte)(P8x8[P_14,b[1]] ^ b1(k32[3]));
  				b[2] = (byte)(P8x8[P_24,b[2]] ^ b2(k32[3]));
  				b[3] = (byte)(P8x8[P_34,b[3]] ^ b3(k32[3]));
  				/* fall thru, having pre-processed b[0]..b[3] with k32[3] */
  				goto case 3;
  				case 3:		/* 192 bits of key */
  				b[0] = (byte)(P8x8[P_03,b[0]] ^ b0(k32[2]));
  				b[1] = (byte)(P8x8[P_13,b[1]] ^ b1(k32[2]));
  				b[2] = (byte)(P8x8[P_23,b[2]] ^ b2(k32[2]));
  				b[3] = (byte)(P8x8[P_33,b[3]] ^ b3(k32[2]));
  				/* fall thru, having pre-processed b[0]..b[3] with k32[2] */
  				goto case 2;
  				case 2:		/* 128 bits of key */
  				b[0] = P8x8[P_00, P8x8[P_01, P8x8[P_02, b[0]] ^ b0(k32[1])] ^ b0(k32[0])];
  				b[1] = P8x8[P_10, P8x8[P_11, P8x8[P_12, b[1]] ^ b1(k32[1])] ^ b1(k32[0])];
  				b[2] = P8x8[P_20, P8x8[P_21, P8x8[P_22, b[2]] ^ b2(k32[1])] ^ b2(k32[0])];
  				b[3] = P8x8[P_30, P8x8[P_31, P8x8[P_32, b[3]] ^ b3(k32[1])] ^ b3(k32[0])];
  				break;
  			}
  
  
  			/* Now perform the MDS matrix multiply inline. */
  			return	(uint)((M00(b[0]) ^ M01(b[1]) ^ M02(b[2]) ^ M03(b[3]))) ^
  			(uint)((M10(b[0]) ^ M11(b[1]) ^ M12(b[2]) ^ M13(b[3])) <<  8) ^
  			(uint)((M20(b[0]) ^ M21(b[1]) ^ M22(b[2]) ^ M23(b[3])) << 16) ^
  			(uint)((M30(b[0]) ^ M31(b[1]) ^ M32(b[2]) ^ M33(b[3])) << 24) ;
  		}
  
  		/*
  		+*****************************************************************************
  		*
  		* Function Name:	reKey
  		*
  		* Function:			Initialize the Twofish key schedule from key32
  		*
  		* Arguments:		key			=	ptr to keyInstance to be initialized
  		*
  		* Return:			TRUE on success
  		*
  		* Notes:
  		*	Here we precompute all the round subkeys, although that is not actually
  		*	required.  For example, on a smartcard, the round subkeys can 
  		*	be generated on-the-fly	using f32()
  		*
  		-****************************************************************************/
  		protected bool reKey(int keyLen, ref uint[] key32)
  		{
  			int		i,k64Cnt;
  			keyLength	  = keyLen;
  			rounds = numRounds[(keyLen-1)/64];
  			int		subkeyCnt = ROUND_SUBKEYS + 2*rounds;
  			uint	A,B;
  			uint[] k32e = new uint[MAX_KEY_BITS/64];
  			uint[] k32o = new uint[MAX_KEY_BITS/64]; /* even/odd key dwords */
  			
  			k64Cnt=(keyLen+63)/64;		/* round up to next multiple of 64 bits */
  			for (i=0;i<k64Cnt;i++)
  			{						/* split into even/odd key dwords */
  				k32e[i]=key32[2*i  ];
  				k32o[i]=key32[2*i+1];
  				/* compute S-box keys using (12,8) Reed-Solomon code over GF(256) */
  				sboxKeys[k64Cnt-1-i]=RS_MDS_Encode(k32e[i],k32o[i]); /* reverse order */
  			}
  
  			for (i=0;i<subkeyCnt/2;i++)					/* compute round subkeys for PHT */
  			{
  				A = f32((uint)(i*SK_STEP)        ,ref k32e, keyLen);	/* A uses even key dwords */
  				B = f32((uint)(i*SK_STEP+SK_BUMP),ref k32o, keyLen);	/* B uses odd  key dwords */
  				B = ROL(B,8);
  				subKeys[2*i  ] = A+  B;			/* combine with a PHT */
  				subKeys[2*i+1] = ROL(A+2*B,SK_ROTL);
  			}
  
  			return true;
  		}
  
  		public void blockDecrypt(ref uint[] x)
  		{
  			uint t0,t1;
  			uint[] xtemp = new uint[4];
  
  			if (cipherMode == CipherMode.CBC)
  			{
  				x.CopyTo(xtemp,0);
  			}
  
  			for (int i=0;i<BLOCK_SIZE/32;i++)	/* copy in the block, add whitening */
  				x[i] ^= subKeys[OUTPUT_WHITEN+i];
  
  			for (int r=rounds-1;r>=0;r--)			/* main Twofish decryption loop */
  			{
  				t0	 = f32(    x[0]   ,ref sboxKeys,keyLength);
  				t1	 = f32(ROL(x[1],8),ref sboxKeys,keyLength);
  
  				x[2] = ROL(x[2],1);
  				x[2]^= t0 +   t1 + subKeys[ROUND_SUBKEYS+2*r  ]; /* PHT, round keys */
  				x[3]^= t0 + 2*t1 + subKeys[ROUND_SUBKEYS+2*r+1];
  				x[3] = ROR(x[3],1);
  
  				if (r>0)									/* unswap, except for last round */
  				{
  					t0   = x[0]; x[0]= x[2]; x[2] = t0;	
  					t1   = x[1]; x[1]= x[3]; x[3] = t1;
  				}
  			}
  
  			for (int i=0;i<BLOCK_SIZE/32;i++)	/* copy out, with whitening */
  			{
  				x[i] ^= subKeys[INPUT_WHITEN+i];
  				if (cipherMode == CipherMode.CBC)
  				{
  					x[i] ^= IV[i];
  					IV[i] = xtemp[i]; 
  				}
  			}
  		}
  
  		public void blockEncrypt(ref uint[] x)
  		{
  			uint t0,t1,tmp;
  			
  			for (int i=0;i<BLOCK_SIZE/32;i++)	/* copy in the block, add whitening */
  			{
  				x[i] ^= subKeys[INPUT_WHITEN+i];
  				if (cipherMode == CipherMode.CBC)
  					x[i] ^= IV[i];
  			}
  
  			for (int r=0;r<rounds;r++)			/* main Twofish encryption loop */ // 16==rounds
  			{	
  #if FEISTEL
  				t0	 = f32(ROR(x[0],  (r+1)/2),ref sboxKeys,keyLength);
  				t1	 = f32(ROL(x[1],8+(r+1)/2),ref sboxKeys,keyLength);
  											/* PHT, round keys */
  				x[2]^= ROL(t0 +   t1 + subKeys[ROUND_SUBKEYS+2*r  ], r    /2);
  				x[3]^= ROR(t0 + 2*t1 + subKeys[ROUND_SUBKEYS+2*r+1],(r+2) /2);
  
  #else
  				t0	 = f32(    x[0]   ,ref sboxKeys,keyLength);
  				t1	 = f32(ROL(x[1],8),ref sboxKeys,keyLength);
  
  				x[3] = ROL(x[3],1);
  				x[2]^= t0 +   t1 + subKeys[ROUND_SUBKEYS+2*r  ]; /* PHT, round keys */
  				x[3]^= t0 + 2*t1 + subKeys[ROUND_SUBKEYS+2*r+1];
  				x[2] = ROR(x[2],1);
  
  #endif
  				if (r < rounds-1)						/* swap for next round */
  				{
  
gt: 					tmp = x[0]; x[0]= x[2]; x[2] = tmp;