123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
- using System;
- using Org.BouncyCastle.Crypto.Parameters;
- using Org.BouncyCastle.Math;
- using Org.BouncyCastle.Security;
- namespace Org.BouncyCastle.Crypto.Engines
- {
- /**
- * this does your basic ElGamal algorithm.
- */
- public class ElGamalEngine
- : IAsymmetricBlockCipher
- {
- private ElGamalKeyParameters key;
- private SecureRandom random;
- private bool forEncryption;
- private int bitSize;
- public virtual string AlgorithmName
- {
- get { return "ElGamal"; }
- }
- /**
- * initialise the ElGamal engine.
- *
- * @param forEncryption true if we are encrypting, false otherwise.
- * @param param the necessary ElGamal key parameters.
- */
- public virtual void Init(
- bool forEncryption,
- ICipherParameters parameters)
- {
- if (parameters is ParametersWithRandom)
- {
- ParametersWithRandom p = (ParametersWithRandom) parameters;
- this.key = (ElGamalKeyParameters) p.Parameters;
- this.random = p.Random;
- }
- else
- {
- this.key = (ElGamalKeyParameters) parameters;
- this.random = new SecureRandom();
- }
- this.forEncryption = forEncryption;
- this.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.");
- }
- }
- }
- /**
- * Return the maximum size for an input block to this engine.
- * For ElGamal this is always one byte less than the size of P on
- * encryption, and twice the length as the size of P on decryption.
- *
- * @return maximum size for an input block.
- */
- public virtual int GetInputBlockSize()
- {
- if (forEncryption)
- {
- return (bitSize - 1) / 8;
- }
- return 2 * ((bitSize + 7) / 8);
- }
- /**
- * Return the maximum size for an output block to this engine.
- * For ElGamal this is always one byte less than the size of P on
- * decryption, and twice the length as the size of P on encryption.
- *
- * @return maximum size for an output block.
- */
- public virtual int GetOutputBlockSize()
- {
- if (forEncryption)
- {
- return 2 * ((bitSize + 7) / 8);
- }
- return (bitSize - 1) / 8;
- }
- /**
- * Process a single block using the basic ElGamal algorithm.
- *
- * @param in the input array.
- * @param inOff the offset into the input buffer where the data starts.
- * @param length the length of the data to be processed.
- * @return the result of the ElGamal process.
- * @exception DataLengthException the input block is too large.
- */
- public virtual byte[] ProcessBlock(
- byte[] input,
- int inOff,
- int length)
- {
- if (key == null)
- throw new InvalidOperationException("ElGamal engine not initialised");
- int maxLength = forEncryption
- ? (bitSize - 1 + 7) / 8
- : GetInputBlockSize();
- if (length > maxLength)
- throw new DataLengthException("input too large for ElGamal cipher.\n");
- BigInteger p = key.Parameters.P;
- byte[] output;
- if (key is ElGamalPrivateKeyParameters) // decryption
- {
- int halfLength = length / 2;
- BigInteger gamma = new BigInteger(1, input, inOff, halfLength);
- BigInteger phi = new BigInteger(1, input, inOff + halfLength, halfLength);
- ElGamalPrivateKeyParameters priv = (ElGamalPrivateKeyParameters) key;
- // a shortcut, which generally relies on p being prime amongst other things.
- // if a problem with this shows up, check the p and g values!
- BigInteger m = gamma.ModPow(p.Subtract(BigInteger.One).Subtract(priv.X), p).Multiply(phi).Mod(p);
- output = m.ToByteArrayUnsigned();
- }
- else // encryption
- {
- BigInteger tmp = new BigInteger(1, input, inOff, length);
- if (tmp.BitLength >= p.BitLength)
- throw new DataLengthException("input too large for ElGamal cipher.\n");
- ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters) key;
- BigInteger pSub2 = p.Subtract(BigInteger.Two);
- // TODO In theory, a series of 'k', 'g.ModPow(k, p)' and 'y.ModPow(k, p)' can be pre-calculated
- BigInteger k;
- do
- {
- k = new BigInteger(p.BitLength, random);
- }
- while (k.SignValue == 0 || k.CompareTo(pSub2) > 0);
- BigInteger g = key.Parameters.G;
- BigInteger gamma = g.ModPow(k, p);
- BigInteger phi = tmp.Multiply(pub.Y.ModPow(k, p)).Mod(p);
- output = new byte[this.GetOutputBlockSize()];
- // TODO Add methods to allow writing BigInteger to existing byte array?
- byte[] out1 = gamma.ToByteArrayUnsigned();
- byte[] out2 = phi.ToByteArrayUnsigned();
- out1.CopyTo(output, output.Length / 2 - out1.Length);
- out2.CopyTo(output, output.Length - out2.Length);
- }
- return output;
- }
- }
- }
- #endif
|