Skip to content

Use claude to devise more testing#158

Merged
kruton merged 8 commits into
connectbot:mainfrom
kruton:more-testing
May 22, 2026
Merged

Use claude to devise more testing#158
kruton merged 8 commits into
connectbot:mainfrom
kruton:more-testing

Conversation

@kruton
Copy link
Copy Markdown
Member

@kruton kruton commented May 22, 2026

Actually came up with some nice bugs.

kruton added 8 commits May 20, 2026 22:49
A malicious server could send SSH_MSG_KEX_DH_GEX_GROUP with a weak or
degenerate prime (e.g., 16-bit p, g=1, or an even p) to trivially break
the key exchange. Enforce that p falls within the client-requested
[min, max] bit range, that p is odd, and that g satisfies 1 < g < p-1.
…ification

A server could send a signature blob claiming algorithm "ssh-rsa" (SHA-1)
even when the client negotiated "rsa-sha2-256" or "rsa-sha2-512", causing
the client to verify with the weaker SHA-1 hash. Per RFC 8332 §3.2, the
algorithm identifier in the signature blob must match what was negotiated.

SignatureVerifier.verify() now requires the expected algorithm and rejects
mismatches. Agent session binding uses a separate verifyWithKeyType() path
that validates the sig algorithm is compatible with the host key type,
guarding against cross-type forgery in that context.
ZlibCompressor.uncompress() had no bound on output size. A malicious server
could send a small compressed packet (~1 KB) that expands to hundreds of MB,
exhausting client memory. This is exploitable post-KEX on any connection that
negotiates zlib or zlib@openssh.com compression.

Cap decompressed output at MAX_UNCOMPRESSED_SIZE (512 KB) per packet and
throw SshException if exceeded, causing the connection to be torn down.
…32 overflow

A malicious server could send a window adjust that overflows the uint32
maximum (0xFFFFFFFF per RFC 4254 §5.2), causing remoteWindowSize to wrap
negative and deadlock the send loop, or allowing unbounded window growth
that breaks flow control. Reject adjustments ≤ 0 or that would push the
window past the protocol maximum.
A malicious server could send SSH_MSG_CHANNEL_DATA beyond the advertised
local window (RFC 4254 §5.2), causing unbounded memory growth in the
Channel.UNLIMITED queues (DoS) and integer overflow in the window-adjust
computation when localWindowSize went negative.

Extract LocalChannelWindow to centralize both local consume-and-refill
and remote adjust-with-overflow-check logic, eliminating the duplication
across SessionChannel, ForwardingChannel, and AgentChannel. MAX_WINDOW_SIZE
now lives in LocalChannelWindow.
A server could send a degenerate EC point that causes the ECDH agreement
to compute a zero shared secret, yielding completely predictable session
keys. This check mirrors the existing guard in Curve25519KeyExchange and
MlKemHybridKeyExchange.
Replaced ByteArray.contentEquals() with MessageDigest.isEqual() in both
the encrypt-and-MAC and ETM packet receive paths. The non-constant-time
comparison could allow a network attacker to measure differences in
comparison time to forge valid MAC tags byte-by-byte.
Copilot AI review requested due to automatic review settings May 22, 2026 02:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens SSH transport/client security and robustness by adding new tests and tightening implementations around integrity checks, compression limits, signature verification, and channel flow-control window handling.

Changes:

  • Add negative-path tests for MAC tampering (encrypt-and-MAC and ETM), DH GEX parameter validation, ECDH all-zero secrets, channel window bounds, and zlib decompression limits.
  • Harden implementations: constant-time MAC comparison, zlib decompression output cap, stricter DH group validation, and stricter signature verification (including negotiated algorithm enforcement).
  • Refactor channel window tracking into a shared LocalChannelWindow helper and apply it across session/forwarding/agent channels.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
