From d520f9daa0f3954ba208b88813f629eb5ae8163f Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Sat, 27 Jul 2024 12:40:36 +0200 Subject: [PATCH 1/4] Use System.Security.Cryptography for DSA This is the analogue of the RSA change #1373 for DSA. This has a couple of caveats: - The BCL supports only FIPS 186-compliant keys, that is, public (P, Q) lengths of (512 <= P <= 1024, 160) for FIPS 186-1/186-2; and (2048, 256), (3072, 256) for FIPS 186-3/186-4. The latter also specifies (2048, 224) but due to a quirk in the Windows API, the BCL does not support Q values of length 224[^1]. - OpenSSH, based on the SSH spec, only supports (supported) Q values of length 160, but appears to also work in non-FIPS-compliant cases such as in our integration tests with a (2048, 160) host key. That test now fails and I changed that host key to (1024, 160). This basically means that (1024, 160) is the largest DSA key size supported by both SSH.NET and OpenSSH. However, given that OpenSSH deprecated DSA in 2015[^2], and the alternative that I have been considering is just to delete support for DSA in the library, this change seems reasonable to me. I don't think we can justify keeping the current handwritten code around. I think we may still consider dropping DSA from the library, I just had this branch laying around and figured I'd finish it off. [^1]: https://github.com/dotnet/runtime/blob/fadd8313653f71abd0068c8bf914be88edb2c8d3/src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs#L259-L265 [^2]: https://www.openssh.com/txt/release-7.0 --- .../Cryptography/DsaDigitalSignature.cs | 161 ++------------ .../Security/Cryptography/DsaKey.cs | 88 +++++--- .../Security/Cryptography/RsaKey.cs | 4 +- test/Data/Key.SSH2.DSA.pub | 2 +- test/Data/Key.SSH2.DSA.txt | 19 +- .../HostKeyAlgorithmTests.cs | 2 +- .../HostKeyFile.cs | 2 +- .../TestsFixtures/InfrastructureFixture.cs | 8 + .../server/ssh/ssh_host_dsa_key | 28 +-- .../Cryptography/DsaDigitalSignatureTest.cs | 56 +++++ .../Security/Cryptography/DsaKeyTest.cs | 198 ++++++++++++++++++ 11 files changed, 372 insertions(+), 196 deletions(-) create mode 100644 test/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaDigitalSignatureTest.cs create mode 100644 test/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaKeyTest.cs diff --git a/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs index 8fdb6a8f7..82fcfe9bc 100644 --- a/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs +++ b/src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using System.Security.Cryptography; using Renci.SshNet.Common; @@ -11,8 +12,6 @@ namespace Renci.SshNet.Security.Cryptography public class DsaDigitalSignature : DigitalSignature, IDisposable { private readonly DsaKey _key; - private HashAlgorithm _hash; - private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -27,71 +26,23 @@ public DsaDigitalSignature(DsaKey key) } _key = key; - - _hash = SHA1.Create(); } - /// - /// Verifies the signature. - /// - /// The input. - /// The signature. - /// - /// if signature was successfully verified; otherwise . - /// - /// 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) +#if NETSTANDARD2_1_OR_GREATER || NET + return _key.DSA.VerifyData(input, signature, HashAlgorithmName.SHA1); +#else + // VerifyData does not exist on netstandard2.0. + // It does exist on net462, but in order to keep the path tested, + // use it on netfx as well. + using (var sha1 = SHA1.Create()) { - return false; + var hash = sha1.ComputeHash(input); + return _key.DSA.VerifySignature(hash, signature); } - - 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; +#endif } /// @@ -104,60 +55,18 @@ public override bool Verify(byte[] input, byte[] signature) /// 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 +#if NETSTANDARD2_1_OR_GREATER || NET + return _key.DSA.SignData(input, HashAlgorithmName.SHA1); +#else + // SignData does not exist on netstandard2.0. + // It does exist on net462, but in order to keep the path tested, + // use it on netfx as well. + using (var sha1 = SHA1.Create()) { - var 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 + var hash = sha1.ComputeHash(input); + return _key.DSA.CreateSignature(hash); } - 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; +#endif } /// @@ -175,30 +84,6 @@ public void Dispose() /// to release both managed and unmanaged resources; 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; - } - } - - /// - /// Finalizes an instance of the class. - /// - ~DsaDigitalSignature() - { - Dispose(disposing: false); } } } diff --git a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs index b0f915cc0..fa7126184 100644 --- a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable +using System; +using System.Security.Cryptography; using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography; @@ -10,8 +12,9 @@ namespace Renci.SshNet.Security /// public class DsaKey : Key, IDisposable { - private DsaDigitalSignature _digitalSignature; - private bool _isDisposed; + private DsaDigitalSignature? _digitalSignature; + + internal DSA DSA { get; } /// /// Gets the P. @@ -39,10 +42,10 @@ public class DsaKey : Key, IDisposable public BigInteger X { get; } /// - /// Gets the length of the key. + /// Gets the length of the key in bits. /// /// - /// The length of the key. + /// The bit-length of the key. /// public override int KeyLength { @@ -104,6 +107,8 @@ public DsaKey(SshKeyData publicKeyData) Q = publicKeyData.Keys[1]; G = publicKeyData.Keys[2]; Y = publicKeyData.Keys[3]; + + DSA = LoadDSA(); } /// @@ -130,6 +135,8 @@ public DsaKey(byte[] privateKeyData) { throw new InvalidOperationException("Invalid private key (expected EOF)."); } + + DSA = LoadDSA(); } /// @@ -147,6 +154,54 @@ public DsaKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y, BigInteger G = g; Y = y; X = x; + + DSA = LoadDSA(); + } + +#pragma warning disable CA1859 // Use concrete types when possible for improved performance +#pragma warning disable CA5384 // Do Not Use Digital Signature Algorithm (DSA) + private DSA LoadDSA() + { +#if NETFRAMEWORK + // On .NET Framework we use the concrete CNG type which is FIPS-186-3 + // compatible. The CryptoServiceProvider type returned by DSA.Create() + // is limited to FIPS-186-1 (max 1024 bit key). + var dsa = new DSACng(); +#else + var dsa = DSA.Create(); +#endif + dsa.ImportParameters(GetDSAParameters()); + + return dsa; + } +#pragma warning restore CA5384 // Do Not Use Digital Signature Algorithm (DSA) +#pragma warning restore CA1859 // Use concrete types when possible for improved performance + + internal DSAParameters GetDSAParameters() + { + // P, G, Y, Q are required. + // P, G, Y must have the same length. + // If X is present, it must have the same length as Q. + + // See https://github.com/dotnet/runtime/blob/fadd8313653f71abd0068c8bf914be88edb2c8d3/src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs#L23 + // and https://github.com/dotnet/runtime/blob/fadd8313653f71abd0068c8bf914be88edb2c8d3/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs#L18 + // (and similar code in RsaKey.cs) + + var ret = new DSAParameters + { + P = P.ToByteArray(isUnsigned: true, isBigEndian: true), + Q = Q.ToByteArray(isUnsigned: true, isBigEndian: true), + }; + + ret.G = G.ExportKeyParameter(ret.P.Length); + ret.Y = Y.ExportKeyParameter(ret.P.Length); + + if (!X.IsZero) + { + ret.X = X.ExportKeyParameter(ret.Q.Length); + } + + return ret; } /// @@ -164,30 +219,11 @@ public void Dispose() /// to release both managed and unmanaged resources; to release only unmanaged resources. protected virtual void Dispose(bool disposing) { - if (_isDisposed) - { - return; - } - if (disposing) { - var digitalSignature = _digitalSignature; - if (digitalSignature != null) - { - digitalSignature.Dispose(); - _digitalSignature = null; - } - - _isDisposed = true; + _digitalSignature?.Dispose(); + DSA.Dispose(); } } - - /// - /// Finalizes an instance of the class. - /// - ~DsaKey() - { - Dispose(disposing: false); - } } } diff --git a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs index 71390d713..472b097e0 100644 --- a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs @@ -92,10 +92,10 @@ public override string ToString() public BigInteger InverseQ { get; } /// - /// Gets the length of the key. + /// Gets the length of the key in bits. /// /// - /// The length of the key. + /// The bit-length of the key. /// public override int KeyLength { diff --git a/test/Data/Key.SSH2.DSA.pub b/test/Data/Key.SSH2.DSA.pub index 3b07844b3..654400140 100644 --- a/test/Data/Key.SSH2.DSA.pub +++ b/test/Data/Key.SSH2.DSA.pub @@ -1 +1 @@ -ssh-dss AAAAB3NzaC1kc3MAAACBAI8gyHFchkVhkPiwkhkjFDqN6w2nFWTqVy9sLjFs38oEWLMpAw9+c132erUptAhNQ6JZUAVZGllv/3V5hksSDyChe9WY5IfsOlh6X0dcZCwBKysEzQlPyMFqAtbc9uv7oUWNzBfvEbtV6WN/VmcmXf7dyo3EBVXbBFdPl1NKC7W9AAAAFQDY1+bTt7s2iNmYoBE4C9hdWRCyeQAAAIAEtj09ugx/Tdl6bo7X6mX17hcgVgIxcYj5VNONg2k6IHmRFriLviYaS68mIB4SG3jmvvxbXAGqR1bWBUrv90n0wpxxcuuNoCFylJQyuqUkzSsUHb0WMcncZ/tBQt+NJnRB1Zp9sw8n20ocpg3WVPdaXTtc4pk83NYB6ywG6UFPvgAAAIAX+De5dwo33LMl9W8IvA4dY8Q1wshdycAGJzhy+qYF9dCcwD1Pg+4EbPjYPmzJopsVrK97v9QhxyYcXMr/iHhngGwd9nYNzzSKx665vkSjzyeJWpeQ+fvNV3CLItP01ypbUreM+s+Vz1wor5joLKcDS4X0oQ0RIVZNEHnekuLuFg== +ssh-dss AAAAB3NzaC1kc3MAAACBAOCSGuOxxY/DQBowG7fkS30AJmwN4fDPXToyTaAaxwpOWnGJXFhgP4Il+GSKlpaxnUWkajKpMc1b1hhawPWN4sxoT8QRb6SAW30ErnT/pUtsxqoFlkFla4xvWSgNAuHAVkUBrgPsJ4sHehSbNFn3I6q8Rgle2jyHDHTOqPj2KEXhAAAAFQC740YkVJdVpTJRTxd9Myi0Nx3t4wAAAIArvP7AGh5jY+zxeQRb52zxcUamRBkVYL/ferdJNi9hoM8ZaO4++Xgs8wMbpmoEch9DsXdtufjqXWpk7ywlPjcdhhsb3MxJAeEeFtTRsu2/IUTKqKPHIOgoiPzs8q69AxWhV10aDDUdYWLkqPV/tMGl6S/jC7vTJLmhZum4BUv8MQAAAIEAt19oHPIPyv/8mbMaYpu8I6kvj1/97Ejw0neN7Cd9cGZLqIPBwTyUHEQvKSAm4BvNP0Le3JDn3RFayhRmf+8RrAmS4d1MjrCOs6fmeyLnk2kTpRPFZ2x0H1udIRAhzRehyfj6OsAHn7Jclr+mqDhoq7nIfC3tSgWxFH5g948+7ks= imported-openssh-key diff --git a/test/Data/Key.SSH2.DSA.txt b/test/Data/Key.SSH2.DSA.txt index e345b1ef5..a4dc6d077 100644 --- a/test/Data/Key.SSH2.DSA.txt +++ b/test/Data/Key.SSH2.DSA.txt @@ -1,12 +1,13 @@ ---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ---- +Comment: "imported-openssh-key" P2/56wAAAgIAAAAmZGwtbW9kcHtzaWdue2RzYS1uaXN0LXNoYTF9LGRoe3BsYWlufX0AAA -AEbm9uZQAAAcQAAAHAAAAAAAAABACPIMhxXIZFYZD4sJIZIxQ6jesNpxVk6lcvbC4xbN/K -BFizKQMPfnNd9nq1KbQITUOiWVAFWRpZb/91eYZLEg8goXvVmOSH7DpYel9HXGQsASsrBM -0JT8jBagLW3Pbr+6FFjcwX7xG7Veljf1ZnJl3+3cqNxAVV2wRXT5dTSgu1vQAAA/sEtj09 -ugx/Tdl6bo7X6mX17hcgVgIxcYj5VNONg2k6IHmRFriLviYaS68mIB4SG3jmvvxbXAGqR1 -bWBUrv90n0wpxxcuuNoCFylJQyuqUkzSsUHb0WMcncZ/tBQt+NJnRB1Zp9sw8n20ocpg3W -VPdaXTtc4pk83NYB6ywG6UFPvgAAAKDY1+bTt7s2iNmYoBE4C9hdWRCyeQAAA/0X+De5dw -o33LMl9W8IvA4dY8Q1wshdycAGJzhy+qYF9dCcwD1Pg+4EbPjYPmzJopsVrK97v9QhxyYc -XMr/iHhngGwd9nYNzzSKx665vkSjzyeJWpeQ+fvNV3CLItP01ypbUreM+s+Vz1wor5joLK -cDS4X0oQ0RIVZNEHnekuLuFgAAAJ4j+lpXSvEZexhbiACKenUniZ/Qkg== +AEbm9uZQAAAcQAAAHAAAAAAAAABADgkhrjscWPw0AaMBu35Et9ACZsDeHwz106Mk2gGscK +TlpxiVxYYD+CJfhkipaWsZ1FpGoyqTHNW9YYWsD1jeLMaE/EEW+kgFt9BK50/6VLbMaqBZ +ZBZWuMb1koDQLhwFZFAa4D7CeLB3oUmzRZ9yOqvEYJXto8hwx0zqj49ihF4QAAA/4rvP7A +Gh5jY+zxeQRb52zxcUamRBkVYL/ferdJNi9hoM8ZaO4++Xgs8wMbpmoEch9DsXdtufjqXW +pk7ywlPjcdhhsb3MxJAeEeFtTRsu2/IUTKqKPHIOgoiPzs8q69AxWhV10aDDUdYWLkqPV/ +tMGl6S/jC7vTJLmhZum4BUv8MQAAAKC740YkVJdVpTJRTxd9Myi0Nx3t4wAABAC3X2gc8g +/K//yZsxpim7wjqS+PX/3sSPDSd43sJ31wZkuog8HBPJQcRC8pICbgG80/Qt7ckOfdEVrK +FGZ/7xGsCZLh3UyOsI6zp+Z7IueTaROlE8VnbHQfW50hECHNF6HJ+Po6wAefslyWv6aoOG +iruch8Le1KBbEUfmD3jz7uSwAAAJ9mUEtdk3zSMZJ1umUnNSo5zC+UxA== ---- END SSH2 ENCRYPTED PRIVATE KEY ---- diff --git a/test/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs b/test/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs index d827fb47c..e685bddf6 100644 --- a/test/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs +++ b/test/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs @@ -26,7 +26,7 @@ public void TearDown() [TestMethod] public void SshDss() { - DoTest(HostKeyAlgorithm.SshDss, HostKeyFile.Dsa, 2048); + DoTest(HostKeyAlgorithm.SshDss, HostKeyFile.Dsa, 1024); } [TestMethod] diff --git a/test/Renci.SshNet.IntegrationTests/HostKeyFile.cs b/test/Renci.SshNet.IntegrationTests/HostKeyFile.cs index 10dfb294f..5562338c2 100644 --- a/test/Renci.SshNet.IntegrationTests/HostKeyFile.cs +++ b/test/Renci.SshNet.IntegrationTests/HostKeyFile.cs @@ -3,7 +3,7 @@ public sealed class HostKeyFile { 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 }); - 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 }); + 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 }); 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 }); public const string Ecdsa = "/etc/ssh/ssh_host_ecdsa_key"; diff --git a/test/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs b/test/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs index 3482f9692..aaefb81ea 100644 --- a/test/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs +++ b/test/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs @@ -32,6 +32,10 @@ public static InfrastructureFixture Instance public SshUser User = new SshUser("sshnet", "ssh4ever"); + // To get the sshd logs (also uncomment WithOutputConsumer below) + private readonly Stream _fsOut = Stream.Null; // File.Create("fsout.txt"); + private readonly Stream _fsErr = Stream.Null; // File.Create("fserr.txt"); + public async Task InitializeAsync() { _sshServerImage = new ImageFromDockerfileBuilder() @@ -47,6 +51,7 @@ public async Task InitializeAsync() .WithHostname("renci-ssh-tests-server") .WithImage(_sshServerImage) .WithPortBinding(22, true) + //.WithOutputConsumer(Consume.RedirectStdoutAndStderrToStream(_fsOut, _fsErr)) .Build(); await _sshServer.StartAsync(); @@ -74,6 +79,9 @@ public async Task DisposeAsync() { await _sshServerImage.DisposeAsync(); } + + _fsOut.Dispose(); + _fsErr.Dispose(); } public void Dispose() diff --git a/test/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_dsa_key b/test/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_dsa_key index eedaafb05..967ffbc6d 100644 --- a/test/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_dsa_key +++ b/test/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_dsa_key @@ -1,20 +1,12 @@ -----BEGIN DSA PRIVATE KEY----- -MIIDPgIBAAKCAQEAuXza5HoqeOTKgTBY0iTglJGVLmmGvp9mWbrx20Xj8V1ouy8u -0ceju7/4AR6m9BzYWm2sAMAwvQcDeUi6pD4C4oIRzQSOg/nuUJO6RkneLQjMYEzD -61FokmxcUzHXQiKtqRRGL97naxj5fFIOppQXfllRASuvHeiG+I6EiFJL4zL7Uwen -CshEkpZsLZ2Xj8nfaD8yPmviDT/QWRUsZgw8lte7MonYVdKd0yeRQwS3vgJwusZv -fFHP4X7aXSwDJTlTGagxFV7jCktwtSc6QFoLWv5LZ2OAJxmgBM1HJQKOnP8dvn56 -EZub3DQrx3IpAgtsxa/8bxt/xFbbfp4sDHwLLQIVAKTEaiNtqneHljGoEGhUWJrs -+kdpAoIBAQCdBG7aHBIV83/icpkELAZ87I/0XDA9pVG+Sgs/OFgUd24tXi9S+dwp -LsVMVaBnN9TaEwZYR6z7Zg12r2j2q8BDTrRwwYHYJvwjHtsZVqaHi35fgBT2RO4T -SqRKYjrjb4mtPodUEo7CzK5+rLpvLM1SiiHfeqmUJqbkDwxQ9xXkCjRP50huJ+tA -ccgQIUyOYioz9omszJGANZlF5ZabzbAiTcXews2p97OeFWNTGTbXebV3FPSV+KBO -c0A5jxzQhEo3Kk58GXuog8t3OksNISdZPIJxHn+th644ZOj0L1v6PrUbXshPL1hp -VNlbn9fO4/HbQzL4NThmgzaZkT2FqxPxAoIBADuwcLTKtLX2cy9cqFiraeEaBXT3 -lQiPTFSLQKVm/k+iumXuOy5Fh3Akzu35MpNLK2gsdoWN9ZRQ8eWODdcnFXSJrnqX -cMWV6ONQ+nZ9YHrRp47KHKWKe+2c0T++S8QZAimb3KCjSOyEwn+i4aAGIvoaYIoH -+tRKmeL+7z1Ff/zJEB1FYVDmcqxhUKd74En6O17EmUHPfiQvwwTYvP5NvlLB23Hz -9ZO4nwrUSUIyVsWYT01s0JThkjI06N0dqKS1we94Ht1mT7iNJ5x5DhVR6qSNOgQH -FMKxKdXHdSFopwwHUrzm3BpKzKW3NuQHazcdZEl1vHb6LpfTv6O6bZIANyACFGBZ -9othW6gmt8t4cI6IyoaLCtLp +MIIBugIBAAKBgQDK1HgEGb/kOVsvVlJpCOpnotUAwoYN1EIAl2Zqj+sxwtUZGR9j +pBV3XYo2IBqTk+2h8Wup8MD3MyoEEMcOem9Jam3VZmvKFTIMBqJglbb+srszsSqw +OiXueV/TPrqMSIEFfNVfPPgHIJ0PwhZ2f3uPidWDEc6t3GzzYRnBFM2zawIVANqN +Qhz4qsDa7V8UamatuXMlhdkHAoGAf28PeajaIq09LwxxN3Ed7FAzsJ9DkodSpjD3 +2E/ezv3eyw1RaFR34g2G0R8xRql0ZgJpb48my4JT5JLmcDtZX0xTdy5Gh2Ke/qxR +smazFLds8CSqVK7e6CVaS6xv2dfI+KgsIVXhDL1GCy8KRJjMmYdx/+4KHgNzGhCZ +lA04mw4CgYBQtVdXgipcs8jLUI/9BkUvgiPJibi5SZibxpL+vVCEOKiMK3I82TuM +YR7S/SJWz1W3KQb9Lmrd/nVwKlk1Ey7Q4J487UpnOuN5Mu7P6PbZi1TXd442XG2r +WbFVGIJVr78ZURaa4mykZJ2DRaanmemRAPSvWTnFYQ6qC2CoYDPaLwIUYatoN3LK +IwehRooj2lc7/l2xxXQ= -----END DSA PRIVATE KEY----- diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaDigitalSignatureTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaDigitalSignatureTest.cs new file mode 100644 index 000000000..87b2e435f --- /dev/null +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaDigitalSignatureTest.cs @@ -0,0 +1,56 @@ +using System.Text; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Abstractions; +using Renci.SshNet.Security; +using Renci.SshNet.Security.Cryptography; +using Renci.SshNet.Tests.Common; + +namespace Renci.SshNet.Tests.Classes.Security.Cryptography +{ + [TestClass] + public class DsaDigitalSignatureTest : TestBase + { + [TestMethod] + public void Verify() + { + byte[] data = Encoding.UTF8.GetBytes("Hello, World!"); + + DsaKey dsaKey = GetDsaKey("Key.DSA.txt"); + + Assert.AreEqual(1024, dsaKey.P.BitLength); + Assert.AreEqual(160, dsaKey.Q.BitLength); + + var digitalSignature = new DsaDigitalSignature(dsaKey); + + byte[] signedBytes = digitalSignature.Sign(data); + + // We can't compare signatures for value equality because they have a source of randomness + Assert.AreEqual(40, signedBytes.Length); + Assert.IsTrue(digitalSignature.Verify(data, signedBytes)); + + byte[] signatureToVerify = new byte[] + { + // Generated with a previous DsaDigitalSignature implementation in order to confirm consistent + // behaviour. We can't seem to validate against openssl because openssl outputs a DER signature, + // where as we want IEEE P1363 (fixed size) format. + 0x07, 0x4c, 0x5e, 0x15, 0x53, 0x36, 0x21, 0xbe, 0x5a, 0x82, 0x35, 0xd5, 0xb6, 0xe6, 0x7d, 0x2f, + 0x01, 0x2a, 0x78, 0x9b, 0x16, 0x4a, 0xe5, 0x8d, 0x85, 0xa6, 0x34, 0x56, 0x9d, 0x38, 0xd6, 0x1a, + 0xa4, 0xa1, 0x5b, 0x98, 0x7d, 0xd5, 0x35, 0x40 + }; + + Assert.IsTrue(digitalSignature.Verify(data, signatureToVerify)); + + Assert.IsFalse(digitalSignature.Verify(data, CryptoAbstraction.GenerateRandom(40))); + } + + private static DsaKey GetDsaKey(string fileName, string passPhrase = null) + { + using (var stream = GetData(fileName)) + { + return (DsaKey)new PrivateKeyFile(stream, passPhrase).Key; + } + } + } +} diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaKeyTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaKeyTest.cs new file mode 100644 index 000000000..831b49a73 --- /dev/null +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/DsaKeyTest.cs @@ -0,0 +1,198 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Security; +using Renci.SshNet.Tests.Common; + +namespace Renci.SshNet.Tests.Classes.Security.Cryptography +{ + [TestClass] + public class DsaKeyTest : TestBase + { + private static DsaKey GetDsaKey(string fileName, string passPhrase = null) + { + using (var stream = GetData(fileName)) + { + return (DsaKey)new PrivateKeyFile(stream, passPhrase).Key; + } + } + + // This is just to line up any differences in the assertion message. + private static void AssertEqual(byte[] actualBytes, string expectedHex) + { + string actualHex = BitConverter.ToString(actualBytes).Replace("-", ""); + + Assert.AreEqual(expectedHex, actualHex, + $"{Environment.NewLine}Expected: {expectedHex}{Environment.NewLine} Actual: {actualHex}"); + } + + // These tests generated by converting the keys to PKCS8, importing them to BCL DSA, + // and printing out the expected DSAParameter values. + + // Some useful commands: + + // Generate a new params file with specific parameters: + // openssl genpkey -genparam -algorithm dsa -pkeyopt pbits:1024 -pkeyopt qbits:160 -out dsa.1024.params + + // Generate PKCS8 key file from the params: + // openssl genpkey -paramfile dsa.1024.params -out dsa.1024.txt + + // Convert to PKCS1: + // openssl pkcs8 -in dsa.1024.txt -nocrypt -traditional -out dsa.1024.pkcs1.txt + + // Convert PKCS1 to ssh.com: + // puttygen dsa.1024.pkcs1.txt -O private-sshcom -o dsa.1024.ssh2.txt + + // Convert to PKCS8: + // openssl pkcs8 -topk8 -nocrypt -in Key.DSA.txt -out Key.DSA.PKCS8.txt + + /* + + using IndentedTextWriter tw = new(Console.Out); + + foreach (string filePath in Directory.EnumerateFiles(dir, "*.DSA.*txt")) + { + string pkFile = Path.GetFileNameWithoutExtension(filePath); + + tw.WriteLine("[TestMethod]"); + tw.WriteLine($"public void {pkFile.Replace('.', '_')}()"); + tw.WriteLine("{"); + tw.Indent++; + + tw.WriteLine($"DsaKey dsaKey = GetDsaKey(\"{pkFile}.txt\");"); + tw.WriteLine(); + tw.WriteLine("DSAParameters p = dsaKey.GetDSAParameters();"); + tw.WriteLine(); + + using DSA dsa = DSA.Create(); + + dsa.ImportFromPem(File.ReadAllText(filePath)); + + DSAParameters p = dsa.ExportParameters(true); + + WriteParamAssert(p.P); + WriteParamAssert(p.G); + WriteParamAssert(p.Y); + WriteParamAssert(p.Q); + WriteParamAssert(p.X); + + tw.Indent--; + tw.WriteLine("}"); + tw.WriteLine(); + } + + void WriteParamAssert(byte[] bytes, [CallerArgumentExpression(nameof(bytes))] string name = null) + { + tw.WriteLine($"AssertEqual({name}, \"{Convert.ToHexString(bytes)}\");"); + } + */ + + [TestMethod] + public void Key_DSA() + { + DsaKey dsaKey = GetDsaKey("Key.DSA.txt"); + + Assert.AreEqual(1024, dsaKey.P.BitLength); + Assert.AreEqual(160, dsaKey.Q.BitLength); + + DSAParameters p = dsaKey.GetDSAParameters(); + + AssertEqual(p.P, "B565DDF69ED8EE2AC2C00AF794944A15F428C50D3FECA5FEE4F79461FD4FF669B671D296B4F19D35970A5D20F752847826849C30E12F19B8682BF5020E01FF2BDC338BB7E2A92668D2F2D8B880C62A9DA1B65C346EB53EAC2A779203929DFC2C1B27F2D99BD76C6EF4D6A5A547CE892101D5CC82AF8CC564CAE6D30B5DC89415"); + AssertEqual(p.G, "0E549E37E14011DC79FA940E6758D7C53AE5151F75BB9C968FD054098883F1EC651B7713BDAAD0CD4DB5A458BDDCF6AC79F81ECE95EE3133B72FC973EB3505180C7085952F947B6C7721E26B91D7D9907F5E3CFDB9CB9034278FDCFBC5D7BD06A3E330399DFC35DE8CB93EDC9DEDDDCAAB9B440CCF0A8957488709178D40373D"); + AssertEqual(p.Y, "9BC2066506AD4BB33A01F3484CD586E1323B6766914232DBF7F316248203EDCFD5438CDD4D9746DE4A64D068FD9F8C6B7A8A4AE4D99801D8FCAED15F3C18265E5B6C2EAB7E6C1717929C56FFFED60B6F563975B7B7DD3249387E716B967EBF5F57B99FE1097FEAEFD9220A5036F9CD61ECDACEDDDF2BC1178C8D5D01712E4311"); + AssertEqual(p.Q, "AEFA2364A9FFD838062362B1D20871665807C461"); + AssertEqual(p.X, "18463B393E02299EA1BF7AE04F3E1EC40D97275E"); + } + + + [TestMethod] + public void Key_SSH2_DSA_Encrypted_Des_CBC_12345() + { + DsaKey dsaKey = GetDsaKey("Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt", "12345"); + + Assert.AreEqual(1024, dsaKey.P.BitLength); + Assert.AreEqual(160, dsaKey.Q.BitLength); + + DSAParameters p = dsaKey.GetDSAParameters(); + + AssertEqual(p.P, "8F20C8715C86456190F8B0921923143A8DEB0DA71564EA572F6C2E316CDFCA0458B329030F7E735DF67AB529B4084D43A2595005591A596FFF7579864B120F20A17BD598E487EC3A587A5F475C642C012B2B04CD094FC8C16A02D6DCF6EBFBA1458DCC17EF11BB55E9637F5667265DFEDDCA8DC40555DB04574F97534A0BB5BD"); + AssertEqual(p.G, "04B63D3DBA0C7F4DD97A6E8ED7EA65F5EE17205602317188F954D38D83693A20799116B88BBE261A4BAF26201E121B78E6BEFC5B5C01AA4756D6054AEFF749F4C29C7172EB8DA02172949432BAA524CD2B141DBD1631C9DC67FB4142DF8D267441D59A7DB30F27DB4A1CA60DD654F75A5D3B5CE2993CDCD601EB2C06E9414FBE"); + AssertEqual(p.Y, "17F837B9770A37DCB325F56F08BC0E1D63C435C2C85DC9C006273872FAA605F5D09CC03D4F83EE046CF8D83E6CC9A29B15ACAF7BBFD421C7261C5CCAFF887867806C1DF6760DCF348AC7AEB9BE44A3CF27895A9790F9FBCD57708B22D3F4D72A5B52B78CFACF95CF5C28AF98E82CA7034B85F4A10D1121564D1079DE92E2EE16"); + AssertEqual(p.Q, "D8D7E6D3B7BB3688D998A011380BD85D5910B279"); + AssertEqual(p.X, "23FA5A574AF1197B185B88008A7A7527899FD092"); + } + + [TestMethod] + public void Key_SSH2_DSA() + { + DsaKey dsaKey = GetDsaKey("Key.SSH2.DSA.txt"); + + Assert.AreEqual(1024, dsaKey.P.BitLength); + Assert.AreEqual(160, dsaKey.Q.BitLength); + + DSAParameters p = dsaKey.GetDSAParameters(); + + AssertEqual(p.P, "E0921AE3B1C58FC3401A301BB7E44B7D00266C0DE1F0CF5D3A324DA01AC70A4E5A71895C58603F8225F8648A9696B19D45A46A32A931CD5BD6185AC0F58DE2CC684FC4116FA4805B7D04AE74FFA54B6CC6AA059641656B8C6F59280D02E1C0564501AE03EC278B077A149B3459F723AABC46095EDA3C870C74CEA8F8F62845E1"); + AssertEqual(p.G, "2BBCFEC01A1E6363ECF179045BE76CF17146A644191560BFDF7AB749362F61A0CF1968EE3EF9782CF3031BA66A04721F43B1776DB9F8EA5D6A64EF2C253E371D861B1BDCCC4901E11E16D4D1B2EDBF2144CAA8A3C720E82888FCECF2AEBD0315A1575D1A0C351D6162E4A8F57FB4C1A5E92FE30BBBD324B9A166E9B8054BFC31"); + AssertEqual(p.Y, "B75F681CF20FCAFFFC99B31A629BBC23A92F8F5FFDEC48F0D2778DEC277D70664BA883C1C13C941C442F292026E01BCD3F42DEDC90E7DD115ACA14667FEF11AC0992E1DD4C8EB08EB3A7E67B22E7936913A513C5676C741F5B9D211021CD17A1C9F8FA3AC0079FB25C96BFA6A83868ABB9C87C2DED4A05B1147E60F78F3EEE4B"); + AssertEqual(p.Q, "BBE34624549755A532514F177D3328B4371DEDE3"); + AssertEqual(p.X, "66504B5D937CD2319275BA6527352A39CC2F94C4"); + } + + [TestMethod] + public void Key_DSA_3072_256() + { + // Not supported by OpenSSH but easy enough to test here. + + var keyString = """ + -----BEGIN DSA PRIVATE KEY----- + MIIE1gIBAAKCAYEA6Rb9Cogx64CZcrnP35Nr8W9sjcUqoCpSfZrJdvIhTxgyAkEl + d9U868azCTZ1QGvUuPOCGB5Ll8nRQ6QSK9bicSQ3q3C69BcjzTfZcFuZEi533wEl + 9m9xZTbMAW8643jgARKszoNWIeTGsk2JcXQ2c6+VfPOml6F7yMv3KDIpqjUQlpK+ + RoZn5Eg5R2+2VqcNwwqbP8dyQGnGmgX2hlzbWx9Cld8NTG2b0B6Taaea4zqiMLHC + MeOUh+3WkbGM+aVQ7kthQbkhTEMmZeB/zyJrlEQGmKJl/cuvx69iO8nPSIZJDSEp + sZwg6p+Fqlm0+IyaFaxJMF+SuQirYLR+ee5oor5lcWS3Szaaikz0u1ONO3ndWAmO + eEHq7BL4vuHc/SDxfO5RouhEirCUFtGwtYq/Kf7x53ccd0Jmlj1FalubKHaiyYSg + baHrvN8rFv12XDMI0vPDeOxLVaxaB3zSJTz/ZwDjUcFx7PbuWsvOoSZeagd0sCm9 + aVNQoe7dy/5YCrM7AiEA1oznSuokVWnPt9GaBlmggzk9lMjZ/XVr9MFuDzWDycsC + ggGAYVSdm43WuonZQ40M9An5oBZuWsv22ZNXFnMXzcrKkvdslWGP+za/Ipa+tm+j + YE82vuqCdGjDuDJViJbrx2FProe4J/to2Gpd0jc0uV2c1PFO0B1hDk7tkglQyhmV + q9GAQatA4XUM5cIiInseF5JxHMmfgwEKmfIUDotWnpFQImMvehHxZfI29ngrB2ND + Ba4wBIj1Ua4UqsQi2beNfope7+XSsBQm98prswjvdR6n6wg9Xkvtn4ti3lLbEHzO + ZlwnxoRxFlNE/s2RaygGySRRYAe9TPriGWeMpWirYF3/SDAQyt5qDJsPQU0fWAuF + WsuxDlOsaLmJNJNnvySwx5Qmiw4e17U+6/IKptVbOHWowq05PxZKSVYmcP5+Jbt2 + eVzozKPNGkEw6aBy18l4t+ehx7fiUha7uCPaZ+VTPodLRZE6sKT1uiKOBtPqhrsp + bxKJ3zzmF1KZpoOG++1JHzqOPL+Npokd+K3ce9vNfU7eWTYP2DuiooyF9EktJt7w + AVilAoIBgBCpFSwJxhdAIQRefnQmweW+eXRbx8KL+8t53YsPS/fQhdzIEVEi5LWz + cJ0iAU2l2Mj43AC/6yehA8KF6vJEy4LWSXNwoGVqEoSA2LxBaomLxLARqlFyfCZN + yBlUVfXDiqFw2ajRaLQKUIxWbfC6ard69brWZuS61YQd8Jrk782VAa+sQO6Ca6Ii + vtyyRjNKxbeYwKLkZydi9JFdYYR6kmVG1ge7spemMHlozza6VvNcDU3hE4T4PwbY + Ns555ihK79EWGO2zCNjhPEIN34IjN2WjbUidCBWPGgXLhw9BYyEEREhfP1QUTe2q + S3gup/j8//v46O+OZFcB6g/MZB2IFpRRSw16qM2+pNZFKVTvXs/dUq5tEXylbMCg + +7jS5eLVAkDYFwSUai4Ht9VOHGASz7VyfPfngL8nx+KLNyegB12OLwr6ho5tc9dE + Rib63kEJnsK6CzIjg1+iFblWy4pQsHnKEgvBWxk4+sLyEZFTtjCe7KjigdH6WRDH + U5ejA3bnFQIhAIkT3ff8AjkByyJg1CRkpwDCvFag1fbPXEdg1Ru1E+l/ + -----END DSA PRIVATE KEY----- + """; + + using MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(keyString)); + + DsaKey dsaKey = (DsaKey)new PrivateKeyFile(stream).Key; + + Assert.AreEqual(3072, dsaKey.P.BitLength); + Assert.AreEqual(256, dsaKey.Q.BitLength); + + DSAParameters p = dsaKey.GetDSAParameters(); + + AssertEqual(p.P, "E916FD0A8831EB809972B9CFDF936BF16F6C8DC52AA02A527D9AC976F2214F183202412577D53CEBC6B3093675406BD4B8F382181E4B97C9D143A4122BD6E2712437AB70BAF41723CD37D9705B99122E77DF0125F66F716536CC016F3AE378E00112ACCE835621E4C6B24D8971743673AF957CF3A697A17BC8CBF7283229AA35109692BE468667E44839476FB656A70DC30A9B3FC7724069C69A05F6865CDB5B1F4295DF0D4C6D9BD01E9369A79AE33AA230B1C231E39487EDD691B18CF9A550EE4B6141B9214C432665E07FCF226B94440698A265FDCBAFC7AF623BC9CF4886490D2129B19C20EA9F85AA59B4F88C9A15AC49305F92B908AB60B47E79EE68A2BE657164B74B369A8A4CF4BB538D3B79DD58098E7841EAEC12F8BEE1DCFD20F17CEE51A2E8448AB09416D1B0B58ABF29FEF1E7771C774266963D456A5B9B2876A2C984A06DA1EBBCDF2B16FD765C3308D2F3C378EC4B55AC5A077CD2253CFF6700E351C171ECF6EE5ACBCEA1265E6A0774B029BD695350A1EEDDCBFE580AB33B"); + AssertEqual(p.G, "61549D9B8DD6BA89D9438D0CF409F9A0166E5ACBF6D99357167317CDCACA92F76C95618FFB36BF2296BEB66FA3604F36BEEA827468C3B832558896EBC7614FAE87B827FB68D86A5DD23734B95D9CD4F14ED01D610E4EED920950CA1995ABD18041AB40E1750CE5C222227B1E1792711CC99F83010A99F2140E8B569E915022632F7A11F165F236F6782B07634305AE300488F551AE14AAC422D9B78D7E8A5EEFE5D2B01426F7CA6BB308EF751EA7EB083D5E4BED9F8B62DE52DB107CCE665C27C68471165344FECD916B2806C924516007BD4CFAE219678CA568AB605DFF483010CADE6A0C9B0F414D1F580B855ACBB10E53AC68B989349367BF24B0C794268B0E1ED7B53EEBF20AA6D55B3875A8C2AD393F164A49562670FE7E25BB76795CE8CCA3CD1A4130E9A072D7C978B7E7A1C7B7E25216BBB823DA67E5533E874B45913AB0A4F5BA228E06D3EA86BB296F1289DF3CE6175299A68386FBED491F3A8E3CBF8DA6891DF8ADDC7BDBCD7D4EDE59360FD83BA2A28C85F4492D26DEF00158A5"); + AssertEqual(p.Y, "10A9152C09C6174021045E7E7426C1E5BE79745BC7C28BFBCB79DD8B0F4BF7D085DCC8115122E4B5B3709D22014DA5D8C8F8DC00BFEB27A103C285EAF244CB82D6497370A0656A128480D8BC416A898BC4B011AA51727C264DC8195455F5C38AA170D9A8D168B40A508C566DF0BA6AB77AF5BAD666E4BAD5841DF09AE4EFCD9501AFAC40EE826BA222BEDCB246334AC5B798C0A2E4672762F4915D61847A926546D607BBB297A6307968CF36BA56F35C0D4DE11384F83F06D836CE79E6284AEFD11618EDB308D8E13C420DDF82233765A36D489D08158F1A05CB870F4163210444485F3F54144DEDAA4B782EA7F8FCFFFBF8E8EF8E645701EA0FCC641D881694514B0D7AA8CDBEA4D6452954EF5ECFDD52AE6D117CA56CC0A0FBB8D2E5E2D50240D81704946A2E07B7D54E1C6012CFB5727CF7E780BF27C7E28B3727A0075D8E2F0AFA868E6D73D7444626FADE41099EC2BA0B3223835FA215B956CB8A50B079CA120BC15B1938FAC2F2119153B6309EECA8E281D1FA5910C75397A30376E715"); + AssertEqual(p.Q, "D68CE74AEA245569CFB7D19A0659A083393D94C8D9FD756BF4C16E0F3583C9CB"); + AssertEqual(p.X, "8913DDF7FC023901CB2260D42464A700C2BC56A0D5F6CF5C4760D51BB513E97F"); + } + } +} From 881eefe5e8a73894be3697718af9546d15b0df10 Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Sat, 3 Aug 2024 11:04:36 +0200 Subject: [PATCH 2/4] Appease mono --- .editorconfig | 6 +++++- .../Security/Cryptography/DsaKey.cs | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.editorconfig b/.editorconfig index 05a2600d0..94e456aba 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -// Avoid looking for .editorconfig files in parent directories +// Avoid looking for .editorconfig files in parent directories root=true [*] @@ -87,6 +87,10 @@ dotnet_diagnostic.S1144.severity = none # This is a duplicate of IDE0060. dotnet_diagnostic.S1172.severity = none +# S1199: Nested code blocks should not be used +# https://rules.sonarsource.com/csharp/RSPEC-1199 +dotnet_diagnostic.S1199.severity = none + # S1481: Unused local variables should be removed # https://rules.sonarsource.com/csharp/RSPEC-1481 # diff --git a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs index fa7126184..df14beccc 100644 --- a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs @@ -158,24 +158,32 @@ public DsaKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y, BigInteger DSA = LoadDSA(); } -#pragma warning disable CA1859 // Use concrete types when possible for improved performance #pragma warning disable CA5384 // Do Not Use Digital Signature Algorithm (DSA) private DSA LoadDSA() { + DSA dsa; + #if NETFRAMEWORK // On .NET Framework we use the concrete CNG type which is FIPS-186-3 // compatible. The CryptoServiceProvider type returned by DSA.Create() // is limited to FIPS-186-1 (max 1024 bit key). - var dsa = new DSACng(); -#else - var dsa = DSA.Create(); + + // We give some consideration to a mono build on non-Windows. + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + dsa = new DSACng(); + } + else #endif + { + dsa = DSA.Create(); + } + dsa.ImportParameters(GetDSAParameters()); return dsa; } #pragma warning restore CA5384 // Do Not Use Digital Signature Algorithm (DSA) -#pragma warning restore CA1859 // Use concrete types when possible for improved performance internal DSAParameters GetDSAParameters() { From 316fee9f452c4875ffee732608ed6930460840d3 Mon Sep 17 00:00:00 2001 From: Robert Hague Date: Tue, 6 Aug 2024 12:40:11 +0200 Subject: [PATCH 3/4] test experiment --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 88a674cff..22b8fb13d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,7 +28,7 @@ for: - sh: dotnet test -f net8.0 -c Debug --no-restore --no-build --results-directory artifacts --logger Appveyor --logger "console;verbosity=normal" --logger "liquid.md;LogFileName=linux_unit_test_net_8_report.md" -p:CollectCoverage=true -p:CoverletOutputFormat=cobertura -p:CoverletOutput=../../artifacts/linux_unit_test_net_8_coverage.xml test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj - sh: echo "Run integration tests" - sh: dotnet test -f net8.0 -c Debug --no-restore --no-build --results-directory artifacts --logger Appveyor --logger "console;verbosity=normal" --logger "liquid.md;LogFileName=linux_integration_test_net_8_report.md" -p:CollectCoverage=true -p:CoverletOutputFormat=cobertura -p:CoverletOutput=../../artifacts/linux_integration_test_net_8_coverage.xml test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj - - sh: dotnet test -f net48 -c Debug --no-restore --no-build --results-directory artifacts --logger Appveyor --logger "console;verbosity=normal" --logger "liquid.md;LogFileName=linux_integration_test_net_48_report.md" -p:CollectCoverage=true -p:CoverletOutputFormat=cobertura -p:CoverletOutput=../../artifacts/linux_integration_test_net_48_coverage.xml --filter Name\!~ECDsa test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj + - sh: dotnet test -f net48 -c Debug --no-restore --no-build --results-directory artifacts --logger Appveyor --logger "console;verbosity=normal" --logger "liquid.md;LogFileName=linux_integration_test_net_48_report.md" -p:CollectCoverage=true -p:CoverletOutputFormat=cobertura -p:CoverletOutput=../../artifacts/linux_integration_test_net_48_coverage.xml --filter "Name~Ecdh|Name~Zlib" test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj - matrix: From 01c07c6a7dd4652bd2e1702e82ff3bb487ac8416 Mon Sep 17 00:00:00 2001 From: Robert Hague Date: Tue, 6 Aug 2024 16:24:42 +0200 Subject: [PATCH 4/4] Revert "Appease mono" This reverts commit 881eefe5e8a73894be3697718af9546d15b0df10. --- .editorconfig | 6 +----- .../Security/Cryptography/DsaKey.cs | 18 +++++------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.editorconfig b/.editorconfig index 94e456aba..05a2600d0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -// Avoid looking for .editorconfig files in parent directories +// Avoid looking for .editorconfig files in parent directories root=true [*] @@ -87,10 +87,6 @@ dotnet_diagnostic.S1144.severity = none # This is a duplicate of IDE0060. dotnet_diagnostic.S1172.severity = none -# S1199: Nested code blocks should not be used -# https://rules.sonarsource.com/csharp/RSPEC-1199 -dotnet_diagnostic.S1199.severity = none - # S1481: Unused local variables should be removed # https://rules.sonarsource.com/csharp/RSPEC-1481 # diff --git a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs index df14beccc..fa7126184 100644 --- a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs @@ -158,32 +158,24 @@ public DsaKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y, BigInteger DSA = LoadDSA(); } +#pragma warning disable CA1859 // Use concrete types when possible for improved performance #pragma warning disable CA5384 // Do Not Use Digital Signature Algorithm (DSA) private DSA LoadDSA() { - DSA dsa; - #if NETFRAMEWORK // On .NET Framework we use the concrete CNG type which is FIPS-186-3 // compatible. The CryptoServiceProvider type returned by DSA.Create() // is limited to FIPS-186-1 (max 1024 bit key). - - // We give some consideration to a mono build on non-Windows. - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - dsa = new DSACng(); - } - else + var dsa = new DSACng(); +#else + var dsa = DSA.Create(); #endif - { - dsa = DSA.Create(); - } - dsa.ImportParameters(GetDSAParameters()); return dsa; } #pragma warning restore CA5384 // Do Not Use Digital Signature Algorithm (DSA) +#pragma warning restore CA1859 // Use concrete types when possible for improved performance internal DSAParameters GetDSAParameters() {