Skip to content
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) },
{ "[email protected]", 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() },
{ "[email protected]", () => 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