No description
  • Scheme 78.4%
  • Rust 9.5%
  • Shell 9.3%
  • Makefile 2.7%
  • Common Lisp 0.1%
Find a file
2026-07-30 15:47:42 -06:00
.builds ci: set umask 022 in package/verify tasks too (jerbuild creates group-writable cache dirs during pkg operations) 2026-07-22 13:11:00 -06:00
.jerboa Resolve security audit findings 2026-07-11 17:29:30 -06:00
bin Add OATH import and native YubiKey transport 2026-05-20 15:40:12 -06:00
docs Resolve security audit findings 2026-07-11 17:29:30 -06:00
scripts Resolve security audit findings 2026-07-11 17:29:30 -06:00
support Resolve security audit findings 2026-07-11 17:29:30 -06:00
test Resolve security audit findings 2026-07-11 17:29:30 -06:00
yubikey fix: use audited CSPRNG, warn on default key, fix string-replace-all, remove dead code, ccid timeout 2026-07-21 10:37:08 -06:00
yubikey-native fix: use audited CSPRNG, warn on default key, fix string-replace-all, remove dead code, ccid timeout 2026-07-21 10:37:08 -06:00
.gitignore jpkg: complete binary package setup 2026-07-30 15:24:22 -06:00
.gitsafeignore Initial M1: PC/SC transport + OATH + OTP + CLI + auth API. 2026-05-13 11:23:48 -06:00
.jerbuild build: build ykjr with installed jerbuild (no ~/mine/jerboa dependency) 2026-05-28 18:21:55 -06:00
AGENTS.md docs: remove jerboa-emacs restriction 2026-07-30 15:47:42 -06:00
jpkg.lock jpkg: complete binary package setup 2026-07-30 15:24:22 -06:00
jpkg.policy.sexp jpkg: complete binary package setup 2026-07-30 15:24:22 -06:00
jpkg.sexp jpkg: complete binary package setup 2026-07-30 15:24:22 -06:00
LICENSE Switch to MIT license 2026-07-21 13:42:22 -06:00
LINUX-PIV-JSH-HANDOFF.md Add Linux PIV jsh handoff 2026-06-03 13:16:08 -06:00
Makefile Resolve security audit findings 2026-07-11 17:29:30 -06:00
PLAN-NATIVE.md Add yubikey-native: pure-Rust USB CCID transport. 2026-05-13 14:58:34 -06:00
PLAN.md Add native FIDO2 assertion API 2026-05-28 19:01:37 -06:00
README.md Resolve security audit findings 2026-07-11 17:29:30 -06:00
rust-toolchain.toml Resolve security audit findings 2026-07-11 17:29:30 -06:00
SECURITY.md Resolve security audit findings 2026-07-11 17:29:30 -06:00
yubikey.json Integrate YubiKey native backend updates 2026-06-21 13:05:19 -06:00

jerboa-yubikey

YubiKey support for Jerboa — a pure-Jerboa replacement for Yubico's ykman, plus a library that other Jerboa apps can use for YubiKey-backed authentication.

Status

Pre-alpha but usable: device info, OATH, OTP slot config, and PIV all work over built-in PC/SC. PIV can authenticate the management key, generate a key on-card, and decrypt / unwrap — enough to provision and unlock a slot with no external tool (no ykman, no yubico-piv-tool). A native FIDO2 assertion primitive (HID CTAP2) is also included. See PLAN.md for the roadmap.

What's in scope

Everything ykman does, in three milestones:

  • M1 — OATH + info. TOTP/HOTP credential management over CCID.
  • M2 — Challenge-response. HMAC-SHA1 for password derivation (KeePassXC-style). This is the feature that unlocks YubiKey-backed auth for jerboa-shell.
  • M3 — Management, PIV, OpenPGP, FIDO. Full ykman parity.

Why not just call ykman?

ykman is a Python CLI. Calling it from another program forces secrets through argv and stdout — a regression in security. As a library, jerboa-yubikey keeps secret material in-process.

CLI

umask 077
ykjr info                              # device info (serial, firmware, apps)
ykjr list                              # list connected YubiKeys

