From 25c5cd054c4e9987bd577bf24aa2013bd1495fd5 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Fri, 29 Sep 2023 16:38:43 +0800 Subject: [PATCH 1/8] Fix https://github.com/sshnet/SSH.NET/issues/1191 --- .../Classes/SessionTest_ConnectedBase.cs | 10 ++-- ...essionTest_Connected_ServerIsOpenSSH6.6.cs | 50 +++++++++++++++++++ src/Renci.SshNet/Session.cs | 7 +++ 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIsOpenSSH6.6.cs diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs index 8f4bed7c0..32ca73579 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs @@ -46,7 +46,8 @@ public abstract class SessionTest_ConnectedBase protected Session Session { get; private set; } protected Socket ClientSocket { get; private set; } protected Socket ServerSocket { get; private set; } - internal SshIdentification ServerIdentification { get; private set; } + internal SshIdentification ServerIdentification { get; set; } + protected virtual bool CallSessionConnectWhenArrange { get; set; } = true; [TestInitialize] public void Setup() @@ -180,7 +181,7 @@ private void SetupMocks() _ = ServiceFactoryMock.Setup(p => p.CreateProtocolVersionExchange()) .Returns(_protocolVersionExchangeMock.Object); _ = _protocolVersionExchangeMock.Setup(p => p.Start(Session.ClientVersion, ClientSocket, ConnectionInfo.Timeout)) - .Returns(ServerIdentification); + .Returns(() => ServerIdentification); _ = ServiceFactoryMock.Setup(p => p.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, new[] { _keyExchangeAlgorithm })).Returns(_keyExchangeMock.Object); _ = _keyExchangeMock.Setup(p => p.Name) .Returns(_keyExchangeAlgorithm); @@ -212,7 +213,10 @@ protected void Arrange() SetupData(); SetupMocks(); - Session.Connect(); + if (CallSessionConnectWhenArrange) + { + Session.Connect(); + } } protected virtual void ClientAuthentication_Callback() diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIsOpenSSH6.6.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIsOpenSSH6.6.cs new file mode 100644 index 000000000..cc1121b38 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIsOpenSSH6.6.cs @@ -0,0 +1,50 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Connection; + +namespace Renci.SshNet.Tests.Classes +{ + [TestClass] + public class SessionTest_Connected_ServerIsOpenSSH6_6 : SessionTest_ConnectedBase + { + protected override bool CallSessionConnectWhenArrange { get; set; } = false; + + protected override void Act() + { + } + + [TestMethod] + [DataRow("OpenSSH_6.5")] + [DataRow("OpenSSH_6.5p1")] + [DataRow("OpenSSH_6.5 PKIX")] + [DataRow("OpenSSH_6.6")] + [DataRow("OpenSSH_6.6p1")] + [DataRow("OpenSSH_6.6 PKIX")] + public void ShouldExcludeCurve25519KexWhenServerIs(string softwareVersion) + { + ServerIdentification = new SshIdentification("2.0", softwareVersion); + + Session.Connect(); + + Assert.IsFalse(ConnectionInfo.KeyExchangeAlgorithms.Keys.Contains("curve25519-sha256")); + Assert.IsFalse(ConnectionInfo.KeyExchangeAlgorithms.Keys.Contains("curve25519-sha256@libssh.org")); + } + + [TestMethod] + [DataRow("OpenSSH_6.6.1")] + [DataRow("OpenSSH_6.6.1p1")] + [DataRow("OpenSSH_6.6.1 PKIX")] + [DataRow("OpenSSH_6.7")] + [DataRow("OpenSSH_6.7p1")] + [DataRow("OpenSSH_6.7 PKIX")] + public void ShouldIncludeCurve25519KexWhenServerIs(string softwareVersion) + { + ServerIdentification = new SshIdentification("2.0", softwareVersion); + + Session.Connect(); + + Assert.IsTrue(ConnectionInfo.KeyExchangeAlgorithms.Keys.Contains("curve25519-sha256")); + Assert.IsTrue(ConnectionInfo.KeyExchangeAlgorithms.Keys.Contains("curve25519-sha256@libssh.org")); + } + } +} diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 257cf8c2c..b49a8318b 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -618,6 +618,13 @@ public void Connect() DisconnectReason.ProtocolVersionNotSupported); } + if ((serverIdentification.SoftwareVersion.StartsWith("OpenSSH_6.5") || serverIdentification.SoftwareVersion.StartsWith("OpenSSH_6.6")) + && !serverIdentification.SoftwareVersion.StartsWith("OpenSSH_6.6.1")) + { + _ = ConnectionInfo.KeyExchangeAlgorithms.Remove("curve25519-sha256"); + _ = ConnectionInfo.KeyExchangeAlgorithms.Remove("curve25519-sha256@libssh.org"); + } + // Register Transport response messages RegisterMessage("SSH_MSG_DISCONNECT"); RegisterMessage("SSH_MSG_IGNORE"); From 5c4a84dff951694a0b80cb8bb82dbcbb92294565 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Wed, 15 Nov 2023 21:21:42 +0800 Subject: [PATCH 2/8] Expose `SshIdentificationReceived` event so that lib consumer can adjust based on server identification --- src/Renci.SshNet/BaseClient.cs | 13 ++++++ .../Common/SshIdentificationEventArgs.cs | 26 +++++++++++ .../Connection/SshIdentification.cs | 2 +- src/Renci.SshNet/ISession.cs | 5 +++ src/Renci.SshNet/Session.cs | 44 ++++++++++--------- 5 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 src/Renci.SshNet/Common/SshIdentificationEventArgs.cs diff --git a/src/Renci.SshNet/BaseClient.cs b/src/Renci.SshNet/BaseClient.cs index ddd434c2e..3f324accd 100644 --- a/src/Renci.SshNet/BaseClient.cs +++ b/src/Renci.SshNet/BaseClient.cs @@ -153,6 +153,11 @@ public TimeSpan KeepAliveInterval /// public event EventHandler HostKeyReceived; + /// + /// Occurs when SSH identification received. + /// + public event EventHandler SshIdentificationReceived; + /// /// Initializes a new instance of the class. /// @@ -390,6 +395,11 @@ private void Session_HostKeyReceived(object sender, HostKeyEventArgs e) HostKeyReceived?.Invoke(this, e); } + private void Session_SshIdentificationReceived(object sender, SshIdentificationEventArgs e) + { + SshIdentificationReceived?.Invoke(this, e); + } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -532,6 +542,7 @@ private Timer CreateKeepAliveTimer(TimeSpan dueTime, TimeSpan period) private ISession CreateAndConnectSession() { var session = _serviceFactory.CreateSession(ConnectionInfo, _serviceFactory.CreateSocketFactory()); + session.SshIdentificationReceived += Session_SshIdentificationReceived; session.HostKeyReceived += Session_HostKeyReceived; session.ErrorOccured += Session_ErrorOccured; @@ -550,6 +561,7 @@ private ISession CreateAndConnectSession() private async Task CreateAndConnectSessionAsync(CancellationToken cancellationToken) { var session = _serviceFactory.CreateSession(ConnectionInfo, _serviceFactory.CreateSocketFactory()); + session.SshIdentificationReceived += Session_SshIdentificationReceived; session.HostKeyReceived += Session_HostKeyReceived; session.ErrorOccured += Session_ErrorOccured; @@ -569,6 +581,7 @@ private void DisposeSession(ISession session) { session.ErrorOccured -= Session_ErrorOccured; session.HostKeyReceived -= Session_HostKeyReceived; + session.SshIdentificationReceived -= Session_SshIdentificationReceived; session.Dispose(); } diff --git a/src/Renci.SshNet/Common/SshIdentificationEventArgs.cs b/src/Renci.SshNet/Common/SshIdentificationEventArgs.cs new file mode 100644 index 000000000..48273db24 --- /dev/null +++ b/src/Renci.SshNet/Common/SshIdentificationEventArgs.cs @@ -0,0 +1,26 @@ +using System; + +using Renci.SshNet.Connection; + +namespace Renci.SshNet.Common +{ + /// + /// Provides data for the SshIdentificationReceived events. + /// + public class SshIdentificationEventArgs : EventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// The SSH identification. + public SshIdentificationEventArgs(SshIdentification sshIdentification) + { + SshIdentification = sshIdentification; + } + + /// + /// Gets the SSH identification. + /// + public SshIdentification SshIdentification { get; private set; } + } +} diff --git a/src/Renci.SshNet/Connection/SshIdentification.cs b/src/Renci.SshNet/Connection/SshIdentification.cs index 931656296..727cc4d94 100644 --- a/src/Renci.SshNet/Connection/SshIdentification.cs +++ b/src/Renci.SshNet/Connection/SshIdentification.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Connection /// /// Represents an SSH identification. /// - internal sealed class SshIdentification + public sealed class SshIdentification { /// /// Initializes a new instance of the class with the specified protocol version diff --git a/src/Renci.SshNet/ISession.cs b/src/Renci.SshNet/ISession.cs index 5a035b104..1abe65783 100644 --- a/src/Renci.SshNet/ISession.cs +++ b/src/Renci.SshNet/ISession.cs @@ -260,6 +260,11 @@ internal interface ISession : IDisposable /// event EventHandler ErrorOccured; + /// + /// Occurs when SSH identification received. + /// + event EventHandler SshIdentificationReceived; + /// /// Occurs when host key received. /// diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 4a7f78fd0..c5ee1c17b 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -313,20 +313,20 @@ public Message ClientInitMessage get { _clientInitMessage ??= new KeyExchangeInitMessage - { - KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), - ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), - EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(), - EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(), - MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - LanguagesClientToServer = new[] { string.Empty }, - LanguagesServerToClient = new[] { string.Empty }, - FirstKexPacketFollows = false, - Reserved = 0 - }; + { + KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), + ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), + EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(), + EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(), + MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), + MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), + CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), + CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), + LanguagesClientToServer = new[] { string.Empty }, + LanguagesServerToClient = new[] { string.Empty }, + FirstKexPacketFollows = false, + Reserved = 0 + }; return _clientInitMessage; } @@ -366,6 +366,11 @@ public Message ClientInitMessage /// public event EventHandler Disconnected; + /// + /// Occurs when SSH identification received. + /// + public event EventHandler SshIdentificationReceived; + /// /// Occurs when host key received. /// @@ -624,12 +629,7 @@ public void Connect() DisconnectReason.ProtocolVersionNotSupported); } - if ((serverIdentification.SoftwareVersion.StartsWith("OpenSSH_6.5") || serverIdentification.SoftwareVersion.StartsWith("OpenSSH_6.6")) - && !serverIdentification.SoftwareVersion.StartsWith("OpenSSH_6.6.1")) - { - _ = ConnectionInfo.KeyExchangeAlgorithms.Remove("curve25519-sha256"); - _ = ConnectionInfo.KeyExchangeAlgorithms.Remove("curve25519-sha256@libssh.org"); - } + SshIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification)); // Register Transport response messages RegisterMessage("SSH_MSG_DISCONNECT"); @@ -743,6 +743,8 @@ public async Task ConnectAsync(CancellationToken cancellationToken) DisconnectReason.ProtocolVersionNotSupported); } + SshIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification)); + // Register Transport response messages RegisterMessage("SSH_MSG_DISCONNECT"); RegisterMessage("SSH_MSG_IGNORE"); @@ -1190,7 +1192,7 @@ private Message ReceiveMessage(Socket socket) // Determine the size of the first block, which is 8 or cipher block size (whichever is larger) bytes var blockSize = _serverCipher is null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize); - var serverMacLength = _serverMac != null ? _serverMac.HashSize/8 : 0; + var serverMacLength = _serverMac != null ? _serverMac.HashSize / 8 : 0; byte[] data; uint packetLength; From 826dba6b1ad9af26c7ec32d83286678d13a5dfc7 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Wed, 15 Nov 2023 21:27:01 +0800 Subject: [PATCH 3/8] revert unrelated code style change --- src/Renci.SshNet/Session.cs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index c5ee1c17b..a862f2be1 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -313,20 +313,20 @@ public Message ClientInitMessage get { _clientInitMessage ??= new KeyExchangeInitMessage - { - KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), - ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), - EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(), - EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(), - MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - LanguagesClientToServer = new[] { string.Empty }, - LanguagesServerToClient = new[] { string.Empty }, - FirstKexPacketFollows = false, - Reserved = 0 - }; + { + KeyExchangeAlgorithms = ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), + ServerHostKeyAlgorithms = ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), + EncryptionAlgorithmsClientToServer = ConnectionInfo.Encryptions.Keys.ToArray(), + EncryptionAlgorithmsServerToClient = ConnectionInfo.Encryptions.Keys.ToArray(), + MacAlgorithmsClientToServer = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), + MacAlgorithmsServerToClient = ConnectionInfo.HmacAlgorithms.Keys.ToArray(), + CompressionAlgorithmsClientToServer = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), + CompressionAlgorithmsServerToClient = ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), + LanguagesClientToServer = new[] { string.Empty }, + LanguagesServerToClient = new[] { string.Empty }, + FirstKexPacketFollows = false, + Reserved = 0 + }; return _clientInitMessage; } @@ -1192,7 +1192,7 @@ private Message ReceiveMessage(Socket socket) // Determine the size of the first block, which is 8 or cipher block size (whichever is larger) bytes var blockSize = _serverCipher is null ? (byte) 8 : Math.Max((byte) 8, _serverCipher.MinimumSize); - var serverMacLength = _serverMac != null ? _serverMac.HashSize / 8 : 0; + var serverMacLength = _serverMac != null ? _serverMac.HashSize/8 : 0; byte[] data; uint packetLength; From d0db91e7a6c3fa5ea7a7af103ca5fb1351ae300f Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Wed, 15 Nov 2023 21:32:05 +0800 Subject: [PATCH 4/8] revert OpenSSH 6.6 related tests --- .../Classes/SessionTest_ConnectedBase.cs | 8 +-- ...essionTest_Connected_ServerIsOpenSSH6.6.cs | 50 ------------------- 2 files changed, 2 insertions(+), 56 deletions(-) delete mode 100644 test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIsOpenSSH6.6.cs diff --git a/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs b/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs index 32ca73579..c3c50fe3d 100644 --- a/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs +++ b/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs @@ -47,7 +47,6 @@ public abstract class SessionTest_ConnectedBase protected Socket ClientSocket { get; private set; } protected Socket ServerSocket { get; private set; } internal SshIdentification ServerIdentification { get; set; } - protected virtual bool CallSessionConnectWhenArrange { get; set; } = true; [TestInitialize] public void Setup() @@ -181,7 +180,7 @@ private void SetupMocks() _ = ServiceFactoryMock.Setup(p => p.CreateProtocolVersionExchange()) .Returns(_protocolVersionExchangeMock.Object); _ = _protocolVersionExchangeMock.Setup(p => p.Start(Session.ClientVersion, ClientSocket, ConnectionInfo.Timeout)) - .Returns(() => ServerIdentification); + .Returns(ServerIdentification); _ = ServiceFactoryMock.Setup(p => p.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, new[] { _keyExchangeAlgorithm })).Returns(_keyExchangeMock.Object); _ = _keyExchangeMock.Setup(p => p.Name) .Returns(_keyExchangeAlgorithm); @@ -213,10 +212,7 @@ protected void Arrange() SetupData(); SetupMocks(); - if (CallSessionConnectWhenArrange) - { - Session.Connect(); - } + Session.Connect(); } protected virtual void ClientAuthentication_Callback() diff --git a/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIsOpenSSH6.6.cs b/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIsOpenSSH6.6.cs deleted file mode 100644 index cc1121b38..000000000 --- a/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIsOpenSSH6.6.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -using Renci.SshNet.Connection; - -namespace Renci.SshNet.Tests.Classes -{ - [TestClass] - public class SessionTest_Connected_ServerIsOpenSSH6_6 : SessionTest_ConnectedBase - { - protected override bool CallSessionConnectWhenArrange { get; set; } = false; - - protected override void Act() - { - } - - [TestMethod] - [DataRow("OpenSSH_6.5")] - [DataRow("OpenSSH_6.5p1")] - [DataRow("OpenSSH_6.5 PKIX")] - [DataRow("OpenSSH_6.6")] - [DataRow("OpenSSH_6.6p1")] - [DataRow("OpenSSH_6.6 PKIX")] - public void ShouldExcludeCurve25519KexWhenServerIs(string softwareVersion) - { - ServerIdentification = new SshIdentification("2.0", softwareVersion); - - Session.Connect(); - - Assert.IsFalse(ConnectionInfo.KeyExchangeAlgorithms.Keys.Contains("curve25519-sha256")); - Assert.IsFalse(ConnectionInfo.KeyExchangeAlgorithms.Keys.Contains("curve25519-sha256@libssh.org")); - } - - [TestMethod] - [DataRow("OpenSSH_6.6.1")] - [DataRow("OpenSSH_6.6.1p1")] - [DataRow("OpenSSH_6.6.1 PKIX")] - [DataRow("OpenSSH_6.7")] - [DataRow("OpenSSH_6.7p1")] - [DataRow("OpenSSH_6.7 PKIX")] - public void ShouldIncludeCurve25519KexWhenServerIs(string softwareVersion) - { - ServerIdentification = new SshIdentification("2.0", softwareVersion); - - Session.Connect(); - - Assert.IsTrue(ConnectionInfo.KeyExchangeAlgorithms.Keys.Contains("curve25519-sha256")); - Assert.IsTrue(ConnectionInfo.KeyExchangeAlgorithms.Keys.Contains("curve25519-sha256@libssh.org")); - } - } -} From 38589dc4a00bbfa20d78ebb697ed8ecc8f4f1026 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Wed, 15 Nov 2023 21:34:19 +0800 Subject: [PATCH 5/8] revert ConnectionBase --- test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs b/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs index c3c50fe3d..8f4bed7c0 100644 --- a/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs +++ b/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs @@ -46,7 +46,7 @@ public abstract class SessionTest_ConnectedBase protected Session Session { get; private set; } protected Socket ClientSocket { get; private set; } protected Socket ServerSocket { get; private set; } - internal SshIdentification ServerIdentification { get; set; } + internal SshIdentification ServerIdentification { get; private set; } [TestInitialize] public void Setup() From b7f600ecd9aebd1f45725e9cac1b1a9bb7bf3b9c Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Thu, 16 Nov 2023 20:50:10 +0800 Subject: [PATCH 6/8] Add unit tests --- .../Classes/SessionTest_ConnectedBase.cs | 12 +++- ..._Connected_ServerIdentificationReceived.cs | 65 +++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIdentificationReceived.cs diff --git a/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs b/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs index 8f4bed7c0..3c1c2fdcf 100644 --- a/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs +++ b/test/Renci.SshNet.Tests/Classes/SessionTest_ConnectedBase.cs @@ -46,7 +46,8 @@ public abstract class SessionTest_ConnectedBase protected Session Session { get; private set; } protected Socket ClientSocket { get; private set; } protected Socket ServerSocket { get; private set; } - internal SshIdentification ServerIdentification { get; private set; } + internal SshIdentification ServerIdentification { get; set; } + protected bool CallSessionConnectWhenArrange { get; set; } [TestInitialize] public void Setup() @@ -159,6 +160,8 @@ protected virtual void SetupData() ServerListener.Start(); ClientSocket = new DirectConnector(_socketFactory).Connect(ConnectionInfo); + + CallSessionConnectWhenArrange = true; } private void CreateMocks() @@ -180,7 +183,7 @@ private void SetupMocks() _ = ServiceFactoryMock.Setup(p => p.CreateProtocolVersionExchange()) .Returns(_protocolVersionExchangeMock.Object); _ = _protocolVersionExchangeMock.Setup(p => p.Start(Session.ClientVersion, ClientSocket, ConnectionInfo.Timeout)) - .Returns(ServerIdentification); + .Returns(() => ServerIdentification); _ = ServiceFactoryMock.Setup(p => p.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms, new[] { _keyExchangeAlgorithm })).Returns(_keyExchangeMock.Object); _ = _keyExchangeMock.Setup(p => p.Name) .Returns(_keyExchangeAlgorithm); @@ -212,7 +215,10 @@ protected void Arrange() SetupData(); SetupMocks(); - Session.Connect(); + if (CallSessionConnectWhenArrange) + { + Session.Connect(); + } } protected virtual void ClientAuthentication_Callback() diff --git a/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIdentificationReceived.cs b/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIdentificationReceived.cs new file mode 100644 index 000000000..b4cc04da3 --- /dev/null +++ b/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIdentificationReceived.cs @@ -0,0 +1,65 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Renci.SshNet.Connection; + +namespace Renci.SshNet.Tests.Classes +{ + [TestClass] + public class SessionTest_Connected_ServerIdentificationReceived : SessionTest_ConnectedBase + { + protected override void SetupData() + { + base.SetupData(); + + CallSessionConnectWhenArrange = false; + + Session.SshIdentificationReceived += (s, e) => + { + if ((e.SshIdentification.SoftwareVersion.StartsWith("OpenSSH_6.5", System.StringComparison.Ordinal) || e.SshIdentification.SoftwareVersion.StartsWith("OpenSSH_6.6", System.StringComparison.Ordinal)) + && !e.SshIdentification.SoftwareVersion.StartsWith("OpenSSH_6.6.1", System.StringComparison.Ordinal)) + { + _ = ConnectionInfo.KeyExchangeAlgorithms.Remove("curve25519-sha256"); + _ = ConnectionInfo.KeyExchangeAlgorithms.Remove("curve25519-sha256@libssh.org"); + } + }; + } + + protected override void Act() + { + } + + [TestMethod] + [DataRow("OpenSSH_6.5")] + [DataRow("OpenSSH_6.5p1")] + [DataRow("OpenSSH_6.5 PKIX")] + [DataRow("OpenSSH_6.6")] + [DataRow("OpenSSH_6.6p1")] + [DataRow("OpenSSH_6.6 PKIX")] + public void ShouldExcludeCurve25519KexWhenServerIs(string softwareVersion) + { + ServerIdentification = new SshIdentification("2.0", softwareVersion); + + Session.Connect(); + + Assert.IsFalse(ConnectionInfo.KeyExchangeAlgorithms.ContainsKey("curve25519-sha256")); + Assert.IsFalse(ConnectionInfo.KeyExchangeAlgorithms.ContainsKey("curve25519-sha256@libssh.org")); + } + + [TestMethod] + [DataRow("OpenSSH_6.6.1")] + [DataRow("OpenSSH_6.6.1p1")] + [DataRow("OpenSSH_6.6.1 PKIX")] + [DataRow("OpenSSH_6.7")] + [DataRow("OpenSSH_6.7p1")] + [DataRow("OpenSSH_6.7 PKIX")] + public void ShouldIncludeCurve25519KexWhenServerIs(string softwareVersion) + { + ServerIdentification = new SshIdentification("2.0", softwareVersion); + + Session.Connect(); + + Assert.IsTrue(ConnectionInfo.KeyExchangeAlgorithms.ContainsKey("curve25519-sha256")); + Assert.IsTrue(ConnectionInfo.KeyExchangeAlgorithms.ContainsKey("curve25519-sha256@libssh.org")); + } + } +} From 04b20f05c3d56d0f21034dddc21e3edd45da8521 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Thu, 16 Nov 2023 20:56:38 +0800 Subject: [PATCH 7/8] Rename to `ServerIdentificationReceived` --- src/Renci.SshNet/BaseClient.cs | 14 +++++++------- src/Renci.SshNet/ISession.cs | 4 ++-- src/Renci.SshNet/Session.cs | 8 ++++---- ...nTest_Connected_ServerIdentificationReceived.cs | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Renci.SshNet/BaseClient.cs b/src/Renci.SshNet/BaseClient.cs index 3f324accd..bdfd5cb5f 100644 --- a/src/Renci.SshNet/BaseClient.cs +++ b/src/Renci.SshNet/BaseClient.cs @@ -154,9 +154,9 @@ public TimeSpan KeepAliveInterval public event EventHandler HostKeyReceived; /// - /// Occurs when SSH identification received. + /// Occurs when server identification received. /// - public event EventHandler SshIdentificationReceived; + public event EventHandler ServerIdentificationReceived; /// /// Initializes a new instance of the class. @@ -395,9 +395,9 @@ private void Session_HostKeyReceived(object sender, HostKeyEventArgs e) HostKeyReceived?.Invoke(this, e); } - private void Session_SshIdentificationReceived(object sender, SshIdentificationEventArgs e) + private void Session_ServerIdentificationReceived(object sender, SshIdentificationEventArgs e) { - SshIdentificationReceived?.Invoke(this, e); + ServerIdentificationReceived?.Invoke(this, e); } /// @@ -542,7 +542,7 @@ private Timer CreateKeepAliveTimer(TimeSpan dueTime, TimeSpan period) private ISession CreateAndConnectSession() { var session = _serviceFactory.CreateSession(ConnectionInfo, _serviceFactory.CreateSocketFactory()); - session.SshIdentificationReceived += Session_SshIdentificationReceived; + session.ServerIdentificationReceived += Session_ServerIdentificationReceived; session.HostKeyReceived += Session_HostKeyReceived; session.ErrorOccured += Session_ErrorOccured; @@ -561,7 +561,7 @@ private ISession CreateAndConnectSession() private async Task CreateAndConnectSessionAsync(CancellationToken cancellationToken) { var session = _serviceFactory.CreateSession(ConnectionInfo, _serviceFactory.CreateSocketFactory()); - session.SshIdentificationReceived += Session_SshIdentificationReceived; + session.ServerIdentificationReceived += Session_ServerIdentificationReceived; session.HostKeyReceived += Session_HostKeyReceived; session.ErrorOccured += Session_ErrorOccured; @@ -581,7 +581,7 @@ private void DisposeSession(ISession session) { session.ErrorOccured -= Session_ErrorOccured; session.HostKeyReceived -= Session_HostKeyReceived; - session.SshIdentificationReceived -= Session_SshIdentificationReceived; + session.ServerIdentificationReceived -= Session_ServerIdentificationReceived; session.Dispose(); } diff --git a/src/Renci.SshNet/ISession.cs b/src/Renci.SshNet/ISession.cs index 1abe65783..e78ff75f8 100644 --- a/src/Renci.SshNet/ISession.cs +++ b/src/Renci.SshNet/ISession.cs @@ -261,9 +261,9 @@ internal interface ISession : IDisposable event EventHandler ErrorOccured; /// - /// Occurs when SSH identification received. + /// Occurs when server identification received. /// - event EventHandler SshIdentificationReceived; + event EventHandler ServerIdentificationReceived; /// /// Occurs when host key received. diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 656fe0bf7..c984d3109 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -367,9 +367,9 @@ public Message ClientInitMessage public event EventHandler Disconnected; /// - /// Occurs when SSH identification received. + /// Occurs when server identification received. /// - public event EventHandler SshIdentificationReceived; + public event EventHandler ServerIdentificationReceived; /// /// Occurs when host key received. @@ -629,7 +629,7 @@ public void Connect() DisconnectReason.ProtocolVersionNotSupported); } - SshIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification)); + ServerIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification)); // Register Transport response messages RegisterMessage("SSH_MSG_DISCONNECT"); @@ -743,7 +743,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken) DisconnectReason.ProtocolVersionNotSupported); } - SshIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification)); + ServerIdentificationReceived?.Invoke(this, new SshIdentificationEventArgs(serverIdentification)); // Register Transport response messages RegisterMessage("SSH_MSG_DISCONNECT"); diff --git a/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIdentificationReceived.cs b/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIdentificationReceived.cs index b4cc04da3..7b5ff1d86 100644 --- a/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIdentificationReceived.cs +++ b/test/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerIdentificationReceived.cs @@ -13,7 +13,7 @@ protected override void SetupData() CallSessionConnectWhenArrange = false; - Session.SshIdentificationReceived += (s, e) => + Session.ServerIdentificationReceived += (s, e) => { if ((e.SshIdentification.SoftwareVersion.StartsWith("OpenSSH_6.5", System.StringComparison.Ordinal) || e.SshIdentification.SoftwareVersion.StartsWith("OpenSSH_6.6", System.StringComparison.Ordinal)) && !e.SshIdentification.SoftwareVersion.StartsWith("OpenSSH_6.6.1", System.StringComparison.Ordinal)) From ddad1182aaec3cdc0236d8b0f5b72a2aca167f04 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Thu, 16 Nov 2023 21:27:03 +0800 Subject: [PATCH 8/8] rename --- src/Renci.SshNet/Common/SshIdentificationEventArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Renci.SshNet/Common/SshIdentificationEventArgs.cs b/src/Renci.SshNet/Common/SshIdentificationEventArgs.cs index 48273db24..f618112bd 100644 --- a/src/Renci.SshNet/Common/SshIdentificationEventArgs.cs +++ b/src/Renci.SshNet/Common/SshIdentificationEventArgs.cs @@ -5,7 +5,7 @@ namespace Renci.SshNet.Common { /// - /// Provides data for the SshIdentificationReceived events. + /// Provides data for the ServerIdentificationReceived events. /// public class SshIdentificationEventArgs : EventArgs {