ElGamalEngine.cs 4.9 KB

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