From e9b8077fc2b01b4520d9d5ed6b93d033c8c38d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 5 Apr 2023 16:38:46 +0200 Subject: [PATCH] Add trussed-auth as backend dependency --- Cargo.toml | 11 ++- examples/usbip.rs | 31 +++++--- examples/vpicc.rs | 4 +- src/lib.rs | 2 + src/virt.rs | 183 +++++++++++++++++++++++++++++++++++++++++++++ src/vpicc.rs | 7 +- tests/card/mod.rs | 4 +- tests/setup/mod.rs | 10 ++- 8 files changed, 226 insertions(+), 26 deletions(-) create mode 100644 src/virt.rs diff --git a/Cargo.toml b/Cargo.toml index 902a8d1..88d11a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ required-features = ["vpicc"] [[example]] name = "usbip" -required-features = ["apdu-dispatch"] +required-features = ["apdu-dispatch", "virt"] [dependencies] apdu-dispatch = { version = "0.1", optional = true } @@ -29,6 +29,7 @@ interchange = "0.2.2" iso7816 = "0.1" serde = { version = "1", default-features = false, features = ["derive"] } trussed = { version = "0.1" } +trussed-auth = { version = "0.2" } untrusted = "0.9" vpicc = { version = "0.1.0", optional = true } log = "0.4" @@ -52,7 +53,7 @@ stoppable_thread = "0.2.1" expectrl = "0.6.0" # Examples -trussed-usbip = { git = "https://github.com/trussed-dev/pc-usbip-runner", default-features = false, features = ["ccid"], rev = "d2957b6c24c2b0cafbbfacd6fecd62c80943630b"} +trussed-usbip = { git = "https://github.com/trussed-dev/pc-usbip-runner", default-features = false, features = ["ccid"], rev = "f3a680ca4c9a1411838ae0774f1713f79d4c2979"} usbd-ccid = { version = "0.2.0", features = ["highspeed-usb"]} rand = "0.8.5" @@ -60,7 +61,8 @@ rand = "0.8.5" default = [] strict-pin = [] std = [] -vpicc = ["std", "dep:vpicc", "trussed-rsa-alloc/virt"] +vpicc = ["std", "dep:vpicc", "virt"] +virt = ["std"] pivy-tests = [] opensc-tests = [] @@ -72,7 +74,8 @@ log-warn = [] log-error = [] [patch.crates-io] - trussed = { git = "https://github.com/Nitrokey/trussed", tag = "v0.1.0-nitrokey.8"} +trussed = { git = "https://github.com/Nitrokey/trussed", tag = "v0.1.0-nitrokey.8"} +trussed-auth = { git = "https://github.com/trussed-dev/trussed-auth", tag = "v0.2.1"} littlefs2 = { git = "https://github.com/Nitrokey/littlefs2", tag = "v0.3.2-nitrokey-2" } [profile.dev.package.rsa] diff --git a/examples/usbip.rs b/examples/usbip.rs index fa62ce9..065eb86 100644 --- a/examples/usbip.rs +++ b/examples/usbip.rs @@ -3,9 +3,15 @@ use trussed::virt::{self, Ram, UserInterface}; use trussed::{ClientImplementation, Platform}; +use trussed_usbip::ClientBuilder; -use piv_authenticator as piv; -use trussed_usbip::Syscall; +use piv_authenticator::{ + self as piv, + virt::dispatch::{self, Dispatch}, +}; + +type VirtClient = + ClientImplementation, dispatch::Dispatch>; const MANUFACTURER: &str = "Nitrokey"; const PRODUCT: &str = "Nitrokey 3"; @@ -13,16 +19,17 @@ const VID: u16 = 0x20a0; const PID: u16 = 0x42b2; struct PivApp { - piv: piv::Authenticator>>>, + piv: piv::Authenticator, } -impl trussed_usbip::Apps>>, ()> for PivApp { - fn new( - make_client: impl Fn(&str) -> ClientImplementation>>, - _data: (), - ) -> Self { +impl trussed_usbip::Apps for PivApp { + type Data = (); + fn new>(builder: &B, _data: ()) -> Self { PivApp { - piv: piv::Authenticator::new(make_client("piv"), piv::Options::default()), + piv: piv::Authenticator::new( + builder.build("piv", dispatch::BACKENDS), + piv::Options::default(), + ), } } @@ -44,11 +51,13 @@ fn main() { vid: VID, pid: PID, }; - trussed_usbip::Runner::new(virt::Ram::default(), options) + trussed_usbip::Builder::new(virt::Ram::default(), options) + .dispatch(Dispatch::new()) .init_platform(move |platform| { let ui: Box = Box::new(UserInterface::new()); platform.user_interface().set_inner(ui); }) - .exec::(|_platform| {}); + .build::() + .exec(|_platform| {}); } diff --git a/examples/vpicc.rs b/examples/vpicc.rs index e4002d8..687eed5 100644 --- a/examples/vpicc.rs +++ b/examples/vpicc.rs @@ -11,12 +11,12 @@ // TODO: add CLI -use piv_authenticator::{Authenticator, Options}; +use piv_authenticator::{virt::with_ram_client, Authenticator, Options}; fn main() { env_logger::init(); - trussed_rsa_alloc::virt::with_ram_client("piv-authenticator", |client| { + with_ram_client("piv-authenticator", |client| { let card = Authenticator::new(client, Options::default()); let mut vpicc_card = piv_authenticator::vpicc::VpiccCard::new(card); let vpicc = vpicc::connect().expect("failed to connect to vpicc"); diff --git a/src/lib.rs b/src/lib.rs index 08a7c58..7f6bec4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,8 @@ mod tlv; pub use piv_types::{AsymmetricAlgorithms, Pin, Puk}; +#[cfg(feature = "virt")] +pub mod virt; #[cfg(feature = "vpicc")] pub mod vpicc; diff --git a/src/virt.rs b/src/virt.rs new file mode 100644 index 0000000..b5d21c7 --- /dev/null +++ b/src/virt.rs @@ -0,0 +1,183 @@ +// Copyright (C) 2022 Nitrokey GmbH +// SPDX-License-Identifier: LGPL-3.0-only + +//! Virtual trussed client (mostly for testing) + +pub mod dispatch { + + use trussed::{ + api::{reply, request, Reply, Request}, + backend::{Backend as _, BackendId}, + error::Error, + platform::Platform, + serde_extensions::{ExtensionDispatch, ExtensionId, ExtensionImpl as _}, + service::ServiceResources, + types::{Bytes, Context, Location}, + }; + use trussed_auth::{AuthBackend, AuthContext, AuthExtension, MAX_HW_KEY_LEN}; + + use trussed_rsa_alloc::SoftwareRsa; + + /// Backends used by opcard + pub const BACKENDS: &[BackendId] = &[ + BackendId::Custom(Backend::Auth), + BackendId::Custom(Backend::Rsa), + BackendId::Core, + ]; + + #[derive(Debug, Clone, Copy)] + pub enum Backend { + Auth, + Rsa, + } + + #[derive(Debug, Clone, Copy)] + pub enum Extension { + Auth, + } + + impl From for u8 { + fn from(extension: Extension) -> Self { + match extension { + Extension::Auth => 0, + } + } + } + + impl TryFrom for Extension { + type Error = Error; + + fn try_from(id: u8) -> Result { + match id { + 0 => Ok(Extension::Auth), + _ => Err(Error::InternalError), + } + } + } + + /// Dispatch implementation with the backends required by opcard + #[derive(Debug)] + pub struct Dispatch { + auth: AuthBackend, + } + + /// Dispatch context for the backends required by opcard + #[derive(Default, Debug)] + pub struct DispatchContext { + auth: AuthContext, + } + + impl Dispatch { + pub fn new() -> Self { + Self { + auth: AuthBackend::new(Location::Internal), + } + } + + pub fn with_hw_key(hw_key: Bytes) -> Self { + Self { + auth: AuthBackend::with_hw_key(Location::Internal, hw_key), + } + } + } + + impl Default for Dispatch { + fn default() -> Self { + Self::new() + } + } + + impl ExtensionDispatch for Dispatch { + type BackendId = Backend; + type Context = DispatchContext; + type ExtensionId = Extension; + + fn core_request( + &mut self, + backend: &Self::BackendId, + ctx: &mut Context, + request: &Request, + resources: &mut ServiceResources

