summary refs log tree commit diff
path: root/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs')
-rw-r--r--Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs288
1 files changed, 288 insertions, 0 deletions
diff --git a/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs b/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
new file mode 100644
index 000000000..fb8a92ba3
--- /dev/null
+++ b/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
@@ -0,0 +1,288 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Paddings
+{
+	/**
+	* A wrapper class that allows block ciphers to be used to process data in
+	* a piecemeal fashion with padding. The PaddedBufferedBlockCipher
+	* outputs a block only when the buffer is full and more data is being added,
+	* or on a doFinal (unless the current block in the buffer is a pad block).
+	* The default padding mechanism used is the one outlined in Pkcs5/Pkcs7.
+	*/
+	public class PaddedBufferedBlockCipher
+		: BufferedBlockCipher
+	{
+		private readonly IBlockCipherPadding padding;
+
+		/**
+		* Create a buffered block cipher with the desired padding.
+		*
+		* @param cipher the underlying block cipher this buffering object wraps.
+		* @param padding the padding type.
+		*/
+		public PaddedBufferedBlockCipher(
+			IBlockCipher		cipher,
+			IBlockCipherPadding	padding)
+		{
+			this.cipher = cipher;
+			this.padding = padding;
+
+			buf = new byte[cipher.GetBlockSize()];
+			bufOff = 0;
+		}
+
+		/**
+		* Create a buffered block cipher Pkcs7 padding
+		*
+		* @param cipher the underlying block cipher this buffering object wraps.
+		*/
+		public PaddedBufferedBlockCipher(
+			IBlockCipher cipher)
+			: this(cipher, new Pkcs7Padding())    { }
+
+		/**
+		* initialise the cipher.
+		*
+		* @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 override void Init(
+			bool				forEncryption,
+			ICipherParameters	parameters)
+		{
+			this.forEncryption = forEncryption;
+
+			SecureRandom initRandom = null;
+			if (parameters is ParametersWithRandom)
+			{
+				ParametersWithRandom p = (ParametersWithRandom)parameters;
+				initRandom = p.Random;
+				parameters = p.Parameters;
+			}
+
+			Reset();
+			padding.Init(initRandom);
+			cipher.Init(forEncryption, parameters);
+		}
+
+		/**
+		* return the minimum size of the output buffer required for an update
+		* 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 update and doFinal
+		* with len bytes of input.
+		*/
+		public override int GetOutputSize(
+			int length)
+		{
+			int total = length + bufOff;
+			int leftOver = total % buf.Length;
+
+			if (leftOver == 0)
+			{
+				if (forEncryption)
+				{
+					return total + buf.Length;
+				}
+
+				return total;
+			}
+
+			return total - leftOver + buf.Length;
+		}
+
+		/**
+		* return the size of the output buffer required for an update
+		* an input of len bytes.
+		*
+		* @param len the length of the input.
+		* @return the space required to accommodate a call to update
+		* with len 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;
+		}
+
+		/**
+		* 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);
+				bufOff = 0;
+			}
+
+			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 len 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 length!");
+			}
+
+			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);
+
+				bufOff = 0;
+				length -= gapLen;
+				inOff += gapLen;
+
+				while (length > buf.Length)
+				{
+					resultLen += cipher.ProcessBlock(input, inOff, output, outOff + resultLen);
+
+					length -= blockSize;
+					inOff += blockSize;
+				}
+			}
+
+			Array.Copy(input, inOff, buf, bufOff, length);
+
+			bufOff += length;
+
+			return resultLen;
+		}
+
+		/**
+		* Process the last block in the buffer. If the buffer is currently
+		* full and padding needs to be added a call to doFinal will produce
+		* 2 * GetBlockSize() bytes.
+		*
+		* @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 or we are decrypting and the input is not block size aligned.
+		* @exception InvalidOperationException if the underlying cipher is not
+		* initialised.
+		* @exception InvalidCipherTextException if padding is expected and not found.
+		*/
+		public override int DoFinal(
+			byte[]  output,
+			int     outOff)
+		{
+			int blockSize = cipher.GetBlockSize();
+			int resultLen = 0;
+
+			if (forEncryption)
+			{
+				if (bufOff == blockSize)
+				{
+					if ((outOff + 2 * blockSize) > output.Length)
+					{
+						Reset();
+
+						throw new DataLengthException("output buffer too short");
+					}
+
+					resultLen = cipher.ProcessBlock(buf, 0, output, outOff);
+					bufOff = 0;
+				}
+
+				padding.AddPadding(buf, bufOff);
+
+				resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen);
+
+				Reset();
+			}
+			else
+			{
+				if (bufOff == blockSize)
+				{
+					resultLen = cipher.ProcessBlock(buf, 0, buf, 0);
+					bufOff = 0;
+				}
+				else
+				{
+					Reset();
+
+					throw new DataLengthException("last block incomplete in decryption");
+				}
+
+				try
+				{
+					resultLen -= padding.PadCount(buf);
+
+					Array.Copy(buf, 0, output, outOff, resultLen);
+				}
+				finally
+				{
+					Reset();
+				}
+			}
+
+			return resultLen;
+		}
+	}
+
+}