Skip to content

feat(ssh): name key exchange, cipher and MAC failures on the Mac - #3779

Merged
datlechin merged 2 commits into
mainfrom
fix/3767-mac-ssh-negotiation-error
Oct 22, 2026
Merged

datlechin merged 2 commits into
mainfrom
fix/3767-mac-ssh-negotiation-error

Conversation

@datlechin

@datlechin datlechin commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Mac: a handshake that fails with LIBSSH2_ERROR_KEX_FAILURE throws SSHTunnelError.negotiationFailed, naming the stage (key exchange or host key, cipher, MAC), the server software and the methods TablePro supports, with a recovery suggestion. It used to be tunnelCreationFailed("SSH handshake failed: Unable to exchange encryption keys").
  • The sentences move from the iOS SSHTunnelError to SSHNegotiationFailure.explanation in TableProSSHTransport. The libssh2 half moves to TablePro/Core/SSH/LibSSH2Handshake.swift, shared with iOS by file reference; the iOS LibSSH2HandshakeFailure goes.
  • libssh2 also returns KEX_FAILURE when the server closes the connection or sends DISCONNECT before its KEXINIT. The handshake now watches libssh2's socket I/O and DISCONNECT callback and reports that as a closed connection, with the server's reason. Both apps had this.
  • A jump hop that fails negotiation keeps negotiationFailed and names the hop, on both apps. iOS used to fold it into jumpHostFailed, whose advice was to check the hop's address and key.
  • Mac: any other failure on a jump host is jumpHostFailed(host:reason:), as on iOS. A channel a hop could not open keeps libssh2's reason. A cancelled sign-in stays a cancellation.
  • Mac: a key file that does not exist no longer brings up the passphrase prompt.
  • Probe: the macOS harness builds again (stand-in for ConnectionPreflightReporting, missing since feat: finish the follow-ups from the iOS feature PRs (#3680-#3690) #3697). New servers kex10 (sntrup761x25519-sha512 only) and cipher10 (3des-cbc only), which libs-5's libssh2 lacks. New cells: cipher stage, key exchange failure on the first hop, on the server behind a hop and on the second of two hops, a missing key file. Both apps expect the named error.
  • Changed test: SSHTunnelFactoryTests.namedHopFailureNamesTheHop drops its negotiation argument; hopNegotiationFailureKeepsItsCase covers it.

Needs the maintainer

  • libs-3 and later build libssh2 master, which turns off SHA-1 MACs and key exchange, ssh-rsa, ssh-dss and 3des-cbc by default; 0.83.0-beta.1 (libs-2) offers them. A server limited to those now fails with this error. Turning them back on is a libssh2 rebuild and a libs publish.

Test plan

  • LibSSH2NegotiationTests (new): a scripted server on a socket pair drives createSession to each stage, a hang-up and a DISCONNECT after the banner, and a non-SSH banner.
  • SSHTunnelErrorTests, SSHNegotiationFailureTests, iOS SSHTunnelFactoryTests and SSHTunnelErrorClassifierTests.
  • apple/scripts/probes/check-ssh-tunnel-matrix.sh with libs-5 against the pinned OpenSSH 10.3 and 9.9 containers: 186 of 186 cells (104 iOS, 82 macOS).

Docs: TableProApp/docs#119 (draft, lands after this).

Fixes #3767

@datlechin
datlechin enabled auto-merge (squash) October 22, 2026 08:11
@datlechin
datlechin merged commit 911a8ca into main Oct 22, 2026
9 checks passed
@datlechin
datlechin deleted the fix/3767-mac-ssh-negotiation-error branch October 22, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Name SSH key exchange, cipher and MAC failures on the Mac as the iOS app does

1 participant