[RFC violation] Publickey authentication is impossible unless the server sends EXT_INFO server-sig-algs
Component: client publickey path — src/internal.c (PrepareUserAuthRequestPublicKey, MatchIdLists)
Affected versions: v1.5.0-stable; master 6e49506b (2026-09-18) — both. Re-verified present at 6ed8e121 (2026-09-25), master HEAD at the time of writing
Type: RFC 4252 / RFC 8308 interoperability
Severity: Low — but it needs no attacker at all
Discovery method: differential fuzzing with tlspuffin, then reduced to the standalone PoC below
Summary
A wolfSSH client cannot authenticate by public key against a server that does not send
SSH_MSG_EXT_INFO with server-sig-algs. RFC 8308 makes sending EXT_INFO a MAY, so a fully conformant
server may omit it — and against such a server wolfSSH publickey authentication fails outright.
This is an interoperability defect against honest peers, not a security issue. We report it separately from
our security findings for that reason.
RFC citation
RFC 8308 §2.2:
Implementations MAY send SSH_MSG_EXT_INFO […]
RFC 4252 §7 defines publickey authentication with no dependence on RFC 8308. A client that requires an
optional extension in order to perform a mandatory-to-implement authentication method has made the optional
one mandatory.
Reproduction
Attached reproducer — one file, runs both modes, prints a verdict
repro_issue_3_publickey_requires_ext_info.py (attached) is self-contained: a server speaking SSH-2.0
directly (curve25519-sha256 + aes256-gcm@openssh.com, no SSH library, so the result does not depend on our
tooling), which generates its own host key and drives your own examples/client/client with an ordinary
RSA key from your keys/ directory. Requires python3 with cryptography.
$ python3 repro_issue_3_publickey_requires_ext_info.py \
--client ./examples/client/client --wolfssh-root .
mode EXT_INFO sent client signature algorithm client result
none no (none offered) FAILED <-- the bug
post yes, after NEWKEYS rsa-sha2-256 authenticated
VERDICT: REPRODUCED -- with no EXT_INFO the client offers no signature
algorithm at all; the `post` control shows the only variable was
whether an optional RFC 8308 message was sent.
In --mode none the server simply does not send EXT_INFO — which is all a conformant server need do.
The client then sends no publickey USERAUTH_REQUEST at all and fails with WS_MATCH_KEY_ALGO_E; the server
records no signature algorithm, because none was ever offered.
The post control is the point: the identical exchange with server-sig-algs after NEWKEYS
authenticates normally, so the only variable is whether an optional message was sent. It exits 0 when
reproduced and 1 when not. The output above is a real run against v1.5.0-stable.
Root cause
PrepareUserAuthRequestPublicKey (internal.c:15430) builds the client's candidate list and then
intersects it with the peer's:
matchId = MatchIdLists(WOLFSSH_ENDPOINT_CLIENT, algoId, algoIdSz,
ssh->peerSigId, ssh->peerSigIdSz); /* internal.c:15464 */
if (matchId == ID_UNKNOWN) {
ret = WS_MATCH_KEY_ALGO_E;
}
MatchIdLists returns ID_UNKNOWN whenever either list is NULL or empty — its guard is
left != NULL && leftSz > 0 && right != NULL && rightSz > 0. And ssh->peerSigId is populated in exactly
one place: DoExtInfoServerSigAlgs (internal.c:6814).
So with no EXT_INFO received, peerSigId is empty, the intersection is empty, and the client sets
WS_MATCH_KEY_ALGO_E. There is no fallback to the client's own preference.
The client does advertise ext-info-c (internal.c:11085), so a server that implements RFC 8308 will
normally send EXT_INFO — which is presumably why this has not been hit. But the RFC does not require it.
Suggested fix
Fall back to the client's own strongest supported algorithm for the key type when peerSigId is empty or
yields no match:
matchId = MatchIdLists(WOLFSSH_ENDPOINT_CLIENT, algoId, algoIdSz,
ssh->peerSigId, ssh->peerSigIdSz);
if (matchId == ID_UNKNOWN) {
if (ssh->peerSigIdSz == 0) {
matchId = algoId[0]; /* no server-sig-algs: use our own preference */
} else {
ret = WS_MATCH_KEY_ALGO_E; /* server listed algorithms, none of them ours */
}
}
The distinction matters: "the server told us what it accepts and we support none of them" is a genuine
failure; "the server told us nothing" is not.
Impact
Low, and no attacker is involved. The failure occurs against any honest server that omits an optional
message. Its practical significance is interoperability: a wolfSSH client cannot use publickey
authentication against such a peer, and the error surfaced (WS_MATCH_KEY_ALGO_E, "cannot match key algo
with peer") suggests an algorithm mismatch rather than a missing extension, which is misleading when
diagnosing.
Acknowledgements
Found with the tlspuffin fuzzer — https://github.com/tlspuffin/tlspuffin — designed and developed by the
tlspuffin team: Nataël Baffou, Olivier Demengeon, Tom Gouville, Lucca Hirschi, Steve Kremer (Inria, LORIA, France).
repro_issue_3_publickey_requires_ext_info.py
[RFC violation] Publickey authentication is impossible unless the server sends EXT_INFO
server-sig-algsComponent: client publickey path —
src/internal.c(PrepareUserAuthRequestPublicKey,MatchIdLists)Affected versions: v1.5.0-stable; master
6e49506b(2026-09-18) — both. Re-verified present at6ed8e121(2026-09-25), master HEAD at the time of writingType: RFC 4252 / RFC 8308 interoperability
Severity: Low — but it needs no attacker at all
Discovery method: differential fuzzing with tlspuffin, then reduced to the standalone PoC below
Summary
A wolfSSH client cannot authenticate by public key against a server that does not send
SSH_MSG_EXT_INFOwithserver-sig-algs. RFC 8308 makes sending EXT_INFO a MAY, so a fully conformantserver may omit it — and against such a server wolfSSH publickey authentication fails outright.
This is an interoperability defect against honest peers, not a security issue. We report it separately from
our security findings for that reason.
RFC citation
RFC 8308 §2.2:
RFC 4252 §7 defines publickey authentication with no dependence on RFC 8308. A client that requires an
optional extension in order to perform a mandatory-to-implement authentication method has made the optional
one mandatory.
Reproduction
Attached reproducer — one file, runs both modes, prints a verdict
repro_issue_3_publickey_requires_ext_info.py(attached) is self-contained: a server speaking SSH-2.0directly (curve25519-sha256 + aes256-gcm@openssh.com, no SSH library, so the result does not depend on our
tooling), which generates its own host key and drives your own
examples/client/clientwith an ordinaryRSA key from your
keys/directory. Requirespython3withcryptography.In
--mode nonethe server simply does not send EXT_INFO — which is all a conformant server need do.The client then sends no publickey
USERAUTH_REQUESTat all and fails withWS_MATCH_KEY_ALGO_E; the serverrecords no signature algorithm, because none was ever offered.
The
postcontrol is the point: the identical exchange withserver-sig-algsafter NEWKEYSauthenticates normally, so the only variable is whether an optional message was sent. It exits 0 when
reproduced and 1 when not. The output above is a real run against v1.5.0-stable.
Root cause
PrepareUserAuthRequestPublicKey(internal.c:15430) builds the client's candidate list and thenintersects it with the peer's:
MatchIdListsreturnsID_UNKNOWNwhenever either list is NULL or empty — its guard isleft != NULL && leftSz > 0 && right != NULL && rightSz > 0. Andssh->peerSigIdis populated in exactlyone place:
DoExtInfoServerSigAlgs(internal.c:6814).So with no EXT_INFO received,
peerSigIdis empty, the intersection is empty, and the client setsWS_MATCH_KEY_ALGO_E. There is no fallback to the client's own preference.The client does advertise
ext-info-c(internal.c:11085), so a server that implements RFC 8308 willnormally send EXT_INFO — which is presumably why this has not been hit. But the RFC does not require it.
Suggested fix
Fall back to the client's own strongest supported algorithm for the key type when
peerSigIdis empty oryields no match:
The distinction matters: "the server told us what it accepts and we support none of them" is a genuine
failure; "the server told us nothing" is not.
Impact
Low, and no attacker is involved. The failure occurs against any honest server that omits an optional
message. Its practical significance is interoperability: a wolfSSH client cannot use publickey
authentication against such a peer, and the error surfaced (
WS_MATCH_KEY_ALGO_E, "cannot match key algowith peer") suggests an algorithm mismatch rather than a missing extension, which is misleading when
diagnosing.
Acknowledgements
Found with the tlspuffin fuzzer — https://github.com/tlspuffin/tlspuffin — designed and developed by the
tlspuffin team: Nataël Baffou, Olivier Demengeon, Tom Gouville, Lucca Hirschi, Steve Kremer (Inria, LORIA, France).
repro_issue_3_publickey_requires_ext_info.py