Skip to content

ssh: add a rekey-state accessor - #1260

Open
yosuke-wolfssl wants to merge 2 commits into
wolfSSL:masterfrom
yosuke-wolfssl:feat/rekey-accessor
Open

yosuke-wolfssl wants to merge 2 commits into
wolfSSL:masterfrom
yosuke-wolfssl:feat/rekey-accessor

Conversation

@yosuke-wolfssl

@yosuke-wolfssl yosuke-wolfssl commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Problem

Consumers have no way to ask whether a key exchange is running. Across apps/,
examples/, ide/ and zephyr/, 24 of 118 wolfSSH_get_error() / ssh->error
reads ask exactly that; 21 of them are one repeated pump in
examples/sftpclient/sftpclient.c, which is also the only consumer code in the
tree that reads the WOLFSSH struct directly. wolfSSH_worker() withholds
WS_REKEYING when its flush failed, so that pump can exit with the rekey
unfinished.

SendKexInit() also set WOLFSSH_SELF_IS_KEYING before it built the packet, so
a send that never reached the transport still left the session believing a rekey
was in flight.

Fix (src/ssh.c, src/internal.c)

  • wolfSSH_RekeyPending(const WOLFSSH*) returns nonzero while a key exchange
    is in flight, and 0 otherwise — including for a NULL session, so it is safe as
    a loop condition.
  • A pure ssh->isKeying read, so it cannot disagree with
    wolfSSH_worker()'s WS_REKEYING return.
  • SendKexInit() captures ssh->txFlushCount before the send and sets the
    flag when SendPacketDelivered() reports the packet away. The send's return
    cannot serve as that signal: wolfSSH_SendPacket() forwards
    HighwaterCheck()'s value, so an application highwater callback can fail with
    the KEXINIT already on the wire. PurgePacket() runs on the same decision.

Tests

File Coverage
tests/regress.c each keying bit alone, both together, and a NULL session
tests/unit.c a KEX init whose send fails outright, one that short-writes, and one the transport takes whole behind a failing highwater callback

Verification

  • Clean under six gcc-13 -Werror configs.
  • unit 167 (baseline 166), plus regress, testsuite, kex, api and
    auth; the scp, sshclient, get-put and sftp scripts pass serially.
  • Negative controls: inverting the accessor, and reading the send's return in
    place of SendPacketDelivered(), each fail only the test written for them.

Not in this PR

  • No consumer conversion. The sftpclient pumps and the Renesas loop should
    change against a settled contract, not alongside it.
  • tests/testsuite.c loses its wolfSSH_OutputPending() probe. It was the
    only such export check among 331 public functions; applying the pattern
    consistently would mean adding it to all of them, which is not worth it.

- wolfSSH_RekeyPending() reports whether a key exchange is in flight,
  returning 0 for a NULL session so a caller may test it directly in
  a loop condition.
- the wolfSSH_worker() block in ssh.h names it as the way to ask,
  alongside wolfSSH_OutputPending().
- tests/regress.c covers each keying bit alone, both together, and a
  NULL session for both predicates.
- tests/testsuite.c drops its wolfSSH_OutputPending() call; a public
  function without a WOLFSSH_API prototype fails -Wmissing-prototypes
  in src/ssh.c, so the call proved nothing the build did not.
@yosuke-wolfssl yosuke-wolfssl self-assigned this Sep 17, 2026
Copilot AI lite review requested due to automatic review settings September 17, 2026 06:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The API addition and keying-flag gating change are consistent with existing worker/error semantics, and the PR includes targeted regression/unit coverage for the new behavior and the previously problematic edge case.

Pull request overview

This PR adds a small public API to let consumers query whether a key exchange (rekey) is currently in flight, and fixes a keying-state edge case where SendKexInit() could leave the session stuck “keying” even when the KEXINIT send failed outright.

Changes:

  • Add wolfSSH_RekeyPending(const WOLFSSH*) accessor (NULL-safe, pure ssh->isKeying read) and document its intended use alongside wolfSSH_worker() / wolfSSH_get_error().
  • Adjust SendKexInit() to set WOLFSSH_SELF_IS_KEYING only once the KEXINIT packet is successfully sent or queued, avoiding a stale keying flag on outright send failure.
  • Add regression/unit tests for the accessor and for the “KEX init send fails / short-writes” keying-flag behavior; remove an export-visibility check from tests/testsuite.c.
File summaries
File Description
wolfssh/ssh.h Documents the contract and adds the public wolfSSH_RekeyPending() declaration.
wolfssh/internal.h Updates the semantics comment for WOLFSSH_SELF_IS_KEYING to match the new gating.
src/ssh.c Implements wolfSSH_RekeyPending() as a NULL-safe isKeying predicate.
src/internal.c Moves WOLFSSH_SELF_IS_KEYING set to only after KEXINIT is sent/queued.
tests/unit.c Adds assertions covering predicate behavior and the KEXINIT send-failure/short-write keying gate.
tests/testsuite.c Removes the wolfSSH_OutputPending() export check from the testsuite harness.
tests/regress.c Adds regression coverage for wolfSSH_RekeyPending() over NULL and keying-bit combinations.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fenrir Automated Review — PR #1260

Scan targets checked: wolfssh-src, wolfssh-bugs

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Reported findings require changes before merge.

Comment thread src/internal.c Outdated
- SendKexInit() takes ssh->txFlushCount before wolfSSH_SendPacket()
  and sets WOLFSSH_SELF_IS_KEYING when SendPacketDelivered() reports
  the packet away, in place of setting it before the packet is built.
- PurgePacket() runs on the same decision, so a packet the transport
  took is not purged behind an error the highwater callback raised.
- internal.h describes the flag as set once the KEX init is sent or
  queued.
- tests/unit.c covers a KEX init whose send fails outright, one that
  short-writes, and one the transport takes whole behind a highwater
  callback that fails.
- FailHighwater() moves beside the other shared send callbacks and
  gains WS_MAYBE_UNUSED, so the client test uses it too.
Comment thread src/internal.c Outdated

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fenrir Automated Review — PR #1260

Scan targets checked: wolfssh-src, wolfssh-bugs

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Reported findings require changes before merge.

Comment thread tests/unit.c
if (ctx == NULL)
return -1897;
/* No refusals, so the first write resets the socket. */
s_sendRefusals = 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

New rekey test breaks client-only builds · Logic errors

test_KexInitSendAwayGatesKeying() is compiled when the client is enabled, but s_sendRefusals and RefuseThenResetIoSend are server-only. Defining NO_WOLFSSH_SERVER makes tests/unit.c fail to compile.

Related known finding #10542 (similar but distinct): Both are build-configuration defects, but #10542 incorrectly gates public-key authentication in DoUserAuthFailure/GetAllowedAuth, whereas this test references server-only symbols in a client-enabled compilation path. The faulting operations, root causes, locations, and required patches differ.

Suggested fix: Move the shared refusal state and callback outside the server-only block, marking the callback unused where necessary.
Basis: ISO C17 §6.5.1 requires identifiers used as primary expressions to designate declared objects or functions.

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.

3 participants