Skip to content

Releases: zcsizmadia/SshSharp

v1.0.0

Choose a tag to compare

@zcsizmadia zcsizmadia released this 17 Oct 19:18
4b1b45f

SshSharp 1.0: the public API is now stable and follows semantic versioning. Breaking changes wait for a major version.

dotnet add package SshSharp
dotnet add package SshSharp.Extensions.DependencyInjection # new: named endpoints, a shared reconnecting connection, health checks
dotnet add package SshSharp.IO.Abstractions                 # an IFileSystem over SFTP
dotnet add package SshSharp.FileProviders                   # an IFileProvider over SFTP

New

  • Dependency injection (SshSharp.Extensions.DependencyInjection, new package, #256):
    • services.AddSshClient("name").BindConfiguration("Ssh:Name").ValidateOnStart() registers a named endpoint.
    • ISshClientFactory opens new connections.
    • ISshConnectionProvider keeps one shared connection per endpoint, reconnects with a growing wait, and disposes with the container.
    • AddHealthChecks().AddSsh("name") adds a health check.
    • Unknown hosts fail by default; AcceptNewHostKeys opts in.
    • See Dependency injection.
  • SFTP is no longer experimental. The SSHSHARP001 opt-in is gone from SFTP, SshSharp.FileProviders and SshSharp.IO.Abstractions. Its API is final:
    • Preserve keeps times and permission bits, as sftp -p does, but never setuid, setgid or sticky.
    • SftpTransferProgress reports per-file progress (path, bytes, size).
    • GetOwnerNamesAsync returns SftpOwnerNames.
    • New: OpenSSH's expand-path, lsetstat and users-groups-by-id extensions.
  • Keys held elsewhere: SshPrivateKey.FromRsa and FromECDsa cover Windows CNG and TPM keys and other non-exportable keys. FromSigner with an SshKeySigner covers key vaults, KMS, HSMs and smart cards. A signer receives an SshSignRequest that says what it signs for (logging in, or an SSH signature with its namespace), so it can apply a policy.
  • SshExpect.ReadLineAsync: read a shell's output line by line, sharing a buffer with ExpectAsync. The migration guide maps SSH.NET's ShellStream to three options.
  • Exit signals: SshExitSignal carries the core-dumped flag and the server's error message, as RFC 4254 defines.
  • SshConfig.ResolveAsync: Match exec and host canonicalization callbacks are asynchronous. SshExecMatcher.RunShellAsync honours cancellation.
  • Samples:
    • an ASP.NET Core minimal API using the DI package, SFTP and a file provider;
    • a worker service that survives dropped connections;
    • new SFTP, IFileSystem and CNG-key commands in the console tool.
    • See samples.

Security

  • A ProxyJump hop is never offered the destination's password or keyboard-interactive answers. It logs in with keys or the agent. Before, every hop was offered them.
  • When a send fails because the network broke, IsConnected is false before the caller sees the error.
  • An ssh_config Include is bounded in total files (1,024), not only in depth.
  • An SFTP tree download fetches a name listed twice only once.
  • Exit-signal names are cleaned before they're logged.

Breaking changes since 0.6.0

The 0.6.0 names no longer exist; update to the new ones:

0.6.0 1.0
SshPortForwarder, SshSocksProxy SshLocalPortForwarder, SshDynamicPortForwarder
SshRemotePortForwarder.Port RemotePort
SshRemoteSocketForwarder.SocketPath RemoteSocketPath
KnownHostsVerifier.Ask AskHostKey
SshCertificate.CertificateType Algorithm
SshScpOptions.PreserveTimes; FileMode/DirectoryMode as int Preserve; UnixFileMode
SshScpProgress (record struct) a sealed record class
SshSession.CloseInputAsync returns ValueTask Task
SshAgent.Pageant() SshAgent.FromPageant()
SshPrivateKey.LoadFileAsync, SshAllowedSigners.LoadFileAsync LoadAsync
SshHostKeyContext.KnownHostsLookup (a tuple) a KnownHostsLookup record
SshSession.ExitSignal, SshCommandResult.ExitSignal (string?) SshExitSignal? (.Name is the old string)
SshConfigResolveOptions.ExecMatcher, HostCanonicalizer (synchronous) ValueTask-returning, with a CancellationToken; use SshConfig.ResolveAsync, since Resolve throws NotSupportedException for a config that needs them
SshExecMatcher.RunShell RunShellAsync (ExecMatcher = (c, ct) => new(SshExecMatcher.RunShellAsync(c, ct)))
SshKeySigner.SignAsync(algorithm, data, ct) SignAsync(SshSignRequest request, ct)
SshPrivateKey.FromRsa with a key under 2048 bits: FormatException ArgumentException
SshGssapiOptions (record) a sealed class
SshAlgorithmList (public) internal; set algorithms through ssh_config
SFTP: PreserveTimes, IProgress<long>, the tuple from GetOwnerNamesAsync Preserve, IProgress<SftpTransferProgress>, SftpOwnerNames
<NoWarn>SSHSHARP001</NoWarn> no longer needed: remove it

Behaviour changes:

  • A ProxyJump hop that accepts only a password no longer gets the destination's password.
  • A Match exec command runs once per resolution, including in the Match final pass.

Also

  • Interop: Apache MINA SSHD and Erlang/OTP ssh joined the server matrix. CI also runs ssh-audit in client mode.
  • Docs: the threat model is reviewed up to 1.0.0. The managed cryptography has had an internal review; an outside one is planned.

v0.6.0

Choose a tag to compare

@zcsizmadia zcsizmadia released this 16 Oct 17:18
927a3c9
dotnet add package SshSharp --version 0.6.0
dotnet add package SshSharp.IO.Abstractions --version 0.6.0
dotnet add package SshSharp.FileProviders --version 0.6.0

New

Authentication and agents

  • Kerberos: gssapi-with-mic (RFC 4462) through .NET's NegotiateAuthentication, and GSSAPIAuthentication in ssh_config. A rejected last token falls back to the next method instead of failing the connection (#168, #177).
  • Agent key management (ssh-add): add, remove and lock keys, and AddKeysToAgent (#155).
  • Agent destination constraints (ssh-add -h, restrict-destination-v00@openssh.com) (#166).
  • PuTTY's Pageant is found as an agent on Windows (#148).

Host keys

  • Revoked host keys: RevokedHostKeys reads OpenSSH KRLs and plain key lists, and they are checked before known_hosts (#162, #190).
  • KnownHostsCommand (opt-in), with ${VAR} in its arguments and the certificate passed to it, as OpenSSH does (#165, #190, #193).

Sessions and connections

  • ping@openssh.com: PingAsync and ping-based keep-alives, plus ext-info-in-auth@openssh.com (#153, #196).
  • OpenSSH session extensions: eow@openssh.com, no-more-sessions@openssh.com and INFO@openssh.com (#192).
  • ObscureKeystrokeTiming: hides keystroke timing in terminal sessions, as OpenSSH 9.5+ does (#198).
  • SshSession.SendBreakAsync: BREAK (RFC 4335), as OpenSSH's ~B (#164).
  • Client-side channel idle timeouts (ChannelTimeout, OpenSSH 9.2+) (#172).

ssh_config

  • RefuseConnection, Match localnetwork, Match sessiontype and Match command (#165, #191).

SFTP (experimental API)

  • Segmented downloads over several SFTP sessions at once (#159).

Telemetry

  • OpenTelemetry's general attributes (client span, network.*, server.port, error.type). Failed connects are counted in the duration metric, and connect telemetry covers the network connection (#157, #176).

Changed

  • Host certificates: a host certificate without principals is refused, and principals are matched as patterns, as OpenSSH does. RequiredRSASize is supported (#215).
  • RSA signatures: a signature that is not reduced modulo n (s ≥ n) is refused on every platform, as RFC 8017 requires. .NET on macOS accepted one (#222).
  • ObscureKeystrokeTiming is on (20 ms, as ssh) when connecting by an ssh_config alias, unless the config says otherwise. In SshClientOptions it stays off by default (#198).
  • Synchronous SFTP adapters: the operation timeout starts when the work does, not when it is queued (#194).

Fixed

  • Pooled buffers: a received chunk could be released twice, which corrupted pooled buffers (#181). Stream disposal could release a chunk that a read in progress was still using (#199).
  • Disposal hang: disposing a client could wait forever when a write was stalled in a stream that honours cancellation (#210).
  • SFTP: symbolic links are created in the argument order the server expects (AsyncSSH follows the draft) (#204). Seekable IO.Abstractions streams work without a known size, and a segmented download handles a replaced file (#175). Three bugs found by the coverage work (#173).
  • Agent: the agent is resolved once per host, a slow pipe listing keeps the OpenSSH agent, the pipe owner is checked, and the lock passphrase is wiped (#174, #178).
  • Diagnostics: a host key signature that fails says what it was: algorithm, length, key size and key exchange (#209). ConnectionClosed is signalled before operations fail (#186).
  • Fixes from two reviews of the changes since 0.5.0 (#205, #214).

Quality

  • Project Wycheproof: about 6,400 edge-case vectors run in the unit tests: X25519, Ed25519, ChaCha20-Poly1305, ECDH and ECDSA on P-256/384/521, RSA verification (2048 to 8192 bits) and signing, AES-GCM, ML-KEM and HMAC. They found the macOS RSA issue above (#220, #223).
  • More interop: AsyncSSH and Dropbear's sftp-server join the server matrix, OpenSSH for Windows runs again for the stdin-EOF and UpdateHostKeys tests, and a fault-injection proxy adds one-way stalls, cuts after N bytes, small slices and low bandwidth (#151, #159, #200, #213).
  • Stress runs: a stress workflow loops the whole suite on every platform with an instrumented build that checks buffer ownership and received data. It found the buffer and disposal bugs above. All its intermittent failures are root-caused (#188).
  • Coverage: SFTP is at the 95% target. Crypto review: sntrup761's JIT output was inspected for secret-dependent branches (#158, #173).
  • Docs: a Testing and validation page (#221).
  • Logo: the packages and docs have one now.
  • Package validation: 0.6.0 makes no breaking change from 0.5.0.

Full changes: v0.5.0...v0.6.0

v0.5.0

Choose a tag to compare

@zcsizmadia zcsizmadia released this 14 Oct 23:39
59761bf
dotnet add package SshSharp --version 0.5.0
dotnet add package SshSharp.IO.Abstractions --version 0.5.0   # new: SFTP as an IFileSystem
dotnet add package SshSharp.FileProviders --version 0.5.0     # new: SFTP as an IFileProvider

New

SFTP (experimental API)

  • SFTP client in the core package (namespace SshSharp.Sftp), opened with client.OpenSftpAsync():
    • sessions, file and directory operations, links, and attributes;
    • seekable remote file streams;
    • pipelined uploads and downloads with progress, kept times and resume;
    • recursive directory transfers with path safety;
    • OpenSSH's extensions: statvfs, server-side copy (copy-data), fsync, posix-rename, hard links and limits.
    • The API is marked [Experimental("SSHSHARP001")]; opt in with <NoWarn>$(NoWarn);SSHSHARP001</NoWarn>. (#7, #129–#132)
  • New package SshSharp.IO.Abstractions: SftpFileSystem implements System.IO.Abstractions' IFileSystem over SFTP, so code written against IFileSystem works on the server unchanged (#134).
  • New package SshSharp.FileProviders: SftpFileProvider serves a remote directory as an IFileProvider, for ASP.NET Core static files, Razor and configuration (#133).

Connections and forwarding

  • HTTP CONNECT and SOCKS 4/4a/5 proxies (SshProxy), and client.CreateHttpConnectCallback(), which runs HttpClient through the SSH connection (#94).
  • Bind to a local address or interface: SshClientOptions.LocalEndPoint, and BindAddress / BindInterface in ssh_config (#127).
  • Remote dynamic forwarding (ssh -R port): a SOCKS proxy on the client side for the server's applications, with a destination filter (#114).
  • Remote Unix-socket forwarding (ssh -R /socket:target), for example gpg-agent forwarding (#137).
  • Destination-constrained agent keys (ssh-add -h): session-bind@openssh.com and publickey-hostbound (#91).
  • SshExpect: expect-style scripting of interactive shells (#115).
  • Host key scanning without logging in, as ssh-keyscan does: SshClient.ScanHostKeysAsync (#116).

Keys, signatures and algorithms

  • Key generation: SshPrivateKey.Generate and SaveAsync write OpenSSH private keys, encrypted or not, as ssh-keygen does (#117).
  • SSH signatures (ssh-keygen -Y sign/verify/find-principals, SSHSIG): SshSignature signs and verifies files and git commits with any key, including agent keys and certificates. SshAllowedSigners reads allowed-signers files (#139).
  • Post-quantum key exchange everywhere: sntrup761x25519-sha512 (and its @openssh.com name) with a managed sntrup761, validated against the official known-answer tests and checked for constant time. OpenSSH 8.5–9.8 servers, and every .NET 8 client, now get post-quantum key exchange. It is offered after the ML-KEM hybrids and before curve25519 (#97).

Changed

  • Default key exchange order: sntrup761 now comes before curve25519, as OpenSSH 10 orders them.
  • New limit: forwarded connections the server opens are capped by SshClientOptions.MaxForwardedConnections (1,000 by default).
  • Proxy timeout: a proxy handshake times out after 30 seconds.
  • Host key scan timeout: each connection times out after 5 seconds by default (SshHostKeyScanOptions.Timeout).
  • .NET 8 dependencies: the packages now depend on the 8.0 versions of Microsoft.Extensions.Logging.Abstractions, System.IO.Pipelines and Microsoft.Extensions.FileProviders.Abstractions on .NET 8.

Fixed

  • A forwarding from ssh_config that could not start was logged with its full specification, which could include host names and socket paths. It is now logged by keyword and reason only.

Quality

  • Pre-release review: every change since 0.4.0 was reviewed: the SFTP client, both adapters, connections and forwarding, keys and crypto, and the docs. The findings were fixed before the release (#142–#146). They include:
    • a remote-dynamic-forwarding filter bypass through alternative address spellings;
    • an SFTP session stall when the server logs to stderr;
    • copying a file onto itself;
    • leftover private key bytes when writing key files.
  • Interop: SFTP is tested in CI against OpenSSH (Linux, Windows, and older and newer releases in containers), atmoz/sftp, and a Go pkg/sftp server. sntrup761 is tested against OpenSSH.
  • Fuzzing: every SFTP reply parser is fuzzed. Mutation testing: sntrup761 is now in scope.
  • Package validation: 0.5.0 makes no breaking change from 0.4.0.

Full changes: v0.4.0...v0.5.0

v0.4.0

Choose a tag to compare

@zcsizmadia zcsizmadia released this 14 Oct 16:30
8dae94f
dotnet add package SshSharp --version 0.4.0

Documentation is now published at zcsizmadia.github.io/SshSharp: the guides and the full API reference, updated with every release.

New

  • Subsystems (ssh -s): SshClient.StartSubsystemAsync("netconf") starts a protocol the server runs by name. The session's streams carry it byte for byte. This is the foundation for the upcoming SshSharp.Sftp package (#90).
  • Negotiated algorithms: client.Algorithms shows the key exchange, host key algorithm, ciphers, MACs and strict key exchange (#96).
  • Post-quantum visibility: client.Algorithms.IsPostQuantum reports whether the key exchange is post-quantum. A connection that is not post-quantum is logged at Information level, the counterpart of OpenSSH 10.1's WarnWeakCrypto, and traces and the connect metric carry ssh.kex.post_quantum (#96).
  • FIPS-compatible post-quantum key exchange: mlkem768nistp256-sha256 and mlkem1024nistp384-sha384 (RFC 10042), on .NET 10 with platform ML-KEM. FipsMode now offers post-quantum key exchange first (#95).
  • Agent standards (RFC 9987):
    • agent forwarding uses agent-req / agent-connect with servers that announce agent-forward in their extension info, and auth-agent-req@openssh.com otherwise;
    • SshAgent.QueryExtensionsAsync lists an agent's extensions;
    • FIDO security-key certificates (sk-*-cert-v01@openssh.com) held by the agent can now be used to authenticate (#92).

Fixed

  • SshCertificate.BaseAlgorithm and CertificateKeyType mapped security-key certificate types to the wrong names (sk-ssh-ed25519 instead of sk-ssh-ed25519@openssh.com), so agent signatures for those certificates would have been refused.

Quality

  • Interop: CI's sshd containers now run the internal-sftp subsystem, so the subsystem API is tested against real OpenSSH.
  • Mutation testing: weekly runs are incremental, and the large files run as their own jobs (#107).
  • Package validation: each release is validated against the previous one on nuget.org. 0.4.0 makes no breaking change from 0.3.0.

Full changes: v0.3.0...v0.4.0

v0.3.0

Choose a tag to compare

@zcsizmadia zcsizmadia released this 14 Oct 13:57
0f9cc24

The first release of SshSharp: a managed, async-first SSH client library for .NET 8 and .NET 10. It works with OpenSSH ssh_config and known_hosts.

dotnet add package SshSharp

Highlights

  • Connect the way ssh does. SshClient.ConnectAsync("alias") resolves ~/.ssh/config (Host, Match, Include, tokens) and verifies the host against known_hosts. That covers hashed hosts, @cert-authority, @revoked, StrictHostKeyChecking and UpdateHostKeys.
  • Authentication.
    • password
    • public keys: OpenSSH, PKCS#1/#8, SEC1, PuTTY .ppk
    • OpenSSH user certificates
    • ssh-agent, including the Windows OpenSSH agent, and agent forwarding
    • keyboard-interactive, and multi-factor chains
  • Connections.
    • ProxyJump chains and ProxyCommand
    • connect over any Stream
    • keep-alive
    • TCP and Unix-socket tunnels
    • local, remote and dynamic (SOCKS) port forwarding, also started from ssh_config
  • Sessions. Commands, streamed stdin, stdout and stderr, shells with a pseudo-terminal, signals and window resizing.
  • SCP for files, streams and directories. Names sent by the server are validated, so a hostile server can't write outside the target.
  • Modern algorithms only. mlkem768x25519-sha256 post-quantum key exchange (.NET 10), curve25519, ECDH, AES-GCM, ChaCha20-Poly1305, Ed25519/ECDSA/RSA-SHA2. Strict key exchange (Terrapin) is always on, and there is a FIPS mode.
  • Built for production.
    • Async everywhere, with CancellationToken and IAsyncDisposable.
    • Zero-copy packet handling: about 90 bytes of garbage per 32 KiB packet.
    • Trimming and Native AOT compatible.
    • ILogger logging, ActivitySource traces and Meter metrics, with no secrets recorded.

Quality

  • Tested on Windows, Linux and macOS (x64 and Arm64), on net8.0 and net10.0.
  • Interop tests against real servers:
    • OpenSSH 7.5–10.0
    • Dropbear
    • OpenSSH for Windows
  • Coverage-guided fuzzing every night, plus mutation fuzzing of every parser in each test run.
  • Weekly Stryker.NET mutation testing.
  • CodeQL code scanning.
  • A threat model, and an internal constant-time review of the managed X25519/Ed25519 with a statistical timing test.

Compared with SSH.NET

On the same OpenSSH server and algorithms:

  • connect: 1.11× SSH.NET's time
  • parallel commands: 0.65×
  • SCP upload: 0.46×
  • streaming throughput on one client core: 434 MiB/s vs 265 MiB/s

See docs/benchmarks.md. Moving from SSH.NET? See the migration guide.

Before 1.0

  • The API may still change; open questions are in #88.
  • The managed cryptography has had an internal review and is waiting for an outside one.
  • SFTP is next (#7), after a public subsystem API (#90).
  • Known limitations with OpenSSH for Windows 9.5 servers: keep-alives (documented), and #84 and #86.

Documentation: README, guides, security policy.