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

EaxBlockCipher

using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Crypto.Modes { public class EaxBlockCipher : IAeadBlockCipher, IAeadCipher { 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; private bool cipherInitialized; private byte[] initialAssociatedText; public virtual string AlgorithmName => cipher.UnderlyingCipher.AlgorithmName + "/EAX"; public virtual IBlockCipher UnderlyingCipher => cipher; public EaxBlockCipher(IBlockCipher cipher) { blockSize = cipher.GetBlockSize(); mac = new CMac(cipher); macBlock = new byte[blockSize]; associatedTextMac = new byte[mac.GetMacSize()]; nonceMac = new byte[mac.GetMacSize()]; this.cipher = new SicBlockCipher(cipher); } public virtual int GetBlockSize() { return cipher.GetBlockSize(); } public virtual void Init(bool forEncryption, ICipherParameters parameters) { this.forEncryption = forEncryption; AeadParameters aeadParameters = parameters as AeadParameters; ReadOnlySpan<byte> input; ICipherParameters parameters2; if (aeadParameters != null) { input = aeadParameters.Nonce; initialAssociatedText = aeadParameters.GetAssociatedText(); macSize = aeadParameters.MacSize / 8; parameters2 = aeadParameters.Key; } else { ParametersWithIV parametersWithIV = parameters as ParametersWithIV; if (parametersWithIV == null) throw new ArgumentException("invalid parameters passed to EAX"); input = parametersWithIV.IV; initialAssociatedText = null; macSize = mac.GetMacSize() / 2; parameters2 = parametersWithIV.Parameters; } bufBlock = new byte[forEncryption ? blockSize : (blockSize + macSize)]; byte[] array = new byte[blockSize]; mac.Init(parameters2); array[blockSize - 1] = 0; mac.BlockUpdate(array, 0, blockSize); mac.BlockUpdate(input); mac.DoFinal(nonceMac, 0); cipher.Init(true, new ParametersWithIV(null, nonceMac)); Reset(); } private void InitCipher() { if (!cipherInitialized) { cipherInitialized = true; mac.DoFinal(associatedTextMac, 0); byte[] array = new byte[blockSize]; array[blockSize - 1] = 2; mac.BlockUpdate(array, 0, blockSize); } } private void CalculateMac() { byte[] array = new byte[blockSize]; mac.DoFinal(array, 0); for (int i = 0; i < macBlock.Length; i++) { macBlock[i] = (byte)(nonceMac[i] ^ associatedTextMac[i] ^ array[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[] array = new byte[blockSize]; array[blockSize - 1] = 1; mac.BlockUpdate(array, 0, blockSize); cipherInitialized = false; if (initialAssociatedText != null) ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); } public virtual void ProcessAadByte(byte input) { if (cipherInitialized) throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun."); mac.Update(input); } public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len) { if (cipherInitialized) throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun."); mac.BlockUpdate(inBytes, inOff, len); } public virtual void ProcessAadBytes(ReadOnlySpan<byte> input) { if (cipherInitialized) throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun."); mac.BlockUpdate(input); } public virtual int ProcessByte(byte input, byte[] outBytes, int outOff) { InitCipher(); return Process(input, Spans.FromNullable(outBytes, outOff)); } public virtual int ProcessByte(byte input, Span<byte> output) { InitCipher(); return Process(input, output); } public virtual int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) { return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff)); } public virtual int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output) { InitCipher(); int length = input.Length; int num = 0; for (int i = 0; i != length; i++) { int num2 = num; byte b = input[i]; int num3 = num; num = num2 + Process(b, output.Slice(num3, output.Length - num3)); } return num; } public virtual int DoFinal(byte[] outBytes, int outOff) { return DoFinal(outBytes.AsSpan(outOff)); } public unsafe virtual int DoFinal(Span<byte> output) { InitCipher(); int num = bufOff; int num2 = bufBlock.Length; Span<byte> span; if (num2 <= 128) { int num3 = num2; span = new Span<byte>(stackalloc byte[(int)(uint)num3], num3); } else span = new byte[num2]; Span<byte> output2 = span; bufOff = 0; if (forEncryption) { Check.OutputLength(output, num + macSize, "output buffer too short"); cipher.ProcessBlock(bufBlock, output2); span = output2.Slice(0, num); span.CopyTo(output); mac.BlockUpdate(output2.Slice(0, num)); CalculateMac(); span = macBlock.AsSpan(0, macSize); int num3 = num; span.CopyTo(output.Slice(num3, output.Length - num3)); Reset(false); return num + macSize; } if (num < macSize) throw new InvalidCipherTextException("data too short"); Check.OutputLength(output, num - macSize, "output buffer too short"); if (num > macSize) { mac.BlockUpdate(bufBlock.AsSpan(0, num - macSize)); cipher.ProcessBlock(bufBlock, output2); span = output2.Slice(0, num - macSize); span.CopyTo(output); } CalculateMac(); if (!VerifyMac(bufBlock, num - macSize)) throw new InvalidCipherTextException("mac check in EAX failed"); Reset(false); return num - macSize; } public virtual byte[] GetMac() { byte[] array = new byte[macSize]; Array.Copy(macBlock, 0, array, 0, macSize); return array; } public virtual int GetUpdateOutputSize(int len) { int num = len + bufOff; if (!forEncryption) { if (num < macSize) return 0; num -= macSize; } return num - num % blockSize; } public virtual int GetOutputSize(int len) { int num = len + bufOff; if (forEncryption) return num + macSize; if (num >= macSize) return num - macSize; return 0; } private int Process(byte b, Span<byte> output) { bufBlock[bufOff++] = b; if (bufOff == bufBlock.Length) { Check.OutputLength(output, blockSize, "output buffer too short"); int result; if (forEncryption) { result = cipher.ProcessBlock(bufBlock, output); mac.BlockUpdate(output.Slice(0, blockSize)); } else { mac.BlockUpdate(bufBlock.AsSpan(0, blockSize)); result = cipher.ProcessBlock(bufBlock, output); } bufOff = 0; if (!forEncryption) { Array.Copy(bufBlock, blockSize, bufBlock, 0, macSize); bufOff = macSize; } return result; } return 0; } private bool VerifyMac(byte[] mac, int off) { return Arrays.FixedTimeEquals(macSize, mac, off, macBlock, 0); } } }