sshlib/src/test/kotlin/org/connectbot/sshlib/transport/PacketIOTest.kt Adds a regression test ensuring tampered MACs are rejected in encrypt-and-MAC mode.
sshlib/src/test/kotlin/org/connectbot/sshlib/transport/PacketIOEtmTest.kt Adds a regression test ensuring tampered MACs are rejected in ETM mode.
sshlib/src/test/kotlin/org/connectbot/sshlib/crypto/ZlibCompressorTest.kt Adds tests for decompression-bomb rejection and within-limit decompression acceptance.
sshlib/src/test/kotlin/org/connectbot/sshlib/crypto/SignatureVerifierTest.kt New tests asserting negotiated-signature-algorithm enforcement for RSA SHA-2 vs SHA-1.
sshlib/src/test/kotlin/org/connectbot/sshlib/crypto/EcdhKeyExchangeTest.kt Adds test rejecting an all-zero ECDH shared secret.
sshlib/src/test/kotlin/org/connectbot/sshlib/crypto/DiffieHellmanGroupExchangeTest.kt Adds tests for DH GEX group parameter validation (bit sizes, generator bounds, parity).
sshlib/src/test/kotlin/org/connectbot/sshlib/client/SessionChannelTest.kt Adds tests for window-adjust validation and local-window enforcement on incoming data.
sshlib/src/test/kotlin/org/connectbot/sshlib/client/LocalChannelWindowTest.kt New unit tests for LocalChannelWindow window consumption and adjustment rules.
sshlib/src/main/kotlin/org/connectbot/sshlib/transport/PacketIO.kt Switches MAC comparisons to constant-time MessageDigest.isEqual.
sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/ZlibCompressor.kt Adds an uncompressed-output cap to mitigate decompression-bomb DoS.
sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/SignatureVerifier.kt Enforces negotiated signature algorithm during KEX; adds key-type compatibility verifier for self-described signatures.
sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/EcdhKeyExchange.kt Rejects all-zero shared secret via a new helper used by the main compute path.
sshlib/src/main/kotlin/org/connectbot/sshlib/crypto/DiffieHellmanGroupExchange.kt Adds basic DH group sanity checks (min/max size, odd p, generator range).
sshlib/src/main/kotlin/org/connectbot/sshlib/client/SshConnection.kt Plumbs negotiated host key algorithm into SignatureVerifier.verify.
sshlib/src/main/kotlin/org/connectbot/sshlib/client/SessionChannel.kt Replaces ad-hoc window tracking with LocalChannelWindow.
sshlib/src/main/kotlin/org/connectbot/sshlib/client/LocalChannelWindow.kt New helper to centralize channel window accounting and validation.
sshlib/src/main/kotlin/org/connectbot/sshlib/client/ForwardingChannel.kt Replaces ad-hoc window tracking with LocalChannelWindow.
sshlib/src/main/kotlin/org/connectbot/sshlib/client/AgentProtocolHandler.kt Uses verifyWithKeyType for session-bind signature verification.
sshlib/src/main/kotlin/org/connectbot/sshlib/client/AgentChannel.kt Adds local window enforcement and migrates remote window tracking to LocalChannelWindow.
Comments suppressed due to low confidence (1)

sshlib/src/main/kotlin/org/connectbot/sshlib/client/ForwardingChannel.kt:92

  • Same issue as SessionChannel: window.remoteRemaining.toInt() can overflow when the remote window exceeds Int.MAX_VALUE (valid for SSH uint32 windows). Calculate chunkSize using Long min bounds (including maxPacketSize) and then convert safely to Int.
            while (window.remoteRemaining <= 0) {
                windowAvailable.receive()
            }
            val chunkSize = minOf(
                data.size - offset,
                window.remoteRemaining.toInt(),
                maxPacketSize,
            )

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread sshlib/src/main/kotlin/org/connectbot/sshlib/client/SessionChannel.kt Outdated
Comment thread sshlib/src/main/kotlin/org/connectbot/sshlib/client/ForwardingChannel.kt Outdated
Comment thread sshlib/src/main/kotlin/org/connectbot/sshlib/client/AgentChannel.kt Outdated
@kruton kruton enabled auto-merge (rebase) May 22, 2026 02:45
@kruton kruton merged commit 31b2097 into connectbot:main May 22, 2026
6 checks passed
@kruton kruton deleted the more-testing branch May 22, 2026 03:02
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.

2 participants