/// <summary>
        ///     Run four bytes through keyed S-boxes and apply MDS matrix.
        /// </summary>
        private static DWord F32(DWord x, IList <DWord> k32, int keyLen)
        {
            if (keyLen >= 256)
            {
                x.B0 = (byte)(P8X8[P04, x.B0] ^ k32[3].B0);
                x.B1 = (byte)(P8X8[P14, x.B1] ^ k32[3].B1);
                x.B2 = (byte)(P8X8[P24, x.B2] ^ k32[3].B2);
                x.B3 = (byte)(P8X8[P34, x.B3] ^ k32[3].B3);
            }

            if (keyLen >= 192)
            {
                x.B0 = (byte)(P8X8[P03, x.B0] ^ k32[2].B0);
                x.B1 = (byte)(P8X8[P13, x.B1] ^ k32[2].B1);
                x.B2 = (byte)(P8X8[P23, x.B2] ^ k32[2].B2);
                x.B3 = (byte)(P8X8[P33, x.B3] ^ k32[2].B3);
            }

            if (keyLen >= 128)
            {
                x = MdsTable[0, P8X8[P01, P8X8[P02, x.B0] ^ k32[1].B0] ^ k32[0].B0]
                    ^ MdsTable[1, P8X8[P11, P8X8[P12, x.B1] ^ k32[1].B1] ^ k32[0].B1]
                    ^ MdsTable[2, P8X8[P21, P8X8[P22, x.B2] ^ k32[1].B2] ^ k32[0].B2]
                    ^ MdsTable[3, P8X8[P31, P8X8[P32, x.B3] ^ k32[1].B3] ^ k32[0].B3];
            }

            return(x);
        }
        /// <summary>
        ///     Initialize the Twofish key schedule from key32
        /// </summary>
        private void ReKey()
        {
            BuildMds(); // built only first time it is accessed

            var k32E = new DWord[_key.Length / 2];
            var k32O = new DWord[_key.Length / 2]; // even/odd key dwords

            var k64Cnt = _key.Length / 2;

            for (var i = 0; i < k64Cnt; i++)
            {
                // split into even/odd key dwords
                k32E[i] = _key[2 * i];
                k32O[i] = _key[2 * i + 1];
                _sBoxKeys[k64Cnt - 1 - i] =
                    ReedSolomonMdsEncode(k32E[i],
                                         k32O[i]); // compute S-box keys using (12,8) Reed-Solomon code over GF(256)
            }

            const int subkeyCnt = RoundSubkeys + 2 * Rounds;
            var       keyLen    = _key.Length * 4 * 8;

            for (var i = 0; i < subkeyCnt / 2; i++)
            {
                // compute round subkeys for PHT
                var a = F32((DWord)(i * SubkeyStep), k32E, keyLen);              // A uses even key dwords
                var b = F32((DWord)(i * SubkeyStep + SubkeyBump), k32O, keyLen); // B uses odd  key dwords
                b = RotateLeft(b, 8);
                _subKeys[2 * i]     = a + b;                                     // combine with a PHT
                _subKeys[2 * i + 1] = RotateLeft(a + 2 * b, SubkeyRotateLeft);
            }
        }
        /// <summary>
        ///     Decrypt block(s) of data using Twofish.
        /// </summary>
        internal void BlockDecrypt(byte[] inputBuffer, int inputOffset, byte[] outputBuffer, int outputBufferOffset)
        {
            var x     = new DWord[BlockSize / 32];
            var input = new DWord[BlockSize / 32];

            for (var i = 0; i < BlockSize / 32; i++)
            {
                // copy in the block, add whitening
                input[i] = new DWord(inputBuffer, inputOffset + i * 4);
                x[i]     = input[i] ^ _subKeys[OutputWhiten + i];
            }

            var keyLen = _key.Length * 4 * 8;

            for (var r = Rounds - 1; r >= 0; r--)
            {
                // main Twofish decryption loop
                var t0 = F32(x[0], _sBoxKeys, keyLen);
                var t1 = F32(RotateLeft(x[1], 8), _sBoxKeys, keyLen);

                x[2]  = RotateLeft(x[2], 1);
                x[2] ^= t0 + t1 + _subKeys[RoundSubkeys + 2 * r]; // PHT, round keys
                x[3] ^= t0 + 2 * t1 + _subKeys[RoundSubkeys + 2 * r + 1];
                x[3]  = RotateRight(x[3], 1);

                if (r <= 0)
                {
                    continue;
                }

                // 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 (var i = 0; i < BlockSize / 32; i++)
            {
                // copy out, with whitening
                x[i] ^= _subKeys[InputWhiten + i];
                if (_cipherMode == CipherMode.CBC)
                {
                    x[i]  ^= _iv[i];
                    _iv[i] = input[i];
                }

                outputBuffer[outputBufferOffset + i * 4 + 0] = x[i].B0;
                outputBuffer[outputBufferOffset + i * 4 + 1] = x[i].B1;
                outputBuffer[outputBufferOffset + i * 4 + 2] = x[i].B2;
                outputBuffer[outputBufferOffset + i * 4 + 3] = x[i].B3;
            }
        }
        /// <summary>
        ///     Encrypt block(s) of data using Twofish.
        /// </summary>
        internal void BlockEncrypt(byte[] inputBuffer, int inputOffset, byte[] outputBuffer, int outputBufferOffset)
        {
            var x = new DWord[BlockSize / 32];

            for (var i = 0; i < BlockSize / 32; i++)
            {
                // copy in the block, add whitening
                x[i] = new DWord(inputBuffer, inputOffset + i * 4) ^ _subKeys[InputWhiten + i];
                if (_cipherMode == CipherMode.CBC)
                {
                    x[i] ^= _iv[i];
                }
            }

            var keyLen = _key.Length * 4 * 8;

            for (var r = 0; r < Rounds; r++)
            {
                // main Twofish encryption loop
                var t0 = F32(x[0], _sBoxKeys, keyLen);
                var t1 = F32(RotateLeft(x[1], 8), _sBoxKeys, keyLen);

                x[3]  = RotateLeft(x[3], 1);
                x[2] ^= t0 + t1 + _subKeys[RoundSubkeys + 2 * r]; // PHT, round keys
                x[3] ^= t0 + 2 * t1 + _subKeys[RoundSubkeys + 2 * r + 1];
                x[2]  = RotateRight(x[2], 1);

                if (r >= Rounds - 1)
                {
                    continue;
                }

                // swap for next round
                var tmp = x[0];
                x[0] = x[2];
                x[2] = tmp;
                tmp  = x[1];
                x[1] = x[3];
                x[3] = tmp;
            }

            for (var i = 0; i < BlockSize / 32; i++)
            {
                // copy out, with whitening
                var outValue = x[i] ^ _subKeys[OutputWhiten + i];
                outputBuffer[outputBufferOffset + i * 4 + 0] = outValue.B0;
                outputBuffer[outputBufferOffset + i * 4 + 1] = outValue.B1;
                outputBuffer[outputBufferOffset + i * 4 + 2] = outValue.B2;
                outputBuffer[outputBufferOffset + i * 4 + 3] = outValue.B3;
                if (_cipherMode == CipherMode.CBC)
                {
                    _iv[i] = outValue;
                }
            }
        }
        private const uint RsGfFdbk = 0x14D; //field generator

        /// <summary>
        ///     Use (12,8) Reed-Solomon code over GF(256) to produce a key S-box dword from two key material dwords.
        /// </summary>
        /// <param name="k0">1st dword</param>
        /// <param name="k1">2nd dword</param>
        private static DWord ReedSolomonMdsEncode(DWord k0, DWord k1)
        {
            var r = new DWord();

            for (var i = 0; i < 2; i++)
            {
                r ^= i > 0 ? k0 : k1; // merge in 32 more key bits
                for (var j = 0; j < 4; j++)
                {
                    // shift one byte at a time
                    var b  = (byte)(r >> 24);
                    var g2 = (byte)((b << 1) ^ ((b & 0x80) > 0 ? RsGfFdbk : 0));
                    var g3 = (byte)(((b >> 1) & 0x7F) ^ ((b & 1) > 0 ? RsGfFdbk >> 1 : 0) ^ g2);
                    r.B3 = (byte)(r.B2 ^ g3);
                    r.B2 = (byte)(r.B1 ^ g2);
                    r.B1 = (byte)(r.B0 ^ g3);
                    r.B0 = b;
                }
            }

            return(r);
        }
 private static DWord RotateRight(DWord x, int n)
 {
     return((x >> n) | (x << (32 - n)));
 }
 private static DWord RotateLeft(DWord x, int n)
 {
     return((x << n) | (x >> (32 - n)));
 }