ElGamalEngine
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Engines
{
    public class ElGamalEngine : IAsymmetricBlockCipher
    {
        private ElGamalKeyParameters key;
        private SecureRandom random;
        private bool forEncryption;
        private int bitSize;
        public virtual string AlgorithmName => "ElGamal";
        public virtual void Init(bool forEncryption, ICipherParameters parameters)
        {
            ParametersWithRandom parametersWithRandom = parameters as ParametersWithRandom;
            if (parametersWithRandom != null) {
                key = (ElGamalKeyParameters)parametersWithRandom.Parameters;
                random = parametersWithRandom.Random;
            } else {
                key = (ElGamalKeyParameters)parameters;
                random = (forEncryption ? CryptoServicesRegistrar.GetSecureRandom() : null);
            }
            this.forEncryption = forEncryption;
            bitSize = key.Parameters.P.BitLength;
            if (forEncryption) {
                if (!(key is ElGamalPublicKeyParameters))
                    throw new ArgumentException("ElGamalPublicKeyParameters are required for encryption.");
            } else if (!(key is ElGamalPrivateKeyParameters)) {
                throw new ArgumentException("ElGamalPrivateKeyParameters are required for decryption.");
            }
        }
        public virtual int GetInputBlockSize()
        {
            if (forEncryption)
                return (bitSize - 1) / 8;
            return 2 * ((bitSize + 7) / 8);
        }
        public virtual int GetOutputBlockSize()
        {
            if (forEncryption)
                return 2 * ((bitSize + 7) / 8);
            return (bitSize - 1) / 8;
        }
        public virtual byte[] ProcessBlock(byte[] input, int inOff, int length)
        {
            if (key == null)
                throw new InvalidOperationException("ElGamal engine not initialised");
            int num = forEncryption ? ((bitSize - 1 + 7) / 8) : GetInputBlockSize();
            if (length > num)
                throw new DataLengthException("input too large for ElGamal cipher.\n");
            BigInteger p = key.Parameters.P;
            byte[] array;
            if (key is ElGamalPrivateKeyParameters) {
                int num2 = length / 2;
                BigInteger bigInteger = new BigInteger(1, input, inOff, num2);
                BigInteger val = new BigInteger(1, input, inOff + num2, num2);
                ElGamalPrivateKeyParameters elGamalPrivateKeyParameters = (ElGamalPrivateKeyParameters)key;
                array = bigInteger.ModPow(p.Subtract(BigInteger.One).Subtract(elGamalPrivateKeyParameters.X), p).Multiply(val).Mod(p)
                    .ToByteArrayUnsigned();
            } else {
                BigInteger bigInteger2 = new BigInteger(1, input, inOff, length);
                if (bigInteger2.BitLength >= p.BitLength)
                    throw new DataLengthException("input too large for ElGamal cipher.\n");
                ElGamalPublicKeyParameters elGamalPublicKeyParameters = (ElGamalPublicKeyParameters)key;
                BigInteger other = p.Subtract(BigInteger.Two);
                BigInteger bigInteger3;
                do {
                    bigInteger3 = new BigInteger(p.BitLength, random);
                } while (bigInteger3.SignValue == 0 || bigInteger3.CompareTo(other) > 0);
                BigInteger n = key.Parameters.G.ModPow(bigInteger3, p);
                BigInteger n2 = bigInteger2.Multiply(elGamalPublicKeyParameters.Y.ModPow(bigInteger3, p)).Mod(p);
                array = new byte[GetOutputBlockSize()];
                int num3 = array.Length / 2;
                BigIntegers.AsUnsignedByteArray(n, array, 0, num3);
                BigIntegers.AsUnsignedByteArray(n2, array, num3, array.Length - num3);
            }
            return array;
        }
    }
}