Skip to content

Commit

Permalink
Replace FIDO example with a dummy app
Browse files Browse the repository at this point in the history
Maintaining a working setup for the FIDO and admin app is a lot of work
and is redundant to the usbip runner provided as part of the Nitrokey 3
firmware.  Therefore, this patch replaces the fido example with a much
simpler example that just setups up a dummy client responding to a
vendor command that requests random data.

This is sufficient for basic testing and getting started.  For more
advanced use cases, the Nitrokey 3 runner should be used.

Fixes: #31
  • Loading branch information
robin-nitrokey committed Jun 6, 2024
1 parent 7aed9ff commit 503f0b5
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 266 deletions.
53 changes: 1 addition & 52 deletions Cargo.lock

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

11 changes: 1 addition & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,17 @@ clap = { version = "3.0.0", features = ["derive"] }
clap-num = "1.0.0"
delog = { version = "0.1.6", features = ["std-log"] }
pretty_env_logger = "0.4.0"
trussed = { version = "0.1", features = ["clients-3"] }

# applications
admin-app = { version = "0.1", features = ["log-all"] }
fido-authenticator = { version = "0.1", features = ["dispatch", "log-all"] }
trussed = { version = "0.1", features = ["clients-1"] }

[features]
default = ["ctaphid", "ccid"]
ctaphid = ["ctaphid-dispatch", "usbd-ctaphid"]
ccid = ["apdu-dispatch", "usbd-ccid"]

[[example]]
name = "fido"
required-features = ["ctaphid"]

[patch.crates-io]
trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "51e68500d7601d04f884f5e95567d14b9018a6cb" }

usbd-ctaphid = { git = "https://github.com/trussed-dev/usbd-ctaphid", rev = "e9cbf904f548979685c4c06d75479b75e3695160" }
usbd-ccid = { git = "https://github.com/trussed-dev/usbd-ccid", tag = "0.3.0" }
ctaphid-dispatch = { git = "https://github.com/trussed-dev/ctaphid-dispatch", rev = "57cb3317878a8593847595319aa03ef17c29ec5b" }
apdu-dispatch = { git = "https://github.com/trussed-dev/apdu-dispatch.git", rev = "b72d5eb9f4d7a3f107a78a2f0e41f3c403f4c7a4" }

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
EXAMPLE_NAME := fido
EXAMPLE_NAME := dummy

all: | start-sim attach finish-message

Expand Down
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
# USB/IP Simulation
# trussed-usbip

This runner allows using USB/IP as a means to simulate device connection
to the OS, and should allow faster development of the embedded applications.
This crate facilitates simulation of Trussed devices using USB/IP.
It should only be used for development and testing.

Remarks:
- Extensible with CTAP apps: currently FIDO and Admin are active;
- Does not work with Firefox at the moment;
- Allows to inject own FIDO certificates, and device properties;
- Requires multiple `usbip attach` calls to make it work [1].
- Works best with CTAPHID. CCID is supported but often unstable.

[1] https://github.com/Sawchord/usbip-device#known-bugs

## Examples

[`examples/dummy.rs`](`examples/dummy.rs`) contains a very simple example that
shows how to run a simulated Trussed device.
For a more complex example, see the [usbip runner][] of the Nitrokey 3 that
provides all features of the Nitrokey 3.

[usbip runner]: https://github.com/Nitrokey/nitrokey-3-firmware/tree/main/runners/usbip

## Setup

USB/IP tools are required to work, as well as kernel supporting it.
Expand Down
139 changes: 139 additions & 0 deletions examples/dummy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//! USB/IP simulation of a Trussed device.
//!
//! This example contains a dummy app that responds with random data to the CTAPHID vendor command
//! 0x60. It can be tested with `nitropy nk3 list` and `nitropy nk3 rng`.
//!
//! For a more complete example, see the [usbip runner][] for the Nitrokey 3.
//!
//! [usbip runner]: https://github.com/Nitrokey/nitrokey-3-firmware/tree/main/runners/usbip
use std::path::PathBuf;

