Explorar o código

Enable trim analysis and fix warnings (#1216)

* Enable trim analysis and fix warnings

* Use EnableTrimAnalyzer instead of IsTrimmable

I don't know how IsTrimmable works with references
(i.e. to SshNet.Security.Cryptography)

* Add additional aot/trimming related analyzers

* Initialise the hash instance in the constructor

---------

Co-authored-by: Wojciech Nagórski <wojtpl2@gmail.com>
Rob Hague hai 1 ano
pai
achega
64b428f7bf

+ 0 - 17
src/Renci.SshNet/Common/Extensions.cs

@@ -93,23 +93,6 @@ namespace Renci.SshNet.Common
             Debug.WriteLine(sb.ToString());
         }
 
-        /// <summary>
-        /// Creates an instance of the specified type using that type's default constructor.
-        /// </summary>
-        /// <typeparam name="T">The type to create.</typeparam>
-        /// <param name="type">Type of the instance to create.</param>
-        /// <returns>A reference to the newly created object.</returns>
-        internal static T CreateInstance<T>(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)

+ 16 - 15
src/Renci.SshNet/ConnectionInfo.cs

@@ -7,6 +7,7 @@ using System.Text;
 
 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 @@ namespace Renci.SshNet
         /// <summary>
         /// Gets supported key exchange algorithms for this connection.
         /// </summary>
-        public IDictionary<string, Type> KeyExchangeAlgorithms { get; private set; }
+        public IDictionary<string, Func<IKeyExchange>> KeyExchangeAlgorithms { get; private set; }
 
         /// <summary>
         /// Gets supported encryptions for this connection.
@@ -71,7 +72,7 @@ namespace Renci.SshNet
         /// <summary>
         /// Gets supported compression algorithms for this connection.
         /// </summary>
-        public IDictionary<string, Type> CompressionAlgorithms { get; private set; }
+        public IDictionary<string, Func<Compressor>> CompressionAlgorithms { get; private set; }
 
         /// <summary>
         /// Gets the supported channel requests for this connection.
@@ -337,19 +338,19 @@ namespace Renci.SshNet
             MaxSessions = 10;
             Encoding = Encoding.UTF8;
 
-            KeyExchangeAlgorithms = new Dictionary<string, Type>
+            KeyExchangeAlgorithms = new Dictionary<string, Func<IKeyExchange>>
                 {
-                    { "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<string, CipherInfo>
@@ -402,7 +403,7 @@ namespace Renci.SshNet
                     { "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(new SshKeyData(data))) },
                 };
 
-            CompressionAlgorithms = new Dictionary<string, Type>
+            CompressionAlgorithms = new Dictionary<string, Func<Compressor>>
                 {
                     { "none", null },
                 };

+ 2 - 2
src/Renci.SshNet/IServiceFactory.cs

@@ -73,7 +73,7 @@ namespace Renci.SshNet
         /// Negotiates a key exchange algorithm, and creates a <see cref="IKeyExchange" /> for the negotiated
         /// algorithm.
         /// </summary>
-        /// <param name="clientAlgorithms">A <see cref="IDictionary{String, Type}"/> 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.</param>
+        /// <param name="clientAlgorithms">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.</param>
         /// <param name="serverAlgorithms">The names of the key exchange algorithms supported by the SSH server.</param>
         /// <returns>
         /// A <see cref="IKeyExchange"/> that was negotiated between client and server.
@@ -81,7 +81,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="clientAlgorithms"/> is <see langword="null"/>.</exception>
         /// <exception cref="ArgumentNullException"><paramref name="serverAlgorithms"/> is <see langword="null"/>.</exception>
         /// <exception cref="SshConnectionException">No key exchange algorithm is supported by both client and server.</exception>
-        IKeyExchange CreateKeyExchange(IDictionary<string, Type> clientAlgorithms, string[] serverAlgorithms);
+        IKeyExchange CreateKeyExchange(IDictionary<string, Func<IKeyExchange>> clientAlgorithms, string[] serverAlgorithms);
 
         /// <summary>
         /// Creates an <see cref="ISftpFileReader"/> for the specified file and with the specified

+ 7 - 1
src/Renci.SshNet/Renci.SshNet.csproj

@@ -5,11 +5,17 @@
     <TargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0</TargetFrameworks>
   </PropertyGroup>
 
+  <PropertyGroup Condition=" $([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0')) ">
+    <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
+    <EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer>
+    <EnableAotAnalyzer>true</EnableAotAnalyzer>
+  </PropertyGroup>
+
   <PropertyGroup Condition=" '$(TargetFramework)' == 'net462' ">
     <DefineConstants>$(DefineConstants);FEATURE_BINARY_SERIALIZATION;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160</DefineConstants>
   </PropertyGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net8.0' ">
+  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or $([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'netstandard2.1')) ">
     <PackageReference Include="SshNet.Security.Cryptography" Version="[1.3.0]" />
   </ItemGroup>
 

+ 18 - 28
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
     /// </summary>
     public class RsaDigitalSignature : CipherDigitalSignature, IDisposable
     {
-        private HashAlgorithm _hash;
+#if NET462
+        private readonly HashAlgorithm _hash;
+#else
+        private readonly IncrementalHash _hash;
+#endif
 
         /// <summary>
         /// Initializes a new instance of the <see cref="RsaDigitalSignature"/> class with the SHA-1 hash algorithm.
@@ -29,8 +34,14 @@ namespace Renci.SshNet.Security.Cryptography
         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
         }
 
         /// <summary>
@@ -42,13 +53,16 @@ namespace Renci.SshNet.Security.Cryptography
         /// </returns>
         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;
-
         /// <summary>
         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
         /// </summary>
@@ -64,31 +78,7 @@ namespace Renci.SshNet.Security.Cryptography
         /// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> 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="RsaDigitalSignature"/> is reclaimed by garbage collection.
-        /// </summary>
-        ~RsaDigitalSignature()
-        {
-            Dispose(disposing: false);
+            _hash.Dispose();
         }
 
         #endregion

+ 8 - 8
src/Renci.SshNet/Security/KeyExchange.cs

@@ -21,8 +21,8 @@ namespace Renci.SshNet.Security
         private CipherInfo _serverCipherInfo;
         private HashInfo _clientHashInfo;
         private HashInfo _serverHashInfo;
-        private Type _compressionType;
-        private Type _decompressionType;
+        private Func<Compressor> _compressorFactory;
+        private Func<Compressor> _decompressorFactory;
 
         /// <summary>
         /// Gets the session.
@@ -148,8 +148,8 @@ namespace Renci.SshNet.Security
             _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];
         }
 
         /// <summary>
