Skip to content

Enable trim analysis and fix warnings #1216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jan 26, 2024
Merged
17 changes: 0 additions & 17 deletions src/Renci.SshNet/Common/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,23 +93,6 @@ internal static void DebugPrint(this IEnumerable<byte> bytes)
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)
Expand Down
31 changes: 16 additions & 15 deletions src/Renci.SshNet/ConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -46,7 +47,7 @@ public class ConnectionInfo : IConnectionInfoInternal
/// <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.
Expand All @@ -71,7 +72,7 @@ public class ConnectionInfo : IConnectionInfoInternal
/// <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.
Expand Down Expand Up @@ -337,19 +338,19 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
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>
Expand Down Expand Up @@ -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<string, Type>
CompressionAlgorithms = new Dictionary<string, Func<Compressor>>
{
{ "none", null },
};
Expand Down
4 changes: 2 additions & 2 deletions src/Renci.SshNet/IServiceFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ internal partial interface IServiceFactory
/// 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.
/// </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 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
Expand Down
8 changes: 7 additions & 1 deletion src/Renci.SshNet/Renci.SshNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
46 changes: 18 additions & 28 deletions src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Security.Cryptography;

using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography.Ciphers;

Expand All @@ -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.
Expand All @@ -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
}

/// <summary>
Expand All @@ -42,13 +53,16 @@ public RsaDigitalSignature(RsaKey rsaKey, HashAlgorithmName hashAlgorithmName)
/// </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>
Expand All @@ -64,31 +78,7 @@ public void Dispose()
/// <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
Expand Down
16 changes: 8 additions & 8 deletions src/Renci.SshNet/Security/KeyExchange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Compressor> _compressorFactory;
private Func<Compressor> _decompressorFactory;

/// <summary>
/// Gets the session.
Expand Down Expand Up @@ -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];
}

/// <summary>
Expand Down Expand Up @@ -269,7 +269,7 @@ public HashAlgorithm CreateClientHash()
/// </returns>
public Compressor CreateCompressor()
{
if (_compressionType is null)
if (_compressorFactory is null)
{
return null;
}
Expand All @@ -278,7 +278,7 @@ public Compressor CreateCompressor()
Session.ToHex(Session.SessionId),
Session.ConnectionInfo.CurrentClientCompressionAlgorithm));

var compressor = _compressionType.CreateInstance<Compressor>();
var compressor = _compressorFactory();

compressor.Init(Session);

Expand All @@ -293,7 +293,7 @@ public Compressor CreateCompressor()
/// </returns>
public Compressor CreateDecompressor()
{
if (_decompressionType is null)
if (_decompressorFactory is null)
{
return null;
}
Expand All @@ -302,7 +302,7 @@ public Compressor CreateDecompressor()
Session.ToHex(Session.SessionId),
Session.ConnectionInfo.CurrentServerCompressionAlgorithm));

var decompressor = _decompressionType.CreateInstance<Compressor>();
var decompressor = _decompressorFactory();

decompressor.Init(Session);

Expand Down
21 changes: 5 additions & 16 deletions src/Renci.SshNet/ServiceFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,8 @@ public PipeStream CreatePipeStream()
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)
{
Expand All @@ -104,17 +93,17 @@ public IKeyExchange CreateKeyExchange(IDictionary<string, Type> 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<IKeyExchange>();
return keyExchangeAlgorithmFactory();
}

/// <summary>
Expand Down