#[cfg(feature = "ccid")]
use apdu_dispatch::command::SIZE as ApduCommandSize;
#[cfg(feature = "ctaphid")]
use ctaphid_dispatch::{
command::{Command, VendorCommand},
types::{AppResult, Error, Message},
};

use clap::Parser;
use clap_num::maybe_hex;
use trussed::{
backend::CoreOnly,
client::{Client, ClientBuilder},
service::Service,
syscall,
types::Vec,
virt::{self, Platform, StoreProvider},
};
use trussed_usbip::Syscall;

/// USP/IP based virtualization a Trussed device.
#[derive(Parser, Debug)]
#[clap(about, version, author)]
struct Args {
/// USB Name string
#[clap(short, long, default_value = "Trussed")]
name: String,

/// USB Manufacturer string
#[clap(short, long, default_value = "Trussed")]
manufacturer: String,

/// Trussed state file
#[clap(long, default_value = "trussed-state.bin")]
state_file: PathBuf,

/// USB VID id
#[clap(short, long, parse(try_from_str=maybe_hex), default_value_t = 0x20a0)]
vid: u16,

/// USB PID id
#[clap(short, long, parse(try_from_str=maybe_hex), default_value_t = 0x42b2)]
pid: u16,
}

struct DummyApp<C: Client> {
client: C,
}

impl<C: Client> DummyApp<C> {
fn rng<const N: usize>(&mut self, response: &mut Vec<u8, N>) {
let bytes = syscall!(self.client.random_bytes(57)).bytes;
response.extend_from_slice(&bytes).unwrap();
}
}

#[cfg(feature = "ctaphid")]
const CTAPHID_COMMAND_RNG: Command = Command::Vendor(VendorCommand::H60);

#[cfg(feature = "ctaphid")]
impl<'a, C: Client> ctaphid_dispatch::app::App<'a> for DummyApp<C> {
fn commands(&self) -> &'static [Command] {
&[CTAPHID_COMMAND_RNG]
}

fn call(&mut self, command: Command, _request: &Message, response: &mut Message) -> AppResult {
match command {
CTAPHID_COMMAND_RNG => self.rng(response),
_ => return Err(Error::InvalidCommand),
}
Ok(())
}
}

struct Apps<C: Client> {
dummy: DummyApp<C>,
}

impl<'a, S: StoreProvider> trussed_usbip::Apps<'a, S, CoreOnly>
for Apps<trussed_usbip::Client<CoreOnly>>
{
type Data = ();

fn new(service: &mut Service<Platform<S>, CoreOnly>, syscall: Syscall, _data: ()) -> Self {
let client = ClientBuilder::new("dummy")
.prepare(service)
.unwrap()
.build(syscall);
let dummy = DummyApp { client };
Self { dummy }
}

#[cfg(feature = "ctaphid")]
fn with_ctaphid_apps<T>(
&mut self,
f: impl FnOnce(&mut [&mut dyn ctaphid_dispatch::app::App<'a>]) -> T,
) -> T {
f(&mut [&mut self.dummy])
}

#[cfg(feature = "ccid")]
fn with_ccid_apps<T>(
&mut self,
f: impl FnOnce(&mut [&mut dyn apdu_dispatch::app::App<ApduCommandSize, ApduCommandSize>]) -> T,
) -> T {
f(&mut [])
}
}

fn main() {
pretty_env_logger::init();

let args = Args::parse();

let store = virt::Filesystem::new(args.state_file);
let options = trussed_usbip::Options {
manufacturer: Some(args.manufacturer),
product: Some(args.name),
serial_number: None,
vid: args.vid,
pid: args.pid,
};

log::info!("Initializing Trussed");
trussed_usbip::Builder::new(store, options)
.build::<Apps<_>>()
.exec(|_| ());
}
Loading

0 comments on commit 503f0b5

Please sign in to comment.