ElGamalEngine.cs 4.8 KB

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