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