Skip to content

Commit 76b4ebc

Browse files
Merge branch 'develop' into net7
2 parents 01978a6 + fe65570 commit 76b4ebc

File tree

14 files changed

+376
-200
lines changed

14 files changed

+376
-200
lines changed
Lines changed: 23 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
#nullable enable
2+
using System;
23
using System.Security.Cryptography;
34

45
using Renci.SshNet.Common;
@@ -11,8 +12,6 @@ namespace Renci.SshNet.Security.Cryptography
1112
public class DsaDigitalSignature : DigitalSignature, IDisposable
1213
{
1314
private readonly DsaKey _key;
14-
private HashAlgorithm _hash;
15-
private bool _isDisposed;
1615

1716
/// <summary>
1817
/// Initializes a new instance of the <see cref="DsaDigitalSignature" /> class.
@@ -27,71 +26,23 @@ public DsaDigitalSignature(DsaKey key)
2726
}
2827

2928
_key = key;
30-
31-
_hash = SHA1.Create();
3229
}
3330

34-
/// <summary>
35-
/// Verifies the signature.
36-
/// </summary>
37-
/// <param name="input">The input.</param>
38-
/// <param name="signature">The signature.</param>
39-
/// <returns>
40-
/// <see langword="true"/> if signature was successfully verified; otherwise <see langword="false"/>.
41-
/// </returns>
42-
/// <exception cref="InvalidOperationException">Invalid signature.</exception>
31+
/// <inheritdoc/>
4332
public override bool Verify(byte[] input, byte[] signature)
4433
{
45-
var hashInput = _hash.ComputeHash(input);
46-
47-
var hm = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }));
48-
49-
if (signature.Length != 40)
50-
{
51-
throw new InvalidOperationException("Invalid signature.");
52-
}
53-
54-
// Extract r and s numbers from the signature
55-
var rBytes = new byte[21];
56-
var sBytes = new byte[21];
57-
58-
for (int i = 0, j = 20; i < 20; i++, j--)
59-
{
60-
rBytes[i] = signature[j - 1];
61-
sBytes[i] = signature[j + 20 - 1];
62-
}
63-
64-
var r = new BigInteger(rBytes);
65-
var s = new BigInteger(sBytes);
66-
67-
// Reject the signature if 0 < r < q or 0 < s < q is not satisfied.
68-
if (r <= 0 || r >= _key.Q)
34+
#if NETSTANDARD2_1_OR_GREATER || NET
35+
return _key.DSA.VerifyData(input, signature, HashAlgorithmName.SHA1);
36+
#else
37+
// VerifyData does not exist on netstandard2.0.
38+
// It does exist on net462, but in order to keep the path tested,
39+
// use it on netfx as well.
40+
using (var sha1 = SHA1.Create())
6941
{
70-
return false;
42+
var hash = sha1.ComputeHash(input);
43+
return _key.DSA.VerifySignature(hash, signature);
7144
}
72-
73-
if (s <= 0 || s >= _key.Q)
74-
{
75-
return false;
76-
}
77-
78-
// Calculate w = s−1 mod q
79-
var w = BigInteger.ModInverse(s, _key.Q);
80-
81-
// Calculate u1 = H(m)·w mod q
82-
var u1 = (hm * w) % _key.Q;
83-
84-
// Calculate u2 = r * w mod q
85-
var u2 = (r * w) % _key.Q;
86-
87-
u1 = BigInteger.ModPow(_key.G, u1, _key.P);
88-
u2 = BigInteger.ModPow(_key.Y, u2, _key.P);
89-
90-
// Calculate v = ((g pow u1 * y pow u2) mod p) mod q
91-
var v = ((u1 * u2) % _key.P) % _key.Q;
92-
93-
// The signature is valid if v = r
94-
return v == r;
45+
#endif
9546
}
9647

