context: using System;
  
  using Org.BouncyCastle.Crypto.Parameters;
  
  namespace Org.BouncyCastle.Crypto.Engines
  {
      /**
      * A class that provides a basic International Data Encryption Algorithm (IDEA) engine.
      * <p>
      * This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM"
      * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (baring 1 typo at the
      * end of the mulinv function!).
      * </p>
      * <p>
      * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/
      * </p>
      * <p>
      * Note 1: This algorithm is patented in the USA, Japan, and Europe including
      * at least Austria, France, Germany, Italy, Netherlands, Spain, Sweden, Switzerland
      * and the United Kingdom. Non-commercial use is free, however any commercial
      * products are liable for royalties. Please see
      * <a href="http://www.mediacrypt.com">www.mediacrypt.com</a> for
      * further details. This announcement has been included at the request of
      * the patent holders.
      * </p>
      * <p>
      * Note 2: Due to the requests concerning the above, this algorithm is now only
      * included in the extended assembly. It is not included in the default distributions.
      * </p>
      */
      public class IdeaEngine
          : IBlockCipher
      {
          private const int  BLOCK_SIZE = 8;
          private int[] workingKey;
          /**
          * standard constructor.
          */
          public IdeaEngine()
          {
          }
          /**
          * initialise an IDEA cipher.
          *
          * @param forEncryption whether or not we are for encryption.
          * @param parameters the parameters required to set up the cipher.
          * @exception ArgumentException if the parameters argument is
          * inappropriate.
          */
          public void Init(
              bool				forEncryption,
              ICipherParameters	parameters)
          {
              if (!(parameters is KeyParameter))
                  throw new ArgumentException("invalid parameter passed to IDEA init - " + parameters.GetType().ToString());
  
              workingKey = GenerateWorkingKey(forEncryption,
                  ((KeyParameter)parameters).GetKey());
          }
  
          public string AlgorithmName
          {
              get { return "IDEA"; }
          }
  
          public bool IsPartialBlockOkay
          {
              get { return false; }
          }
  
          public int GetBlockSize()
          {
              return BLOCK_SIZE;
          }
  
          public int ProcessBlock(
              byte[] input,
              int inOff,
              byte[] output,
              int outOff)
          {
              if (workingKey == null)
              {
                  throw new InvalidOperationException("IDEA engine not initialised");
              }
              if ((inOff + BLOCK_SIZE) > input.Length)
              {
                  throw new DataLengthException("input buffer too short");
              }
              if ((outOff + BLOCK_SIZE) > output.Length)
              {
                  throw new DataLengthException("output buffer too short");
              }
              IdeaFunc(workingKey, input, inOff, output, outOff);
              return BLOCK_SIZE;
          }
          public void Reset()
          {
          }
          private static readonly int    MASK = 0xffff;
          private static readonly int    BASE = 0x10001;
          private int BytesToWord(
              byte[]  input,
              int     inOff)
          {
              return ((input[inOff] << 8) & 0xff00) + (input[inOff + 1] & 0xff);
          }
          private void WordToBytes(
              int     word,
              byte[]  outBytes,
              int     outOff)
          {
              outBytes[outOff] = (byte)((uint) word >> 8);
              outBytes[outOff + 1] = (byte)word;
          }
          /**
          * return x = x * y where the multiplication is done modulo
          * 65537 (0x10001) (as defined in the IDEA specification) and
          * a zero input is taken to be 65536 (0x10000).
          *
          * @param x the x value
          * @param y the y value
          * @return x = x * y
          */
          private int Mul(
              int x,
              int y)
          {
              if (x == 0)
              {
                  x = (BASE - y);
              }
              else if (y == 0)
              {
                  x = (BASE - x);
              }
              else
              {
                  int     p = x * y;
                  y = p & MASK;
                  x = (int) ((uint) p >> 16);
                  x = y - x + ((y < x) ? 1 : 0);
              }
              return x & MASK;
          }
          private void IdeaFunc(
              int[]   workingKey,
              byte[]  input,
              int     inOff,
              byte[]  outBytes,
              int     outOff)
          {
              int     x0, x1, x2, x3, t0, t1;
              int     keyOff = 0;
              x0 = BytesToWord(input, inOff);
              x1 = BytesToWord(input, inOff + 2);
              x2 = BytesToWord(input, inOff + 4);
              x3 = BytesToWord(input, inOff + 6);
              for (int round = 0; round < 8; round++)
              {
                  x0 = Mul(x0, workingKey[keyOff++]);
                  x1 += workingKey[keyOff++];
                  x1 &= MASK;
                  x2 += workingKey[keyOff++];
                  x2 &= MASK;
                  x3 = Mul(x3, workingKey[keyOff++]);
                  t0 = x1;
                  t1 = x2;
                  x2 ^= x0;
                  x1 ^= x3;
                  x2 = Mul(x2, workingKey[keyOff++]);
                  x1 += x2;
                  x1 &= MASK;
                  x1 = Mul(x1, workingKey[keyOff++]);
                  x2 += x1;
                  x2 &= MASK;
                  x0 ^= x1;
                  x3 ^= x2;
                  x1 ^= t1;
                  x2 ^= t0;
              }
              WordToBytes(Mul(x0, workingKey[keyOff++]), outBytes, outOff);
              WordToBytes(x2 + workingKey[keyOff++], outBytes, outOff + 2);  /* NB: Order */
              WordToBytes(x1 + workingKey[keyOff++], outBytes, outOff + 4);
              WordToBytes(Mul(x3, workingKey[keyOff]), outBytes, outOff + 6);
          }
          /**
          * The following function is used to expand the user key to the encryption
          * subkey. The first 16 bytes are the user key, and the rest of the subkey
          * is calculated by rotating the previous 16 bytes by 25 bits to the left,
          * and so on until the subkey is completed.
          */
          private int[] ExpandKey(
              byte[]  uKey)
          {
              int[]   key = new int[52];
              if (uKey.Length < 16)
              {
                  byte[]  tmp = new byte[16];
                  Array.Copy(uKey, 0, tmp, tmp.Length - uKey.Length, uKey.Length);
                  uKey = tmp;
              }
              for (int i = 0; i < 8; i++)
              {
                  key[i] = BytesToWord(uKey, i * 2);
              }
              for (int i = 8; i < 52; i++)
              {
                  if ((i & 7) < 6)
                  {
                      key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7) & MASK;
                  }
                  else if ((i & 7) == 6)
                  {
                      key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7) & MASK;
                  }
                  else
                  {
                      key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7) & MASK;
                  }
              }
              return key;
          }
          /**
          * This function computes multiplicative inverse using Euclid's Greatest
          * Common Divisor algorithm. Zero and one are self inverse.
          * <p>
          * i.e. x * MulInv(x) == 1 (modulo BASE)
          * </p>
          */
          private int MulInv(
              int x)
          {
              int t0, t1, q, y;
  
              if (x < 2)
              {
                  return x;
              }
              t0 = 1;
              t1 = BASE / x;
              y  = BASE % x;
              while (y != 1)
              {
                  q = x / y;
                  x = x % y;
                  t0 = (t0 + (t1 * q)) & MASK;
  
gt:                 if (x == 1)