internal: send a disconnect when key exchange fails - #1171
Conversation
There was a problem hiding this comment.
Pull request overview
This PR ensures wolfSSH sends an explicit SSH_MSG_DISCONNECT with reason WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED when key exchange fails during KEXDH_INIT / KEXDH_REPLY, instead of silently dropping the transport. It also extends the duplex regression harness to mutate KEX packets and assert the disconnect reason is transmitted on the wire.
Changes:
- Add disconnect-on-fatal-KEX-failure guards to
DoKexDhInit()(server side) andDoKexDhReply()(client side). - Extend the regression duplex mutator to truncate
f(inKEXDH_REPLY) and truncate/emptye(inKEXDH_INIT), plus record outbound disconnect reason codes. - Add new regression cases asserting the disconnect reason is observed, and assert successful handshakes do not produce disconnects.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/internal.c |
Sends SSH_MSG_DISCONNECT with KEY_EXCHANGE_FAILED when KEX fails mid-exchange in both client/server KEX handlers. |
tests/regress.c |
Adds new KEX packet mutation modes and disconnect sniffing/assertions for KEX failure paths. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
09ad881 to
be9d8d2
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1171
Scan targets checked: wolfssh-bugs, wolfssh-src
Findings: 4
4 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
be9d8d2 to
226e6ca
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1171
Scan targets checked: wolfssh-bugs, wolfssh-src
Findings: 4
4 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
- Send SSH_MSG_DISCONNECT with KEY_EXCHANGE_FAILED from DoKexDhInit(), DoKexDhReply() and DoKexDhGexGroup() when the key exchange fails. - Add KEXDH_REPLY f-truncation and KEXDH_INIT e-truncation and e-empty mutator modes to the regression harness. - Record outbound disconnects on the duplex endpoints and assert the reason code in the truncated-key and host-key rejection tests. - Scan only plaintext records for the disconnect, validate minimum packet framing, and bound the reason code against the payload. - Assert no disconnect is seen on a successful handshake. Issue: F-8838
226e6ca to
33118c0
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1171
Scan targets checked: none
Failed targets: wolfssh-bugs, wolfssh-src
Problem
Every key exchange failure path aborted the handshake without transmitting
SSH_MSG_DISCONNECT. The peer saw only a dropped transport with no reasoncode.
DoKexInit()already answers a KEXINIT negotiation failure withWOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED, so the convention existed but wasnot applied once key agreement began.
Fix (
src/internal.c)Two guards at the KEX message boundaries, mirroring the
DoKexInit()pattern:DoKexDhReply()covers the client: all ofKeyAgree_client().DoKexDhInit()covers the server: theeSzrange check and all ofSendKexDhReply().Both handlers also serve the GEX message IDs, so two insertion points cover
every KEX algorithm. Both codes are terminal, so the guard cannot misfire on
a
WS_WANT_WRITEfromSendNewKeys(). The(void)cast is required: takingthe return would mask the KEX error with
SendDisconnect()'s own status.Closes
f-8838.Tests (
tests/regress.c)New mutator modes on the duplex harness — truncated
finKEXDH_REPLY,truncated and zero-length
einKEXDH_INIT— plus disconnect recording onboth endpoints. Five cases now assert the reason code reaches the wire:
fWS_CRYPTO_FAILEDeWS_CRYPTO_FAILEDeWS_PUBKEY_REJECTED_EWS_PUBKEY_REJECTED_EWS_PUBKEY_REJECTED_EA successful handshake asserts no disconnect is seen, so the tests cannot pass
on a detector that always fires. DH and ML-KEM are covered by construction, not
by test: a truncated DH mpint is still a valid group element.
Verification
regress,unit,kex,api).WS_CRYPTO_FAILED,or forcing the detector to always fire each breaks the expected test.
gcc-13 -Werroracross all 6 preflight configs.--enable-sshdonly.Not in this PR
Host key signature verification failures (
WS_RSA_E,WS_ECC_E,WS_ED25519_E,WS_MLDSA_E) still abort without a disconnect. That is serverauthentication rather than key exchange, and OpenSSH is the precedent: its
client calls
fatal("Host key verification failed.")and sends nothing. Forreference, OpenSSH defines
SSH2_DISCONNECT_KEY_EXCHANGE_FAILEDandHOST_KEY_NOT_VERIFIABLEbut sends neither anywhere — all its protocol errorsgo out as
PROTOCOL_ERROR. wolfSSH's more specific reason code follows its ownDoKexInit()precedent.