Repository navigation
feat(ssh): add the built-in internal SecurityKeyProvider over USB HID - #334
Merged
Merged
Conversation
`SecurityKeyProvider internal` now signs with attached FIDO authenticators instead of printing the #328 "not yet supported" diagnostic, and it becomes the default, as in OpenSSH 10.3p1 built with ENABLE_SK_INTERNAL (distribution builds, `ssh -G` prints `securitykeyprovider internal`). The `$SSH_SK_PROVIDER` indirection still works when configured explicitly. The provider is `sk-usbhid.c` without libfido2: an in-tree CTAPHID layer over a `HidDevice`/`HidBackend` trait, canonical CBOR, CTAP2 getInfo/getAssertion/clientPIN (PIN protocols 1 and 2), a U2F fallback for devices without CBOR, and `sk_probe`-style device selection (silent per-device assertion to find the credential holder, selection by touch when a PIN or user verification is involved). Key flags, status codes and the packed `sig_r`/`sig_s` follow `sk_sign` and `fidoerr_to_skerr`, so the response is byte-identical to the external provider path, and the provider plugs into `open_provider` reusing the signer's lock, prompts and decline-and-continue path. hidapi (default features off) is the only new crate: its pure-Rust hidraw backend on Linux keeps the static musl builds working without libudev, and its IOKit backend on macOS links only system frameworks. p256, aes and cbc were already in the graph. Validation: mock-transport tests for every layer and the end-to-end signature (verified with ssh-key), each failing when its behavior is reverted; fmt, clippy, the -G and query contract tests, and the hostkey-agent, keytype, limit-keytype, agent, connect and sshcfgparse regress tests pass. A check with a physical authenticator is pending. Closes #331
Member
Author
Implementation Review SummaryIntent
Findings AddressedNo CRITICAL or HIGH findings, so nothing was changed on the branch. Remaining Items
Verification
|
Member
Author
Security and Performance ReviewNo CRITICAL or HIGH findings, so nothing was changed on the branch. The threat model was a malicious HID device on the FIDO usage page, a malicious server, and a hostile local environment. Checked and sound
Remaining Items
Verification
|
Apply the remaining review findings on the built-in internal SecurityKeyProvider so a misbehaving or hostile HID device cannot stall or steer signing. - Touch selection counts only the CTAP2 statuses libfido2's fido_dev_get_touch_status accepts (success, PIN_AUTH_INVALID, PIN_INVALID, PIN_NOT_SET); any other reply drops the device, so it can no longer win the race and receive the PIN exchange. Polling rounds are paced 200 ms apart (POLL_SLEEP_NS). - Enumerated devices are deduplicated by path and capped at MAX_FIDO_DEVICES (8). - A whole internal sign call is bounded at 120 s, covering KEEPALIVE and foreign-channel streams and the U2F presence loop. On expiry the pending CTAP2 request is cancelled and the signature fails with "timed out waiting for the security key", so authentication moves on to the next identity. This deviates from libfido2's unbounded wait because bssh serializes provider calls across all hosts; the module doc, compatibility status and man page say so. - U2F devices, including CBOR devices forced to U2F, never get CTAPHID CANCEL, and the REGISTER touch probe uses P1 0. A timed-out getInfo re-INITs the channel so its late reply cannot answer the first U2F request. - PIN protocol 2 uses the hkdf crate (same hmac 0.13 generation) into Zeroizing buffers; GetAssertionRequest's Debug redacts pinUvAuthParam; hidapi keeps one context for enumeration and opens, and opens devices non-exclusively on macOS (macos-shared-device). Each behavioral fix has a mock-transport test that fails with the fix reverted; Timing makes the waits injectable. Refs #331
Describe what a user hits with SecurityKeyProvider internal: hidraw access on Linux usually needs a udev rule and the failure reads "N FIDO device(s) found but none could be opened", macOS opens devices non-exclusively, other platforms get the "not available on this platform" error, U2F-only keys cannot sign Ed25519 or take a PIN, and enrollment, resident keys and sensor-only UV tokens are unsupported. Updated ARCHITECTURE.md, README.md, docs/man/bssh.1, docs/architecture/ssh-client.md and docs/openssh-compatibility-status.md. The 120 s deadline, eight-device cap and selection by credential or touch were already documented and match the code. No source changes. Refs #331
Closed
6 of 7 tasks
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.
Summary
SecurityKeyProvider internalsigns with attached FIDO2 and U2F authenticators over USB HID forsk-ssh-ed25519@openssh.comandsk-ecdsa-sha2-nistp256@openssh.comidentities on Linux and macOS, and it is now the default (OpenSSH built withENABLE_SK_INTERNAL, as distribution builds are: Ubuntu's OpenSSH 9.6p1 printssecuritykeyprovider internal).$SSH_SK_PROVIDERand library paths keep working when configured; other platforms report that the built-in provider is unavailable.sk-usbhid.cwithout libfido2 (src/ssh/security_key/internal/): CTAPHID framing and channels over aHidDevice/HidBackendtrait, canonical CBOR, CTAP2 getInfo/getAssertion/clientPIN with PIN protocols 1 and 2 (permission-scoped tokens on CTAP 2.1 devices), U2F check-only and enforce-presence fallback (ECDSA only, no PIN), andsk_probedevice selection: silent per-device assertion to find the credential holder, selection by touch when a PIN orverify-requiredis involved. Flags and statuses map as insk_signandfidoerr_to_skerr, and the packed response matches the external provider path byte for byte.open_provideris the only integration point; the signer's process-wide FIFO lock, "Confirm user presence" notice, PIN prompt and decline-and-continue behavior are reused. HID devices are enumerated only after the server accepts ansk-*key. PINs, PIN hashes, shared secrets and tokens areZeroizingand never logged.Changes during review
MAX_FIDO_DEVICES), since hidapi lists one entry per top-level usage.sk-*signature in the run. Documented in the compatibility status and man page.hkdfcrate for PIN protocol 2,pinUvAuthParamredacted fromDebug, and non-exclusive device open on macOS (macos-shared-device).Dependency choice
hidapi 2.6.7with default features off:linux-native-basic-udevon Linux is pure Rust (no libudev) andcargo check -p hidapi --target aarch64-unknown-linux-muslpasses; on macOS it compiles its vendored IOKit backend and links only system frameworks. No change todebian/control, the PPA rules or the Homebrew formula; the Linux release binary still links only libc, libm and libgcc_s.p256,aes,cbcandhkdfwere already in the lock.ctap-hid-fido2(pins hidapi's Clinux-static-hidrawbackend, which needs libudev, and addsring), and libfido2 bindings.Test plan
ssh-key; each group fails when its behavior is reverted.cargo fmt --check,cargo clippy -- -D warnings,cargo test --lib(1911 passed),cargo test --tests -- --skip integration_test, andcargo build --locked --releasepass locally on 0b26a7e (the last code commit).hostkey-agent,keytype,limit-keytype,agent,connect,proxy-connect,sshcfgparse,hostkey-rotate,rekeypass; CI regress on Linux and macOS (the macOS job builds bssh with the IOKit backend) shows no new failures.Follow-ups: key enrollment (
ssh-keygen -t ed25519-sk) and resident keys (ssh-keygen -K,ssh-add -K), and UV tokens for sensor-only devices (getPinUvAuthTokenUsingUv).Closes #331