9748
/// <summary>
@@ -104,60 +55,18 @@ public override bool Verify(byte[] input, byte[] signature)
10455
/// <exception cref="SshException">Invalid DSA key.</exception>
10556
public override byte[] Sign(byte[] input)
10657
{
107-
var hashInput = _hash.ComputeHash(input);
108-
109-
var m = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }));
110-
111-
BigInteger s;
112-
BigInteger r;
113-
114-
do
58+
#if NETSTANDARD2_1_OR_GREATER || NET
59+
return _key.DSA.SignData(input, HashAlgorithmName.SHA1);
60+
#else
61+
// SignData does not exist on netstandard2.0.
62+
// It does exist on net462, but in order to keep the path tested,
63+
// use it on netfx as well.
64+
using (var sha1 = SHA1.Create())
11565
{
116-
var k = BigInteger.Zero;
117-
118-
do
119-
{
120-
// Generate a random per-message value k where 0 < k < q
121-
var bitLength = _key.Q.BitLength;
122-
123-
if (_key.Q < BigInteger.Zero)
124-
{
125-
throw new SshException("Invalid DSA key.");
126-
}
127-
128-
while (k <= 0 || k >= _key.Q)
129-
{
130-
k = BigInteger.Random(bitLength);
131-
}
132-
133-
// Calculate r = ((g pow k) mod p) mod q
134-
r = BigInteger.ModPow(_key.G, k, _key.P) % _key.Q;
135-
136-
// In the unlikely case that r = 0, start again with a different random k
137-
}
138-
while (r.IsZero);
139-
140-
// Calculate s = ((k pow −1)(H(m) + x*r)) mod q
141-
k = BigInteger.ModInverse(k, _key.Q) * (m + (_key.X * r));
142-
143-
s = k % _key.Q;
144-
145-
// In the unlikely case that s = 0, start again with a different random k
66+
var hash = sha1.ComputeHash(input);
67+
return _key.DSA.CreateSignature(hash);
14668
}
147-
while (s.IsZero);
148-
149-
// The signature is (r, s)
150-
var signature = new byte[40];
151-
152-
// issue #1918: pad part with zero's on the left if length is less than 20
153-
var rBytes = r.ToByteArray().Reverse().TrimLeadingZeros();
154-
Array.Copy(rBytes, 0, signature, 20 - rBytes.Length, rBytes.Length);
155-
156-
// issue #1918: pad part with zero's on the left if length is less than 20
157-
var sBytes = s.ToByteArray().Reverse().TrimLeadingZeros();
158-
Array.Copy(sBytes, 0, signature, 40 - sBytes.Length, sBytes.Length);
159-
160-
return signature;
69+
#endif
16170
}
16271

16372
/// <summary>
@@ -175,30 +84,6 @@ public void Dispose()
17584
/// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.</param>
17685
protected virtual void Dispose(bool disposing)
17786
{
178-
if (_isDisposed)
179-
{
180-
return;
181-
}
182-
183-
if (disposing)
184-
{
185-
var hash = _hash;
186-
if (hash != null)
187-
{
188-
hash.Dispose();
189-
_hash = null;
190-
}
191-
192-
_isDisposed = true;
193-
}
194-
}
195-
196-
/// <summary>
197-
/// Finalizes an instance of the <see cref="DsaDigitalSignature"/> class.
198-
/// </summary>
199-
~DsaDigitalSignature()
200-
{
201-
Dispose(disposing: false);
20287
}
20388
}
20489
}

src/Renci.SshNet/Security/Cryptography/DsaKey.cs

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System;
1+
#nullable enable
2+
using System;
3+
using System.Security.Cryptography;
24

35
using Renci.SshNet.Common;
46
using Renci.SshNet.Security.Cryptography;
@@ -10,8 +12,9 @@ namespace Renci.SshNet.Security
1012
/// </summary>
1113
public class DsaKey : Key, IDisposable
1214
{
13-
private DsaDigitalSignature _digitalSignature;
14-
private bool _isDisposed;
15+
private DsaDigitalSignature? _digitalSignature;
16+
17+
internal DSA DSA { get; }
1518

1619
/// <summary>
1720
/// Gets the P.
@@ -39,10 +42,10 @@ public class DsaKey : Key, IDisposable
3942
public BigInteger X { get; }
4043

4144
/// <summary>
42-
/// Gets the length of the key.
45+
/// Gets the length of the key in bits.
4346
/// </summary>
4447
/// <value>
45-
/// The length of the key.
48+
/// The bit-length of the key.
4649
/// </value>
4750
public override int KeyLength
4851
{
@@ -104,6 +107,8 @@ public DsaKey(SshKeyData publicKeyData)
104107
Q = publicKeyData.Keys[1];
105108
G = publicKeyData.Keys[2];
106109
Y = publicKeyData.Keys[3];
110+
111+
DSA = LoadDSA();
107112
}
108113

109114
/// <summary>
@@ -130,6 +135,8 @@ public DsaKey(byte[] privateKeyData)
130135
{
131136
throw new InvalidOperationException("Invalid private key (expected EOF).");
132137
}
138+
139+
DSA = LoadDSA();
133140
}
134141

