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);
+ }
+ }
+}
|