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
}
}