IesEngine
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Engines
{
    public class IesEngine
    {
        private readonly IBasicAgreement agree;
        private readonly IDerivationFunction kdf;
        private readonly IMac mac;
        private readonly BufferedBlockCipher cipher;
        private readonly byte[] macBuf;
        private bool forEncryption;
        private ICipherParameters privParam;
        private ICipherParameters pubParam;
        private IesParameters param;
        public IesEngine(IBasicAgreement agree, IDerivationFunction kdf, IMac mac)
        {
            this.agree = agree;
            this.kdf = kdf;
            this.mac = mac;
            macBuf = new byte[mac.GetMacSize()];
        }
        public IesEngine(IBasicAgreement agree, IDerivationFunction kdf, IMac mac, BufferedBlockCipher cipher)
        {
            this.agree = agree;
            this.kdf = kdf;
            this.mac = mac;
            macBuf = new byte[mac.GetMacSize()];
            this.cipher = cipher;
        }
        public virtual void Init(bool forEncryption, ICipherParameters privParameters, ICipherParameters pubParameters, ICipherParameters iesParameters)
        {
            this.forEncryption = forEncryption;
            privParam = privParameters;
            pubParam = pubParameters;
            param = (IesParameters)iesParameters;
        }
        private byte[] DecryptBlock(byte[] in_enc, int inOff, int inLen, byte[] z)
        {
            byte[] array = null;
            KeyParameter keyParameter = null;
            KdfParameters kdfParameters = new KdfParameters(z, param.GetDerivationV());
            int macKeySize = param.MacKeySize;
            kdf.Init(kdfParameters);
            if (inLen < mac.GetMacSize())
                throw new InvalidCipherTextException("Length of input must be greater than the MAC");
            inLen -= mac.GetMacSize();
            if (cipher == null) {
                byte[] array2 = GenerateKdfBytes(kdfParameters, inLen + macKeySize / 8);
                array = new byte[inLen];
                for (int i = 0; i != inLen; i++) {
                    array[i] = (byte)(in_enc[inOff + i] ^ array2[i]);
                }
                keyParameter = new KeyParameter(array2, inLen, macKeySize / 8);
            } else {
                int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize;
                byte[] key = GenerateKdfBytes(kdfParameters, cipherKeySize / 8 + macKeySize / 8);
                cipher.Init(false, new KeyParameter(key, 0, cipherKeySize / 8));
                array = cipher.DoFinal(in_enc, inOff, inLen);
                keyParameter = new KeyParameter(key, cipherKeySize / 8, macKeySize / 8);
            }
            byte[] encodingV = param.GetEncodingV();
            mac.Init(keyParameter);
            mac.BlockUpdate(in_enc, inOff, inLen);
            mac.BlockUpdate(encodingV, 0, encodingV.Length);
            mac.DoFinal(macBuf, 0);
            inOff += inLen;
            if (!Arrays.FixedTimeEquals(Arrays.CopyOfRange(in_enc, inOff, inOff + macBuf.Length), macBuf))
                throw new InvalidCipherTextException("Invalid MAC.");
            return array;
        }
        private byte[] EncryptBlock(byte[] input, int inOff, int inLen, byte[] z)
        {
            byte[] array = null;
            KeyParameter keyParameter = null;
            KdfParameters kParam = new KdfParameters(z, param.GetDerivationV());
            int num = 0;
            int macKeySize = param.MacKeySize;
            if (cipher == null) {
                byte[] array2 = GenerateKdfBytes(kParam, inLen + macKeySize / 8);
                array = new byte[inLen + mac.GetMacSize()];
                num = inLen;
                for (int i = 0; i != inLen; i++) {
                    array[i] = (byte)(input[inOff + i] ^ array2[i]);
                }
                keyParameter = new KeyParameter(array2, inLen, macKeySize / 8);
            } else {
                int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize;
                byte[] key = GenerateKdfBytes(kParam, cipherKeySize / 8 + macKeySize / 8);
                cipher.Init(true, new KeyParameter(key, 0, cipherKeySize / 8));
                num = cipher.GetOutputSize(inLen);
                byte[] array3 = new byte[num];
                int num2 = cipher.ProcessBytes(input, inOff, inLen, array3, 0);
                num2 += cipher.DoFinal(array3, num2);
                array = new byte[num2 + mac.GetMacSize()];
                num = num2;
                Array.Copy(array3, 0, array, 0, num2);
                keyParameter = new KeyParameter(key, cipherKeySize / 8, macKeySize / 8);
            }
            byte[] encodingV = param.GetEncodingV();
            mac.Init(keyParameter);
            mac.BlockUpdate(array, 0, num);
            mac.BlockUpdate(encodingV, 0, encodingV.Length);
            mac.DoFinal(array, num);
            return array;
        }
        private byte[] GenerateKdfBytes(KdfParameters kParam, int length)
        {
            byte[] array = new byte[length];
            kdf.Init(kParam);
            kdf.GenerateBytes(array, 0, array.Length);
            return array;
        }
        public virtual byte[] ProcessBlock(byte[] input, int inOff, int inLen)
        {
            agree.Init(privParam);
            BigInteger n = agree.CalculateAgreement(pubParam);
            byte[] array = BigIntegers.AsUnsignedByteArray(agree.GetFieldSize(), n);
            try {
                return forEncryption ? EncryptBlock(input, inOff, inLen, array) : DecryptBlock(input, inOff, inLen, array);
            } finally {
                Array.Clear(array, 0, array.Length);
            }
        }
    }
}