DsaDigitalSignature.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. using System;
  2. using System.Linq;
  3. using System.Security.Cryptography;
  4. using Renci.SshNet.Common;
  5. namespace Renci.SshNet.Security.Cryptography
  6. {
  7. /// <summary>
  8. /// Implements DSA digital signature algorithm.
  9. /// </summary>
  10. public class DsaDigitalSignature : DigitalSignature, IDisposable
  11. {
  12. private HashAlgorithm _hash;
  13. private readonly DsaKey _key;
  14. /// <summary>
  15. /// Initializes a new instance of the <see cref="DsaDigitalSignature" /> class.
  16. /// </summary>
  17. /// <param name="key">The DSA key.</param>
  18. /// <exception cref="System.ArgumentNullException">key</exception>
  19. public DsaDigitalSignature(DsaKey key)
  20. {
  21. if (key == null)
  22. throw new ArgumentNullException("key");
  23. this._key = key;
  24. this._hash = new SHA1Hash();
  25. }
  26. /// <summary>
  27. /// Verifies the signature.
  28. /// </summary>
  29. /// <param name="input">The input.</param>
  30. /// <param name="signature">The signature.</param>
  31. /// <returns>
  32. /// <c>True</c> if signature was successfully verified; otherwise <c>false</c>.
  33. /// </returns>
  34. /// <exception cref="System.InvalidOperationException">Invalid signature.</exception>
  35. public override bool Verify(byte[] input, byte[] signature)
  36. {
  37. var hashInput = this._hash.ComputeHash(input);
  38. BigInteger hm = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }).ToArray());
  39. if (signature.Length != 40)
  40. throw new InvalidOperationException("Invalid signature.");
  41. // Extract r and s numbers from the signature
  42. var rBytes = new byte[21];
  43. var sBytes = new byte[21];
  44. for (int i = 0, j = 20; i < 20; i++, j--)
  45. {
  46. rBytes[i] = signature[j - 1];
  47. sBytes[i] = signature[j + 20 - 1];
  48. }
  49. BigInteger r = new BigInteger(rBytes);
  50. BigInteger s = new BigInteger(sBytes);
  51. // Reject the signature if 0 < r < q or 0 < s < q is not satisfied.
  52. if (r <= 0 || r >= this._key.Q)
  53. return false;
  54. if (s <= 0 || s >= this._key.Q)
  55. return false;
  56. // Calculate w = s−1 mod q
  57. BigInteger w = BigInteger.ModInverse(s, this._key.Q);
  58. // Calculate u1 = H(m)·w mod q
  59. BigInteger u1 = hm * w % this._key.Q;
  60. // Calculate u2 = r * w mod q
  61. BigInteger u2 = r * w % this._key.Q;
  62. u1 = BigInteger.ModPow(this._key.G, u1, this._key.P);
  63. u2 = BigInteger.ModPow(this._key.Y, u2, this._key.P);
  64. // Calculate v = ((g pow u1 * y pow u2) mod p) mod q
  65. BigInteger v = ((u1 * u2) % this._key.P) % this._key.Q;
  66. // The signature is valid if v = r
  67. return v == r;
  68. }
  69. /// <summary>
  70. /// Creates the signature.
  71. /// </summary>
  72. /// <param name="input">The input.</param>
  73. /// <returns>
  74. /// Signed input data.
  75. /// </returns>
  76. /// <exception cref="SshException">Invalid DSA key.</exception>
  77. public override byte[] Sign(byte[] input)
  78. {
  79. var hashInput = this._hash.ComputeHash(input);
  80. BigInteger m = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }).ToArray());
  81. BigInteger s;
  82. BigInteger r;
  83. do
  84. {
  85. BigInteger k = BigInteger.Zero;
  86. do
  87. {
  88. // Generate a random per-message value k where 0 < k < q
  89. var bitLength = this._key.Q.BitLength;
  90. if (this._key.Q < BigInteger.Zero)
  91. throw new SshException("Invalid DSA key.");
  92. while (k <= 0 || k >= this._key.Q)
  93. {
  94. k = BigInteger.Random(bitLength);
  95. }
  96. // Calculate r = ((g pow k) mod p) mod q
  97. r = BigInteger.ModPow(this._key.G, k, this._key.P) % this._key.Q;
  98. // In the unlikely case that r = 0, start again with a different random k
  99. } while (r.IsZero);
  100. // Calculate s = ((k pow −1)(H(m) + x*r)) mod q
  101. k = (BigInteger.ModInverse(k, this._key.Q) * (m + this._key.X * r));
  102. s = k % this._key.Q;
  103. // In the unlikely case that s = 0, start again with a different random k
  104. } while (s.IsZero);
  105. // The signature is (r, s)
  106. var signature = new byte[40];
  107. // issue #1918: pad part with zero's on the left if length is less than 20
  108. var rBytes = r.ToByteArray().Reverse().TrimLeadingZero().ToArray();
  109. Array.Copy(rBytes, 0, signature, 20 - rBytes.Length, rBytes.Length);
  110. // issue #1918: pad part with zero's on the left if length is less than 20
  111. var sBytes = s.ToByteArray().Reverse().TrimLeadingZero().ToArray();
  112. Array.Copy(sBytes, 0, signature, 40 - sBytes.Length, sBytes.Length);
  113. return signature;
  114. }
  115. #region IDisposable Members
  116. private bool _isDisposed;
  117. /// <summary>
  118. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages.
  119. /// </summary>
  120. public void Dispose()
  121. {
  122. Dispose(true);
  123. GC.SuppressFinalize(this);
  124. }
  125. /// <summary>
  126. /// Releases unmanaged and - optionally - managed resources
  127. /// </summary>
  128. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
  129. protected virtual void Dispose(bool disposing)
  130. {
  131. // Check to see if Dispose has already been called.
  132. if (!this._isDisposed)
  133. {
  134. // If disposing equals true, dispose all managed
  135. // and unmanaged ResourceMessages.
  136. if (disposing)
  137. {
  138. // Dispose managed ResourceMessages.
  139. if (this._hash != null)
  140. {
  141. this._hash.Clear();
  142. this._hash = null;
  143. }
  144. }
  145. // Note disposing has been done.
  146. this._isDisposed = true;
  147. }
  148. }
  149. /// <summary>
  150. /// Releases unmanaged resources and performs other cleanup operations before the
  151. /// <see cref="SshCommand"/> is reclaimed by garbage collection.
  152. /// </summary>
  153. ~DsaDigitalSignature()
  154. {
  155. // Do not re-create Dispose clean-up code here.
  156. // Calling Dispose(false) is optimal in terms of
  157. // readability and maintainability.
  158. Dispose(false);
  159. }
  160. #endregion
  161. }
  162. }