Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cacd84c
Revise project structure in README.md
rainzhang05 Nov 5, 2025
be1caa4
Revise README for tech stack and dependency updates
rainzhang05 Nov 5, 2025
9188fa3
Add libfido2 installation commands to README
rainzhang05 Nov 5, 2025
1af9d13
Update for clarity and feature highlights
rainzhang05 May 25, 2026
d62de0f
Merge branch 'main' into main
rainzhang05 May 25, 2026
ccb6027
Remove tracked scratch artifact and tighten .gitignore
rainzhang05 May 25, 2026
2969767
Move maintainer scratchpad to docs/development-notes.md
rainzhang05 May 25, 2026
dae5119
Fix README curl line and replace placeholder author fields
rainzhang05 May 25, 2026
b89b206
Drop duplicate authenticator bin in pc-hid-runner
rainzhang05 May 25, 2026
48c4c4d
Pin toolchain to 1.83 and add rustfmt config
rainzhang05 May 25, 2026
691cf07
Drop pc-usbip-runner and patched usbip-device/ssmarshal crates
rainzhang05 May 25, 2026
e6a6ad2
Remove experimental PQC PIN/UV protocol 101 and ML-KEM dependency
rainzhang05 May 25, 2026
2d6b13b
Delete trussed-mlkem crate
rainzhang05 May 25, 2026
64ab65c
Replace liboqs FFI in trussed-mldsa with pure-Rust fips204 crate
rainzhang05 May 25, 2026
94b1626
Delete prebuilt_liboqs blobs and update README
rainzhang05 May 25, 2026
0e3a81b
Add basic GitHub Actions CI (fmt, clippy, test)
rainzhang05 May 25, 2026
198d917
Expose persistent PIN state and reset helpers from transport-core
rainzhang05 May 25, 2026
f006bd6
Add PIN and reset service helpers to pc-hid-runner
rainzhang05 May 25, 2026
a91efec
Add attach/detach aliases, reset, and pin subcommands to CLI
rainzhang05 May 25, 2026
444108d
Persist authenticator PIN state across daemon restarts
rainzhang05 May 25, 2026
fc7dccf
Implement CTAP authenticatorReset (command 0x07)
rainzhang05 May 25, 2026
996740c
Document the unified CLI surface and supported algorithms
rainzhang05 May 25, 2026
10c8cb0
Use stable toolchain and apply rustfmt fixes
rainzhang05 May 25, 2026
9275804
Relax CI clippy gate to warnings while older code is cleaned up
rainzhang05 May 25, 2026
46bc0d3
Drop stale ccid feature scaffolding and use default features in CI tests
rainzhang05 May 25, 2026
4f12926
Make ML-DSA sign() degrade instead of panicking on bad inputs
rainzhang05 May 25, 2026
52424aa
Fix stale CTAPHID test offsets and drop dead descriptor rewriter
rainzhang05 May 25, 2026
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
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

env:
CARGO_TERM_COLOR: always

jobs:
fmt:
name: cargo fmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check

clippy:
name: cargo clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libudev-dev libclang-dev
- uses: Swatinem/rust-cache@v2
# Warnings are surfaced but not treated as errors yet; existing
# transport-core code triggers a number of clippy nits that will be
# cleaned up in follow-up commits.
- run: cargo clippy --workspace --all-targets

test:
name: cargo test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libudev-dev libclang-dev
- uses: Swatinem/rust-cache@v2
- run: cargo test --workspace
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
.idea
target
Cargo.lock

# Editor / OS noise
.DS_Store
*.swp
*~

# Tooling scratch artifacts (e.g. download-ripgrepXXXXXX/)
download-ripgrep*/
*.tmp
11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
members = [
"trussed-mldsa",
"authenticator",
"trussed-mlkem",
"pc-usbip-runner",
"pc-hid-runner",
"transport-core"
]

resolver = "2"

[workspace.package]
edition = "2021"
license = "Apache-2.0 OR MIT"
authors = ["Feitian Technologies <info@ftsafe.com>"]
repository = "https://github.com/FeitianTech/FidoSoftwareAuthenticator"
rust-version = "1.85"