ykjr oath accounts list                # list OATH credentials
ykjr oath accounts code github:alice   # generate TOTP code
ykjr oath accounts add github:alice --secret-fd 3 3<oath-seed.txt
ykjr oath accounts import --uri-fd 3 3<otpauth-uri.txt
ykjr oath accounts import ./qr.png --qr          # import QR image via zbarimg
ykjr oath accounts import ./otpauth.txt          # import text containing URI

ykjr otp chalresp 2 --key-fd 3 3<otp-key.hex
ykjr otp chalresp 2 --generate --export-key-fd 3 3>otp-key.hex

ykjr piv info                          # PIV slots
ykjr piv pin verify --pin-fd 3 3<piv-pin.txt

With no dedicated-fd option, secret-bearing commands prompt on the controlling terminal with echo disabled. Secrets are never accepted through argv. Each named fd consumes exactly one newline-delimited value, so change/unblock commands may use separate fds or consecutive lines on one shared fd. Generated OTP keys are never printed and explicit export refuses a terminal descriptor.

Library usage (from another Jerboa app)

(import (yubikey auth))

;; Challenge-response: HMAC-SHA1 with key-resident secret.
(yubikey-chalresp 2 #vu8(0 1 2 ...))     ;; => bytevector(20)

;; Deterministic password derivation per host.
(yubikey-derive-password "host.example" 2)

;; OATH lookup.
(yubikey-oath-code "github:alice")       ;; => "123456"

;; WebAuthn/FIDO2 assertion for a service-supplied challenge.
(import (yubikey fido2))
(define assertion
  (yubikey-fido2-assert "proton.me" client-data-json credential-id))

client-data-json must be the exact WebAuthn clientDataJSON bytes sent back to the service. The native CTAP backend hashes those bytes once before asking the YubiKey to sign, and returns credential ID, authenticator data, and signature bytevectors.

PIV is the basis for "use my YubiKey to unlock an encrypted file" — provision a slot and unwrap a key with no external tool (built-in PC/SC). The management key is mutually authenticated (AES, default key) before GENERATE:

(import (yubikey piv) (yubikey transport connection))

(yk-with-connection
  (lambda (conn)
    ;; one-time provisioning (default AES-192 management key on YK 5.7+):
    (yk-piv-authenticate-management-key conn PIV-DEFAULT-MGMT-KEY)
    (yk-piv-generate-key conn PIV-SLOT-KEY-MGMT
      'algorithm: PIV-ALG-RSA-2048 'pin-policy: 3 'touch-policy: 3)   ;; → public key

    ;; later: unwrap a key wrapped to that public key (you do RSA-OAEP in software):
    (yk-piv-pin-verify conn "123456")
    (yk-piv-decrypt conn PIV-SLOT-KEY-MGMT wrapped-256-bytes)))       ;; → plaintext block

Build

make run ARGS='info'        # development (interpreter)
make test                   # tests
cargo build --manifest-path yubikey-native/Cargo.toml --release
                              # optional shared native backend
make binary                 # native binary (dynamic, requires Chez)
make release-evidence       # tests, binary smoke, audit, SBOM, reproducibility

Release evidence and dependency provenance are documented in docs/release-evidence.md and docs/dependency-provenance.md.

Dependencies

  • A Yubico YubiKey (any series, ideally YK 5 or later).
  • Transport:
    • Linux/BSD: the direct-USB Rust backend (yubikey-native, built by make binary) is the primary — no pcscd, no libpcsclite, no daemons. Linux just needs a udev rule granting your user access to the YubiKey's USB device node (the standard Yubico 69-yubikey.rules works). pcsc-lite is kept as an opportunistic fallback if the native backend fails to claim the device.
    • macOS: PC/SC via Apple's built-in PCSC.framework — nothing to install. The native backend is not used on macOS because the OS owns the CCID interface and won't let a second process claim it.
  • FIDO2 assertions use the native HID backend (CTAP2 is not a PC/SC applet), so they still need yubikey-native built.
  • Jerboa toolchain.
  • Optional QR image import: zbarimg (zbar-tools on many Linux distros).

License

Apache License 2.0. See LICENSE.