Repository navigation
Releases: zcsizmadia/SshSharp
Releases · zcsizmadia/SshSharp
Release list
v1.0.0
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.ISshClientFactoryopens new connections.ISshConnectionProviderkeeps 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;
AcceptNewHostKeysopts in. - See Dependency injection.
- SFTP is no longer experimental. The
SSHSHARP001opt-in is gone from SFTP,SshSharp.FileProvidersandSshSharp.IO.Abstractions. Its API is final:Preservekeeps times and permission bits, assftp -pdoes, but never setuid, setgid or sticky.SftpTransferProgressreports per-file progress (path, bytes, size).GetOwnerNamesAsyncreturnsSftpOwnerNames.- New: OpenSSH's
expand-path,lsetstatandusers-groups-by-idextensions.
- Keys held elsewhere:
SshPrivateKey.FromRsaandFromECDsacover Windows CNG and TPM keys and other non-exportable keys.FromSignerwith anSshKeySignercovers key vaults, KMS, HSMs and smart cards. A signer receives anSshSignRequestthat 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 withExpectAsync. The migration guide maps SSH.NET'sShellStreamto three options.- Exit signals:
SshExitSignalcarries the core-dumped flag and the server's error message, as RFC 4254 defines. SshConfig.ResolveAsync:Match execand host canonicalization callbacks are asynchronous.SshExecMatcher.RunShellAsynchonours 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,
IFileSystemand 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,
IsConnectedis false before the caller sees the error. - An
ssh_configIncludeis 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 execcommand runs once per resolution, including in theMatch finalpass.
Also
- Interop: Apache MINA SSHD and Erlang/OTP
sshjoined the server matrix. CI also runsssh-auditin 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
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'sNegotiateAuthentication, andGSSAPIAuthenticationinssh_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, andAddKeysToAgent(#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:
RevokedHostKeysreads OpenSSH KRLs and plain key lists, and they are checked beforeknown_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:PingAsyncand ping-based keep-alives, plusext-info-in-auth@openssh.com(#153, #196).- OpenSSH session extensions:
eow@openssh.com,no-more-sessions@openssh.comandINFO@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
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.
RequiredRSASizeis 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).
ObscureKeystrokeTimingis on (20 ms, asssh) when connecting by anssh_configalias, unless the config says otherwise. InSshClientOptionsit 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.Abstractionsstreams 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).
ConnectionClosedis 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-serverjoin 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
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 withclient.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:SftpFileSystemimplements System.IO.Abstractions'IFileSystemover SFTP, so code written againstIFileSystemworks on the server unchanged (#134). - New package
SshSharp.FileProviders:SftpFileProviderserves a remote directory as anIFileProvider, for ASP.NET Core static files, Razor and configuration (#133).
Connections and forwarding
- HTTP CONNECT and SOCKS 4/4a/5 proxies (
SshProxy), andclient.CreateHttpConnectCallback(), which runsHttpClientthrough the SSH connection (#94). - Bind to a local address or interface:
SshClientOptions.LocalEndPoint, andBindAddress/BindInterfaceinssh_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.comandpublickey-hostbound(#91). SshExpect: expect-style scripting of interactive shells (#115).- Host key scanning without logging in, as
ssh-keyscandoes:SshClient.ScanHostKeysAsync(#116).
Keys, signatures and algorithms
- Key generation:
SshPrivateKey.GenerateandSaveAsyncwrite OpenSSH private keys, encrypted or not, asssh-keygendoes (#117). - SSH signatures (
ssh-keygen -Y sign/verify/find-principals, SSHSIG):SshSignaturesigns and verifies files and git commits with any key, including agent keys and certificates.SshAllowedSignersreads allowed-signers files (#139). - Post-quantum key exchange everywhere:
sntrup761x25519-sha512(and its@openssh.comname) 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.PipelinesandMicrosoft.Extensions.FileProviders.Abstractionson .NET 8.
Fixed
- A forwarding from
ssh_configthat 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 Gopkg/sftpserver. 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
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 upcomingSshSharp.Sftppackage (#90). - Negotiated algorithms:
client.Algorithmsshows the key exchange, host key algorithm, ciphers, MACs and strict key exchange (#96). - Post-quantum visibility:
client.Algorithms.IsPostQuantumreports whether the key exchange is post-quantum. A connection that is not post-quantum is logged atInformationlevel, the counterpart of OpenSSH 10.1'sWarnWeakCrypto, and traces and the connect metric carryssh.kex.post_quantum(#96). - FIPS-compatible post-quantum key exchange:
mlkem768nistp256-sha256andmlkem1024nistp384-sha384(RFC 10042), on .NET 10 with platform ML-KEM.FipsModenow offers post-quantum key exchange first (#95). - Agent standards (RFC 9987):
- agent forwarding uses
agent-req/agent-connectwith servers that announceagent-forwardin their extension info, andauth-agent-req@openssh.comotherwise; SshAgent.QueryExtensionsAsynclists an agent's extensions;- FIDO security-key certificates (
sk-*-cert-v01@openssh.com) held by the agent can now be used to authenticate (#92).
- agent forwarding uses
Fixed
SshCertificate.BaseAlgorithmandCertificateKeyTypemapped security-key certificate types to the wrong names (sk-ssh-ed25519instead ofsk-ssh-ed25519@openssh.com), so agent signatures for those certificates would have been refused.
Quality
- Interop: CI's sshd containers now run the
internal-sftpsubsystem, 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
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
sshdoes.SshClient.ConnectAsync("alias")resolves~/.ssh/config(Host,Match,Include, tokens) and verifies the host againstknown_hosts. That covers hashed hosts,@cert-authority,@revoked,StrictHostKeyCheckingandUpdateHostKeys. - 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-sha256post-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
CancellationTokenandIAsyncDisposable. - Zero-copy packet handling: about 90 bytes of garbage per 32 KiB packet.
- Trimming and Native AOT compatible.
ILoggerlogging,ActivitySourcetraces andMetermetrics, with no secrets recorded.
- Async everywhere, with
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.