You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
6.3 KiB
C#

9 months ago
using System;
using System.Security.Cryptography;
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
namespace Renci.SshNet.Security.Cryptography
{
/// <summary>
/// Implements DSA digital signature algorithm.
/// </summary>
public class DsaDigitalSignature : DigitalSignature, IDisposable
{
private HashAlgorithm _hash;
private readonly DsaKey _key;
/// <summary>
/// Initializes a new instance of the <see cref="DsaDigitalSignature" /> class.
/// </summary>
/// <param name="key">The DSA key.</param>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <c>null</c>.</exception>
public DsaDigitalSignature(DsaKey key)
{
if (key == null)
throw new ArgumentNullException("key");
_key = key;
_hash = CryptoAbstraction.CreateSHA1();
}
/// <summary>
/// Verifies the signature.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="signature">The signature.</param>
/// <returns>
/// <c>true</c> if signature was successfully verified; otherwise <c>false</c>.
/// </returns>
/// <exception cref="InvalidOperationException">Invalid signature.</exception>
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 = s1 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;
}
/// <summary>
/// Creates the signature.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>
/// Signed input data.
/// </returns>
/// <exception cref="SshException">Invalid DSA key.</exception>
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;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
return;
if (disposing)
{
var hash = _hash;
if (hash != null)
{
hash.Dispose();
_hash = null;
}
_isDisposed = true;
}
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="DsaDigitalSignature"/> is reclaimed by garbage collection.
/// </summary>
~DsaDigitalSignature()
{
Dispose(false);
}
#endregion
}
}