135142
/// <summary>
@@ -147,6 +154,54 @@ public DsaKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y, BigInteger
147154
G = g;
148155
Y = y;
149156
X = x;
157+
158+
DSA = LoadDSA();
159+
}
160+
161+
#pragma warning disable CA1859 // Use concrete types when possible for improved performance
162+
#pragma warning disable CA5384 // Do Not Use Digital Signature Algorithm (DSA)
163+
private DSA LoadDSA()
164+
{
165+
#if NETFRAMEWORK
166+
// On .NET Framework we use the concrete CNG type which is FIPS-186-3
167+
// compatible. The CryptoServiceProvider type returned by DSA.Create()
168+
// is limited to FIPS-186-1 (max 1024 bit key).
169+
var dsa = new DSACng();
170+
#else
171+
var dsa = DSA.Create();
172+
#endif
173+
dsa.ImportParameters(GetDSAParameters());
174+
175+
return dsa;
176+
}
177+
#pragma warning restore CA5384 // Do Not Use Digital Signature Algorithm (DSA)
178+
#pragma warning restore CA1859 // Use concrete types when possible for improved performance
179+
180+
internal DSAParameters GetDSAParameters()
181+
{
182+
// P, G, Y, Q are required.
183+
// P, G, Y must have the same length.
184+
// If X is present, it must have the same length as Q.
185+
186+
// See https://github.com/dotnet/runtime/blob/fadd8313653f71abd0068c8bf914be88edb2c8d3/src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs#L23
187+
// and https://github.com/dotnet/runtime/blob/fadd8313653f71abd0068c8bf914be88edb2c8d3/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs#L18
188+
// (and similar code in RsaKey.cs)
189+
190+
var ret = new DSAParameters
191+
{
192+
P = P.ToByteArray(isUnsigned: true, isBigEndian: true),
193+
Q = Q.ToByteArray(isUnsigned: true, isBigEndian: true),
194+
};
195+
196+
ret.G = G.ExportKeyParameter(ret.P.Length);
197+
ret.Y = Y.ExportKeyParameter(ret.P.Length);
198+
199+
if (!X.IsZero)
200+
{
201+
ret.X = X.ExportKeyParameter(ret.Q.Length);
202+
}
203+
204+
return ret;
150205
}
151206

152207
/// <summary>
@@ -164,30 +219,11 @@ public void Dispose()
164219
/// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.</param>
165220
protected virtual void Dispose(bool disposing)
166221
{
167-
if (_isDisposed)
168-
{
169-
return;
170-
}
171-
172222
if (disposing)
173223
{
174-
var digitalSignature = _digitalSignature;
175-
if (digitalSignature != null)
176-
{
177-
digitalSignature.Dispose();
178-
_digitalSignature = null;
179-
}
180-
181-
_isDisposed = true;
224+
_digitalSignature?.Dispose();
225+
DSA.Dispose();
182226
}
183227
}
184-
185-
/// <summary>
186-
/// Finalizes an instance of the <see cref="DsaKey"/> class.
187-
/// </summary>
188-
~DsaKey()
189-
{
190-
Dispose(disposing: false);
191-
}
192228
}
193229
}

src/Renci.SshNet/Security/Cryptography/RsaKey.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ public override string ToString()
9292
public BigInteger InverseQ { get; }
9393

