<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.0" />

CcmBlockCipher

using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; using System; using System.IO; namespace Org.BouncyCastle.Crypto.Modes { public class CcmBlockCipher : IAeadBlockCipher, IAeadCipher { private static readonly int BlockSize = 16; private readonly IBlockCipher cipher; private readonly byte[] macBlock; private bool forEncryption; private byte[] nonce; private byte[] initialAssociatedText; private int macSize; private ICipherParameters keyParam; private readonly MemoryStream associatedText = new MemoryStream(); private readonly MemoryStream data = new MemoryStream(); public virtual IBlockCipher UnderlyingCipher => cipher; public virtual string AlgorithmName => cipher.AlgorithmName + "/CCM"; public CcmBlockCipher(IBlockCipher cipher) { this.cipher = cipher; macBlock = new byte[BlockSize]; if (cipher.GetBlockSize() != BlockSize) throw new ArgumentException("cipher required with a block size of " + BlockSize.ToString() + "."); } public virtual void Init(bool forEncryption, ICipherParameters parameters) { this.forEncryption = forEncryption; AeadParameters aeadParameters = parameters as AeadParameters; ICipherParameters cipherParameters; if (aeadParameters != null) { nonce = aeadParameters.GetNonce(); initialAssociatedText = aeadParameters.GetAssociatedText(); macSize = GetMacSize(forEncryption, aeadParameters.MacSize); cipherParameters = aeadParameters.Key; } else { ParametersWithIV parametersWithIV = parameters as ParametersWithIV; if (parametersWithIV == null) throw new ArgumentException("invalid parameters passed to CCM"); nonce = parametersWithIV.GetIV(); initialAssociatedText = null; macSize = GetMacSize(forEncryption, 64); cipherParameters = parametersWithIV.Parameters; } if (cipherParameters != null) keyParam = cipherParameters; if (nonce.Length < 7 || nonce.Length > 13) throw new ArgumentException("nonce must have length from 7 to 13 octets"); Reset(); } public virtual int GetBlockSize() { return cipher.GetBlockSize(); } public virtual void ProcessAadByte(byte input) { associatedText.WriteByte(input); } public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len) { associatedText.Write(inBytes, inOff, len); } 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) { Check.DataLength(inBytes, inOff, inLen, "input buffer too short"); data.Write(inBytes, inOff, inLen); return 0; } public virtual int DoFinal(byte[] outBytes, int outOff) { byte[] buffer = data.GetBuffer(); int inLen = Convert.ToInt32(data.Length); int result = ProcessPacket(buffer, 0, inLen, outBytes, outOff); Reset(); return result; } public virtual void Reset() { associatedText.SetLength(0); data.SetLength(0); } public virtual byte[] GetMac() { return Arrays.CopyOfRange(macBlock, 0, macSize); } public virtual int GetUpdateOutputSize(int len) { return 0; } public virtual int GetOutputSize(int len) { int num = Convert.ToInt32(data.Length) + len; if (forEncryption) return num + macSize; if (num >= macSize) return num - macSize; return 0; } public virtual byte[] ProcessPacket(byte[] input, int inOff, int inLen) { Check.DataLength(input, inOff, inLen, "input buffer too short"); byte[] array; if (forEncryption) array = new byte[inLen + macSize]; else { if (inLen < macSize) throw new InvalidCipherTextException("data too short"); array = new byte[inLen - macSize]; } ProcessPacket(input, inOff, inLen, array, 0); return array; } public virtual int ProcessPacket(byte[] input, int inOff, int inLen, byte[] output, int outOff) { Check.DataLength(input, inOff, inLen, "input buffer too short"); if (keyParam == null) throw new InvalidOperationException("CCM cipher unitialized."); int num = nonce.Length; int num2 = 15 - num; if (num2 < 4) { int num3 = 1 << 8 * num2; int num4 = 0; if (!forEncryption) num4 = 16; if (inLen - num4 >= num3) throw new InvalidOperationException("CCM packet too large for choice of q."); } byte[] array = new byte[BlockSize]; array[0] = (byte)((num2 - 1) & 7); nonce.CopyTo(array, 1); SicBlockCipher sicBlockCipher = new SicBlockCipher(cipher); sicBlockCipher.Init(forEncryption, new ParametersWithIV(keyParam, array)); int i = inOff; int num5 = outOff; int num6; if (forEncryption) { num6 = inLen + macSize; Check.OutputLength(output, outOff, num6, "output buffer too short"); CalculateMac(input, inOff, inLen, macBlock); byte[] array2 = new byte[BlockSize]; sicBlockCipher.ProcessBlock(macBlock, 0, array2, 0); for (; i < inOff + inLen - BlockSize; i += BlockSize) { sicBlockCipher.ProcessBlock(input, i, output, num5); num5 += BlockSize; } byte[] array3 = new byte[BlockSize]; Array.Copy(input, i, array3, 0, inLen + inOff - i); sicBlockCipher.ProcessBlock(array3, 0, array3, 0); Array.Copy(array3, 0, output, num5, inLen + inOff - i); Array.Copy(array2, 0, output, outOff + inLen, macSize); } else { if (inLen < macSize) throw new InvalidCipherTextException("data too short"); num6 = inLen - macSize; Check.OutputLength(output, outOff, num6, "output buffer too short"); Array.Copy(input, inOff + num6, macBlock, 0, macSize); sicBlockCipher.ProcessBlock(macBlock, 0, macBlock, 0); for (int j = macSize; j != macBlock.Length; j++) { macBlock[j] = 0; } for (; i < inOff + num6 - BlockSize; i += BlockSize) { sicBlockCipher.ProcessBlock(input, i, output, num5); num5 += BlockSize; } byte[] array4 = new byte[BlockSize]; Array.Copy(input, i, array4, 0, num6 - (i - inOff)); sicBlockCipher.ProcessBlock(array4, 0, array4, 0); Array.Copy(array4, 0, output, num5, num6 - (i - inOff)); byte[] b = new byte[BlockSize]; CalculateMac(output, outOff, num6, b); if (!Arrays.FixedTimeEquals(macBlock, b)) throw new InvalidCipherTextException("mac check in CCM failed"); } return num6; } private int CalculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock) { CbcBlockCipherMac cbcBlockCipherMac = new CbcBlockCipherMac(cipher, macSize * 8); cbcBlockCipherMac.Init(keyParam); byte[] array = new byte[16]; if (HasAssociatedText()) array[0] |= 64; array[0] |= (byte)((((cbcBlockCipherMac.GetMacSize() - 2) / 2) & 7) << 3); array[0] |= (byte)((15 - nonce.Length - 1) & 7); Array.Copy(nonce, 0, array, 1, nonce.Length); int num = dataLen; int num2 = 1; while (num > 0) { array[array.Length - num2] = (byte)(num & 255); num >>= 8; num2++; } cbcBlockCipherMac.BlockUpdate(array, 0, array.Length); if (HasAssociatedText()) { int associatedTextLength = GetAssociatedTextLength(); int num3; if (associatedTextLength < 65280) { cbcBlockCipherMac.Update((byte)(associatedTextLength >> 8)); cbcBlockCipherMac.Update((byte)associatedTextLength); num3 = 2; } else { cbcBlockCipherMac.Update(byte.MaxValue); cbcBlockCipherMac.Update(254); cbcBlockCipherMac.Update((byte)(associatedTextLength >> 24)); cbcBlockCipherMac.Update((byte)(associatedTextLength >> 16)); cbcBlockCipherMac.Update((byte)(associatedTextLength >> 8)); cbcBlockCipherMac.Update((byte)associatedTextLength); num3 = 6; } if (initialAssociatedText != null) cbcBlockCipherMac.BlockUpdate(initialAssociatedText, 0, initialAssociatedText.Length); if (associatedText.Length > 0) { byte[] buffer = associatedText.GetBuffer(); int len = Convert.ToInt32(associatedText.Length); cbcBlockCipherMac.BlockUpdate(buffer, 0, len); } num3 = (num3 + associatedTextLength) % 16; if (num3 != 0) { for (int i = num3; i < 16; i++) { cbcBlockCipherMac.Update(0); } } } cbcBlockCipherMac.BlockUpdate(data, dataOff, dataLen); return cbcBlockCipherMac.DoFinal(macBlock, 0); } private int GetMacSize(bool forEncryption, int requestedMacBits) { if (forEncryption && (requestedMacBits < 32 || requestedMacBits > 128 || (requestedMacBits & 15) != 0)) throw new ArgumentException("tag length in octets must be one of {4,6,8,10,12,14,16}"); return requestedMacBits >> 3; } private int GetAssociatedTextLength() { return Convert.ToInt32(associatedText.Length) + ((initialAssociatedText != null) ? initialAssociatedText.Length : 0); } private bool HasAssociatedText() { return GetAssociatedTextLength() > 0; } } }