A PKCS#11 shared library (libtrezor_pkcs11.so / trezor_pkcs11.dll) that exposes a Trezor hardware wallet as a PKCS#11 token. Any PKCS#11 consumer — ssh-add, git commit signing, OpenSSL, browsers — can use the Trezor as an HSM. Keys are derived on-device via SLIP-0013 SignIdentity using gpg:// URIs. The private key never leaves the device.
| Curve | Aliases | Supported | Default in example config |
|---|---|---|---|
| nist256p1 | P-256, prime256v1 | yes | yes |
| secp256k1 | K-256 | yes | no |
| ed25519 | Ed25519 | yes | no |
Default in example config = no means the curve is opt-in, not unsupported.
| Mechanism | Input | Typical consumer | Supported |
|---|---|---|---|
CKM_ECDSA |
32-byte pre-hashed digest | OpenSSH (ssh-add) |
yes |
CKM_ECDSA_SHA256 |
Raw data (hashed internally) | OpenSSL, git signing | yes |
CKM_EDDSA |
Raw data (Ed25519 hashes internally) | OpenSSH, PKCS#11 v3.0 consumers | yes |
| Device | VID | PID | Transport | Validation status |
|---|---|---|---|---|
| Trezor Safe 3 | 0x1209 | 0x53c1 | USB bulk (WebUSB) | tested by maintainer |
| Trezor Model T | 0x1209 | 0x53c1 | USB bulk (WebUSB) | expected to work, community verification wanted |
| Trezor Safe 5 | 0x1209 | 0x53c1 | USB bulk (WebUSB) | expected to work, community verification wanted |
| Trezor One | 0x534c | 0x0001 | HID | expected to work, community verification wanted |
Only Trezor Safe 3 has been tested by the maintainer so far. If you test with Model T, Safe 5, or Trezor One, please open an issue or PR with results.
The Trezor protobuf definitions are pulled in as a git submodule. After cloning, initialise it before building:
git submodule update --init --depth 1Install system dependencies:
sudo apt install libudev-dev libusb-1.0-0-dev protobuf-compiler libengine-pkcs11-openssllibengine-pkcs11-openssl is required if you want to use OpenSSL with -engine pkcs11.
Then build:
cargo build --release
# Output: target/release/libtrezor_pkcs11.soNative Windows builds are supported.
Install dependencies and build from a Windows terminal (PowerShell or cmd.exe) in the repository root:
cargo build --release
# Output: target/release/trezor_pkcs11.dllRecommended setup:
- Install Rust (
rustup) with the MSVC toolchain. - Install Protocol Buffers (
protoc) and ensure it is onPATH. - Use a native Windows checkout path (for example under
C:\dev\...) for the smoothest build experience.
The library reads a TOML config file to know which PKCS#11 slots to expose and which key identity to use for each.
Search order:
$TREZOR_PKCS11_CONF(environment variable, full path)- Linux:
$XDG_CONFIG_HOME/trezor-pkcs11/config - Linux:
~/.config/trezor-pkcs11/config - Windows:
%APPDATA%\trezor-pkcs11\config - Windows:
%LOCALAPPDATA%\trezor-pkcs11\config - Windows:
%USERPROFILE%\.config\trezor-pkcs11\config - Windows fallback:
C:\ProgramData\trezor-pkcs11\config - Linux fallback:
/etc/trezor-pkcs11.conf
Example config (~/.config/trezor-pkcs11/config):
[slot0]
uri = "gpg://ssh@myhomelab"
label = "ssh-auth"
curve = "nist256p1"
[slot1]
uri = "gpg://commit@myhomelab"
label = "git-signing"
curve = "nist256p1"Each [slotN] section defines one PKCS#11 slot. The uri is the SLIP-0013 identity URI passed to the Trezor. The label appears in token info and key listings. The curve is optional and defaults to nist256p1.
Without the udev rules the Trezor USB device is owned by root and the library will fail to open it.
Install rules permanently:
sudo cp pkg/51-trezor.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm triggerThen re-plug the device.
Quick workaround for testing (resets on next plug):
sudo chmod a+rw /dev/bus/usb/<bus>/<device># OpenSSH 8.9+ restricts which PKCS#11 modules ssh-agent will load.
# For a development build outside a system library path, start the agent
# with -P to allowlist the directory:
eval $(ssh-agent -P "/path/to/libtrezor_pkcs11.so" -s)
ssh-add -s /path/to/libtrezor_pkcs11.so
# Show the derived public key (ecdsa-sha2-nistp256):
ssh-add -LBy default, ssh-agent only loads PKCS#11 modules from system library paths (/usr/lib*, /usr/local/lib*). Development builds in target/release/ are outside this allowlist and will be rejected unless you pass -P when starting the agent.
The .deb package installs the library to /usr/lib/x86_64-linux-gnu/pkcs11/trezor-pkcs11.so, which is inside the default allowlist — no -P flag needed when using the installed package:
eval $(ssh-agent -s)
ssh-add -s /usr/lib/x86_64-linux-gnu/pkcs11/trezor-pkcs11.so
ssh-add -Lssh-add -L >> ~/.ssh/authorized_keys # or append to the remote's authorized_keysEvery SSH authentication will require a button press on the Trezor.
Git supports PKCS#11 tokens via its SSH signing backend.
# ~/.gitconfig
[gpg]
format = ssh
[gpg "ssh"]
allowedSignersFile = ~/.ssh/allowed_signers
[user]
signingKey = /usr/lib/x86_64-linux-gnu/pkcs11/trezor-pkcs11.so
[commit]
gpgsign = truePopulate ~/.ssh/allowed_signers with the output of ssh-add -L (prefix each line with your@email.address ).
Every signed commit will require a button press on the Trezor.
When using OpenSSL with this module, force the module path with PKCS11_MODULE_PATH and pass -CAkeyform ENGINE so the PKCS#11 URI is handled by the engine.
PKCS11_MODULE_PATH="$PWD/target/release/libtrezor_pkcs11.so" \
openssl x509 -req \
-in ee.csr \
-engine pkcs11 \
-CAkeyform ENGINE \
-CAkey "pkcs11:token=my-token-label;type=private" \
-CA ca.crt \
-out ee.pem \
-days 90Notes:
- Replace
my-token-labelwith your configured slotlabelfrom thetrezor-pkcs11config file. - Use your actual CA certificate filename (for example
ca.crt, notca.pemif that file does not exist). -CAkeyform ENGINEis important; using only-keyform enginecan make OpenSSL try to open the PKCS#11 URI as a file.
cargo install cargo-deb
cargo deb
# Output: target/debian/trezor-pkcs11_*.deb
sudo dpkg -i target/debian/trezor-pkcs11_*.debThe package installs the library to /usr/lib/x86_64-linux-gnu/pkcs11/trezor-pkcs11.so and the udev rules to /etc/udev/rules.d/51-trezor.rules.
# List slots — works without a Trezor connected:
pkcs11-tool --module target/release/libtrezor_pkcs11.so -Lpkcs11-tool --module target/release/libtrezor_pkcs11.so --list-objectspip install PyKCS11 pytest cryptography
pytest test_integration.py -vThe integration tests open PKCS#11 sessions and perform real sign operations. Each signing call will require a button press on the Trezor. See test_integration.py for the full list of tests.
When at least one slot is configured with curve = "ed25519", the suite also exercises CKM_EDDSA and verifies Ed25519 signatures end-to-end.
- The private key is derived inside the Trezor and never leaves the device.
- Every key derivation and signing operation requires physical confirmation on the Trezor screen (button press).
- The PKCS#11 PIN (
C_Login) is a no-op — authentication is enforced by the device itself. - Key derivation uses SLIP-0013 SignIdentity with
gpg://URIs, producing a deterministic key per URI.