9494
/// <summary>
95-
/// Gets the length of the key.
95+
/// Gets the length of the key in bits.
9696
/// </summary>
9797
/// <value>
98-
/// The length of the key.
98+
/// The bit-length of the key.
9999
/// </value>
100100
public override int KeyLength
101101
{

test/Data/Key.SSH2.DSA.pub

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ssh-dss AAAAB3NzaC1kc3MAAACBAI8gyHFchkVhkPiwkhkjFDqN6w2nFWTqVy9sLjFs38oEWLMpAw9+c132erUptAhNQ6JZUAVZGllv/3V5hksSDyChe9WY5IfsOlh6X0dcZCwBKysEzQlPyMFqAtbc9uv7oUWNzBfvEbtV6WN/VmcmXf7dyo3EBVXbBFdPl1NKC7W9AAAAFQDY1+bTt7s2iNmYoBE4C9hdWRCyeQAAAIAEtj09ugx/Tdl6bo7X6mX17hcgVgIxcYj5VNONg2k6IHmRFriLviYaS68mIB4SG3jmvvxbXAGqR1bWBUrv90n0wpxxcuuNoCFylJQyuqUkzSsUHb0WMcncZ/tBQt+NJnRB1Zp9sw8n20ocpg3WVPdaXTtc4pk83NYB6ywG6UFPvgAAAIAX+De5dwo33LMl9W8IvA4dY8Q1wshdycAGJzhy+qYF9dCcwD1Pg+4EbPjYPmzJopsVrK97v9QhxyYcXMr/iHhngGwd9nYNzzSKx665vkSjzyeJWpeQ+fvNV3CLItP01ypbUreM+s+Vz1wor5joLKcDS4X0oQ0RIVZNEHnekuLuFg==
1+
ssh-dss AAAAB3NzaC1kc3MAAACBAOCSGuOxxY/DQBowG7fkS30AJmwN4fDPXToyTaAaxwpOWnGJXFhgP4Il+GSKlpaxnUWkajKpMc1b1hhawPWN4sxoT8QRb6SAW30ErnT/pUtsxqoFlkFla4xvWSgNAuHAVkUBrgPsJ4sHehSbNFn3I6q8Rgle2jyHDHTOqPj2KEXhAAAAFQC740YkVJdVpTJRTxd9Myi0Nx3t4wAAAIArvP7AGh5jY+zxeQRb52zxcUamRBkVYL/ferdJNi9hoM8ZaO4++Xgs8wMbpmoEch9DsXdtufjqXWpk7ywlPjcdhhsb3MxJAeEeFtTRsu2/IUTKqKPHIOgoiPzs8q69AxWhV10aDDUdYWLkqPV/tMGl6S/jC7vTJLmhZum4BUv8MQAAAIEAt19oHPIPyv/8mbMaYpu8I6kvj1/97Ejw0neN7Cd9cGZLqIPBwTyUHEQvKSAm4BvNP0Le3JDn3RFayhRmf+8RrAmS4d1MjrCOs6fmeyLnk2kTpRPFZ2x0H1udIRAhzRehyfj6OsAHn7Jclr+mqDhoq7nIfC3tSgWxFH5g948+7ks= imported-openssh-key

test/Data/Key.SSH2.DSA.txt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----
2+
Comment: "imported-openssh-key"
23
P2/56wAAAgIAAAAmZGwtbW9kcHtzaWdue2RzYS1uaXN0LXNoYTF9LGRoe3BsYWlufX0AAA
3-
AEbm9uZQAAAcQAAAHAAAAAAAAABACPIMhxXIZFYZD4sJIZIxQ6jesNpxVk6lcvbC4xbN/K
4-
BFizKQMPfnNd9nq1KbQITUOiWVAFWRpZb/91eYZLEg8goXvVmOSH7DpYel9HXGQsASsrBM
5-
0JT8jBagLW3Pbr+6FFjcwX7xG7Veljf1ZnJl3+3cqNxAVV2wRXT5dTSgu1vQAAA/sEtj09
6-
ugx/Tdl6bo7X6mX17hcgVgIxcYj5VNONg2k6IHmRFriLviYaS68mIB4SG3jmvvxbXAGqR1
7-
bWBUrv90n0wpxxcuuNoCFylJQyuqUkzSsUHb0WMcncZ/tBQt+NJnRB1Zp9sw8n20ocpg3W
8-
VPdaXTtc4pk83NYB6ywG6UFPvgAAAKDY1+bTt7s2iNmYoBE4C9hdWRCyeQAAA/0X+De5dw
9-
o33LMl9W8IvA4dY8Q1wshdycAGJzhy+qYF9dCcwD1Pg+4EbPjYPmzJopsVrK97v9QhxyYc
10-
XMr/iHhngGwd9nYNzzSKx665vkSjzyeJWpeQ+fvNV3CLItP01ypbUreM+s+Vz1wor5joLK
11-
cDS4X0oQ0RIVZNEHnekuLuFgAAAJ4j+lpXSvEZexhbiACKenUniZ/Qkg==
4+
AEbm9uZQAAAcQAAAHAAAAAAAAABADgkhrjscWPw0AaMBu35Et9ACZsDeHwz106Mk2gGscK
5+
TlpxiVxYYD+CJfhkipaWsZ1FpGoyqTHNW9YYWsD1jeLMaE/EEW+kgFt9BK50/6VLbMaqBZ
6+
ZBZWuMb1koDQLhwFZFAa4D7CeLB3oUmzRZ9yOqvEYJXto8hwx0zqj49ihF4QAAA/4rvP7A
7+
Gh5jY+zxeQRb52zxcUamRBkVYL/ferdJNi9hoM8ZaO4++Xgs8wMbpmoEch9DsXdtufjqXW
8+
pk7ywlPjcdhhsb3MxJAeEeFtTRsu2/IUTKqKPHIOgoiPzs8q69AxWhV10aDDUdYWLkqPV/
9+
tMGl6S/jC7vTJLmhZum4BUv8MQAAAKC740YkVJdVpTJRTxd9Myi0Nx3t4wAABAC3X2gc8g
10+
/K//yZsxpim7wjqS+PX/3sSPDSd43sJ31wZkuog8HBPJQcRC8pICbgG80/Qt7ckOfdEVrK
11+
FGZ/7xGsCZLh3UyOsI6zp+Z7IueTaROlE8VnbHQfW50hECHNF6HJ+Po6wAefslyWv6aoOG
12+
iruch8Le1KBbEUfmD3jz7uSwAAAJ9mUEtdk3zSMZJ1umUnNSo5zC+UxA==
1213
---- END SSH2 ENCRYPTED PRIVATE KEY ----

test/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public void TearDown()
2626
[TestMethod]
2727
public void SshDss()
2828
{
29-
DoTest(HostKeyAlgorithm.SshDss, HostKeyFile.Dsa, 2048);
29+
DoTest(HostKeyAlgorithm.SshDss, HostKeyFile.Dsa, 1024);
3030
}
3131

3232
[TestMethod]

test/Renci.SshNet.IntegrationTests/HostKeyFile.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
public sealed class HostKeyFile
44
{
55
public static readonly HostKeyFile Rsa = new HostKeyFile("ssh-rsa", "/etc/ssh/ssh_host_rsa_key", new byte[] { 0x3d, 0x90, 0xd8, 0x0d, 0xd5, 0xe0, 0xb6, 0x13, 0x42, 0x7c, 0x78, 0x1e, 0x19, 0xa3, 0x99, 0x2b });
6-
public static readonly HostKeyFile Dsa = new HostKeyFile("ssh-dsa", "/etc/ssh/ssh_host_dsa_key", new byte[] { 0x50, 0xe0, 0xd5, 0x11, 0xf7, 0xed, 0x54, 0x75, 0x0d, 0x03, 0xc6, 0x52, 0x9b, 0x3b, 0x3c, 0x9f });
6+
public static readonly HostKeyFile Dsa = new HostKeyFile("ssh-dsa", "/etc/ssh/ssh_host_dsa_key", new byte[] { 0xcc, 0xb4, 0x4c, 0xe1, 0xba, 0x6d, 0x15, 0x79, 0xec, 0xe1, 0x31, 0x9f, 0xc0, 0x4a, 0x07, 0x9d });
77
public static readonly HostKeyFile Ed25519 = new HostKeyFile("ssh-ed25519", "/etc/ssh/ssh_host_ed25519_key", new byte[] { 0xb3, 0xb9, 0xd0, 0x1b, 0x73, 0xc4, 0x60, 0xb4, 0xce, 0xed, 0x06, 0xf8, 0x58, 0x49, 0xa3, 0xda });
88
public const string Ecdsa = "/etc/ssh/ssh_host_ecdsa_key";
99

0 commit comments

Comments
 (0)