A native cross-platform SSH agent for OnlyKey hardware security devices, written in C/C++. Provides OpenSSH compatibility without Python dependencies.
Note: This project was created as a learning experience exploring AI-assisted development with Claude Code, VSCode, and MCPs (Serena). Everything is this project except this blurb was generated by Claude through iterative collaboration and testing against hardware on Windows and Mac. I never edited any code and only gave cursory looks when Claude seemed to need some guidance. So while it works I can't speak to the quality of it. As of initial checkin the agent can
- generate stored ECC1-16 signing keys
- output their pub keys
- output derived pub keys
- sign the ssh requests with any of the above to allow login
- prompt for the OnlyKey 3 Digit/Keypress on windows (does not work properly on Mac)
- provides most of the functionality of the onlykey-cli python tool
begin Claude:
- SSH Agent Mode - Full OpenSSH agent protocol support for SSH authentication
- Stored Keys - Use Ed25519 keys stored in ECC1-16 slots
- Derived Keys - Generate deterministic keys from identity strings
- Key Management - Generate, load, and manage keys directly on the device
- Slot Management - Configure password slots (1a-6b) with labels, URLs, credentials
- Device Preferences - Configure timeout, LED brightness, touch sensitivity, and more
- Cross-Platform - Windows, macOS, and Linux support
- CMake 3.16+
- C++17 compatible compiler
- Platform-specific:
- Windows: Visual Studio 2022
- macOS: Xcode
- Linux: GCC or Clang
choose preset debug or release
cmake --preset debug cmake --build --preset debug
onlykey-agent <command> [options]
Start the SSH agent to enable SSH authentication using your OnlyKey:
# Start agent with all available keys
onlykey-agent agent
# Start with specific stored key slots only
onlykey-agent agent --key ECC1,ECC3,ECC5
# Start with derived identities
onlykey-agent agent user@github.com admin@server.local
# Skip stored keys, use only derived keys
onlykey-agent agent --no-stored user@example.com
# Skip derived keys from config file
onlykey-agent agent --no-derived
# Use alternate config file
onlykey-agent agent --config /path/to/config| Option | Description |
|---|---|
--no-stored |
Skip stored ECC key enumeration |
--no-derived |
Skip loading derived keys from config file |
--key SLOTS |
Only load specific stored slots (comma-separated) |
--identity IDS |
Only load specific identities (comma-separated) |
--config FILE |
Use alternate config file |
Derived identities can be stored in ~/.onlykey-agent.conf (one identity per line):
user@github.com
admin@production.server.com
deploy@staging.example.org
| Platform | Transport |
|---|---|
| Windows | Named pipe \\.\pipe\openssh-ssh-agent |
| Linux | Unix socket $SSH_AUTH_SOCK |
| macOS | Unix socket $SSH_AUTH_SOCK |
# Generate a new Ed25519 signing key
onlykey-agent genkey ECC1 x s
# Generate NIST P-256 key with signing and backup
onlykey-agent genkey ECC2 n sb
# Load an existing key (hex-encoded)
onlykey-agent setkey ECC3 x s <key_hex>
# Set a label for a key slot
onlykey-agent setkey ECC1 label "GitHub SSH Key"
# Get the public key from a slot
onlykey-agent getpubkey ECC1
# Wipe a key slot
onlykey-agent wipekey ECC1
# Show all key labels
onlykey-agent getkeylabels| Type | Curve |
|---|---|
x |
Ed25519 |
n |
NIST P-256 |
s |
SECP256K1 |
h |
HMAC |
| Feature | Description |
|---|---|
s |
Signing |
d |
Decryption |
b |
Backup |
Key slots: RSA1-RSA4, ECC1-ECC16, HMAC1-HMAC2
# Set slot label
onlykey-agent setslot 1a label "My Account"
# Set slot URL
onlykey-agent setslot 1a url "https://example.com"
# Set slot username
onlykey-agent setslot 1a username "myuser"
# Set slot password
onlykey-agent setslot 1a password "mypassword"
# Wipe a slot
onlykey-agent wipeslot 1a
# Show all slot labels
onlykey-agent getlabelsSlots: 1a-6a, 1b-6b, or 1-12
Field types: label, url, username, password, delay1-3, addchar1-5, 2fa, totpkey, typespeed
# Set lock timeout (minutes, 0=disable)
onlykey-agent idletimeout 30
# Set LED brightness (1-10)
onlykey-agent ledbrightness 5
# Set touch sensitivity (2-100)
onlykey-agent touchsense 50
# Set keyboard layout (1-28)
onlykey-agent keylayout 1
# Set typing speed (1-10)
onlykey-agent keytypespeed 5
# Set wipe mode (1=sensitive only, 2=full wipe)
onlykey-agent wipemode 1
# Set stored key challenge mode (0=challenge, 1=button)
onlykey-agent storedkeymode 1
# Set derived key challenge mode (0=challenge, 1=button)
onlykey-agent derivedkeymode 0
# Set lock button (0=disable, 1-6=button number)
onlykey-agent lockbutton 1# Show CLI version
onlykey-agent version
# Show device firmware version
onlykey-agent fwversion
# Show help
onlykey-agent help┌─────────────────────────────────────────────────────────────────────┐
│ Application │
│ (main.cpp) │
│ CLI parsing, config loading, identity management │
└──────────────────────────┬──────────────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────────────┐
│ SSH Agent Server │
│ (agent_server.cpp) │
│ Listens for connections, dispatches requests │
└──────────────────────────┬──────────────────────────────────────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ SSH Agent │ │ Protocol │ │ Device │
│ (ssh_agent) │ │ (ok_protocol) │ │ (ok_device) │
│ SSH messages │ │ Packet encode │ │ Key ops, sign │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
└──────────────────┴──────────────────┘
│
┌──────────────────────────▼──────────────────────────────────────────┐
│ Platform Abstraction Layer │
├─────────────┬─────────────┬─────────────────┬───────────────────────┤
│ HID │ Transport │ Crypto │ Notifications │
│ (hidapi) │ Pipes/Socks │ (libsodium) │ MessageBox/Console │
└─────────────┴─────────────┴─────────────────┴───────────────────────┘
ok/
├── CMakeLists.txt # Cross-platform build configuration
├── CMakePresets.json # Build presets (Windows/macOS debug/release)
├── README.md # This file
│
├── include/ # Public headers
│ ├── agent_server.h # SshAgentServer class
│ ├── common.h # Common definitions
│ ├── ok_device.h # OnlyKey device interface
│ ├── ok_protocol.h # Protocol constants & HID packet definitions
│ └── ssh_agent.h # SSH agent protocol handling
│
├── src/
│ ├── main.cpp # Application entry point
│ │
│ ├── cli/ # Command-line interface
│ │ ├── cli_commands.cpp # Command implementations
│ │ ├── cli_commands.h
│ │ ├── cli_parser.cpp # Argument parsing utilities
│ │ └── cli_parser.h
│ │
│ ├── platform/ # Platform Abstraction Layer
│ │ ├── platform.h # Platform detection macros
│ │ │
│ │ ├── hid/ # USB HID communication
│ │ │ ├── hid_interface.h # IHidDevice, IHidManager interfaces
│ │ │ └── hid_hidapi.cpp # hidapi implementation (all platforms)
│ │ │
│ │ ├── transport/ # IPC transport layer
│ │ │ ├── transport_interface.h # ITransportServer, ITransportConnection
│ │ │ ├── transport_win.cpp # Windows named pipes
│ │ │ └── transport_unix.cpp # Unix domain sockets
│ │ │
│ │ ├── crypto/ # Cryptography
│ │ │ ├── crypto_interface.h # ICrypto interface
│ │ │ └── crypto_sodium.cpp # libsodium + custom MD5
│ │ │
│ │ └── notify/ # User notifications
│ │ ├── notify_interface.h # INotification interface
│ │ ├── notify_win.cpp # Windows MessageBox
│ │ ├── notify_macos.mm # macOS NSUserNotification
│ │ └── notify_linux.cpp # Linux console output
│ │
│ ├── protocol/ # OnlyKey protocol layer
│ │ ├── ok_protocol.cpp # Message encoding/decoding
│ │ └── ok_device.cpp # Device communication, key operations
│ │
│ ├── server/ # SSH agent server
│ │ └── agent_server.cpp # Transport-agnostic server loop
│ │
│ └── ssh/ # SSH protocol handling
│ └── ssh_agent.cpp # SSH agent message parsing
│
└── tests/ # Test suite
├── test_hid.cpp # HID communication tests
├── test_protocol.cpp # Protocol layer tests
└── test_ssh.cpp # SSH agent tests
| Layer | Description |
|---|---|
| Application | Entry point with CLI argument parsing, config file management |
| SSH Agent Server | Accepts client connections and handles SSH agent protocol |
| SSH Agent | Parses/constructs SSH agent messages (identities, sign requests) |
| Protocol | Encodes/decodes OnlyKey HID packets (64-byte reports) |
| Device | Manages OnlyKey hardware: key generation, signing, slot management |
| Platform Layer | Abstracts OS differences through interfaces (HID, Transport, Crypto, Notify) |
All dependencies are fetched automatically via CMake FetchContent:
- USB HID packet size: 64 bytes
- Primary VID/PID:
0x16C0/0x0486 - Secondary VID/PID:
0x1D50/0x60FC
- trezor-agent - Python SSH agent for Trezor (reference implementation)
- onlykey-agent - Official Python OnlyKey SSH agent
MIT