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