[patch.crates-io]
trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "024e0eca5fb7dbd2457831f7c7bffe4341e08775" }
usbip-device = { path = "patches/usbip-device" }
ssmarshal = { path = "patches/ssmarshal" }
238 changes: 99 additions & 139 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,176 +1,136 @@
# FIDO Software Authenticator

Software authenticator implementing post-quantum ML-DSA 44/65/87 with a Trussed-based stack in Rust, using the liboqs library for ML-DSA algorithm implementations. The host runner provisions a virtual HID token through `/dev/uhid`, letting browsers and libfido2 talk to the authenticator without custom kernel modules. A legacy USB/IP transport remains available for environments that still need it. The project exposes a FIDO2/CTAP2 HID interface and CCID that browsers and tooling can use for WebAuthn.
A software-based FIDO2/WebAuthn authenticator written in Rust. It uses the
Trussed framework and provides **Post-Quantum Cryptography** signatures
(ML-DSA-44/65/87 per FIPS 204) alongside classical ES256, with no C
dependencies — the cryptography is entirely pure Rust via the
[`fips204`](https://docs.rs/fips204) crate.

Highlights
- Algorithms: ML-DSA 44/65/87, ES256.
- Transports: USB HID (CTAPHID) presented through `/dev/uhid`. Optional CCID smartcard interface for smartcard-style applications.
- Runner: Host UHID runner that creates a virtual hidraw node and bridges CTAPHID to Trussed. Optional USB/IP backend for legacy testing.
The application runs on a Linux host and provisions a virtual HID token
through `/dev/uhid`. Web browsers and tools like `libfido2` interact with it
as if it were a physical hardware security key — no custom kernel modules
required.

## Tech stack
### Key Features
* **Algorithms:** Post-quantum ML-DSA-44/65/87 (FIPS 204) and standard ES256.
* **Virtual Hardware:** Acts as a standard USB HID (CTAPHID) device via `/dev/uhid`.
* **Smartcard Support:** Optional CCID interface.
* **Pure-Rust crypto:** No C toolchain or prebuilt `liboqs` binaries required.

- Languages and FFI: Rust across the workspace crates, C via liboqs for ML-DSA and ML-KEM bindings consumed by `trussed-mldsa` and `trussed-mlkem`.
- Core crates:
- `authenticator` - Trussed application that implements CTAP2/CTAPHID and CCID flows on top of the PQC wrappers.
- `transport-core` - shared storage/state crate providing littlefs-backed persistence, attestation helpers, and CTAP/CCID glue code.
- `pc-hid-runner` - host runner exposing `/dev/uhid`.
- `pc-usbip-runner` (`trussed-usbip`) - legacy USB/IP transport kept for regression coverage.
- `trussed-mldsa` & `trussed-mlkem` - thin zeroise-aware wrappers around liboqs ML-DSA/ML-KEM exports.
- Key ecosystem crates: Trussed (patched to commit `024e0ec`), `ctaphid-dispatch`, `usbd-ctaphid`, `usbd-ccid`, `apdu-dispatch`, `littlefs2`, `ciborium`, `serde`, `rcgen`, `p256`, `chacha20`, `rand_chacha`, `nix`, `signal-hook`, `daemonize`, and `clap`.
- Tooling for validation: `libfido2`/`python-fido2` for host interoperability tests, and USB/IP utilities for the legacy backend.
---

## Patches and runner tools
## Prerequisites

- Crate patches:
- `trussed` is pinned to upstream commit `024e0ec` for the Trussed features used by the authenticator stack.
- `usbip-device` is vendored under `patches/usbip-device` with fixes for packet sizing, speed reporting, and EP0 hygiene to stabilise the legacy USB/IP transport.
- `ssmarshal` lives under `patches/ssmarshal` to guarantee the `no_std` feature set and serde 1.0 compatibility relied on by `transport-core`.
- Runner tooling:
- `pc-hid-runner` offers `/dev/uhid`, foreground/background service modes, permission checks, and helpers aligned with the packaged `contrib/udev` rule and `systemd` service unit.
- `pc-usbip-runner (patch)` continues to expose the authenticator over USB/IP, with examples under `pc-usbip-runner/examples/` for CTAPHID and CCID exercise.
You will need a Linux host with Rust and a standard C toolchain installed.

## Project structure

```
.
|-- authenticator/
| |-- Cargo.toml
| `-- src/
| |-- ctap.rs
| |-- ctap/
| | `-- tests.rs
| `-- lib.rs
|-- contrib/
| |-- systemd/
| | `-- feitian-authenticator.service
| `-- udev/
| `-- 70-feitian-authenticator.rules
|-- download-ripgrepSCJSfZ/
| `-- tmp-file
|-- patches/
| |-- ssmarshal/
| `-- usbip-device/
|-- pc-hid-runner/
| |-- Cargo.toml
| `-- src/
| |-- bin/
| | `-- authenticator.rs
| |-- cli.rs
| |-- lib.rs
| |-- permissions.rs
| |-- service.rs
| |-- transport/
| | |-- ctaphid_host.rs
| | `-- mod.rs
| `-- uhid.rs
|-- pc-usbip-runner/
| |-- Cargo.toml
| |-- Dockerfile
| |-- Makefile
| |-- README.md
| |-- examples/
| `-- src/
|-- prebuilt_liboqs/
| |-- linux-aarch64/
| | |-- include/oqs/
| | `-- lib/
| `-- linux-x86_64/
| |-- include/oqs/
| `-- lib/
|-- target/
| `-- ... (build artifacts)
|-- transport-core/
| |-- Cargo.toml
| `-- src/
| |-- ctap/
| |-- lib.rs
| |-- logging.rs
| `-- state.rs
|-- trussed-mldsa/
| |-- Cargo.toml
| |-- build.rs
| `-- src/lib.rs
|-- trussed-mlkem/
| |-- Cargo.toml
| |-- build.rs
| `-- src/lib.rs
|-- Cargo.toml
`-- README.md
```

## Dependencies

**System (Linux host)**
- Rust toolchain via `rustup` (2021 edition-compatible toolchain recommended).
- A C toolchain and binutils (`nm`) for linking against liboqs from `prebuilt_liboqs/`.
- Optional testing utilities: `libfido2-tools`, `python3-fido2`, and the USB/IP userspace (`usbip`, `vhci-hcd`) for exercising the legacy transport.
- Service integration helpers: `systemd` (to deploy `contrib/systemd/feitian-authenticator.service`) and udev (for `contrib/udev/70-feitian-authenticator.rules`).

**Rust crates of note**
- Transport and protocol layers: `ctaphid-dispatch`, `usbd-ctaphid`, `usbd-ccid`, `apdu-dispatch`, `transport-core`.
- Storage and serialization: `littlefs2`, `littlefs2-core`, `ciborium`, vendored `ssmarshal`, `serde`.
- Host runner and orchestration: `nix`, `signal-hook`, `daemonize`, `clap`, `heapless-bytes`.
- Cryptography: `trussed` (patched), `trussed-mldsa`, `trussed-mlkem`, `p256`, `rcgen`, `sha2`, `chacha20`, `rand_chacha`, `zeroize`.

Install (example on Ubuntu/Debian)
**Ubuntu/Debian Setup:**
```bash
# Rust (recommended):
curl https://sh.rustup.rs -sSf | sh
# 1. Install Rust (if you haven't already)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# System packages:
# 2. Install required system packages
sudo apt update
sudo apt install -y build-essential pkg-config libclang-dev libudev-dev

# Optional testing and legacy-runner tools:
# (Optional) Install tools for testing
sudo apt install -y libfido2-1 libfido2-dev libfido2-tools usbip python3-pip
```

## Build
# At the repository root:
cargo build # Debug build
cargo build --release # Release build
```

## Select a liboqs bundle
---

Prebuilt ML-DSA binaries are supplied under `prebuilt_liboqs/`. Choose the directory that matches your host architecture, then add the `lib` folder to your `LD_LIBRARY_PATH` (or install the contents under `/usr/local`):
## Quick Start

### 1. Build the Project
From the repository root, build the project using Cargo:
```bash
# For x86_64 Linux
export LD_LIBRARY_PATH="$PWD/prebuilt_liboqs/linux-x86_64/lib:${LD_LIBRARY_PATH:-}"

# For aarch64 Linux
export LD_LIBRARY_PATH="$PWD/prebuilt_liboqs/linux-aarch64/lib:${LD_LIBRARY_PATH:-}"
cargo build --release
```

If you build liboqs yourself, ensure the resulting shared library matches the target architecture of the runner binary.
The cryptography is pure Rust — there is no `liboqs` build step and nothing
to add to `LD_LIBRARY_PATH`.

## Run the HID runner
### 2. Setup Virtual HID Permissions
To allow the authenticator to create a virtual USB device, you need to load the `uhid` kernel module and grant your user the correct permissions.

The HID runner provisions a virtual authenticator through `/dev/uhid`, creating a `hidraw` node that browsers can use transparently. From the root of the repository:
```bash
# Starting with clean state
sudo pkill -f pc-hid-runner || true
sudo rmmod uhid 2>/dev/null || true
# Load the uhid module
sudo modprobe uhid

# Configure permission
# Create a udev rule to grant access to the 'plugdev' group
echo 'KERNEL=="uhid", MODE="0660", GROUP="plugdev"' | sudo tee /etc/udev/rules.d/70-uhid.rules
sudo udevadm control --reload-rules
sudo udevadm trigger

# Apply permissions immediately
sudo chown root:plugdev /dev/uhid
sudo chmod 660 /dev/uhid
newgrp plugdev
```

### 3. Run the Authenticator
Launch the virtual authenticator in the foreground. It will automatically
handle WebAuthn requests from your browser:

# Launching the runner
RUST_LOG=info cargo run -p pc-hid-runner -- start --foreground
```bash
RUST_LOG=info cargo run -p pc-hid-runner -- attach --foreground
```

Useful flags:
`attach` and `detach` are the new primary verbs; the legacy `start` and
`stop` aliases continue to work.

---

## CLI Reference

The `pc-hid-runner` binary is the single management tool. Every subcommand
takes an optional `--state-dir <path>` (defaults to
`$XDG_DATA_HOME/feitian-mldsa-authenticator`).

### Lifecycle

| Command | Purpose |
|---------|---------|
| `pc-hid-runner attach [--foreground]` | Start the daemon and expose the virtual security key |
| `pc-hid-runner detach` | Stop a running daemon and remove the virtual device |
| `pc-hid-runner status` | Report whether the daemon is currently running |
| `pc-hid-runner reset [--yes]` | Wipe all credentials and PIN state (daemon must be detached) |

### PIN management

`pin` subcommands must be run with the daemon detached; the daemon reloads
the persisted PIN state on its next `attach`.

| Command | Purpose |
|---------|---------|
| `pc-hid-runner pin status` | Show whether a PIN is set, retries remaining, and blocked state |
| `pc-hid-runner pin set [--pin <PIN>]` | Set a new PIN on a PIN-less device |
| `pc-hid-runner pin change [--current <PIN> --new <PIN>]` | Change the existing PIN |
| `pc-hid-runner pin remove [--current <PIN>]` | Remove the PIN entirely |

When `--pin` / `--current` / `--new` are omitted, the CLI reads the values
interactively from stdin.

### Attach-time flags

| Flag | Purpose |
|------|---------|
| `--foreground` | Run in the foreground (useful for systemd integration) |
| `--manual-user-presence` | Require manual approval rather than auto-approving user presence |
| `--suppress-attestation` | Hide attestation certificates for privacy testing |

---

## Supported Algorithms

The authenticator advertises all of the following COSE algorithms in
`authenticatorGetInfo` and will register and assert credentials against any
of them:

- `--state-dir <path>` — override the persistent storage directory (defaults to `$XDG_DATA_HOME/feitian-mldsa-authenticator`)
- `--manual-user-presence` — require manual approval of user presence (auto-UP remains the default)
- `--suppress-attestation` — mask attestation certs for privacy testing
- `--pqc-policy <prefer|required|disabled>` — choose the PQC PIN/UV transport policy
* `ES256` (-7) — classical NIST P-256 ECDSA
* `ML-DSA-44` (-48) — post-quantum, NIST level 2
* `ML-DSA-65` (-49) — post-quantum, NIST level 3
* `ML-DSA-87` (-50) — post-quantum, NIST level 5

Omit `--foreground` to run the service as a background daemon. The CLI also exposes `status` and `stop` subcommands that inspect or terminate that daemonised instance.
PIN/UV uses the standard CTAP2.1 protocols (1 and 2). The authenticator
also handles `authenticatorReset` (CTAP command 0x07) so credentials can be
wiped over the wire if needed.
Loading
Loading