Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CI workflow #36

Merged
merged 2 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

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

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install Rust toolchain
run: |
rustup show

- name: Install build dependencies
run: >
sudo apt-get update -y -qq &&
sudo apt-get install -y -qq llvm libclang-dev


- name: Check all targets
run: |
cargo check --workspace --all-targets --no-default-features
cargo check --workspace --all-targets --no-default-features --features ctaphid
cargo check --workspace --all-targets --no-default-features --features ccid
cargo check --workspace --all-targets
cargo check --workspace --all-targets --all-features

- name: Check formatting
run: cargo fmt --all -- --check

- name: Check clippy lints
run: cargo clippy --all-features --all-targets -- --deny warnings

- name: Check documentation
run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
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
Loading