Add client-side keyboard-interactive authentication (RFC 4256) - #242
Open
noelmom wants to merge 2 commits into
Open
Add client-side keyboard-interactive authentication (RFC 4256)#242noelmom wants to merge 2 commits into
noelmom wants to merge 2 commits into
Conversation
added 2 commits
June 30, 2026 20:29
Implements client support for SSH `keyboard-interactive` user authentication, resolving apple#136. - Add `.keyboardInteractive` to `NIOSSHAvailableUserAuthenticationMethods` (parsed/serialized as `keyboard-interactive`). It is excluded from `.all` because server-side support is not yet implemented, but is offered to the client on its initial optimistic request. - Add a `.keyboardInteractive(languageTag:submethods:)` client auth offer. - Add public `NIOSSHKeyboardInteractiveChallenge` / `NIOSSHKeyboardInteractivePrompt` model types. - Add a `respondToKeyboardInteractiveChallenge(_:responsePromise:)` delegate callback with a default implementation that fails the attempt, preserving source compatibility for existing delegates. - Add `SSH_MSG_USERAUTH_INFO_REQUEST` (60) and `SSH_MSG_USERAUTH_INFO_RESPONSE` (61) message coding. Identifier 60 is overloaded with `SSH_MSG_USERAUTH_PK_OK`; the packet parser now decodes it context-sensitively based on the in-flight auth method, driven by the connection state machine. - Extend the client user-auth state machine to handle any number of challenge/response rounds, including zero-prompt requests, and to fail cleanly on a response-count mismatch. - Preserve existing password, public-key, and host-based auth behaviour. Adds unit and integration-style tests covering method negotiation, message coding, overloaded identifier-60 decoding, and single/multi-round client flows.
The overloaded message identifier 60 (SSH_MSG_USERAUTH_PK_OK vs SSH_MSG_USERAUTH_INFO_REQUEST) was disambiguated by speculatively setting a parser flag when the outbound keyboard-interactive request was written. That coupled correct decoding to write/read ordering. Derive the decision from the authoritative user-auth state instead: `UserAuthenticationStateMachine.expectingKeyboardInteractiveInfoRequest` is true exactly while the client awaits the outcome of a keyboard-interactive attempt. The connection state machine re-syncs the parser from it immediately before every inbound read, so identifier 60 is always interpreted correctly regardless of ordering. Adds an encrypted parser round-trip test for identifier 60 and state machine assertions covering the expectation flag across a full keyboard-interactive attempt and a password attempt.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Resolves #136. NIOSSH had no support for SSH
keyboard-interactiveuser authentication (RFC 4256), which is required to authenticate against servers that drive password/OTP/2FA prompts through PAM. This adds client-side support; server-side is intentionally left for a follow-up.Changes
Public API
NIOSSHAvailableUserAuthenticationMethods.keyboardInteractive(parsed/serialized askeyboard-interactive). Excluded from.allbecause there is no server-side support yet, but offered on the client's initial optimistic request.NIOSSHUserAuthenticationOffer.Offer.keyboardInteractive(_:)withOffer.KeyboardInteractive(languageTag:submethods:)(both default"").NIOSSHKeyboardInteractiveChallenge/NIOSSHKeyboardInteractivePromptmodel types.NIOSSHClientUserAuthenticationDelegate.respondToKeyboardInteractiveChallenge(_:responsePromise:), with a default implementation that fails the attempt — existing delegates remain source-compatible.NIOSSHError.ErrorType.unsupportedUserAuthenticationMethod,.invalidKeyboardInteractiveResponse.Internals
SSH_MSG_USERAUTH_INFO_REQUEST(60) andSSH_MSG_USERAUTH_INFO_RESPONSE(61) message coding, plus thekeyboard-interactivemethod onUSERAUTH_REQUEST.SSH_MSG_USERAUTH_PK_OK. It is disambiguated from the authoritative auth state (UserAuthenticationStateMachine.expectingKeyboardInteractiveInfoRequest, true only while awaiting the outcome of a keyboard-interactive attempt), re-synced onto the packet parser immediately before every inbound read so decoding is correct independent of write/read ordering.Client usage
Per RFC 4256, prompts with
echo == falseare sensitive; responses are never logged anywhere in the implementation.Testing
PK_OKby default and asINFO_REQUESTwhen a keyboard-interactive attempt is in flight — over both plaintext and encrypted transports.keyboard-interactive.swift format(strict lint) clean.Limitations
Client-only. There is no NIOSSH server that emits
INFO_REQUEST, so a full two-party end-to-end test isn't included; the client path is covered by encrypted parser and state-machine tests. Server-side keyboard-interactive can follow in a later change.