@@ -269,7 +269,7 @@ namespace Renci.SshNet.Security
         /// </returns>
         public Compressor CreateCompressor()
         {
-            if (_compressionType is null)
+            if (_compressorFactory is null)
             {
                 return null;
             }
@@ -278,7 +278,7 @@ namespace Renci.SshNet.Security
                                                     Session.ToHex(Session.SessionId),
                                                     Session.ConnectionInfo.CurrentClientCompressionAlgorithm));
 
-            var compressor = _compressionType.CreateInstance<Compressor>();
+            var compressor = _compressorFactory();
 
             compressor.Init(Session);
 
@@ -293,7 +293,7 @@ namespace Renci.SshNet.Security
         /// </returns>
         public Compressor CreateDecompressor()
         {
-            if (_decompressionType is null)
+            if (_decompressorFactory is null)
             {
                 return null;
             }
@@ -302,7 +302,7 @@ namespace Renci.SshNet.Security
                                                     Session.ToHex(Session.SessionId),
                                                     Session.ConnectionInfo.CurrentServerCompressionAlgorithm));
 
-            var decompressor = _decompressionType.CreateInstance<Compressor>();
+            var decompressor = _decompressorFactory();
 
             decompressor.Init(Session);
 

+ 5 - 16
src/Renci.SshNet/ServiceFactory.cs

@@ -79,19 +79,8 @@ namespace Renci.SshNet
             return new PipeStream();
         }
 
-        /// <summary>
-        /// Negotiates a key exchange algorithm, and creates a <see cref="IKeyExchange" /> for the negotiated
-        /// algorithm.
-        /// </summary>
-        /// <param name="clientAlgorithms">A <see cref="IDictionary{String, Type}"/> 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.</param>
-        /// <param name="serverAlgorithms">The names of the key exchange algorithms supported by the SSH server.</param>
-        /// <returns>
-        /// A <see cref="IKeyExchange"/> that was negotiated between client and server.
-        /// </returns>
-        /// <exception cref="ArgumentNullException"><paramref name="clientAlgorithms"/> is <see langword="null"/>.</exception>
-        /// <exception cref="ArgumentNullException"><paramref name="serverAlgorithms"/> is <see langword="null"/>.</exception>
-        /// <exception cref="SshConnectionException">No key exchange algorithms are supported by both client and server.</exception>
-        public IKeyExchange CreateKeyExchange(IDictionary<string, Type> clientAlgorithms, string[] serverAlgorithms)
+        /// <inheritdoc/>
+        public IKeyExchange CreateKeyExchange(IDictionary<string, Func<IKeyExchange>> clientAlgorithms, string[] serverAlgorithms)
         {
             if (clientAlgorithms is null)
             {
@@ -104,17 +93,17 @@ namespace Renci.SshNet
             }
 
             // 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<IKeyExchange>();
+            return keyExchangeAlgorithmFactory();
         }
 
         /// <summary>