mirror of
https://github.com/trussed-dev/pc-usbip-runner.git
synced 2026-06-20 04:16:19 -07:00
Replace FIDO example with a dummy app
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 sets 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: https://github.com/trussed-dev/pc-usbip-runner/issues/31
This commit is contained in:
Generated
+1
-52
@@ -2,19 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "admin-app"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67d9f4831720ac3f95d708922b71cbec0d085548affb935f4af35395af28366c"
|
||||
dependencies = [
|
||||
"apdu-dispatch",
|
||||
"ctaphid-dispatch",
|
||||
"delog",
|
||||
"iso7816",
|
||||
"trussed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
@@ -521,26 +508,6 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fido-authenticator"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda469ecf5b58ba898e1a6e68f99529b568e24490f45ced8222240d10d7c6508"
|
||||
dependencies = [
|
||||
"apdu-dispatch",
|
||||
"ctap-types",
|
||||
"ctaphid-dispatch",
|
||||
"delog",
|
||||
"heapless",
|
||||
"interchange 0.2.2",
|
||||
"iso7816",
|
||||
"littlefs2 0.3.2",
|
||||
"serde",
|
||||
"serde-indexed",
|
||||
"serde_cbor",
|
||||
"trussed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flexiber"
|
||||
version = "0.1.0"
|
||||
@@ -773,22 +740,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "littlefs2"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dc089501e32d62b3e4d809a29b9e00d6211a197440602d69f304f1e4e82136b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cstr_core",
|
||||
"cty",
|
||||
"delog",
|
||||
"generic-array",
|
||||
"heapless",
|
||||
"littlefs2-sys",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "littlefs2"
|
||||
version = "0.4.0"
|
||||
@@ -1517,7 +1468,7 @@ dependencies = [
|
||||
"hex-literal",
|
||||
"hmac 0.12.1",
|
||||
"interchange 0.3.0",
|
||||
"littlefs2 0.4.0",
|
||||
"littlefs2",
|
||||
"nb 1.1.0",
|
||||
"p256-cortex-m4",
|
||||
"postcard",
|
||||
@@ -1535,13 +1486,11 @@ dependencies = [
|
||||
name = "trussed-usbip"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"admin-app",
|
||||
"apdu-dispatch",
|
||||
"clap",
|
||||
"clap-num",
|
||||
"ctaphid-dispatch",
|
||||
"delog",
|
||||
"fido-authenticator",
|
||||
"interchange 0.3.0",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
|
||||
+1
-10
@@ -24,21 +24,13 @@ 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" }
|
||||
|
||||
@@ -46,4 +38,3 @@ usbd-ctaphid = { git = "https://github.com/trussed-dev/usbd-ctaphid", rev = "e9c
|
||||
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" }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
EXAMPLE_NAME := fido
|
||||
EXAMPLE_NAME := dummy
|
||||
|
||||
all: | start-sim attach finish-message
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(|_| ());
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[cfg(feature = "ccid")]
|
||||
use apdu_dispatch::command::SIZE as ApduCommandSize;
|
||||
|
||||
use clap::Parser;
|
||||
use clap_num::maybe_hex;
|
||||
use log::info;
|
||||
use trussed::platform::{consent, reboot, ui};
|
||||
use trussed::{backend::Dispatch, virt, Client, Platform};
|
||||
use trussed_usbip::ClientBuilder;
|
||||
|
||||
use fido_authenticator::TrussedRequirements;
|
||||
use usbd_ctaphid::constants::MESSAGE_SIZE;
|
||||
|
||||
pub type FidoConfig = fido_authenticator::Config;
|
||||
|
||||
/// USP/IP based virtualization of the Nitrokey 3 / Solo2 device.
|
||||
/// Supports FIDO application at the moment.
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(about, version, author)]
|
||||
struct Args {
|
||||
/// USB Name string
|
||||
#[clap(short, long, default_value = "FIDO authenticator")]
|
||||
name: String,
|
||||
|
||||
/// USB Manufacturer string
|
||||
#[clap(short, long, default_value = "Simulation")]
|
||||
manufacturer: String,
|
||||
|
||||
/// USB Serial string
|
||||
#[clap(long, default_value = "SIM SIM SIM")]
|
||||
serial: String,
|
||||
|
||||
/// Trussed state file
|
||||
#[clap(long, default_value = "trussed-state.bin")]
|
||||
state_file: PathBuf,
|
||||
|
||||
/// FIDO attestation key
|
||||
#[clap(long)]
|
||||
fido_key: Option<PathBuf>,
|
||||
|
||||
/// FIDO attestation cert
|
||||
#[clap(long)]
|
||||
fido_cert: Option<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 Reboot;
|
||||
|
||||
impl admin_app::Reboot for Reboot {
|
||||
fn reboot() -> ! {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn reboot_to_firmware_update() -> ! {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn reboot_to_firmware_update_destructive() -> ! {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn locked() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct UserInterface {
|
||||
start_time: std::time::Instant,
|
||||
}
|
||||
|
||||
impl UserInterface {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
start_time: std::time::Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl trussed::platform::UserInterface for UserInterface {
|
||||
/// Prompt user to type a word for confirmation
|
||||
fn check_user_presence(&mut self) -> consent::Level {
|
||||
// use std::io::Read as _;
|
||||
// This is not nice - we should "peek" and return Level::None
|
||||
// if there is no key pressed yet (unbuffered read from stdin).
|
||||
// Couldn't get this to work (without pulling in ncurses or similar).
|
||||
// std::io::stdin().bytes().next();
|
||||
consent::Level::Normal
|
||||
}
|
||||
|
||||
fn set_status(&mut self, status: ui::Status) {
|
||||
info!("Set status: {:?}", status);
|
||||
|
||||
if status == ui::Status::WaitingForUserPresence {
|
||||
info!(">>>> Received confirmation request. Confirming automatically.");
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh(&mut self) {}
|
||||
|
||||
fn uptime(&mut self) -> core::time::Duration {
|
||||
self.start_time.elapsed()
|
||||
}
|
||||
|
||||
fn reboot(&mut self, to: reboot::To) -> ! {
|
||||
info!("Restart! ({:?})", to);
|
||||
std::process::exit(25);
|
||||
}
|
||||
}
|
||||
|
||||
struct Apps<C: Client + TrussedRequirements> {
|
||||
fido: fido_authenticator::Authenticator<fido_authenticator::Conforming, C>,
|
||||
admin: admin_app::App<C, Reboot>,
|
||||
}
|
||||
|
||||
impl<C: Client + TrussedRequirements, D: Dispatch> trussed_usbip::Apps<C, D> for Apps<C> {
|
||||
type Data = ();
|
||||
|
||||
fn new<B: ClientBuilder<C, D>>(builder: &B, _data: ()) -> Self {
|
||||
let fido = fido_authenticator::Authenticator::new(
|
||||
builder.build("fido", &[]),
|
||||
fido_authenticator::Conforming {},
|
||||
fido_authenticator::Config {
|
||||
max_msg_size: MESSAGE_SIZE,
|
||||
skip_up_timeout: None,
|
||||
},
|
||||
);
|
||||
let admin = admin_app::App::new(builder.build("admin", &[]), [0; 16], 0);
|
||||
Self { fido, admin }
|
||||
}
|
||||
|
||||
fn with_ctaphid_apps<T>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut [&mut dyn ctaphid_dispatch::app::App]) -> T,
|
||||
) -> T {
|
||||
f(&mut [&mut self.fido, &mut self.admin])
|
||||
}
|
||||
|
||||
#[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: Some(args.serial),
|
||||
vid: args.vid,
|
||||
pid: args.pid,
|
||||
};
|
||||
|
||||
log::info!("Initializing Trussed");
|
||||
trussed_usbip::Builder::new(store, options)
|
||||
.init_platform(move |platform| {
|
||||
let ui: Box<dyn trussed::platform::UserInterface + Send + Sync> =
|
||||
Box::new(UserInterface::new());
|
||||
platform.user_interface().set_inner(ui);
|
||||
|
||||
if let Some(fido_key) = &args.fido_key {
|
||||
store_file(platform, fido_key, "fido/sec/00");
|
||||
}
|
||||
if let Some(fido_cert) = &args.fido_cert {
|
||||
store_file(platform, fido_cert, "fido/x5c/00");
|
||||
}
|
||||
})
|
||||
.build::<Apps<_>>()
|
||||
.exec(|_| ());
|
||||
}
|
||||
|
||||
fn store_file(platform: &impl Platform, host_file: &Path, device_file: &str) {
|
||||
log::info!("Writing {} to file system", device_file);
|
||||
let data = std::fs::read(host_file).expect("failed to read file");
|
||||
trussed::store::store(
|
||||
platform.store(),
|
||||
trussed::types::Location::Internal,
|
||||
&trussed::types::PathBuf::from(device_file),
|
||||
&data,
|
||||
)
|
||||
.expect("failed to store file");
|
||||
}
|
||||
Reference in New Issue
Block a user