diff --git a/src/Renci.SshNet/Common/Extensions.cs b/src/Renci.SshNet/Common/Extensions.cs index 1d6ce3ca1..ae390d604 100644 --- a/src/Renci.SshNet/Common/Extensions.cs +++ b/src/Renci.SshNet/Common/Extensions.cs @@ -93,23 +93,6 @@ internal static void DebugPrint(this IEnumerable bytes) Debug.WriteLine(sb.ToString()); } - /// - /// Creates an instance of the specified type using that type's default constructor. - /// - /// The type to create. - /// Type of the instance to create. - /// A reference to the newly created object. - internal static T CreateInstance(this Type type) - where T : class - { - if (type is null) - { - return null; - } - - return Activator.CreateInstance(type) as T; - } - internal static void ValidatePort(this uint value, string argument) { if (value > IPEndPoint.MaxPort) diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index 721a45762..b4cb0fc85 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -7,6 +7,7 @@ using Renci.SshNet.Abstractions; using Renci.SshNet.Common; +using Renci.SshNet.Compression; using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Messages.Connection; using Renci.SshNet.Security; @@ -46,7 +47,7 @@ public class ConnectionInfo : IConnectionInfoInternal /// /// Gets supported key exchange algorithms for this connection. /// - public IDictionary KeyExchangeAlgorithms { get; private set; } + public IDictionary> KeyExchangeAlgorithms { get; private set; } /// /// Gets supported encryptions for this connection. @@ -71,7 +72,7 @@ public class ConnectionInfo : IConnectionInfoInternal /// /// Gets supported compression algorithms for this connection. /// - public IDictionary CompressionAlgorithms { get; private set; } + public IDictionary> CompressionAlgorithms { get; private set; } /// /// Gets the supported channel requests for this connection. @@ -337,19 +338,19 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy MaxSessions = 10; Encoding = Encoding.UTF8; - KeyExchangeAlgorithms = new Dictionary + KeyExchangeAlgorithms = new Dictionary> { - { "curve25519-sha256", typeof(KeyExchangeECCurve25519) }, - { "curve25519-sha256@libssh.org", typeof(KeyExchangeECCurve25519) }, - { "ecdh-sha2-nistp256", typeof(KeyExchangeECDH256) }, - { "ecdh-sha2-nistp384", typeof(KeyExchangeECDH384) }, - { "ecdh-sha2-nistp521", typeof(KeyExchangeECDH521) }, - { "diffie-hellman-group-exchange-sha256", typeof(KeyExchangeDiffieHellmanGroupExchangeSha256) }, - { "diffie-hellman-group-exchange-sha1", typeof(KeyExchangeDiffieHellmanGroupExchangeSha1) }, - { "diffie-hellman-group16-sha512", typeof(KeyExchangeDiffieHellmanGroup16Sha512) }, - { "diffie-hellman-group14-sha256", typeof(KeyExchangeDiffieHellmanGroup14Sha256) }, - { "diffie-hellman-group14-sha1", typeof(KeyExchangeDiffieHellmanGroup14Sha1) }, - { "diffie-hellman-group1-sha1", typeof(KeyExchangeDiffieHellmanGroup1Sha1) }, + { "curve25519-sha256", () => new KeyExchangeECCurve25519() }, + { "curve25519-sha256@libssh.org", () => new KeyExchangeECCurve25519() }, + { "ecdh-sha2-nistp256", () => new KeyExchangeECDH256() }, + { "ecdh-sha2-nistp384", () => new KeyExchangeECDH384() }, + { "ecdh-sha2-nistp521", () => new KeyExchangeECDH521() }, + { "diffie-hellman-group-exchange-sha256", () => new KeyExchangeDiffieHellmanGroupExchangeSha256() }, + { "diffie-hellman-group-exchange-sha1", () => new KeyExchangeDiffieHellmanGroupExchangeSha1() }, + { "diffie-hellman-group16-sha512", () => new KeyExchangeDiffieHellmanGroup16Sha512() }, + { "diffie-hellman-group14-sha256", () => new KeyExchangeDiffieHellmanGroup14Sha256() }, + { "diffie-hellman-group14-sha1", () => new KeyExchangeDiffieHellmanGroup14Sha1() }, + { "diffie-hellman-group1-sha1", () => new KeyExchangeDiffieHellmanGroup1Sha1() }, }; Encryptions = new Dictionary @@ -402,7 +403,7 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy { "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(new SshKeyData(data))) }, }; - CompressionAlgorithms = new Dictionary + CompressionAlgorithms = new Dictionary> { { "none", null }, }; diff --git a/src/Renci.SshNet/IServiceFactory.cs b/src/Renci.SshNet/IServiceFactory.cs index 8b2c8650b..f19110d7f 100644 --- a/src/Renci.SshNet/IServiceFactory.cs +++ b/src/Renci.SshNet/IServiceFactory.cs @@ -73,7 +73,7 @@ internal partial interface IServiceFactory /// Negotiates a key exchange algorithm, and creates a for the negotiated /// algorithm. /// - /// A of the key exchange algorithms supported by the client where the key is the name of the algorithm, and the value is the type implementing this algorithm. + /// A dictionary of the key exchange algorithms supported by the client where the key is the name of the algorithm, and the value is a factory returning this algorithm. /// The names of the key exchange algorithms supported by the SSH server. /// /// A that was negotiated between client and server. @@ -81,7 +81,7 @@ internal partial interface IServiceFactory /// is . /// is . /// No key exchange algorithm is supported by both client and server. - IKeyExchange CreateKeyExchange(IDictionary clientAlgorithms, string[] serverAlgorithms); + IKeyExchange CreateKeyExchange(IDictionary> clientAlgorithms, string[] serverAlgorithms); /// /// Creates an for the specified file and with the specified diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index adda89ad9..91f2149e2 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -5,11 +5,17 @@ net462;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 + + true + true + true + + $(DefineConstants);FEATURE_BINARY_SERIALIZATION;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160 - + diff --git a/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs index 00a4898f9..63982d112 100644 --- a/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs +++ b/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs @@ -1,5 +1,6 @@ using System; using System.Security.Cryptography; + using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography.Ciphers; @@ -10,7 +11,11 @@ namespace Renci.SshNet.Security.Cryptography /// public class RsaDigitalSignature : CipherDigitalSignature, IDisposable { - private HashAlgorithm _hash; +#if NET462 + private readonly HashAlgorithm _hash; +#else + private readonly IncrementalHash _hash; +#endif /// /// Initializes a new instance of the class with the SHA-1 hash algorithm. @@ -29,8 +34,14 @@ public RsaDigitalSignature(RsaKey rsaKey) public RsaDigitalSignature(RsaKey rsaKey, HashAlgorithmName hashAlgorithmName) : base(ObjectIdentifier.FromHashAlgorithmName(hashAlgorithmName), new RsaCipher(rsaKey)) { +#if NET462 _hash = CryptoConfig.CreateFromName(hashAlgorithmName.Name) as HashAlgorithm ?? throw new ArgumentException($"Could not create {nameof(HashAlgorithm)} from `{hashAlgorithmName}`.", nameof(hashAlgorithmName)); +#else + // CryptoConfig.CreateFromName is a somewhat legacy API and is incompatible with trimming. + // Use IncrementalHash instead (which is also more modern and lighter-weight than HashAlgorithm). + _hash = IncrementalHash.CreateHash(hashAlgorithmName); +#endif } /// @@ -42,13 +53,16 @@ public RsaDigitalSignature(RsaKey rsaKey, HashAlgorithmName hashAlgorithmName) /// protected override byte[] Hash(byte[] input) { +#if NET462 return _hash.ComputeHash(input); +#else + _hash.AppendData(input); + return _hash.GetHashAndReset(); +#endif } #region IDisposable Members - private bool _isDisposed; - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -64,31 +78,7 @@ 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; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~RsaDigitalSignature() - { - Dispose(disposing: false); + _hash.Dispose(); } #endregion diff --git a/src/Renci.SshNet/Security/KeyExchange.cs b/src/Renci.SshNet/Security/KeyExchange.cs index f01a4b117..f24dcafe1 100644 --- a/src/Renci.SshNet/Security/KeyExchange.cs +++ b/src/Renci.SshNet/Security/KeyExchange.cs @@ -21,8 +21,8 @@ public abstract class KeyExchange : Algorithm, IKeyExchange private CipherInfo _serverCipherInfo; private HashInfo _clientHashInfo; private HashInfo _serverHashInfo; - private Type _compressionType; - private Type _decompressionType; + private Func _compressorFactory; + private Func _decompressorFactory; /// /// Gets the session. @@ -148,8 +148,8 @@ from a in message.CompressionAlgorithmsServerToClient _serverCipherInfo = session.ConnectionInfo.Encryptions[serverDecryptionAlgorithmName]; _clientHashInfo = session.ConnectionInfo.HmacAlgorithms[clientHmacAlgorithmName]; _serverHashInfo = session.ConnectionInfo.HmacAlgorithms[serverHmacAlgorithmName]; - _compressionType = session.ConnectionInfo.CompressionAlgorithms[compressionAlgorithmName]; - _decompressionType = session.ConnectionInfo.CompressionAlgorithms[decompressionAlgorithmName]; + _compressorFactory = session.ConnectionInfo.CompressionAlgorithms[compressionAlgorithmName]; + _decompressorFactory = session.ConnectionInfo.CompressionAlgorithms[decompressionAlgorithmName]; } /// @@ -269,7 +269,7 @@ public HashAlgorithm CreateClientHash() /// public Compressor CreateCompressor() { - if (_compressionType is null) + if (_compressorFactory is null) { return null; } @@ -278,7 +278,7 @@ public Compressor CreateCompressor() Session.ToHex(Session.SessionId), Session.ConnectionInfo.CurrentClientCompressionAlgorithm)); - var compressor = _compressionType.CreateInstance(); + var compressor = _compressorFactory(); compressor.Init(Session); @@ -293,7 +293,7 @@ public Compressor CreateCompressor() /// public Compressor CreateDecompressor() { - if (_decompressionType is null) + if (_decompressorFactory is null) { return null; } @@ -302,7 +302,7 @@ public Compressor CreateDecompressor() Session.ToHex(Session.SessionId), Session.ConnectionInfo.CurrentServerCompressionAlgorithm)); - var decompressor = _decompressionType.CreateInstance(); + var decompressor = _decompressorFactory(); decompressor.Init(Session); diff --git a/src/Renci.SshNet/ServiceFactory.cs b/src/Renci.SshNet/ServiceFactory.cs index af30f11c8..ece94505b 100644 --- a/src/Renci.SshNet/ServiceFactory.cs +++ b/src/Renci.SshNet/ServiceFactory.cs @@ -79,19 +79,8 @@ public PipeStream CreatePipeStream() return new PipeStream(); } - /// - /// Negotiates a key exchange algorithm, and creates a for the negotiated - /// algorithm. - /// - /// A of the key exchange algorithms supported by the client where key is the name of the algorithm, and value is the type implementing this algorithm. - /// The names of the key exchange algorithms supported by the SSH server. - /// - /// A that was negotiated between client and server. - /// - /// is . - /// is . - /// No key exchange algorithms are supported by both client and server. - public IKeyExchange CreateKeyExchange(IDictionary clientAlgorithms, string[] serverAlgorithms) + /// + public IKeyExchange CreateKeyExchange(IDictionary> clientAlgorithms, string[] serverAlgorithms) { if (clientAlgorithms is null) { @@ -104,17 +93,17 @@ public IKeyExchange CreateKeyExchange(IDictionary clientAlgorithms } // find an algorithm that is supported by both client and server - var keyExchangeAlgorithmType = (from c in clientAlgorithms + var keyExchangeAlgorithmFactory = (from c in clientAlgorithms from s in serverAlgorithms where s == c.Key select c.Value).FirstOrDefault(); - if (keyExchangeAlgorithmType is null) + if (keyExchangeAlgorithmFactory is null) { throw new SshConnectionException("Failed to negotiate key exchange algorithm.", DisconnectReason.KeyExchangeFailed); } - return keyExchangeAlgorithmType.CreateInstance(); + return keyExchangeAlgorithmFactory(); } ///