Skip to content
Merged
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
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@ but not with themselves; omit the key (plain `#[serial]`) when in doubt.
- Private key files with passphrase support
- Password authentication (discouraged in production)
- Public key authentication preferred
- FIDO security-key identities (`sk-ssh-ed25519@openssh.com`, `sk-ecdsa-sha2-nistp256@openssh.com`): the default identity list includes `id_ed25519_sk` and `id_ecdsa_sk`. Signing goes through a `SecurityKeyProvider` (`src/ssh/security_key/`, `src/ssh/tokio_client/security_key_signer.rs`): the default is `$SSH_SK_PROVIDER`, an external library implementing OpenSSH's `sk-api.h` is loaded with `dlopen` and called on a blocking thread, and `internal` reports a diagnostic because bssh has no built-in provider. A missing or failing provider declines that identity and authentication continues with the next one. Keys held by an agent sign through the agent and need no provider. Static musl builds cannot `dlopen` and report a diagnostic instead.
- FIDO security-key identities (`sk-ssh-ed25519@openssh.com`, `sk-ecdsa-sha2-nistp256@openssh.com`): the default identity list includes `id_ed25519_sk` and `id_ecdsa_sk`. Signing goes through a `SecurityKeyProvider` (`src/ssh/security_key/`, `src/ssh/tokio_client/security_key_signer.rs`): the default is `internal`, the built-in USB HID provider (`src/ssh/security_key/internal/`: CTAPHID framing over hidapi, CTAP2 `getAssertion` and `clientPIN`, a U2F fallback, and `sk-usbhid.c`'s device selection), and a path names an external library implementing OpenSSH's `sk-api.h`, loaded with `dlopen`; either is called on a blocking thread. A missing or failing provider declines that identity and authentication continues with the next one. Keys held by an agent sign through the agent and need no provider. Static musl builds cannot `dlopen` an external library and report that, while their built-in provider works. The built-in provider supports Linux (hidraw, usually needing a udev rule) and macOS (non-exclusive IOKit open) only and reports itself unavailable elsewhere; it considers at most eight devices, bounds a whole signature at 120 s (libfido2 waits without limit), selects the device by credential or by touch, and over U2F cannot sign Ed25519 or take a PIN. Enrollment, resident keys and sensor-only UV tokens are not implemented.

#### macOS `UseKeychain`

Expand Down
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,20 @@ socket2 = { version = "0.6", features = ["all"] }
shell-words = "1.1.1"
base64 = "0.22.1"
hmac = "0.13.0"
hkdf = "0.13.0"
sha2 = "0.11.0"
hickory-resolver = { version = "0.26.1", features = ["dnssec-ring"] }
libc = "0.2"
# sk-api (FIDO security key) middleware libraries are dlopened in process;
# see src/ssh/security_key for why no ssh-sk-helper style subprocess exists.
libloading = "0.8"
# The built-in `SecurityKeyProvider internal` (src/ssh/security_key/internal)
# implements CTAPHID, CTAP2 getAssertion/clientPIN and the U2F fallback in
# tree; these three only supply the PIN-protocol crypto and are already in the
# dependency graph through russh/ssh-key, so they add no crate.
p256 = { version = "0.14", features = ["ecdh", "ecdsa"] }
aes = "0.9"
cbc = { version = "0.2", features = ["alloc"] }
ipnetwork = "0.21"
bcrypt = "0.19"
# `argon2` 0.5 uses `crypto-common` 0.1.7, which deliberately pins
Expand All @@ -100,13 +108,27 @@ tokio-rustls = "0.26"
rustls-native-certs = "0.8"
surge-ping = "0.9.1"

# USB HID transport for the built-in security-key provider. Linux uses
# hidapi's pure-Rust hidraw backend (sysfs enumeration, no libudev), so the
# static musl release binaries keep building and no runtime library is added
# to the Debian, PPA or Homebrew packaging. macOS compiles hidapi's IOKit
# backend from the vendored C source and links only system frameworks.
[target.'cfg(target_os = "linux")'.dependencies]
hidapi = { version = "2.6.7", default-features = false, features = ["linux-native-basic-udev"] }

[target.'cfg(target_os = "macos")'.dependencies]
security-framework = "3.7.0"
# `macos-shared-device` opens devices non-exclusively, as libfido2 does, so a
# browser can still use the key while bssh has it open.
hidapi = { version = "2.6.7", default-features = false, features = ["macos-shared-device"] }

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Storage_FileSystem"] }

[dev-dependencies]
# The mock FIDO authenticator signs Ed25519 assertions; already in the graph
# through ssh-key.
ed25519-dalek = "3.0"
tempfile = "3.27.0"
mockito = "1.7.2"
tokio-test = "0.4"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ See [examples/mpi_exit_code.sh](examples/mpi_exit_code.sh) and [examples/health_
bssh supports multiple authentication methods:

### SSH Key Authentication
- **Default keys**: Automatically tries `~/.ssh/id_ed25519`, `~/.ssh/id_ed25519_sk`, `~/.ssh/id_rsa`, `~/.ssh/id_ecdsa`, `~/.ssh/id_ecdsa_sk`, `~/.ssh/id_dsa` (the `*_sk` FIDO keys need a `SecurityKeyProvider`)
- **Default keys**: Automatically tries `~/.ssh/id_ed25519`, `~/.ssh/id_ed25519_sk`, `~/.ssh/id_rsa`, `~/.ssh/id_ecdsa`, `~/.ssh/id_ecdsa_sk`, `~/.ssh/id_dsa` (the `*_sk` FIDO keys sign with an attached authenticator through the built-in `SecurityKeyProvider internal`, or an sk-api library if configured; on Linux the user needs access to the `/dev/hidraw*` device, usually through the FIDO udev rules of the `libfido2` packages; enrollment, resident keys and biometric-only UV tokens are not supported, and Windows needs an sk-api library)
- **Custom key**: Use `-i` flag to specify a key file
- **Encrypted keys**: Automatically detects and prompts for passphrase

Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/ssh-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
- `tokio_client/connection.rs` - Connection management (293 lines)
- `tokio_client/authentication.rs` - Authentication methods (378 lines)
- `tokio_client/security_key_signer.rs` - russh `Signer` for FIDO `sk-*` identities: resolves `SecurityKeyProvider`, checks the provider before decrypting an `sk-*` key file, calls the provider on a blocking thread (serialized process-wide), prints the touch notice, prompts once for a PIN, and declines a failed signature so authentication continues with the next identity (`authenticate_declinable`)
- `ssh/security_key/` - provider interface (`SecurityKeyProvider` trait, `$VAR` resolution, `internal`/`none` handling), the `sk-api.h` `dlopen` binding (`external.rs`), and the `sk-*` signature blob encoder (`signature.rs`)
- `ssh/security_key/` - provider interface (`SecurityKeyProvider` trait, `$VAR` resolution, `internal`/`none` handling), the `sk-api.h` `dlopen` binding (`external.rs`), the `sk-*` signature blob encoder (`signature.rs`), and the built-in provider (`internal/`: `ctaphid.rs` framing and channels over a `HidDevice` trait, `cbor.rs`, `ctap2.rs`, `u2f.rs`, `pin_protocol.rs` for PIN protocols 1 and 2, `device.rs` for one authenticator's `getAssertion`, `select.rs` for `sk_probe`-style device selection, `hidapi_backend.rs` as the only OS-facing file, and `mock.rs` as the scripted test authenticator). The built-in provider exists on Linux and macOS only; elsewhere `internal` fails with a "not available on this platform" error
- `tokio_client/host_key_type.rs` - refuses a server host key or certificate whose type differs from the negotiated host-key algorithm (OpenSSH `kex_verify_host_key`), including `sk-*` types and ECDSA curves
- `tokio_client/channel_manager.rs` - Channel operations (230 lines)
- `tokio_client/file_transfer.rs` - SFTP file operations (285 lines)
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/ssh-config-parser.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ bssh supports 40+ SSH configuration directives organized into categories:
- `ConnectTimeout` - Connection timeout in seconds
- `ServerAliveInterval` - Keepalive interval
- `ServerAliveCountMax` - Keepalive retry count
- `SecurityKeyProvider` - Library implementing OpenSSH's `sk-api.h` for `sk-*` identities, `internal`, `none`, or a `$VAR` reference (default `$SSH_SK_PROVIDER`). Stored verbatim in `security_key_provider`; `-G` prints the raw value and expansion happens in `src/ssh/tokio_client/security_key_signer.rs` when an `sk-*` identity is used.
- `SecurityKeyProvider` - Library implementing OpenSSH's `sk-api.h` for `sk-*` identities, `internal` (the built-in USB HID provider and the default), `none`, or a `$VAR` reference. Stored verbatim in `security_key_provider`; `-G` prints the raw value and expansion happens in `src/ssh/tokio_client/security_key_signer.rs` when an `sk-*` identity is used.
- `Compression` - Advertise transport compression to the server (yes/no, default: no). `yes` offers eager `zlib` ahead of `none`; `no`/unset offers only `none`. `zlib@openssh.com` is never advertised because russh's delayed-zlib codec desyncs the flate2 stream a few packets after activation; wiring is in `SshConnectionConfig::to_russh_config` (`src/ssh/tokio_client/connection.rs`).

**Authentication Options:**
Expand Down
2 changes: 1 addition & 1 deletion docs/man/bssh.1
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,7 @@ Example:

.TP
.B SecurityKeyProvider
Specifies the library used to access FIDO authenticator-hosted keys (sk-ssh-ed25519@openssh.com and sk-ecdsa-sha2-nistp256@openssh.com identity files, as enrolled by ssh-keygen -t ed25519-sk or ecdsa-sk). The value is a path to a shared library implementing OpenSSH's sk-api (for example libsk-libfido2.so), or a name starting with $ that selects an environment variable holding that path. The default is $SSH_SK_PROVIDER; when the variable is unset, or the value is none, sk-* identities are skipped. The value internal (OpenSSH's built-in USB HID provider) is not available in this build and is reported before the next identity is tried. The library is loaded with dlopen(3); statically linked musl builds cannot load it and say so. When a key requires user presence and standard error is a terminal, bssh prints a confirmation notice while waiting for the touch; when the provider asks for a PIN and BatchMode is off, bssh prompts for it once. If signing fails (no device, PIN required in BatchMode, provider error), bssh reports it and continues with the next identity. Keys held by an agent keep signing through the agent.
Specifies how FIDO authenticator-hosted keys (sk-ssh-ed25519@openssh.com and sk-ecdsa-sha2-nistp256@openssh.com identity files, as enrolled by ssh-keygen -t ed25519-sk or ecdsa-sk) are accessed. The default, internal, is bssh's built-in USB HID provider, which talks CTAP2 (with CTAP1/U2F fallback) to attached authenticators on Linux and macOS without any middleware library; when several authenticators are attached, the one holding the key is used, or, when a PIN is involved, the one that is touched. Otherwise the value is a path to a shared library implementing OpenSSH's sk-api (for example libsk-libfido2.so), or a name starting with $ that selects an environment variable holding that path; an unset variable, or the value none, skips sk-* identities. A library is loaded with dlopen(3); statically linked musl builds cannot load one and say so, but their built-in provider works. When a key requires user presence and standard error is a terminal, bssh prints a confirmation notice while waiting for the touch; when the provider asks for a PIN and BatchMode is off, bssh prompts for it once. At most eight attached authenticators are considered. U2F-only keys work for ecdsa-sk keys but not ed25519-sk keys, and cannot take a PIN. Enrolling keys, loading resident keys, and authenticators that verify the user only through a built-in sensor are not supported. On Linux the provider uses /dev/hidraw devices, which normally need a udev rule granting your user access (for example the FIDO rules from the libfido2 udev package on Debian and Ubuntu); without one, signing fails with a message that FIDO devices were found but none could be opened. On macOS devices are opened non-exclusively. On other platforms the built-in provider reports that it is not available, and an sk-api library must be used. Unlike OpenSSH, the built-in provider gives up on a signature after 120 seconds (for example when the key is never touched), because provider calls are serialized across all hosts. If signing fails (no device, PIN required in BatchMode, timeout, provider error), bssh reports it and continues with the next identity. Keys held by an agent keep signing through the agent.
.br
Example:
.I SecurityKeyProvider /usr/lib/libsk-libfido2.so
Expand Down
Loading
Loading