From 8033559857a69cb5e99bee33ef321b203c3deaf0 Mon Sep 17 00:00:00 2001 From: Oliver Schantz Date: Tue, 10 Nov 2026 09:46:21 +0100 Subject: [PATCH] Security: update SSH.NET to 2026.0.0 SSH.NET 2026.0.0 fixes GHSA-vhpg-4g9v-rppq (infinite loop in Channel.SendData when RemotePacketSize=0) and GHSA-h5q6-2gr6-3g3m (uncapped data during protocol version exchange), which affect the SshClient/SftpClient connections the app opens, plus the ScpClient advisories GHSA-q939-rpr3-3284 and GHSA-mggc-4xg6-vcxf (ScpClient is unused). Removes the NU1903 build warnings. SshNet.Keygen moves to 2026.0.0, which requires SSH.NET [2026, 2027). SshNet.PuttyKeyFile has no newer release and stays at 2024.2.0.3; it is referenced but not used in code (PPK files are read by SSH.NET's PrivateKeyFile and written by SshNet.Keygen). Adds PPK round-trip tests: keys written by SshNet.Keygen in PuTTY v2/v3 format (RSA, ED25519, ECDSA, with and without passphrase) must load with SSH.NET's PrivateKeyFile and yield the same public key. Co-Authored-By: Claude Opus 5.5 --- Directory.Packages.props | 4 +- .../Keys/PuttyKeyFileCompatibilityTests.cs | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 OpenSSH_GUI.Tests/Core/Lib/Keys/PuttyKeyFileCompatibilityTests.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 432be66..a6d52bc 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,8 +38,8 @@ - - + + diff --git a/OpenSSH_GUI.Tests/Core/Lib/Keys/PuttyKeyFileCompatibilityTests.cs b/OpenSSH_GUI.Tests/Core/Lib/Keys/PuttyKeyFileCompatibilityTests.cs new file mode 100644 index 0000000..f182f82 --- /dev/null +++ b/OpenSSH_GUI.Tests/Core/Lib/Keys/PuttyKeyFileCompatibilityTests.cs @@ -0,0 +1,46 @@ +using Renci.SshNet; +using Shouldly; +using SshNet.Keygen; +using SshNet.Keygen.Extensions; +using SshNet.Keygen.SshKeyEncryption; +using Xunit; + +namespace OpenSSH_GUI.Tests.Core.Lib.Keys; + +/// +/// Guards the PPK round trip across SSH.NET and SshNet.Keygen upgrades: keys written by SshNet.Keygen in PuTTY format +/// must load with SSH.NET's , which is how the app opens .ppk files. +/// +public sealed class PuttyKeyFileCompatibilityTests : IDisposable +{ + private readonly string directory = Directory.CreateTempSubdirectory("putty-compat").FullName; + + public void Dispose() { Directory.Delete(directory, true); } + + [Theory] + [InlineData(SshKeyType.RSA, SshKeyFormat.PuTTYv2, null)] + [InlineData(SshKeyType.RSA, SshKeyFormat.PuTTYv3, null)] + [InlineData(SshKeyType.ED25519, SshKeyFormat.PuTTYv2, null)] + [InlineData(SshKeyType.ED25519, SshKeyFormat.PuTTYv3, null)] + [InlineData(SshKeyType.ECDSA, SshKeyFormat.PuTTYv3, null)] + [InlineData(SshKeyType.RSA, SshKeyFormat.PuTTYv2, "ppk-passphrase")] + [InlineData(SshKeyType.ED25519, SshKeyFormat.PuTTYv3, "ppk-passphrase")] + [InlineData(SshKeyType.ECDSA, SshKeyFormat.PuTTYv3, "ppk-passphrase")] + public void PrivateKeyFile_PpkWrittenBySshNetKeygen_LoadsSameKey(SshKeyType keyType, SshKeyFormat format, string? passphrase) + { + var path = Path.Combine(directory, $"id_{keyType}_{format}.ppk"); + var info = new SshKeyGenerateInfo(keyType) + { + Comment = "putty-compat", + KeyFormat = format + }; + if (passphrase is not null) info.Encryption = new SshKeyEncryptionAes256(passphrase); + var generated = SshKey.Generate(path, FileMode.CreateNew, info); + + using var loaded = passphrase is null ? new PrivateKeyFile(path) : new PrivateKeyFile(path, passphrase); + + GetPublicKeyBlob(loaded).ShouldBe(GetPublicKeyBlob(generated)); + } + + private static string GetPublicKeyBlob(IPrivateKeySource keySource) => keySource.ToOpenSshPublicFormat().Split(' ')[1]; +}