context: using System;
  using Org.BouncyCastle.Crypto.Parameters;
  
  namespace Org.BouncyCastle.Crypto.Modes
  {
  	/**
  	* implements the GOST 28147 OFB counter mode (GCTR).
  	*/
  	public class GOfbBlockCipher
  		: IBlockCipher
  	{
  		private byte[]	IV;
  		private byte[]	ofbV;
  		private byte[]	ofbOutV;
  
  		private readonly int			blockSize;
  		private readonly IBlockCipher	cipher;
  
  		bool firstStep = true;
  		int N3;
  		int N4;
  		const int C1 = 16843012; //00000001000000010000000100000100
  		const int C2 = 16843009; //00000001000000010000000100000001
  
  		/**
  		* Basic constructor.
  		*
  		* @param cipher the block cipher to be used as the basis of the
  		* counter mode (must have a 64 bit block size).
  		*/
  		public GOfbBlockCipher(
  			IBlockCipher cipher)
  		{
  			this.cipher = cipher;
  			this.blockSize = cipher.GetBlockSize();
  
  			if (blockSize != 8)
  			{
  				throw new ArgumentException("GCTR only for 64 bit block ciphers");
  			}
  
  			this.IV = new byte[cipher.GetBlockSize()];
  			this.ofbV = new byte[cipher.GetBlockSize()];
  			this.ofbOutV = new byte[cipher.GetBlockSize()];
  		}
  
  		/**
  		* return the underlying block cipher that we are wrapping.
  		*
  		* @return the underlying block cipher that we are wrapping.
  		*/
  		public IBlockCipher GetUnderlyingCipher()
  		{
  			return cipher;
  		}
  
  		/**
  		* Initialise the cipher and, possibly, the initialisation vector (IV).
  		* If an IV isn't passed as part of the parameter, the IV will be all zeros.
  		* An IV which is too short is handled in FIPS compliant fashion.
  		*
  		* @param encrypting if true the cipher is initialised for
  		*  encryption, if false for decryption.
  		* @param parameters the key and other data required by the cipher.
  		* @exception ArgumentException if the parameters argument is inappropriate.
  		*/
  		public void Init(
  			bool				forEncryption, //ignored by this CTR mode
  			ICipherParameters	parameters)
  		{
  			firstStep = true;
  			N3 = 0;
  			N4 = 0;
  
  			if (parameters is ParametersWithIV)
  			{
  				ParametersWithIV ivParam = (ParametersWithIV)parameters;
  				byte[]      iv = ivParam.GetIV();
  
  				if (iv.Length < IV.Length)
  				{
  					// prepend the supplied IV with zeros (per FIPS PUB 81)
  					Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
  					for (int i = 0; i < IV.Length - iv.Length; i++)
  					{
  						IV[i] = 0;
  					}
  				}
  				else
  				{
  					Array.Copy(iv, 0, IV, 0, IV.Length);
  				}
  
  				parameters = ivParam.Parameters;
  			}
  
  			Reset();
  
  			cipher.Init(true, parameters);
  		}
  
  		/**
  		* return the algorithm name and mode.
  		*
  		* @return the name of the underlying algorithm followed by "/GCTR"
  		* and the block size in bits
  		*/
  		public string AlgorithmName
  		{
  			get { return cipher.AlgorithmName + "/GCTR"; }
  		}
  
  		public bool IsPartialBlockOkay
  		{
  			get { return true; }
  		}
  
  		/**
  		* return the block size we are operating at (in bytes).
  		*
  		* @return the block size we are operating at (in bytes).
  		*/
  		public int GetBlockSize()
  		{
  			return blockSize;
  		}
  
  		/**
  		* Process one block of input from the array in and write it to
  		* the out array.
  		*
  		* @param in the array containing the input data.
  		* @param inOff offset into the in array the data starts at.
  		* @param out the array the output data will be copied into.
  		* @param outOff the offset into the out array the output will start at.
  		* @exception DataLengthException if there isn't enough data in in, or
  		* space in out.
  		* @exception InvalidOperationException if the cipher isn't initialised.
  		* @return the number of bytes processed and produced.
  		*/
  		public int ProcessBlock(
  			byte[]	input,
  			int		inOff,
  			byte[]	output,
  			int		outOff)
  		{
  			if ((inOff + blockSize) > input.Length)
  			{
  				throw new DataLengthException("input buffer too short");
  			}
  
  			if ((outOff + blockSize) > output.Length)
  			{
  				throw new DataLengthException("output buffer too short");
  			}
  
  			if (firstStep)
  			{
  				firstStep = false;
  				cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
  				N3 = bytesToint(ofbOutV, 0);
  				N4 = bytesToint(ofbOutV, 4);
  			}
  			N3 += C2;
  			N4 += C1;
  			intTobytes(N3, ofbV, 0);
  			intTobytes(N4, ofbV, 4);
  
  			cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
  
  			//
  			// XOR the ofbV with the plaintext producing the cipher text (and
  			// the next input block).
  			//
  			for (int i = 0; i < blockSize; i++)
  			{
  				output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]);
  			}
  
  			//
  			// change over the input block.
  			//
  			Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize);
  			Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize);
  
  			return blockSize;
  		}
  
  		/**
  		* reset the feedback vector back to the IV and reset the underlying
  		* cipher.
  		*/
  		public void Reset()
  		{
  			Array.Copy(IV, 0, ofbV, 0, IV.Length);
  
  			cipher.Reset();
  		}
  
  		//array of bytes to type int
  		private int bytesToint(
  			byte[]  inBytes,
  			int     inOff)
  		{
  			return  (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) +
  					((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff);
  		}
  
  		//int to array of bytes
  		private void intTobytes(
  				int     num,
  				byte[]  outBytes,
  				int     outOff)
  		{
  
gt: 				outBytes[outOff + 3] = (byte)(num >> 24);