summary refs log tree commit diff
path: root/Crypto/src/crypto/modes
diff options
context:
space:
mode:
Diffstat (limited to 'Crypto/src/crypto/modes')
-rw-r--r--Crypto/src/crypto/modes/CbcBlockCipher.cs231
-rw-r--r--Crypto/src/crypto/modes/CcmBlockCipher.cs345
-rw-r--r--Crypto/src/crypto/modes/CfbBlockCipher.cs218
-rw-r--r--Crypto/src/crypto/modes/CtsBlockCipher.cs253
-rw-r--r--Crypto/src/crypto/modes/EAXBlockCipher.cs302
-rw-r--r--Crypto/src/crypto/modes/GCMBlockCipher.cs400
-rw-r--r--Crypto/src/crypto/modes/GOFBBlockCipher.cs223
-rw-r--r--Crypto/src/crypto/modes/IAeadBlockCipher.cs90
-rw-r--r--Crypto/src/crypto/modes/OfbBlockCipher.cs178
-rw-r--r--Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs337
-rw-r--r--Crypto/src/crypto/modes/SicBlockCipher.cs110
-rw-r--r--Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs40
-rw-r--r--Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs20
-rw-r--r--Crypto/src/crypto/modes/gcm/GcmUtilities.cs149
-rw-r--r--Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs10
-rw-r--r--Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs10
-rw-r--r--Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs44
-rw-r--r--Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs64
-rw-r--r--Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs90
19 files changed, 3114 insertions, 0 deletions
diff --git a/Crypto/src/crypto/modes/CbcBlockCipher.cs b/Crypto/src/crypto/modes/CbcBlockCipher.cs
new file mode 100644
index 000000000..0bbc0cb24
--- /dev/null
+++ b/Crypto/src/crypto/modes/CbcBlockCipher.cs
@@ -0,0 +1,231 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+    /**
+    * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
+    */
+    public class CbcBlockCipher
+		: IBlockCipher
+    {
+        private byte[]			IV, cbcV, cbcNextV;
+		private int				blockSize;
+        private IBlockCipher	cipher;
+        private bool			encrypting;
+
+        /**
+        * Basic constructor.
+        *
+        * @param cipher the block cipher to be used as the basis of chaining.
+        */
+        public CbcBlockCipher(
+            IBlockCipher cipher)
+        {
+            this.cipher = cipher;
+            this.blockSize = cipher.GetBlockSize();
+
+            this.IV = new byte[blockSize];
+            this.cbcV = new byte[blockSize];
+            this.cbcNextV = new byte[blockSize];
+        }
+
+        /**
+        * 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.
+        *
+        * @param forEncryption if true the cipher is initialised for
+        *  encryption, if false for decryption.
+        * @param param the key and other data required by the cipher.
+        * @exception ArgumentException if the parameters argument is
+        * inappropriate.
+        */
+        public void Init(
+            bool forEncryption,
+            ICipherParameters parameters)
+        {
+            this.encrypting = forEncryption;
+
+            if (parameters is ParametersWithIV)
+            {
+                ParametersWithIV ivParam = (ParametersWithIV)parameters;
+                byte[]      iv = ivParam.GetIV();
+
+                if (iv.Length != blockSize)
+                {
+                    throw new ArgumentException("initialisation vector must be the same length as block size");
+                }
+
+                Array.Copy(iv, 0, IV, 0, iv.Length);
+
+				parameters = ivParam.Parameters;
+            }
+
+			Reset();
+
+			cipher.Init(encrypting, parameters);
+        }
+
+		/**
+        * return the algorithm name and mode.
+        *
+        * @return the name of the underlying algorithm followed by "/CBC".
+        */
+        public string AlgorithmName
+        {
+            get { return cipher.AlgorithmName + "/CBC"; }
+        }
+
+		public bool IsPartialBlockOkay
+		{
+			get { return false; }
+		}
+
+		/**
+        * return the block size of the underlying cipher.
+        *
+        * @return the block size of the underlying cipher.
+        */
+        public int GetBlockSize()
+        {
+            return cipher.GetBlockSize();
+        }
+
+        /**
+        * 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)
+        {
+            return (encrypting)
+				?	EncryptBlock(input, inOff, output, outOff)
+				:	DecryptBlock(input, inOff, output, outOff);
+        }
+
+        /**
+        * reset the chaining vector back to the IV and reset the underlying
+        * cipher.
+        */
+        public void Reset()
+        {
+            Array.Copy(IV, 0, cbcV, 0, IV.Length);
+			Array.Clear(cbcNextV, 0, cbcNextV.Length);
+
+            cipher.Reset();
+        }
+
+        /**
+        * Do the appropriate chaining step for CBC mode encryption.
+        *
+        * @param in the array containing the data to be encrypted.
+        * @param inOff offset into the in array the data starts at.
+        * @param out the array the encrypted 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.
+        */
+        private int EncryptBlock(
+            byte[]      input,
+            int         inOff,
+            byte[]      outBytes,
+            int         outOff)
+        {
+            if ((inOff + blockSize) > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+
+            /*
+            * XOR the cbcV and the input,
+            * then encrypt the cbcV
+            */
+            for (int i = 0; i < blockSize; i++)
+            {
+                cbcV[i] ^= input[inOff + i];
+            }
+
+            int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff);
+
+            /*
+            * copy ciphertext to cbcV
+            */
+            Array.Copy(outBytes, outOff, cbcV, 0, cbcV.Length);
+
+            return length;
+        }
+
+        /**
+        * Do the appropriate chaining step for CBC mode decryption.
+        *
+        * @param in the array containing the data to be decrypted.
+        * @param inOff offset into the in array the data starts at.
+        * @param out the array the decrypted 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.
+        */
+        private int DecryptBlock(
+            byte[]      input,
+            int         inOff,
+            byte[]      outBytes,
+            int         outOff)
+        {
+            if ((inOff + blockSize) > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+
+            Array.Copy(input, inOff, cbcNextV, 0, blockSize);
+
+            int length = cipher.ProcessBlock(input, inOff, outBytes, outOff);
+
+            /*
+            * XOR the cbcV and the output
+            */
+            for (int i = 0; i < blockSize; i++)
+            {
+                outBytes[outOff + i] ^= cbcV[i];
+            }
+
+            /*
+            * swap the back up buffer into next position
+            */
+            byte[]  tmp;
+
+            tmp = cbcV;
+            cbcV = cbcNextV;
+            cbcNextV = tmp;
+
+            return length;
+        }
+    }
+
+}
diff --git a/Crypto/src/crypto/modes/CcmBlockCipher.cs b/Crypto/src/crypto/modes/CcmBlockCipher.cs
new file mode 100644
index 000000000..abfde237e
--- /dev/null
+++ b/Crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -0,0 +1,345 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+    /**
+    * Implements the Counter with Cipher Block Chaining mode (CCM) detailed in
+    * NIST Special Publication 800-38C.
+    * <p>
+    * <b>Note</b>: this mode is a packet mode - it needs all the data up front.
+	* </p>
+    */
+    public class CcmBlockCipher
+		: IAeadBlockCipher
+    {
+		private static readonly int BlockSize = 16;
+
+		private readonly IBlockCipher	cipher;
+        private readonly byte[]			macBlock;
+        private bool					forEncryption;
+		private byte[]					nonce;
+		private byte[]					associatedText;
+		private int						macSize;
+		private ICipherParameters		keyParam;
+		private readonly MemoryStream	data = new MemoryStream();
+
+        /**
+        * Basic constructor.
+        *
+        * @param cipher the block cipher to be used.
+        */
+        public CcmBlockCipher(
+			IBlockCipher cipher)
+        {
+            this.cipher = cipher;
+            this.macBlock = new byte[BlockSize];
+
+            if (cipher.GetBlockSize() != BlockSize)
+                throw new ArgumentException("cipher required with a block size of " + BlockSize + ".");
+        }
+
+        /**
+        * return the underlying block cipher that we are wrapping.
+        *
+        * @return the underlying block cipher that we are wrapping.
+        */
+        public virtual IBlockCipher GetUnderlyingCipher()
+        {
+            return cipher;
+        }
+
+        public virtual void Init(
+			bool				forEncryption,
+			ICipherParameters	parameters)
+        {
+			this.forEncryption = forEncryption;
+
+			if (parameters is AeadParameters)
+			{
+				AeadParameters param = (AeadParameters) parameters;
+
+				nonce = param.GetNonce();
+				associatedText = param.GetAssociatedText();
+				macSize = param.MacSize / 8;
+				keyParam = param.Key;
+			}
+			else if (parameters is ParametersWithIV)
+			{
+				ParametersWithIV param = (ParametersWithIV) parameters;
+
+				nonce = param.GetIV();
+				associatedText = null;
+				macSize = macBlock.Length / 2;
+				keyParam = param.Parameters;
+			}
+			else
+			{
+				throw new ArgumentException("invalid parameters passed to CCM");
+			}
+        }
+
+		public virtual string AlgorithmName
+        {
+            get { return cipher.AlgorithmName + "/CCM"; }
+        }
+
+		public virtual int GetBlockSize()
+		{
+			return cipher.GetBlockSize();
+		}
+
+		public virtual int ProcessByte(
+			byte	input,
+			byte[]	outBytes,
+			int		outOff)
+		{
+			data.WriteByte(input);
+
+			return 0;
+		}
+
+		public virtual int ProcessBytes(
+			byte[]	inBytes,
+			int		inOff,
+			int		inLen,
+			byte[]	outBytes,
+			int		outOff)
+		{
+			data.Write(inBytes, inOff, inLen);
+
+			return 0;
+		}
+
+		public virtual int DoFinal(
+			byte[]	outBytes,
+			int		outOff)
+		{
+			byte[] text = data.ToArray();
+			byte[] enc = ProcessPacket(text, 0, text.Length);
+
+			Array.Copy(enc, 0, outBytes, outOff, enc.Length);
+
+			Reset();
+
+			return enc.Length;
+		}
+
+		public virtual void Reset()
+		{
+			cipher.Reset();
+			data.SetLength(0);
+		}
+
+		/**
+        * Returns a byte array containing the mac calculated as part of the
+        * last encrypt or decrypt operation.
+        *
+        * @return the last mac calculated.
+        */
+        public virtual byte[] GetMac()
+        {
+			byte[] mac = new byte[macSize];
+
+			Array.Copy(macBlock, 0, mac, 0, mac.Length);
+
+			return mac;
+        }
+
+		public virtual int GetUpdateOutputSize(
+			int len)
+		{
+			return 0;
+		}
+
+		public int GetOutputSize(
+			int len)
+		{
+			if (forEncryption)
+			{
+				return (int) data.Length + len + macSize;
+			}
+
+			return (int) data.Length + len - macSize;
+		}
+
+		public byte[] ProcessPacket(
+			byte[]	input,
+			int		inOff,
+			int		inLen)
+        {
+            if (keyParam == null)
+                throw new InvalidOperationException("CCM cipher unitialized.");
+
+			IBlockCipher ctrCipher = new SicBlockCipher(cipher);
+            byte[] iv = new byte[BlockSize];
+            byte[] output;
+
+            iv[0] = (byte)(((15 - nonce.Length) - 1) & 0x7);
+
+            Array.Copy(nonce, 0, iv, 1, nonce.Length);
+
+			ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv));
+
+			if (forEncryption)
+            {
+                int index = inOff;
+                int outOff = 0;
+
+                output = new byte[inLen + macSize];
+
+                calculateMac(input, inOff, inLen, macBlock);
+
+                ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0);   // S0
+
+                while (index < inLen - BlockSize)                   // S1...
+                {
+                    ctrCipher.ProcessBlock(input, index, output, outOff);
+                    outOff += BlockSize;
+                    index += BlockSize;
+                }
+
+                byte[] block = new byte[BlockSize];
+
+                Array.Copy(input, index, block, 0, inLen - index);
+
+                ctrCipher.ProcessBlock(block, 0, block, 0);
+
+                Array.Copy(block, 0, output, outOff, inLen - index);
+
+                outOff += inLen - index;
+
+                Array.Copy(macBlock, 0, output, outOff, output.Length - outOff);
+            }
+            else
+            {
+                int index = inOff;
+                int outOff = 0;
+
+                output = new byte[inLen - macSize];
+
+                Array.Copy(input, inOff + inLen - macSize, macBlock, 0, macSize);
+
+                ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0);
+
+                for (int i = macSize; i != macBlock.Length; i++)
+                {
+                    macBlock[i] = 0;
+                }
+
+                while (outOff < output.Length - BlockSize)
+                {
+                    ctrCipher.ProcessBlock(input, index, output, outOff);
+                    outOff += BlockSize;
+                    index += BlockSize;
+                }
+
+                byte[] block = new byte[BlockSize];
+
+                Array.Copy(input, index, block, 0, output.Length - outOff);
+
+                ctrCipher.ProcessBlock(block, 0, block, 0);
+
+                Array.Copy(block, 0, output, outOff, output.Length - outOff);
+
+                byte[] calculatedMacBlock = new byte[BlockSize];
+
+                calculateMac(output, 0, output.Length, calculatedMacBlock);
+
+				if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock))
+                    throw new InvalidCipherTextException("mac check in CCM failed");
+            }
+
+			return output;
+        }
+
+		private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
+        {
+			IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8);
+
+			cMac.Init(keyParam);
+
+			//
+            // build b0
+            //
+            byte[] b0 = new byte[16];
+
+			if (hasAssociatedText())
+            {
+                b0[0] |= 0x40;
+            }
+
+            b0[0] |= (byte)((((cMac.GetMacSize() - 2) / 2) & 0x7) << 3);
+
+            b0[0] |= (byte)(((15 - nonce.Length) - 1) & 0x7);
+
+            Array.Copy(nonce, 0, b0, 1, nonce.Length);
+
+            int q = dataLen;
+            int count = 1;
+            while (q > 0)
+            {
+                b0[b0.Length - count] = (byte)(q & 0xff);
+                q >>= 8;
+                count++;
+            }
+
+            cMac.BlockUpdate(b0, 0, b0.Length);
+
+            //
+            // process associated text
+            //
+			if (hasAssociatedText())
+            {
+                int extra;
+
+                if (associatedText.Length < ((1 << 16) - (1 << 8)))
+                {
+                    cMac.Update((byte)(associatedText.Length >> 8));
+                    cMac.Update((byte)associatedText.Length);
+
+                    extra = 2;
+                }
+                else // can't go any higher than 2^32
+                {
+                    cMac.Update((byte)0xff);
+                    cMac.Update((byte)0xfe);
+                    cMac.Update((byte)(associatedText.Length >> 24));
+                    cMac.Update((byte)(associatedText.Length >> 16));
+                    cMac.Update((byte)(associatedText.Length >> 8));
+                    cMac.Update((byte)associatedText.Length);
+
+                    extra = 6;
+                }
+
+                cMac.BlockUpdate(associatedText, 0, associatedText.Length);
+
+                extra = (extra + associatedText.Length) % 16;
+                if (extra != 0)
+                {
+                    for (int i = 0; i != 16 - extra; i++)
+                    {
+                        cMac.Update((byte)0x00);
+                    }
+                }
+            }
+
+            //
+            // add the text
+            //
+            cMac.BlockUpdate(data, dataOff, dataLen);
+
+            return cMac.DoFinal(macBlock, 0);
+        }
+
+		private bool hasAssociatedText()
+		{
+			return associatedText != null && associatedText.Length != 0;
+		}
+    }
+}
diff --git a/Crypto/src/crypto/modes/CfbBlockCipher.cs b/Crypto/src/crypto/modes/CfbBlockCipher.cs
new file mode 100644
index 000000000..b400a72f4
--- /dev/null
+++ b/Crypto/src/crypto/modes/CfbBlockCipher.cs
@@ -0,0 +1,218 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+    /**
+    * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
+    */
+    public class CfbBlockCipher
+		: IBlockCipher
+    {
+        private byte[]	IV;
+        private byte[]	cfbV;
+        private byte[]	cfbOutV;
+		private bool	encrypting;
+
+		private readonly int			blockSize;
+        private readonly IBlockCipher	cipher;
+
+		/**
+        * Basic constructor.
+        *
+        * @param cipher the block cipher to be used as the basis of the
+        * feedback mode.
+        * @param blockSize the block size in bits (note: a multiple of 8)
+        */
+        public CfbBlockCipher(
+            IBlockCipher cipher,
+            int          bitBlockSize)
+        {
+            this.cipher = cipher;
+            this.blockSize = bitBlockSize / 8;
+            this.IV = new byte[cipher.GetBlockSize()];
+            this.cfbV = new byte[cipher.GetBlockSize()];
+            this.cfbOutV = 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 forEncryption if true the cipher is initialised for
+        *  encryption, if false for decryption.
+        * @param param the key and other data required by the cipher.
+        * @exception ArgumentException if the parameters argument is
+        * inappropriate.
+        */
+        public void Init(
+            bool forEncryption,
+            ICipherParameters parameters)
+        {
+            this.encrypting = forEncryption;
+            if (parameters is ParametersWithIV)
+            {
+                ParametersWithIV ivParam = (ParametersWithIV) parameters;
+                byte[] iv = ivParam.GetIV();
+                int diff = IV.Length - iv.Length;
+                Array.Copy(iv, 0, IV, diff, iv.Length);
+                Array.Clear(IV, 0, diff);
+
+                parameters = ivParam.Parameters;
+            }
+            Reset();
+            cipher.Init(true, parameters);
+        }
+        /**
+        * return the algorithm name and mode.
+        *
+        * @return the name of the underlying algorithm followed by "/CFB"
+        * and the block size in bits.
+        */
+        public string AlgorithmName
+        {
+            get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); }
+        }
+
+		public bool IsPartialBlockOkay
+		{
+			get { return true; }
+		}
+
+		/**
+        * return the block size we are operating at.
+        *
+        * @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)
+        {
+            return (encrypting)
+				?	EncryptBlock(input, inOff, output, outOff)
+				:	DecryptBlock(input, inOff, output, outOff);
+        }
+
+		/**
+        * Do the appropriate processing for CFB mode encryption.
+        *
+        * @param in the array containing the data to be encrypted.
+        * @param inOff offset into the in array the data starts at.
+        * @param out the array the encrypted 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 EncryptBlock(
+            byte[]      input,
+            int         inOff,
+            byte[]      outBytes,
+            int         outOff)
+        {
+            if ((inOff + blockSize) > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+            if ((outOff + blockSize) > outBytes.Length)
+            {
+                throw new DataLengthException("output buffer too short");
+            }
+            cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
+            //
+            // XOR the cfbV with the plaintext producing the ciphertext
+            //
+            for (int i = 0; i < blockSize; i++)
+            {
+                outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
+            }
+            //
+            // change over the input block.
+            //
+            Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
+            Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize);
+            return blockSize;
+        }
+        /**
+        * Do the appropriate processing for CFB mode decryption.
+        *
+        * @param in the array containing the data to be decrypted.
+        * @param inOff offset into the in array the data starts at.
+        * @param out the array the encrypted 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 DecryptBlock(
+            byte[]	input,
+            int		inOff,
+            byte[]	outBytes,
+            int		outOff)
+        {
+            if ((inOff + blockSize) > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+            if ((outOff + blockSize) > outBytes.Length)
+            {
+                throw new DataLengthException("output buffer too short");
+            }
+            cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
+            //
+            // change over the input block.
+            //
+            Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
+            Array.Copy(input, inOff, cfbV, cfbV.Length - blockSize, blockSize);
+            //
+            // XOR the cfbV with the ciphertext producing the plaintext
+            //
+            for (int i = 0; i < blockSize; i++)
+            {
+                outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
+            }
+            return blockSize;
+        }
+        /**
+        * reset the chaining vector back to the IV and reset the underlying
+        * cipher.
+        */
+        public void Reset()
+        {
+            Array.Copy(IV, 0, cfbV, 0, IV.Length);
+            cipher.Reset();
+        }
+    }
+}
diff --git a/Crypto/src/crypto/modes/CtsBlockCipher.cs b/Crypto/src/crypto/modes/CtsBlockCipher.cs
new file mode 100644
index 000000000..a32b49675
--- /dev/null
+++ b/Crypto/src/crypto/modes/CtsBlockCipher.cs
@@ -0,0 +1,253 @@
+using System;
+using System.Diagnostics;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+    /**
+    * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
+    * be used to produce cipher text which is the same outLength as the plain text.
+    */
+    public class CtsBlockCipher
+		: BufferedBlockCipher
+    {
+        private readonly int blockSize;
+
+        /**
+        * Create a buffered block cipher that uses Cipher Text Stealing
+        *
+        * @param cipher the underlying block cipher this buffering object wraps.
+        */
+        public CtsBlockCipher(
+            IBlockCipher cipher)
+        {
+			// TODO Should this test for acceptable ones instead?
+			if (cipher is OfbBlockCipher || cipher is CfbBlockCipher)
+                throw new ArgumentException("CtsBlockCipher can only accept ECB, or CBC ciphers");
+
+			this.cipher = cipher;
+
+            blockSize = cipher.GetBlockSize();
+
+            buf = new byte[blockSize * 2];
+            bufOff = 0;
+        }
+
+        /**
+        * return the size of the output buffer required for an update of 'length' bytes.
+        *
+        * @param length the outLength of the input.
+        * @return the space required to accommodate a call to update
+        * with length bytes of input.
+        */
+        public override int GetUpdateOutputSize(
+            int length)
+        {
+            int total = length + bufOff;
+            int leftOver = total % buf.Length;
+
+			if (leftOver == 0)
+            {
+                return total - buf.Length;
+            }
+
+			return total - leftOver;
+        }
+
+        /**
+        * return the size of the output buffer required for an update plus a
+        * doFinal with an input of length bytes.
+        *
+        * @param length the outLength of the input.
+        * @return the space required to accommodate a call to update and doFinal
+        * with length bytes of input.
+        */
+        public override int GetOutputSize(
+            int length)
+        {
+            return length + bufOff;
+        }
+
+        /**
+        * process a single byte, producing an output block if neccessary.
+        *
+        * @param in the input byte.
+        * @param out the space for any output that might be produced.
+        * @param outOff the offset from which the output will be copied.
+        * @return the number of output bytes copied to out.
+        * @exception DataLengthException if there isn't enough space in out.
+        * @exception InvalidOperationException if the cipher isn't initialised.
+        */
+        public override int ProcessByte(
+            byte	input,
+            byte[]	output,
+            int		outOff)
+        {
+            int resultLen = 0;
+
+            if (bufOff == buf.Length)
+            {
+                resultLen = cipher.ProcessBlock(buf, 0, output, outOff);
+				Debug.Assert(resultLen == blockSize);
+
+				Array.Copy(buf, blockSize, buf, 0, blockSize);
+                bufOff = blockSize;
+            }
+
+            buf[bufOff++] = input;
+
+            return resultLen;
+        }
+
+		/**
+        * process an array of bytes, producing output if necessary.
+        *
+        * @param in the input byte array.
+        * @param inOff the offset at which the input data starts.
+        * @param length the number of bytes to be copied out of the input array.
+        * @param out the space for any output that might be produced.
+        * @param outOff the offset from which the output will be copied.
+        * @return the number of output bytes copied to out.
+        * @exception DataLengthException if there isn't enough space in out.
+        * @exception InvalidOperationException if the cipher isn't initialised.
+        */
+        public override int ProcessBytes(
+            byte[]	input,
+            int		inOff,
+            int		length,
+            byte[]	output,
+            int		outOff)
+        {
+            if (length < 0)
+            {
+                throw new ArgumentException("Can't have a negative input outLength!");
+            }
+
+            int blockSize = GetBlockSize();
+            int outLength = GetUpdateOutputSize(length);
+
+            if (outLength > 0)
+            {
+                if ((outOff + outLength) > output.Length)
+                {
+                    throw new DataLengthException("output buffer too short");
+                }
+            }
+
+            int resultLen = 0;
+            int gapLen = buf.Length - bufOff;
+
+            if (length > gapLen)
+            {
+                Array.Copy(input, inOff, buf, bufOff, gapLen);
+
+                resultLen += cipher.ProcessBlock(buf, 0, output, outOff);
+                Array.Copy(buf, blockSize, buf, 0, blockSize);
+
+                bufOff = blockSize;
+
+                length -= gapLen;
+                inOff += gapLen;
+
+                while (length > blockSize)
+                {
+                    Array.Copy(input, inOff, buf, bufOff, blockSize);
+                    resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen);
+                    Array.Copy(buf, blockSize, buf, 0, blockSize);
+
+                    length -= blockSize;
+                    inOff += blockSize;
+                }
+            }
+
+            Array.Copy(input, inOff, buf, bufOff, length);
+
+            bufOff += length;
+
+            return resultLen;
+        }
+
+        /**
+        * Process the last block in the buffer.
+        *
+        * @param out the array the block currently being held is copied into.
+        * @param outOff the offset at which the copying starts.
+        * @return the number of output bytes copied to out.
+        * @exception DataLengthException if there is insufficient space in out for
+        * the output.
+        * @exception InvalidOperationException if the underlying cipher is not
+        * initialised.
+        * @exception InvalidCipherTextException if cipher text decrypts wrongly (in
+        * case the exception will never Get thrown).
+        */
+        public override int DoFinal(
+            byte[]  output,
+            int     outOff)
+        {
+            if (bufOff + outOff > output.Length)
+            {
+                throw new DataLengthException("output buffer too small in doFinal");
+            }
+
+            int blockSize = cipher.GetBlockSize();
+            int length = bufOff - blockSize;
+            byte[] block = new byte[blockSize];
+
+            if (forEncryption)
+            {
+                cipher.ProcessBlock(buf, 0, block, 0);
+
+				if (bufOff < blockSize)
+				{
+					throw new DataLengthException("need at least one block of input for CTS");
+				}
+
+                for (int i = bufOff; i != buf.Length; i++)
+                {
+                    buf[i] = block[i - blockSize];
+                }
+
+                for (int i = blockSize; i != bufOff; i++)
+                {
+                    buf[i] ^= block[i - blockSize];
+                }
+
+				IBlockCipher c = (cipher is CbcBlockCipher)
+					?	((CbcBlockCipher)cipher).GetUnderlyingCipher()
+					:	cipher;
+
+				c.ProcessBlock(buf, blockSize, output, outOff);
+
+				Array.Copy(block, 0, output, outOff + blockSize, length);
+            }
+            else
+            {
+                byte[] lastBlock = new byte[blockSize];
+
+				IBlockCipher c = (cipher is CbcBlockCipher)
+					?	((CbcBlockCipher)cipher).GetUnderlyingCipher()
+					:	cipher;
+
+				c.ProcessBlock(buf, 0, block, 0);
+
+				for (int i = blockSize; i != bufOff; i++)
+                {
+                    lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
+                }
+
+                Array.Copy(buf, blockSize, block, 0, length);
+
+                cipher.ProcessBlock(block, 0, output, outOff);
+                Array.Copy(lastBlock, 0, output, outOff + blockSize, length);
+            }
+
+            int offset = bufOff;
+
+            Reset();
+
+            return offset;
+        }
+    }
+}
diff --git a/Crypto/src/crypto/modes/EAXBlockCipher.cs b/Crypto/src/crypto/modes/EAXBlockCipher.cs
new file mode 100644
index 000000000..b3016d79c
--- /dev/null
+++ b/Crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -0,0 +1,302 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+	/**
+	* A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and 
+	* Efficiency - by M. Bellare, P. Rogaway, D. Wagner.
+	* 
+	* http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
+	* 
+	* EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block 
+	* cipher to encrypt and authenticate data. It's on-line (the length of a 
+	* message isn't needed to begin processing it), has good performances, it's
+	* simple and provably secure (provided the underlying block cipher is secure).
+	* 
+	* Of course, this implementations is NOT thread-safe.
+	*/
+	public class EaxBlockCipher
+		: IAeadBlockCipher
+	{
+		private enum Tag : byte { N, H, C };
+
+		private SicBlockCipher cipher;
+
+		private bool forEncryption;
+
+		private int blockSize;
+
+		private IMac mac;
+
+		private byte[] nonceMac;
+		private byte[] associatedTextMac;
+		private byte[] macBlock;
+
+		private int macSize;
+		private byte[] bufBlock;
+		private int bufOff;
+
+		/**
+		* Constructor that accepts an instance of a block cipher engine.
+		*
+		* @param cipher the engine to use
+		*/
+		public EaxBlockCipher(
+			IBlockCipher cipher)
+		{
+			blockSize = cipher.GetBlockSize();
+			mac = new CMac(cipher);
+			macBlock = new byte[blockSize];
+			bufBlock = new byte[blockSize * 2];
+			associatedTextMac = new byte[mac.GetMacSize()];
+			nonceMac = new byte[mac.GetMacSize()];
+			this.cipher = new SicBlockCipher(cipher);
+		}
+
+		public virtual string AlgorithmName
+		{
+			get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; }
+		}
+
+		public virtual int GetBlockSize()
+		{
+			return cipher.GetBlockSize();
+		}
+
+		public virtual void Init(
+			bool				forEncryption,
+			ICipherParameters	parameters)
+		{
+			this.forEncryption = forEncryption;
+
+			byte[] nonce, associatedText;
+			ICipherParameters keyParam;
+
+			if (parameters is AeadParameters)
+			{
+				AeadParameters param = (AeadParameters) parameters;
+
+				nonce = param.GetNonce();
+				associatedText = param.GetAssociatedText();
+				macSize = param.MacSize / 8;
+				keyParam = param.Key;
+			}
+			else if (parameters is ParametersWithIV)
+			{
+				ParametersWithIV param = (ParametersWithIV) parameters;
+
+				nonce = param.GetIV();
+				associatedText = new byte[0];
+				macSize = mac.GetMacSize() / 2;
+				keyParam = param.Parameters;
+			}
+			else
+			{
+				throw new ArgumentException("invalid parameters passed to EAX");
+			}
+
+			byte[] tag = new byte[blockSize];
+
+			mac.Init(keyParam);
+			tag[blockSize - 1] = (byte) Tag.H;
+			mac.BlockUpdate(tag, 0, blockSize);
+			mac.BlockUpdate(associatedText, 0, associatedText.Length);
+			mac.DoFinal(associatedTextMac, 0);
+
+			tag[blockSize - 1] = (byte) Tag.N;
+			mac.BlockUpdate(tag, 0, blockSize);
+			mac.BlockUpdate(nonce, 0, nonce.Length);
+			mac.DoFinal(nonceMac, 0);
+
+			tag[blockSize - 1] = (byte) Tag.C;
+			mac.BlockUpdate(tag, 0, blockSize);
+
+			cipher.Init(true, new ParametersWithIV(keyParam, nonceMac));
+		}
+
+		private void calculateMac()
+		{
+			byte[] outC = new byte[blockSize];
+			mac.DoFinal(outC, 0);
+
+			for (int i = 0; i < macBlock.Length; i++)
+			{
+				macBlock[i] = (byte)(nonceMac[i] ^ associatedTextMac[i] ^ outC[i]);
+			}
+		}
+
+		public virtual void Reset()
+		{
+			Reset(true);
+		}
+
+		private void Reset(
+			bool clearMac)
+		{
+			cipher.Reset();
+			mac.Reset();
+
+			bufOff = 0;
+			Array.Clear(bufBlock, 0, bufBlock.Length);
+
+			if (clearMac)
+			{
+				Array.Clear(macBlock, 0, macBlock.Length);
+			}
+
+			byte[] tag = new byte[blockSize];
+			tag[blockSize - 1] = (byte) Tag.C;
+			mac.BlockUpdate(tag, 0, blockSize);
+		}
+
+		public virtual int ProcessByte(
+			byte	input,
+			byte[]	outBytes,
+			int		outOff)
+		{
+			return process(input, outBytes, outOff);
+		}
+
+		public virtual int ProcessBytes(
+			byte[]	inBytes,
+			int		inOff,
+			int		len,
+			byte[]	outBytes,
+			int		outOff)
+		{
+			int resultLen = 0;
+
+			for (int i = 0; i != len; i++)
+			{
+				resultLen += process(inBytes[inOff + i], outBytes, outOff + resultLen);
+			}
+
+			return resultLen;
+		}
+
+		public virtual int DoFinal(
+			byte[]	outBytes,
+			int		outOff)
+		{
+			int extra = bufOff;
+			byte[] tmp = new byte[bufBlock.Length];
+
+			bufOff = 0;
+
+			if (forEncryption)
+			{
+				cipher.ProcessBlock(bufBlock, 0, tmp, 0);
+				cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize);
+
+				Array.Copy(tmp, 0, outBytes, outOff, extra);
+
+				mac.BlockUpdate(tmp, 0, extra);
+
+				calculateMac();
+
+				Array.Copy(macBlock, 0, outBytes, outOff + extra, macSize);
+
+				Reset(false);
+
+				return extra + macSize;
+			}
+			else
+			{
+				if (extra > macSize)
+				{
+					mac.BlockUpdate(bufBlock, 0, extra - macSize);
+
+					cipher.ProcessBlock(bufBlock, 0, tmp, 0);
+					cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize);
+
+					Array.Copy(tmp, 0, outBytes, outOff, extra - macSize);
+				}
+
+				calculateMac();
+
+				if (!verifyMac(bufBlock, extra - macSize))
+					throw new InvalidCipherTextException("mac check in EAX failed");
+
+				Reset(false);
+
+				return extra - macSize;
+			}
+		}
+
+		public virtual byte[] GetMac()
+		{
+			byte[] mac = new byte[macSize];
+
+			Array.Copy(macBlock, 0, mac, 0, macSize);
+
+			return mac;
+		}
+
+		public virtual int GetUpdateOutputSize(
+			int len)
+		{
+			return ((len + bufOff) / blockSize) * blockSize;
+		}
+
+		public virtual int GetOutputSize(
+			int len)
+		{
+			if (forEncryption)
+			{
+				return len + bufOff + macSize;
+			}
+
+			return len + bufOff - macSize;
+		}
+
+		private int process(
+			byte	b,
+			byte[]	outBytes,
+			int		outOff)
+		{
+			bufBlock[bufOff++] = b;
+
+			if (bufOff == bufBlock.Length)
+			{
+				int size;
+
+				if (forEncryption)
+				{
+					size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff);
+
+					mac.BlockUpdate(outBytes, outOff, blockSize);
+				}
+				else
+				{
+					mac.BlockUpdate(bufBlock, 0, blockSize);
+
+					size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff);
+				}
+
+				bufOff = blockSize;
+				Array.Copy(bufBlock, blockSize, bufBlock, 0, blockSize);
+
+				return size;
+			}
+
+			return 0;
+		}
+
+		private bool verifyMac(byte[] mac, int off)
+		{
+			for (int i = 0; i < macSize; i++)
+			{
+				if (macBlock[i] != mac[off + i])
+				{
+					return false;
+				}
+			}
+
+			return true;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/modes/GCMBlockCipher.cs b/Crypto/src/crypto/modes/GCMBlockCipher.cs
new file mode 100644
index 000000000..6a3a4463d
--- /dev/null
+++ b/Crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -0,0 +1,400 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Modes.Gcm;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+	/// <summary>
+	/// Implements the Galois/Counter mode (GCM) detailed in
+	/// NIST Special Publication 800-38D.
+	/// </summary>
+	public class GcmBlockCipher
+		: IAeadBlockCipher
+	{
+		private const int					BlockSize = 16;
+		private static readonly byte[]		Zeroes = new byte[BlockSize];
+
+		private readonly IBlockCipher	cipher;
+		private readonly IGcmMultiplier	multiplier;
+
+		// These fields are set by Init and not modified by processing
+		private bool				forEncryption;
+		private int                 macSize;
+		private byte[]              nonce;
+		private byte[]              A;
+		private KeyParameter        keyParam;
+		private byte[]				H;
+		private byte[]				initS;
+		private byte[]              J0;
+
+		// These fields are modified during processing
+		private byte[]		bufBlock;
+		private byte[]		macBlock;
+		private byte[]		S;
+		private byte[]      counter;
+		private int         bufOff;
+		private ulong		totalLength;
+
+		public GcmBlockCipher(
+			IBlockCipher c)
+			: this(c, null)
+		{
+	    }
+
+	    public GcmBlockCipher(
+			IBlockCipher	c,
+			IGcmMultiplier	m)
+	    {
+			if (c.GetBlockSize() != BlockSize)
+				throw new ArgumentException("cipher required with a block size of " + BlockSize + ".");
+
+	        if (m == null)
+	        {
+	            // TODO Consider a static property specifying default multiplier
+	            m = new Tables8kGcmMultiplier();
+	        }
+
+			this.cipher = c;
+			this.multiplier = m;
+		}
+
+		public virtual string AlgorithmName
+		{
+			get { return cipher.AlgorithmName + "/GCM"; }
+		}
+
+		public virtual int GetBlockSize()
+		{
+			return BlockSize;
+		}
+
+		public virtual void Init(
+			bool				forEncryption,
+			ICipherParameters	parameters)
+		{
+			this.forEncryption = forEncryption;
+			this.macBlock = null;
+
+			if (parameters is AeadParameters)
+			{
+				AeadParameters param = (AeadParameters)parameters;
+
+				nonce = param.GetNonce();
+				A = param.GetAssociatedText();
+
+				int macSizeBits = param.MacSize;
+	            if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0)
+	            {
+	                throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
+	            }
+
+	            macSize = macSizeBits / 8; 
+				keyParam = param.Key;
+			}
+			else if (parameters is ParametersWithIV)
+			{
+				ParametersWithIV param = (ParametersWithIV)parameters;
+
+				nonce = param.GetIV();
+				A = null;
+	            macSize = 16; 
+				keyParam = (KeyParameter)param.Parameters;
+			}
+			else
+			{
+				throw new ArgumentException("invalid parameters passed to GCM");
+			}
+
+			int bufLength = forEncryption ? BlockSize : (BlockSize + macSize);
+			this.bufBlock = new byte[bufLength];
+
+			if (nonce == null || nonce.Length < 1)
+			{
+				throw new ArgumentException("IV must be at least 1 byte");
+			}
+
+			if (A == null)
+			{
+				// Avoid lots of null checks
+				A = new byte[0];
+			}
+
+			// Cipher always used in forward mode
+			cipher.Init(true, keyParam);
+
+			// TODO This should be configurable by Init parameters
+			// (but must be 16 if nonce length not 12) (BlockSize?)
+//			this.tagLength = 16;
+
+			this.H = new byte[BlockSize];
+			cipher.ProcessBlock(H, 0, H, 0);
+			multiplier.Init(H);
+
+			this.initS = gHASH(A);
+
+			if (nonce.Length == 12)
+			{
+				this.J0 = new byte[16];
+				Array.Copy(nonce, 0, J0, 0, nonce.Length);
+				this.J0[15] = 0x01;
+			}
+			else
+			{
+				this.J0 = gHASH(nonce);
+				byte[] X = new byte[16];
+				packLength((ulong)nonce.Length * 8UL, X, 8);
+				GcmUtilities.Xor(this.J0, X);
+				multiplier.MultiplyH(this.J0);
+			}
+
+			this.S = Arrays.Clone(initS);
+			this.counter = Arrays.Clone(J0);
+			this.bufOff = 0;
+			this.totalLength = 0;
+		}
+
+		public virtual byte[] GetMac()
+		{
+			return Arrays.Clone(macBlock);
+		}
+
+		public virtual int GetOutputSize(
+			int len)
+		{
+			if (forEncryption)
+			{
+				return len + bufOff + macSize;
+			}
+
+			return len + bufOff - macSize;
+		}
+
+		public virtual int GetUpdateOutputSize(
+			int len)
+		{
+			return ((len + bufOff) / BlockSize) * BlockSize;
+		}
+
+		public virtual int ProcessByte(
+			byte	input,
+			byte[]	output,
+			int		outOff)
+		{
+			return Process(input, output, outOff);
+		}
+
+		public virtual int ProcessBytes(
+			byte[]	input,
+			int		inOff,
+			int		len,
+			byte[]	output,
+			int		outOff)
+		{
+			int resultLen = 0;
+
+			for (int i = 0; i != len; i++)
+			{
+//				resultLen += Process(input[inOff + i], output, outOff + resultLen);
+				bufBlock[bufOff++] = input[inOff + i];
+
+				if (bufOff == bufBlock.Length)
+				{
+					gCTRBlock(bufBlock, BlockSize, output, outOff + resultLen);
+					if (!forEncryption)
+					{
+						Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
+					}
+//		            bufOff = 0;
+					bufOff = bufBlock.Length - BlockSize;
+//		            return bufBlock.Length;
+					resultLen += BlockSize;
+				}
+			}
+
+			return resultLen;
+		}
+
+		private int Process(
+			byte	input,
+			byte[]	output,
+			int		outOff)
+		{
+			bufBlock[bufOff++] = input;
+
+			if (bufOff == bufBlock.Length)
+			{
+				gCTRBlock(bufBlock, BlockSize, output, outOff);
+				if (!forEncryption)
+				{
+					Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
+				}
+	//            bufOff = 0;
+				bufOff = bufBlock.Length - BlockSize;
+	//            return bufBlock.Length;
+				return BlockSize;
+			}
+
+			return 0;
+		}
+
+		public int DoFinal(byte[] output, int outOff)
+		{
+			int extra = bufOff;
+			if (!forEncryption)
+			{
+				if (extra < macSize)
+					throw new InvalidCipherTextException("data too short");
+
+				extra -= macSize;
+			}
+
+			if (extra > 0)
+			{
+				byte[] tmp = new byte[BlockSize];
+				Array.Copy(bufBlock, 0, tmp, 0, extra);
+				gCTRBlock(tmp, extra, output, outOff);
+			}
+
+			// Final gHASH
+			byte[] X = new byte[16];
+			packLength((ulong)A.Length * 8UL, X, 0);
+			packLength(totalLength * 8UL, X, 8);
+
+			GcmUtilities.Xor(S, X);
+			multiplier.MultiplyH(S);
+
+			// TODO Fix this if tagLength becomes configurable
+			// T = MSBt(GCTRk(J0,S))
+			byte[] tag = new byte[BlockSize];
+			cipher.ProcessBlock(J0, 0, tag, 0);
+			GcmUtilities.Xor(tag, S);
+
+			int resultLen = extra;
+
+			// We place into macBlock our calculated value for T
+			this.macBlock = new byte[macSize];
+			Array.Copy(tag, 0, macBlock, 0, macSize);
+
+			if (forEncryption)
+			{
+				// Append T to the message
+				Array.Copy(macBlock, 0, output, outOff + bufOff, macSize);
+				resultLen += macSize;
+			}
+			else
+			{
+				// Retrieve the T value from the message and compare to calculated one
+				byte[] msgMac = new byte[macSize];
+				Array.Copy(bufBlock, extra, msgMac, 0, macSize);
+				if (!Arrays.ConstantTimeAreEqual(this.macBlock, msgMac))
+					throw new InvalidCipherTextException("mac check in GCM failed");
+			}
+
+			Reset(false);
+
+			return resultLen;
+		}
+
+		public virtual void Reset()
+		{
+			Reset(true);
+		}
+
+		private void Reset(
+			bool clearMac)
+		{
+			S = Arrays.Clone(initS);
+			counter = Arrays.Clone(J0);
+			bufOff = 0;
+			totalLength = 0;
+
+			if (bufBlock != null)
+			{
+				Array.Clear(bufBlock, 0, bufBlock.Length);
+			}
+
+			if (clearMac)
+			{
+				macBlock = null;
+			}
+
+			cipher.Reset();
+		}
+
+		private void gCTRBlock(byte[] buf, int bufCount, byte[] output, int outOff)
+		{
+//			inc(counter);
+			for (int i = 15; i >= 12; --i)
+			{
+				if (++counter[i] != 0) break;
+			}
+
+			byte[] tmp = new byte[BlockSize];
+			cipher.ProcessBlock(counter, 0, tmp, 0);
+
+			byte[] hashBytes;
+			if (forEncryption)
+			{
+				Array.Copy(Zeroes, bufCount, tmp, bufCount, BlockSize - bufCount);
+				hashBytes = tmp;
+			}
+			else
+			{
+				hashBytes = buf;
+			}
+
+			for (int i = bufCount - 1; i >= 0; --i)
+			{
+				tmp[i] ^= buf[i];
+				output[outOff + i] = tmp[i];
+			}
+
+//			gHASHBlock(hashBytes);
+			GcmUtilities.Xor(S, hashBytes);
+			multiplier.MultiplyH(S);
+
+			totalLength += (ulong)bufCount;
+		}
+
+		private byte[] gHASH(byte[] b)
+		{
+			byte[] Y = new byte[16];
+
+			for (int pos = 0; pos < b.Length; pos += 16)
+			{
+				byte[] X = new byte[16];
+				int num = System.Math.Min(b.Length - pos, 16);
+				Array.Copy(b, pos, X, 0, num);
+				GcmUtilities.Xor(Y, X);
+				multiplier.MultiplyH(Y);
+			}
+
+			return Y;
+		}
+
+//		private void gHASHBlock(byte[] block)
+//		{
+//			GcmUtilities.Xor(S, block);
+//			multiplier.MultiplyH(S);
+//		}
+
+//		private static void inc(byte[] block)
+//		{
+//			for (int i = 15; i >= 12; --i)
+//			{
+//				if (++block[i] != 0) break;
+//			}
+//		}
+
+		private static void packLength(ulong len, byte[] bs, int off)
+		{
+			Pack.UInt32_To_BE((uint)(len >> 32), bs, off); 
+			Pack.UInt32_To_BE((uint)len, bs, off + 4);
+		}
+	}
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/modes/GOFBBlockCipher.cs b/Crypto/src/crypto/modes/GOFBBlockCipher.cs
new file mode 100644
index 000000000..7db843115
--- /dev/null
+++ b/Crypto/src/crypto/modes/GOFBBlockCipher.cs
@@ -0,0 +1,223 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+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)
+		{
+				outBytes[outOff + 3] = (byte)(num >> 24);
+				outBytes[outOff + 2] = (byte)(num >> 16);
+				outBytes[outOff + 1] = (byte)(num >> 8);
+				outBytes[outOff] =     (byte)num;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/modes/IAeadBlockCipher.cs b/Crypto/src/crypto/modes/IAeadBlockCipher.cs
new file mode 100644
index 000000000..ca7dab44c
--- /dev/null
+++ b/Crypto/src/crypto/modes/IAeadBlockCipher.cs
@@ -0,0 +1,90 @@
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+	/// <summary>
+	/// A block cipher mode that includes authenticated encryption with a streaming mode
+	/// and optional associated data.</summary>
+	/// <see cref="AeadParameters"/>
+	public interface IAeadBlockCipher
+	{
+		/// <summary>The name of the algorithm this cipher implements.</summary>
+		string AlgorithmName { get; }
+
+		/// <summary>Initialise the cipher.</summary>
+		/// <remarks>Parameter can either be an AeadParameters or a ParametersWithIV object.</remarks>
+		/// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
+		/// <param name="parameters">The key or other data required by the cipher.</param>
+		void Init(bool forEncryption, ICipherParameters parameters);
+
+		/// <returns>The block size for this cipher, in bytes.</returns>
+		int GetBlockSize();
+
+		/**
+		* Encrypt/decrypt a single byte.
+		*
+		* @param input the byte to be processed.
+		* @param outBytes the output buffer the processed byte goes into.
+		* @param outOff the offset into the output byte array the processed data starts at.
+		* @return the number of bytes written to out.
+		* @exception DataLengthException if the output buffer is too small.
+		*/
+		int ProcessByte(byte input, byte[] outBytes, int outOff);
+
+		/**
+		* Process a block of bytes from in putting the result into out.
+		*
+		* @param inBytes the input byte array.
+		* @param inOff the offset into the in array where the data to be processed starts.
+		* @param len the number of bytes to be processed.
+		* @param outBytes the output buffer the processed bytes go into.
+		* @param outOff the offset into the output byte array the processed data starts at.
+		* @return the number of bytes written to out.
+		* @exception DataLengthException if the output buffer is too small.
+		*/
+		int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff);
+
+		/**
+		* Finish the operation either appending or verifying the MAC at the end of the data.
+		*
+		* @param outBytes space for any resulting output data.
+		* @param outOff offset into out to start copying the data at.
+		* @return number of bytes written into out.
+		* @throws InvalidOperationException if the cipher is in an inappropriate state.
+		* @throws InvalidCipherTextException if the MAC fails to match.
+		*/
+		int DoFinal(byte[] outBytes, int outOff);
+
+		/**
+		* Return the value of the MAC associated with the last stream processed.
+		*
+		* @return MAC for plaintext data.
+		*/
+		byte[] GetMac();
+
+		/**
+		* Return the size of the output buffer required for a ProcessBytes
+		* an input of len bytes.
+		*
+		* @param len the length of the input.
+		* @return the space required to accommodate a call to ProcessBytes
+		* with len bytes of input.
+		*/
+		int GetUpdateOutputSize(int len);
+
+		/**
+		* Return the size of the output buffer required for a ProcessBytes plus a
+		* DoFinal with an input of len bytes.
+		*
+		* @param len the length of the input.
+		* @return the space required to accommodate a call to ProcessBytes and DoFinal
+		* with len bytes of input.
+		*/
+		int GetOutputSize(int len);
+
+		/// <summary>
+		/// Reset the cipher to the same state as it was after the last init (if there was one).
+		/// </summary>
+		void Reset();
+	}
+}
diff --git a/Crypto/src/crypto/modes/OfbBlockCipher.cs b/Crypto/src/crypto/modes/OfbBlockCipher.cs
new file mode 100644
index 000000000..9408a74d4
--- /dev/null
+++ b/Crypto/src/crypto/modes/OfbBlockCipher.cs
@@ -0,0 +1,178 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+    /**
+    * implements a Output-FeedBack (OFB) mode on top of a simple cipher.
+    */
+    public class OfbBlockCipher
+		: IBlockCipher
+    {
+        private byte[]	IV;
+        private byte[]	ofbV;
+        private byte[]	ofbOutV;
+
+        private readonly int			blockSize;
+        private readonly IBlockCipher	cipher;
+
+        /**
+        * Basic constructor.
+        *
+        * @param cipher the block cipher to be used as the basis of the
+        * feedback mode.
+        * @param blockSize the block size in bits (note: a multiple of 8)
+        */
+        public OfbBlockCipher(
+            IBlockCipher cipher,
+            int         blockSize)
+        {
+            this.cipher = cipher;
+            this.blockSize = blockSize / 8;
+
+            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 forEncryption if true the cipher is initialised for
+        *  encryption, if false for decryption.
+        * @param param 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 OFB mode
+            ICipherParameters	parameters)
+        {
+			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 "/OFB"
+        * and the block size in bits
+        */
+        public string AlgorithmName
+        {
+            get { return cipher.AlgorithmName + "/OFB" + (blockSize * 8); }
+        }
+
+		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");
+            }
+
+            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();
+        }
+    }
+
+}
diff --git a/Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs b/Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs
new file mode 100644
index 000000000..038ca783d
--- /dev/null
+++ b/Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs
@@ -0,0 +1,337 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+    /**
+    * Implements OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode
+    * on top of a simple cipher. This class assumes the IV has been prepended
+    * to the data stream already, and just accomodates the reset after
+    * (blockSize + 2) bytes have been read.
+    * <p>
+    * For further info see <a href="http://www.ietf.org/rfc/rfc2440.html">RFC 2440</a>.
+	* </p>
+    */
+    public class OpenPgpCfbBlockCipher
+        : IBlockCipher
+    {
+        private byte[] IV;
+        private byte[] FR;
+        private byte[] FRE;
+
+		private readonly IBlockCipher cipher;
+		private readonly int blockSize;
+
+		private int count;
+        private bool forEncryption;
+
+		/**
+        * Basic constructor.
+        *
+        * @param cipher the block cipher to be used as the basis of the
+        * feedback mode.
+        */
+        public OpenPgpCfbBlockCipher(
+            IBlockCipher cipher)
+        {
+            this.cipher = cipher;
+
+            this.blockSize = cipher.GetBlockSize();
+            this.IV = new byte[blockSize];
+            this.FR = new byte[blockSize];
+            this.FRE = new byte[blockSize];
+        }
+
+		/**
+        * return the underlying block cipher that we are wrapping.
+        *
+        * @return the underlying block cipher that we are wrapping.
+        */
+        public IBlockCipher GetUnderlyingCipher()
+        {
+            return cipher;
+        }
+
+		/**
+        * return the algorithm name and mode.
+        *
+        * @return the name of the underlying algorithm followed by "/PGPCFB"
+        * and the block size in bits.
+        */
+        public string AlgorithmName
+        {
+            get { return cipher.AlgorithmName + "/OpenPGPCFB"; }
+        }
+
+		public bool IsPartialBlockOkay
+		{
+			get { return true; }
+		}
+
+		/**
+        * return the block size we are operating at.
+        *
+        * @return the block size we are operating at (in bytes).
+        */
+        public int GetBlockSize()
+        {
+            return cipher.GetBlockSize();
+        }
+
+		/**
+        * 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)
+        {
+            return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) : DecryptBlock(input, inOff, output, outOff);
+        }
+
+		/**
+        * reset the chaining vector back to the IV and reset the underlying
+        * cipher.
+        */
+        public void Reset()
+        {
+            count = 0;
+
+			Array.Copy(IV, 0, FR, 0, FR.Length);
+
+			cipher.Reset();
+        }
+
+        /**
+        * 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 forEncryption 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,
+            ICipherParameters parameters)
+        {
+            this.forEncryption = forEncryption;
+
+            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);
+        }
+
+		/**
+        * Encrypt one byte of data according to CFB mode.
+        * @param data the byte to encrypt
+        * @param blockOff offset in the current block
+        * @returns the encrypted byte
+        */
+        private byte EncryptByte(byte data, int blockOff)
+        {
+            return (byte)(FRE[blockOff] ^ data);
+        }
+
+		/**
+        * Do the appropriate processing for CFB IV mode encryption.
+        *
+        * @param in the array containing the data to be encrypted.
+        * @param inOff offset into the in array the data starts at.
+        * @param out the array the encrypted 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.
+        */
+        private int EncryptBlock(
+            byte[]	input,
+            int		inOff,
+            byte[]	outBytes,
+            int		outOff)
+        {
+            if ((inOff + blockSize) > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+
+            if ((outOff + blockSize) > outBytes.Length)
+            {
+                throw new DataLengthException("output buffer too short");
+            }
+
+            if (count > blockSize)
+            {
+                FR[blockSize - 2] = outBytes[outOff] = EncryptByte(input[inOff], blockSize - 2);
+                FR[blockSize - 1] = outBytes[outOff + 1] = EncryptByte(input[inOff + 1], blockSize - 1);
+
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+                for (int n = 2; n < blockSize; n++)
+                {
+					FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
+                }
+            }
+            else if (count == 0)
+            {
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+				for (int n = 0; n < blockSize; n++)
+                {
+					FR[n] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n);
+                }
+
+				count += blockSize;
+            }
+            else if (count == blockSize)
+            {
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+                outBytes[outOff] = EncryptByte(input[inOff], 0);
+                outBytes[outOff + 1] = EncryptByte(input[inOff + 1], 1);
+
+                //
+                // do reset
+                //
+                Array.Copy(FR, 2, FR, 0, blockSize - 2);
+                Array.Copy(outBytes, outOff, FR, blockSize - 2, 2);
+
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+                for (int n = 2; n < blockSize; n++)
+                {
+					FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
+                }
+
+				count += blockSize;
+            }
+
+            return blockSize;
+        }
+
+        /**
+        * Do the appropriate processing for CFB IV mode decryption.
+        *
+        * @param in the array containing the data to be decrypted.
+        * @param inOff offset into the in array the data starts at.
+        * @param out the array the encrypted 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.
+        */
+        private int DecryptBlock(
+            byte[]	input,
+            int		inOff,
+            byte[]	outBytes,
+            int		outOff)
+        {
+            if ((inOff + blockSize) > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+
+            if ((outOff + blockSize) > outBytes.Length)
+            {
+                throw new DataLengthException("output buffer too short");
+            }
+
+            if (count > blockSize)
+            {
+				byte inVal = input[inOff];
+				FR[blockSize - 2] = inVal;
+				outBytes[outOff] = EncryptByte(inVal, blockSize - 2);
+
+				inVal = input[inOff + 1];
+				FR[blockSize - 1] = inVal;
+				outBytes[outOff + 1] = EncryptByte(inVal, blockSize - 1);
+
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+                for (int n = 2; n < blockSize; n++)
+                {
+					inVal = input[inOff + n];
+					FR[n - 2] = inVal;
+					outBytes[outOff + n] = EncryptByte(inVal, n - 2);
+				}
+            }
+            else if (count == 0)
+            {
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+                for (int n = 0; n < blockSize; n++)
+                {
+                    FR[n] = input[inOff + n];
+                    outBytes[n] = EncryptByte(input[inOff + n], n);
+                }
+
+                count += blockSize;
+            }
+            else if (count == blockSize)
+            {
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+				byte inVal1 = input[inOff];
+				byte inVal2 = input[inOff + 1];
+				outBytes[outOff    ] = EncryptByte(inVal1, 0);
+				outBytes[outOff + 1] = EncryptByte(inVal2, 1);
+
+                Array.Copy(FR, 2, FR, 0, blockSize - 2);
+
+				FR[blockSize - 2] = inVal1;
+				FR[blockSize - 1] = inVal2;
+
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+                for (int n = 2; n < blockSize; n++)
+                {
+					byte inVal = input[inOff + n];
+					FR[n - 2] = inVal;
+					outBytes[outOff + n] = EncryptByte(inVal, n - 2);
+                }
+
+                count += blockSize;
+            }
+
+            return blockSize;
+        }
+    }
+}
diff --git a/Crypto/src/crypto/modes/SicBlockCipher.cs b/Crypto/src/crypto/modes/SicBlockCipher.cs
new file mode 100644
index 000000000..c45026e82
--- /dev/null
+++ b/Crypto/src/crypto/modes/SicBlockCipher.cs
@@ -0,0 +1,110 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Modes
+{
+	/**
+	* Implements the Segmented Integer Counter (SIC) mode on top of a simple
+	* block cipher.
+	*/
+	public class SicBlockCipher
+		: IBlockCipher
+	{
+		private readonly IBlockCipher cipher;
+		private readonly int blockSize;
+		private readonly byte[] IV;
+		private readonly byte[] counter;
+		private readonly byte[] counterOut;
+
+		/**
+		* Basic constructor.
+		*
+		* @param c the block cipher to be used.
+		*/
+		public SicBlockCipher(IBlockCipher cipher)
+		{
+			this.cipher = cipher;
+			this.blockSize = cipher.GetBlockSize();
+			this.IV = new byte[blockSize];
+			this.counter = new byte[blockSize];
+			this.counterOut = new byte[blockSize];
+		}
+
+		/**
+		* return the underlying block cipher that we are wrapping.
+		*
+		* @return the underlying block cipher that we are wrapping.
+		*/
+		public IBlockCipher GetUnderlyingCipher()
+		{
+			return cipher;
+		}
+
+		public void Init(
+			bool				forEncryption, //ignored by this CTR mode
+			ICipherParameters	parameters)
+		{
+			if (parameters is ParametersWithIV)
+			{
+				ParametersWithIV ivParam = (ParametersWithIV) parameters;
+				byte[] iv = ivParam.GetIV();
+				Array.Copy(iv, 0, IV, 0, IV.Length);
+
+				Reset();
+				cipher.Init(true, ivParam.Parameters);
+			}
+	        else
+	        {
+	            throw new ArgumentException("SIC mode requires ParametersWithIV", "parameters");
+	        }
+		}
+
+		public string AlgorithmName
+		{
+			get { return cipher.AlgorithmName + "/SIC"; }
+		}
+
+		public bool IsPartialBlockOkay
+		{
+			get { return true; }
+		}
+
+		public int GetBlockSize()
+		{
+			return cipher.GetBlockSize();
+		}
+
+		public int ProcessBlock(
+			byte[]	input,
+			int		inOff,
+			byte[]	output,
+			int		outOff)
+		{
+			cipher.ProcessBlock(counter, 0, counterOut, 0);
+
+			//
+			// XOR the counterOut with the plaintext producing the cipher text
+			//
+			for (int i = 0; i < counterOut.Length; i++)
+			{
+				output[outOff + i] = (byte)(counterOut[i] ^ input[inOff + i]);
+			}
+
+			// Increment the counter
+			int j = counter.Length;
+			while (--j >= 0 && ++counter[j] == 0)
+			{
+			}
+
+			return counter.Length;
+		}
+
+		public void Reset()
+		{
+			Array.Copy(IV, 0, counter, 0, counter.Length);
+			cipher.Reset();
+		}
+	}
+}
diff --git a/Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs b/Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs
new file mode 100644
index 000000000..98049e1db
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs
@@ -0,0 +1,40 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+	public class BasicGcmExponentiator
+		: IGcmExponentiator
+	{
+		private byte[] x;
+
+		public void Init(byte[] x)
+		{
+			this.x = Arrays.Clone(x);
+		}
+
+		public void ExponentiateX(long pow, byte[] output)
+		{
+			// Initial value is little-endian 1
+			byte[] y = GcmUtilities.OneAsBytes();
+
+			if (pow > 0)
+			{
+				byte[] powX = Arrays.Clone(x);
+				do
+				{
+					if ((pow & 1L) != 0)
+					{
+						GcmUtilities.Multiply(y, powX);
+					}
+					GcmUtilities.Multiply(powX, powX);
+					pow >>= 1;
+				}
+				while (pow > 0);
+			}
+
+			Array.Copy(y, 0, output, 0, 16);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs
new file mode 100644
index 000000000..4076de990
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+	public class BasicGcmMultiplier
+		: IGcmMultiplier
+	{
+		private byte[] H;
+
+		public void Init(byte[] H)
+		{
+			this.H = (byte[])H.Clone();
+		}
+
+		public void MultiplyH(byte[] x)
+		{
+			GcmUtilities.Multiply(x, H);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/modes/gcm/GcmUtilities.cs b/Crypto/src/crypto/modes/gcm/GcmUtilities.cs
new file mode 100644
index 000000000..8da125641
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/GcmUtilities.cs
@@ -0,0 +1,149 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+	internal abstract class GcmUtilities
+	{
+		internal static byte[] OneAsBytes()
+		{
+			byte[] tmp = new byte[16];
+			tmp[0] = 0x80;
+			return tmp;
+		}
+
+		internal static uint[] OneAsUints()
+		{
+			uint[] tmp = new uint[4];
+			tmp[0] = 0x80000000;
+			return tmp;
+		}
+
+		internal static uint[] AsUints(byte[] bs)
+		{
+			uint[] us = new uint[4];
+			us[0] = Pack.BE_To_UInt32(bs, 0);
+			us[1] = Pack.BE_To_UInt32(bs, 4);
+			us[2] = Pack.BE_To_UInt32(bs, 8);
+			us[3] = Pack.BE_To_UInt32(bs, 12);
+			return us;
+		}
+
+		internal static void Multiply(byte[] block, byte[] val)
+		{
+			byte[] tmp = Arrays.Clone(block);
+			byte[] c = new byte[16];
+
+			for (int i = 0; i < 16; ++i)
+			{
+				byte bits = val[i];
+				for (int j = 7; j >= 0; --j)
+				{
+					if ((bits & (1 << j)) != 0)
+					{
+						Xor(c, tmp);
+					}
+
+					bool lsb = (tmp[15] & 1) != 0;
+					ShiftRight(tmp);
+					if (lsb)
+					{
+						// R = new byte[]{ 0xe1, ... };
+						//GCMUtilities.Xor(tmp, R);
+						tmp[0] ^= (byte)0xe1;
+					}
+				}
+			}
+
+			Array.Copy(c, 0, block, 0, 16);
+		}
+
+		// P is the value with only bit i=1 set
+		internal static void MultiplyP(uint[] x)
+		{
+			bool lsb = (x[3] & 1) != 0;
+			ShiftRight(x);
+			if (lsb)
+			{
+				// R = new uint[]{ 0xe1000000, 0, 0, 0 };
+				//Xor(v, R);
+				x[0] ^= 0xe1000000;
+			}
+		}
+
+		internal static void MultiplyP8(uint[] x)
+		{
+//			for (int i = 8; i != 0; --i)
+//			{
+//				MultiplyP(x);
+//			}
+
+			uint lsw = x[3];
+			ShiftRightN(x, 8);
+			for (int i = 7; i >= 0; --i)
+			{
+				if ((lsw & (1 << i)) != 0)
+				{
+					x[0] ^= (0xe1000000 >> (7 - i));
+				}
+			}
+		}
+
+		internal static void ShiftRight(byte[] block)
+		{
+			int i = 0;
+			byte bit = 0;
+			for (;;)
+			{
+				byte b = block[i];
+				block[i] = (byte)((b >> 1) | bit);
+				if (++i == 16) break;
+				bit = (byte)(b << 7);
+			}
+		}
+
+		internal static void ShiftRight(uint[] block)
+		{
+			int i = 0;
+			uint bit = 0;
+			for (;;)
+			{
+				uint b = block[i];
+				block[i] = (b >> 1) | bit;
+				if (++i == 4) break;
+				bit = b << 31;
+			}
+		}
+
+		internal static void ShiftRightN(uint[] block, int n)
+		{
+			int i = 0;
+			uint bit = 0;
+			for (;;)
+			{
+				uint b = block[i];
+				block[i] = (b >> n) | bit;
+				if (++i == 4) break;
+				bit = b << (32 - n);
+			}
+		}
+
+		internal static void Xor(byte[] block, byte[] val)
+		{
+			for (int i = 15; i >= 0; --i)
+			{
+				block[i] ^= val[i];
+			}
+		}
+
+		internal static void Xor(uint[] block, uint[] val)
+		{
+			for (int i = 3; i >= 0; --i)
+			{
+				block[i] ^= val[i];
+			}
+		}
+	}
+}
diff --git a/Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs b/Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs
new file mode 100644
index 000000000..5b4ce9d7a
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+	public interface IGcmExponentiator
+	{
+		void Init(byte[] x);
+		void ExponentiateX(long pow, byte[] output);
+	}
+}
diff --git a/Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs
new file mode 100644
index 000000000..ec7b906ee
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+	public interface IGcmMultiplier
+	{
+		void Init(byte[] H);
+		void MultiplyH(byte[] x);
+	}
+}
diff --git a/Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs b/Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs
new file mode 100644
index 000000000..9425a3d9d
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs
@@ -0,0 +1,44 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+	public class Tables1kGcmExponentiator
+		: IGcmExponentiator
+	{
+	    // A lookup table of the power-of-two powers of 'x'
+	    private byte[][] lookupPowX2 = new byte[64][];
+
+		public void Init(byte[] x)
+		{
+			lookupPowX2[0] = GcmUtilities.OneAsBytes();
+			lookupPowX2[1] = Arrays.Clone(x); 
+
+			for (int i = 2; i != 64; ++i)
+			{
+				byte[] tmp = Arrays.Clone(lookupPowX2[i - 1]);
+				GcmUtilities.Multiply(tmp, tmp);
+				lookupPowX2[i] = tmp;
+			}
+		}
+
+		public void ExponentiateX(long pow, byte[] output)
+		{
+			byte[] y = GcmUtilities.OneAsBytes();
+			int powX2 = 1;
+
+			while (pow > 0)
+			{
+				if ((pow & 1L) != 0)
+				{
+					GcmUtilities.Multiply(y, lookupPowX2[powX2]);
+				}
+				++powX2;
+				pow >>= 1;
+			}
+
+			Array.Copy(y, 0, output, 0, 16);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs
new file mode 100644
index 000000000..f089dfe8d
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs
@@ -0,0 +1,64 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+	public class Tables64kGcmMultiplier
+		: IGcmMultiplier
+	{
+		private readonly uint[][][] M = new uint[16][][];
+
+		public void Init(byte[] H)
+		{
+			M[0] = new uint[256][];
+			M[0][0] = new uint[4];
+			M[0][128] = GcmUtilities.AsUints(H);
+			for (int j = 64; j >= 1; j >>= 1)
+			{
+				uint[] tmp = (uint[])M[0][j + j].Clone();
+				GcmUtilities.MultiplyP(tmp);
+				M[0][j] = tmp;
+			}
+			for (int i = 0;;)
+			{
+				for (int j = 2; j < 256; j += j)
+				{
+					for (int k = 1; k < j; ++k)
+					{
+						uint[] tmp = (uint[])M[i][j].Clone();
+						GcmUtilities.Xor(tmp, M[i][k]);
+						M[i][j + k] = tmp;
+					}
+				}
+
+				if (++i == 16) return;
+
+				M[i] = new uint[256][];
+				M[i][0] = new uint[4];
+				for (int j = 128; j > 0; j >>= 1)
+				{
+					uint[] tmp = (uint[])M[i - 1][j].Clone();
+					GcmUtilities.MultiplyP8(tmp);
+					M[i][j] = tmp;
+				}
+			}
+		}
+
+		public void MultiplyH(byte[] x)
+		{
+			uint[] z = new uint[4];
+			for (int i = 0; i != 16; ++i)
+			{
+				//GcmUtilities.Xor(z, M[i][x[i]]);
+				uint[] m = M[i][x[i]];
+				z[0] ^= m[0];
+				z[1] ^= m[1];
+				z[2] ^= m[2];
+				z[3] ^= m[3];
+			}
+
+			Pack.UInt32_To_BE(z, x, 0);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs
new file mode 100644
index 000000000..91d58fab8
--- /dev/null
+++ b/Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs
@@ -0,0 +1,90 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Modes.Gcm
+{
+	public class Tables8kGcmMultiplier
+		: IGcmMultiplier
+	{
+		private readonly uint[][][] M = new uint[32][][];
+		
+		public void Init(byte[] H)
+		{
+			M[0] = new uint[16][];
+			M[1] = new uint[16][];
+			M[0][0] = new uint[4];
+			M[1][0] = new uint[4];
+			M[1][8] = GcmUtilities.AsUints(H);
+
+			for (int j = 4; j >= 1; j >>= 1)
+			{
+				uint[] tmp = (uint[])M[1][j + j].Clone();
+				GcmUtilities.MultiplyP(tmp);
+				M[1][j] = tmp;
+			}
+
+			{
+				uint[] tmp = (uint[])M[1][1].Clone();
+				GcmUtilities.MultiplyP(tmp);
+				M[0][8] = tmp;
+			}
+
+			for (int j = 4; j >= 1; j >>= 1)
+			{
+				uint[] tmp = (uint[])M[0][j + j].Clone();
+				GcmUtilities.MultiplyP(tmp);
+				M[0][j] = tmp;
+			}
+
+			for (int i = 0;;)
+			{
+				for (int j = 2; j < 16; j += j)
+				{
+					for (int k = 1; k < j; ++k)
+					{
+						uint[] tmp = (uint[])M[i][j].Clone();
+						GcmUtilities.Xor(tmp, M[i][k]);
+						M[i][j + k] = tmp;
+					}
+				}
+
+				if (++i == 32) return;
+
+				if (i > 1)
+				{
+					M[i] = new uint[16][];
+					M[i][0] = new uint[4];
+					for(int j = 8; j > 0; j >>= 1)
+					{
+						uint[] tmp = (uint[])M[i - 2][j].Clone();
+						GcmUtilities.MultiplyP8(tmp);
+						M[i][j] = tmp;
+					}
+				}
+			}
+		}
+
+		public void MultiplyH(byte[] x)
+		{
+			uint[] z = new uint[4];
+			for (int i = 15; i >= 0; --i)
+			{
+				//GcmUtilities.Xor(z, M[i + i][x[i] & 0x0f]);
+				uint[] m = M[i + i][x[i] & 0x0f];
+				z[0] ^= m[0];
+				z[1] ^= m[1];
+				z[2] ^= m[2];
+				z[3] ^= m[3];
+				//GcmUtilities.Xor(z, M[i + i + 1][(x[i] & 0xf0) >> 4]);
+				m = M[i + i + 1][(x[i] & 0xf0) >> 4];
+				z[0] ^= m[0];
+				z[1] ^= m[1];
+				z[2] ^= m[2];
+				z[3] ^= m[3];
+			}
+
+			Pack.UInt32_To_BE(z, x, 0);
+		}
+	}
+}