, + ) -> Result { + match backend { + Backend::Auth => { + self.auth + .request(&mut ctx.core, &mut ctx.backends.auth, request, resources) + } + Backend::Rsa => SoftwareRsa.request(&mut ctx.core, &mut (), request, resources), + } + } + + fn extension_request( + &mut self, + backend: &Self::BackendId, + extension: &Self::ExtensionId, + ctx: &mut Context, + request: &request::SerdeExtension, + resources: &mut ServiceResources

, + ) -> Result { + match backend { + Backend::Auth => match extension { + Extension::Auth => self.auth.extension_request_serialized( + &mut ctx.core, + &mut ctx.backends.auth, + request, + resources, + ), + }, + Backend::Rsa => Err(Error::RequestNotAvailable), + } + } + } + + impl ExtensionId for Dispatch { + type Id = Extension; + + const ID: Self::Id = Self::Id::Auth; + } +} + +use std::path::PathBuf; +use trussed::{ + types::Bytes, + virt::{self, Client, Filesystem, Ram, StoreProvider}, +}; + +/// Client type using a dispatcher with the backends required by opcard +pub type VirtClient = Client; + +/// Run a client using a provided store +pub fn with_client(store: S, client_id: &str, f: F) -> R +where + F: FnOnce(VirtClient) -> R, + S: StoreProvider, +{ + #[allow(clippy::unwrap_used)] + virt::with_platform(store, |platform| { + platform.run_client_with_backends( + client_id, + dispatch::Dispatch::with_hw_key(Bytes::from_slice(b"some bytes").unwrap()), + dispatch::BACKENDS, + f, + ) + }) +} + +/// Run the backend with the extensions required by opcard +/// using storage backed by a file +pub fn with_fs_client(internal: P, client_id: &str, f: F) -> R +where + F: FnOnce(VirtClient) -> R, + P: Into, +{ + with_client(Filesystem::new(internal), client_id, f) +} + +/// Run the backend with the extensions required by opcard +/// using a RAM file storage +pub fn with_ram_client(client_id: &str, f: F) -> R +where + F: FnOnce(VirtClient) -> R, +{ + with_client(Ram::default(), client_id, f) +} diff --git a/src/vpicc.rs b/src/vpicc.rs index f2a2b9c..1000997 100644 --- a/src/vpicc.rs +++ b/src/vpicc.rs @@ -3,7 +3,8 @@ use iso7816::{command::FromSliceError, Command, Status}; use trussed::virt::Ram; -use trussed_rsa_alloc::virt::Client; + +use crate::virt::VirtClient; use std::convert::{TryFrom, TryInto}; @@ -19,12 +20,12 @@ const RESPONSE_LEN: usize = 7609; pub struct VpiccCard { request_buffer: RequestBuffer, response_buffer: ResponseBuffer, - card: Authenticator>, + card: Authenticator>, } impl VpiccCard { /// Creates a new virtual smart card from the given card. - pub fn new(card: Authenticator>) -> Self { + pub fn new(card: Authenticator>) -> Self { Self { request_buffer: Default::default(), response_buffer: Default::default(), diff --git a/tests/card/mod.rs b/tests/card/mod.rs index 6c22b8b..e206ad2 100644 --- a/tests/card/mod.rs +++ b/tests/card/mod.rs @@ -1,7 +1,7 @@ // Copyright (C) 2022 Nitrokey GmbH // SPDX-License-Identifier: LGPL-3.0-only -use piv_authenticator::{vpicc::VpiccCard, Authenticator, Options}; +use piv_authenticator::{virt::with_ram_client, vpicc::VpiccCard, Authenticator, Options}; use std::{sync::mpsc, thread::sleep, time::Duration}; use stoppable_thread::spawn; @@ -17,7 +17,7 @@ pub fn with_vsc R, R>(f: F) -> R { let (tx, rx) = mpsc::channel(); let handle = spawn(move |stopped| { - trussed_rsa_alloc::virt::with_ram_client("opcard", |client| { + with_ram_client("opcard", |client| { let card = Authenticator::new(client, Options::default()); let mut vpicc_card = VpiccCard::new(card); let mut result = Ok(()); diff --git a/tests/setup/mod.rs b/tests/setup/mod.rs index a355692..3321e63 100644 --- a/tests/setup/mod.rs +++ b/tests/setup/mod.rs @@ -11,14 +11,16 @@ macro_rules! cmd { }; } -use piv_authenticator::{Authenticator, Options}; +use piv_authenticator::{ + virt::{with_ram_client, VirtClient}, + Authenticator, Options, +}; use trussed::virt::Ram; -use trussed_rsa_alloc::virt::Client; -pub type Piv = piv_authenticator::Authenticator>; +pub type Piv = piv_authenticator::Authenticator>; pub fn piv(test: impl FnOnce(&mut Piv) -> R) -> R { - trussed_rsa_alloc::virt::with_ram_client("test", |client| { + with_ram_client("test", |client| { let mut piv_app = Authenticator::new(client, Options::default()); test(&mut piv_app) })