Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,40 @@ Notes:
`-DWC_SIG_MIN_HASH_TYPE=WC_HASH_TYPE_SHA`. This re-enables a deprecated hash;
prefer ECDSA unless RSA is mandated.

STRICT KEY EXCHANGE
===================

wolfSSH implements strict key exchange, the mitigation for the Terrapin attack
(CVE-2023-48795) described in `draft-miller-sshm-strict-kex`. It is negotiated
in the initial KEXINIT and enabled whenever the peer asks for it too, so no
configuration is needed for the usual case.

With strict KEX in force, wolfSSH accepts nothing but the key exchange itself
and SSH_MSG_DISCONNECT until the peer's SSH_MSG_NEWKEYS arrives, and it zeroes
the packet sequence numbers at every SSH_MSG_NEWKEYS. Together those stop an
attacker splicing packets into the unauthenticated initial exchange to shift
the sequence numbers. A message that arrives out of turn ends the connection
with SSH_MSG_DISCONNECT rather than being ignored.

Note that wolfSSH offers neither `chacha20-poly1305@openssh.com` nor the
`*-etm@openssh.com` MACs, the modes whose nonce comes from the sequence
number. The full silent-truncation form of Terrapin needs one of those, so it
was never reachable here; strict KEX closes the sequence-number shift the
attack is built on.

A caller that has to interoperate with a peer that mishandles the marker can
turn it off, per context or per session:

wolfSSH_CTX_SetStrictKex(ctx, 0); /* seeds every session from ctx */
wolfSSH_SetStrictKex(ssh, 0); /* this session only */

Only the initial KEXINIT carries the marker, and only that exchange decides
whether the mitigation is on, so the session call has to be made before the
connection starts. A rekey neither re-advertises the marker nor revisits the
decision, and a change made to a session already keying is ignored for the
life of that session. The context call is the one that affects anything
later: it seeds the sessions made after it.

WOLFSSH APPLICATIONS
====================

Expand Down
4 changes: 4 additions & 0 deletions examples/echoserver/echoserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1915,6 +1915,10 @@ static int sftp_worker(thread_ctx_t* threadCtx)
continue;
}
#endif
if (ret == WS_WANT_READ) {
/* Part of a packet arrived; wait for the rest. */
continue;
}
if (ret == WS_WANT_WRITE) {
/* recall wolfSSH_worker here because is likely our custom
* highwater callback that returned up a WS_WANT_WRITE */
Expand Down
13 changes: 9 additions & 4 deletions examples/sftpclient/sftpclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -1570,13 +1570,18 @@ static int doAutopilot(int cmd, char* local, char* remote)
ret == WS_FATAL_ERROR);

if (ret != WS_SUCCESS) {
/* ret is a generic failure code; the cause is in the session */
err = wolfSSH_get_error(ssh);

if (cmd == AUTOPILOT_PUT) {
fprintf(stderr, "Unable to copy local file %s to remote file %s\n",
local, fullpath);
fprintf(stderr, "Unable to copy local file %s to remote file %s"
": ret %d, error %d, %s\n",
local, fullpath, ret, err, wolfSSH_ErrorToName(err));
}
else if (cmd == AUTOPILOT_GET) {
fprintf(stderr, "Unable to copy remote file %s to local file %s\n",
fullpath, local);
fprintf(stderr, "Unable to copy remote file %s to local file %s"
": ret %d, error %d, %s\n",
fullpath, local, ret, err, wolfSSH_ErrorToName(err));
}
}

Expand Down
4 changes: 4 additions & 0 deletions scripts/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ endif
# app wasn't built.
dist_noinst_SCRIPTS+= scripts/sshclient.test

# Not gated on anything: the script skips itself when OpenSSH is missing
# or predates strict KEX.
dist_noinst_SCRIPTS+= scripts/openssh-interop.test

dist_noinst_SCRIPTS+= scripts/fwd.test
dist_noinst_SCRIPTS+= scripts/fwd-bulk.test
endif
Expand Down
Loading
Loading