using System; using System.Security.Cryptography; using Renci.SshNet.Abstractions; using Renci.SshNet.Common; namespace Renci.SshNet.Security.Cryptography { /// /// Implements DSA digital signature algorithm. /// public class DsaDigitalSignature : DigitalSignature, IDisposable { private HashAlgorithm _hash; private readonly DsaKey _key; /// /// Initializes a new instance of the class. /// /// The DSA key. /// is null. public DsaDigitalSignature(DsaKey key) { if (key == null) throw new ArgumentNullException("key"); _key = key; _hash = CryptoAbstraction.CreateSHA1(); } /// /// Verifies the signature. /// /// The input. /// The signature. /// /// true if signature was successfully verified; otherwise false. /// /// Invalid signature. public override bool Verify(byte[] input, byte[] signature) { var hashInput = _hash.ComputeHash(input); var hm = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 })); if (signature.Length != 40) throw new InvalidOperationException("Invalid signature."); // Extract r and s numbers from the signature var rBytes = new byte[21]; var sBytes = new byte[21]; for (int i = 0, j = 20; i < 20; i++, j--) { rBytes[i] = signature[j - 1]; sBytes[i] = signature[j + 20 - 1]; } var r = new BigInteger(rBytes); var s = new BigInteger(sBytes); // Reject the signature if 0 < r < q or 0 < s < q is not satisfied. if (r <= 0 || r >= _key.Q) return false; if (s <= 0 || s >= _key.Q) return false; // Calculate w = s−1 mod q var w = BigInteger.ModInverse(s, _key.Q); // Calculate u1 = H(m)·w mod q var u1 = hm * w % _key.Q; // Calculate u2 = r * w mod q var u2 = r * w % _key.Q; u1 = BigInteger.ModPow(_key.G, u1, _key.P); u2 = BigInteger.ModPow(_key.Y, u2, _key.P); // Calculate v = ((g pow u1 * y pow u2) mod p) mod q var v = ((u1 * u2) % _key.P) % _key.Q; // The signature is valid if v = r return v == r; } /// /// Creates the signature. /// /// The input. /// /// Signed input data. /// /// Invalid DSA key. public override byte[] Sign(byte[] input) { var hashInput = _hash.ComputeHash(input); var m = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 })); BigInteger s; BigInteger r; do { BigInteger k = BigInteger.Zero; do { // Generate a random per-message value k where 0 < k < q var bitLength = _key.Q.BitLength; if (_key.Q < BigInteger.Zero) throw new SshException("Invalid DSA key."); while (k <= 0 || k >= _key.Q) { k = BigInteger.Random(bitLength); } // Calculate r = ((g pow k) mod p) mod q r = BigInteger.ModPow(_key.G, k, _key.P) % _key.Q; // In the unlikely case that r = 0, start again with a different random k } while (r.IsZero); // Calculate s = ((k pow −1)(H(m) + x*r)) mod q k = (BigInteger.ModInverse(k, _key.Q) * (m + _key.X * r)); s = k % _key.Q; // In the unlikely case that s = 0, start again with a different random k } while (s.IsZero); // The signature is (r, s) var signature = new byte[40]; // issue #1918: pad part with zero's on the left if length is less than 20 var rBytes = r.ToByteArray().Reverse().TrimLeadingZeros(); Array.Copy(rBytes, 0, signature, 20 - rBytes.Length, rBytes.Length); // issue #1918: pad part with zero's on the left if length is less than 20 var sBytes = s.ToByteArray().Reverse().TrimLeadingZeros(); Array.Copy(sBytes, 0, signature, 40 - sBytes.Length, sBytes.Length); return signature; } #region IDisposable Members private bool _isDisposed; /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_isDisposed) return; if (disposing) { var hash = _hash; if (hash != null) { hash.Dispose(); _hash = null; } _isDisposed = true; } } /// /// Releases unmanaged resources and performs other cleanup operations before the /// is reclaimed by garbage collection. /// ~DsaDigitalSignature() { Dispose(false); } #endregion } }