ElGamalEngine.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Math;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
  7. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines
  8. {
  9. /**
  10. * this does your basic ElGamal algorithm.
  11. */
  12. public class ElGamalEngine
  13. : IAsymmetricBlockCipher
  14. {
  15. private ElGamalKeyParameters key;
  16. private SecureRandom random;
  17. private bool forEncryption;
  18. private int bitSize;
  19. public virtual string AlgorithmName
  20. {
  21. get { return "ElGamal"; }
  22. }
  23. /**
  24. * initialise the ElGamal engine.
  25. *
  26. * @param forEncryption true if we are encrypting, false otherwise.
  27. * @param param the necessary ElGamal key parameters.
  28. */
  29. public virtual void Init(
  30. bool forEncryption,
  31. ICipherParameters parameters)
  32. {
  33. if (parameters is ParametersWithRandom)
  34. {
  35. ParametersWithRandom p = (ParametersWithRandom) parameters;
  36. this.key = (ElGamalKeyParameters) p.Parameters;
  37. this.random = p.Random;
  38. }
  39. else
  40. {
  41. this.key = (ElGamalKeyParameters) parameters;
  42. this.random = new SecureRandom();
  43. }
  44. this.forEncryption = forEncryption;
  45. this.bitSize = key.Parameters.P.BitLength;
  46. if (forEncryption)
  47. {
  48. if (!(key is ElGamalPublicKeyParameters))
  49. {
  50. throw new ArgumentException("ElGamalPublicKeyParameters are required for encryption.");
  51. }
  52. }
  53. else
  54. {
  55. if (!(key is ElGamalPrivateKeyParameters))
  56. {
  57. throw new ArgumentException("ElGamalPrivateKeyParameters are required for decryption.");
  58. }
  59. }
  60. }
  61. /**
  62. * Return the maximum size for an input block to this engine.
  63. * For ElGamal this is always one byte less than the size of P on
  64. * encryption, and twice the length as the size of P on decryption.
  65. *
  66. * @return maximum size for an input block.
  67. */
  68. public virtual int GetInputBlockSize()
  69. {
  70. if (forEncryption)
  71. {
  72. return (bitSize - 1) / 8;
  73. }
  74. return 2 * ((bitSize + 7) / 8);
  75. }
  76. /**
  77. * Return the maximum size for an output block to this engine.
  78. * For ElGamal this is always one byte less than the size of P on
  79. * decryption, and twice the length as the size of P on encryption.
  80. *
  81. * @return maximum size for an output block.
  82. */
  83. public virtual int GetOutputBlockSize()
  84. {
  85. if (forEncryption)
  86. {
  87. return 2 * ((bitSize + 7) / 8);
  88. }
  89. return (bitSize - 1) / 8;
  90. }
  91. /**
  92. * Process a single block using the basic ElGamal algorithm.
  93. *
  94. * @param in the input array.
  95. * @param inOff the offset into the input buffer where the data starts.
  96. * @param length the length of the data to be processed.
  97. * @return the result of the ElGamal process.
  98. * @exception DataLengthException the input block is too large.
  99. */
  100. public virtual byte[] ProcessBlock(
  101. byte[] input,
  102. int inOff,
  103. int length)
  104. {
  105. if (key == null)
  106. throw new InvalidOperationException("ElGamal engine not initialised");
  107. int maxLength = forEncryption
  108. ? (bitSize - 1 + 7) / 8
  109. : GetInputBlockSize();
  110. if (length > maxLength)
  111. throw new DataLengthException("input too large for ElGamal cipher.\n");
  112. BigInteger p = key.Parameters.P;
  113. byte[] output;
  114. if (key is ElGamalPrivateKeyParameters) // decryption
  115. {
  116. int halfLength = length / 2;
  117. BigInteger gamma = new BigInteger(1, input, inOff, halfLength);
  118. BigInteger phi = new BigInteger(1, input, inOff + halfLength, halfLength);
  119. ElGamalPrivateKeyParameters priv = (ElGamalPrivateKeyParameters) key;
  120. // a shortcut, which generally relies on p being prime amongst other things.
  121. // if a problem with this shows up, check the p and g values!
  122. BigInteger m = gamma.ModPow(p.Subtract(BigInteger.One).Subtract(priv.X), p).Multiply(phi).Mod(p);
  123. output = m.ToByteArrayUnsigned();
  124. }
  125. else // encryption
  126. {
  127. BigInteger tmp = new BigInteger(1, input, inOff, length);
  128. if (tmp.BitLength >= p.BitLength)
  129. throw new DataLengthException("input too large for ElGamal cipher.\n");
  130. ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters) key;
  131. BigInteger pSub2 = p.Subtract(BigInteger.Two);
  132. // TODO In theory, a series of 'k', 'g.ModPow(k, p)' and 'y.ModPow(k, p)' can be pre-calculated
  133. BigInteger k;
  134. do
  135. {
  136. k = new BigInteger(p.BitLength, random);
  137. }
  138. while (k.SignValue == 0 || k.CompareTo(pSub2) > 0);
  139. BigInteger g = key.Parameters.G;
  140. BigInteger gamma = g.ModPow(k, p);
  141. BigInteger phi = tmp.Multiply(pub.Y.ModPow(k, p)).Mod(p);
  142. output = new byte[this.GetOutputBlockSize()];
  143. // TODO Add methods to allow writing BigInteger to existing byte array?
  144. byte[] out1 = gamma.ToByteArrayUnsigned();
  145. byte[] out2 = phi.ToByteArrayUnsigned();
  146. out1.CopyTo(output, output.Length / 2 - out1.Length);
  147. out2.CopyTo(output, output.Length - out2.Length);
  148. }
  149. return output;
  150. }
  151. }
  152. }
  153. #pragma warning restore
  154. #endif