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

CMac

public class CMac : IMac
using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Paddings; using Org.BouncyCastle.Crypto.Parameters; using System; namespace Org.BouncyCastle.Crypto.Macs { public class CMac : IMac { private const byte CONSTANT_128 = 135; private const byte CONSTANT_64 = 27; private byte[] ZEROES; private byte[] mac; private byte[] buf; private int bufOff; private IBlockCipherMode m_cipherMode; private int macSize; private byte[] L; private byte[] Lu; private byte[] Lu2; public string AlgorithmName => m_cipherMode.AlgorithmName; public CMac(IBlockCipher cipher) : this(cipher, cipher.GetBlockSize() * 8) { } public CMac(IBlockCipher cipher, int macSizeInBits) { if (macSizeInBits % 8 != 0) throw new ArgumentException("MAC size must be multiple of 8"); if (macSizeInBits > cipher.GetBlockSize() * 8) throw new ArgumentException("MAC size must be less or equal to " + (cipher.GetBlockSize() * 8).ToString()); if (cipher.GetBlockSize() != 8 && cipher.GetBlockSize() != 16) throw new ArgumentException("Block size must be either 64 or 128 bits"); m_cipherMode = new CbcBlockCipher(cipher); macSize = macSizeInBits / 8; mac = new byte[cipher.GetBlockSize()]; buf = new byte[cipher.GetBlockSize()]; ZEROES = new byte[cipher.GetBlockSize()]; bufOff = 0; } private static int ShiftLeft(byte[] block, byte[] output) { int num = block.Length; uint num2 = 0; while (--num >= 0) { uint num3 = block[num]; output[num] = (byte)((num3 << 1) | num2); num2 = ((num3 >> 7) & 1); } return (int)num2; } private static byte[] DoubleLu(byte[] input) { byte[] array = new byte[input.Length]; int num = ShiftLeft(input, array); int num2 = (input.Length == 16) ? 135 : 27; array[input.Length - 1] ^= (byte)(num2 >> (1 - num << 3)); return array; } public void Init(ICipherParameters parameters) { if (parameters is KeyParameter) { m_cipherMode.Init(true, parameters); L = new byte[ZEROES.Length]; m_cipherMode.ProcessBlock(ZEROES, 0, L, 0); Lu = DoubleLu(L); Lu2 = DoubleLu(Lu); } else if (parameters != null) { throw new ArgumentException("CMac mode only permits key to be set.", "parameters"); } Reset(); } public int GetMacSize() { return macSize; } public void Update(byte input) { if (bufOff == buf.Length) { m_cipherMode.ProcessBlock(buf, 0, mac, 0); bufOff = 0; } buf[bufOff++] = input; } public void BlockUpdate(byte[] inBytes, int inOff, int len) { if (len < 0) throw new ArgumentException("Can't have a negative input length!"); BlockUpdate(inBytes.AsSpan(inOff, len)); } public void BlockUpdate(ReadOnlySpan<byte> input) { int blockSize = m_cipherMode.GetBlockSize(); int num = blockSize - bufOff; if (input.Length > num) { input.Slice(0, num).CopyTo(buf.AsSpan(bufOff)); m_cipherMode.ProcessBlock(buf, mac); bufOff = 0; int num2 = num; input = input.Slice(num2, input.Length - num2); while (input.Length > blockSize) { m_cipherMode.ProcessBlock(input, mac); num2 = blockSize; input = input.Slice(num2, input.Length - num2); } } input.CopyTo(buf.AsSpan(bufOff)); bufOff += input.Length; } public int DoFinal(byte[] outBytes, int outOff) { return DoFinal(outBytes.AsSpan(outOff)); } public int DoFinal(Span<byte> output) { int blockSize = m_cipherMode.GetBlockSize(); byte[] array; if (bufOff == blockSize) array = Lu; else { new ISO7816d4Padding().AddPadding(buf, bufOff); array = Lu2; } for (int i = 0; i < mac.Length; i++) { buf[i] ^= array[i]; } m_cipherMode.ProcessBlock(buf, mac); mac.AsSpan(0, macSize).CopyTo(output); Reset(); return macSize; } public void Reset() { Array.Clear(buf, 0, buf.Length); bufOff = 0; m_cipherMode.Reset(); } } }