diff --git a/Cargo.lock b/Cargo.lock index 24b63f7a..0b10f9b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,7 +5,7 @@ version = 3 [[package]] name = "admin-app" version = "0.1.0" -source = "git+https://github.com/Nitrokey/admin-app?tag=v0.1.0-nitrokey.6#b67c3647b7b6ec3ce8c453e9ffbf9327dfd5c813" +source = "git+https://github.com/Nitrokey/admin-app.git?rev=410899311ae7b194360366ff477f74d4d278e056#410899311ae7b194360366ff477f74d4d278e056" dependencies = [ "apdu-dispatch", "cbor-smol", @@ -18,6 +18,7 @@ dependencies = [ "strum_macros", "trussed", "trussed-se050-backend", + "trussed-staging", ] [[package]] @@ -113,6 +114,8 @@ dependencies = [ "embedded-hal", "fido-authenticator", "hex", + "if_chain", + "littlefs2", "ndef-app", "opcard", "piv-authenticator", @@ -1657,6 +1660,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + [[package]] name = "indexmap" version = "1.9.3" @@ -1777,8 +1786,7 @@ checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" [[package]] name = "littlefs2" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c72bdf63e7ad35f391e60c48e4c32560038f1d3a0dd97f90a2891ce09160bf" +source = "git+https://github.com/trussed-dev/littlefs2?rev=e6c46e7ba5ae19129e457a2182e40a439c0322fe#e6c46e7ba5ae19129e457a2182e40a439c0322fe" dependencies = [ "bitflags 1.3.2", "cstr_core", @@ -2168,8 +2176,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "opcard" version = "1.2.0" -source = "git+https://github.com/Nitrokey/opcard-rs?tag=v1.2.0#ad61078c4653d0daa0512bf1a8466bbba7039edc" +source = "git+https://github.com/Nitrokey/opcard-rs?rev=a824c6473ce1b88b45b32de21089401fc9f7f683#a824c6473ce1b88b45b32de21089401fc9f7f683" dependencies = [ + "admin-app", "apdu-dispatch", "delog", "heapless 0.7.16", @@ -3250,7 +3259,7 @@ dependencies = [ [[package]] name = "trussed" version = "0.1.0" -source = "git+https://github.com/Nitrokey/trussed.git?rev=v0.1.0-nitrokey.14#30a4764ddca80fd5f23eff6db1c53770f84c2c2f" +source = "git+https://github.com/trussed-dev/trussed.git?rev=d97c64d0bc5f83ce22b0e0ed034a2b451616b3f9#d97c64d0bc5f83ce22b0e0ed034a2b451616b3f9" dependencies = [ "aes", "bitflags 2.4.1", @@ -3287,7 +3296,7 @@ dependencies = [ [[package]] name = "trussed-auth" version = "0.2.2" -source = "git+https://github.com/Nitrokey/trussed-auth?tag=v0.2.2-nitrokey.1#203a90dd13a7378f596b3099cd986a8da6185137" +source = "git+https://github.com/trussed-dev/trussed-auth?rev=62235294bd63977bbb88eb01e7ac44b8010eb450#62235294bd63977bbb88eb01e7ac44b8010eb450" dependencies = [ "chacha20poly1305", "hkdf", @@ -3303,7 +3312,7 @@ dependencies = [ [[package]] name = "trussed-rsa-alloc" version = "0.1.0" -source = "git+https://github.com/Nitrokey/trussed-rsa-backend.git?tag=v0.1.0#a72ce195a4495a8db26debfb0240a4dc712d8dbe" +source = "git+https://github.com/trussed-dev/trussed-rsa-backend.git?rev=2f51478f0861ff8db19fdd5290f023ab6f4c2fb9#2f51478f0861ff8db19fdd5290f023ab6f4c2fb9" dependencies = [ "delog", "heapless-bytes 0.3.0", @@ -3338,7 +3347,7 @@ dependencies = [ [[package]] name = "trussed-staging" version = "0.1.0" -source = "git+https://github.com/trussed-dev/trussed-staging.git?branch=hmacsha256p256#1b54bf8703d515688a58f2f605c3efa0f2f60ced" +source = "git+https://github.com/nitrokey/trussed-staging.git?branch=hmacsha256p256-rebased#edb966aba4a3c211b6453d6f01259823e82b932b" dependencies = [ "chacha20poly1305", "delog", diff --git a/Cargo.toml b/Cargo.toml index e2ee896e..0f4a9fc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ version = "1.6.0-rc.1" [patch.crates-io] # forked -admin-app = { git = "https://github.com/Nitrokey/admin-app", tag = "v0.1.0-nitrokey.6" } +admin-app = { git = "https://github.com/Nitrokey/admin-app.git", rev = "410899311ae7b194360366ff477f74d4d278e056" } ctap-types = { git = "https://github.com/Nitrokey/ctap-types", tag = "v0.1.2-nitrokey.4" } fido-authenticator = { git = "https://github.com/Nitrokey/fido-authenticator.git", tag = "v0.1.1-nitrokey.8" } flexiber = { git = "https://github.com/Nitrokey/flexiber", tag = "0.1.1.nitrokey" } @@ -23,19 +23,20 @@ serde-indexed = { git = "https://github.com/nitrokey/serde-indexed.git", tag = " apdu-dispatch = { git = "https://github.com/Nitrokey/apdu-dispatch.git", tag = "v0.1.2-nitrokey.2" } ctaphid-dispatch = { git = "https://github.com/Nitrokey/ctaphid-dispatch.git", tag = "v0.1.1-nitrokey.3" } iso7816 = { git = "https://github.com/Nitrokey/iso7816.git", tag = "v0.1.1-nitrokey.2"} -trussed = { git = "https://github.com/Nitrokey/trussed.git", rev = "v0.1.0-nitrokey.14" } +trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "d97c64d0bc5f83ce22b0e0ed034a2b451616b3f9" } usbd-ctaphid = { git = "https://github.com/Nitrokey/usbd-ctaphid.git", tag = "v0.1.0-nitrokey.2" } usbd-ccid = { git = "https://github.com/Nitrokey/usbd-ccid", tag = "v0.2.0-nitrokey.1" } +littlefs2 = { git = "https://github.com/trussed-dev/littlefs2", rev = "e6c46e7ba5ae19129e457a2182e40a439c0322fe" } # unreleased crates secrets-app = { git = "https://github.com/Nitrokey/trussed-secrets-app", tag = "v0.13.0-rc2" } webcrypt = { git = "https://github.com/nitrokey/nitrokey-websmartcard-rust", tag = "v0.8.0-rc4"} -opcard = { git = "https://github.com/Nitrokey/opcard-rs", tag = "v1.2.0" } +opcard = { git = "https://github.com/Nitrokey/opcard-rs", rev = "a824c6473ce1b88b45b32de21089401fc9f7f683" } piv-authenticator = { git = "https://github.com/Nitrokey/piv-authenticator", tag = "v0.3.3" } se05x = { git = "https://github.com/Nitrokey/se05x.git", tag = "v0.1.0"} -trussed-auth = { git = "https://github.com/Nitrokey/trussed-auth", tag = "v0.2.2-nitrokey.1" } -trussed-rsa-alloc = { git = "https://github.com/Nitrokey/trussed-rsa-backend.git", tag = "v0.1.0"} -trussed-staging = { git = "https://github.com/trussed-dev/trussed-staging.git", branch = "hmacsha256p256" } +trussed-auth = { git = "https://github.com/trussed-dev/trussed-auth", rev = "62235294bd63977bbb88eb01e7ac44b8010eb450" } +trussed-rsa-alloc = { git = "https://github.com/trussed-dev/trussed-rsa-backend.git", rev = "2f51478f0861ff8db19fdd5290f023ab6f4c2fb9" } +trussed-staging = { git = "https://github.com/nitrokey/trussed-staging.git", branch = "hmacsha256p256-rebased" } trussed-usbip = { git = "https://github.com/Nitrokey/pc-usbip-runner.git", tag = "v0.0.1-nitrokey.3" } trussed-se050-backend = { git = "https://github.com/Nitrokey/trussed-se050-backend.git", tag = "v0.1.0-test-driver" } diff --git a/components/apps/Cargo.toml b/components/apps/Cargo.toml index af2f5790..40f4b6d0 100644 --- a/components/apps/Cargo.toml +++ b/components/apps/Cargo.toml @@ -11,11 +11,13 @@ trussed = { version = "0.1", features = ["serde-extensions"]} trussed-usbip = { version = "0.0.1", default-features = false, features = ["ctaphid"], optional = true } usbd-ctaphid = { version = "0.1", optional = true } utils = { path = "../utils" } +if_chain = "1.0.2" +littlefs2 = { version = "0.4" } # Backends trussed-auth = { version = "0.2.2", optional = true } trussed-rsa-alloc = { version = "0.1.0", optional = true } -trussed-staging = { version = "0.1.0", features = ["wrap-key-to-file", "chunked", "encrypted-chunked"], optional = true } +trussed-staging = { version = "0.1.0", features = ["wrap-key-to-file", "chunked", "encrypted-chunked", "manage"] } # apps admin-app = "0.1.0" @@ -23,7 +25,7 @@ fido-authenticator = { version = "0.1.1", features = ["dispatch"], optional = tr ndef-app = { path = "../ndef-app", optional = true } webcrypt = { version = "0.8.0", optional = true } secrets-app = { version = "0.13.0", features = ["apdu-dispatch", "ctaphid"], optional = true } -opcard = { version = "1.1.1", features = ["apdu-dispatch", "delog", "rsa2048-gen", "rsa4096"], optional = true } +opcard = { version = "1.1.1", features = ["apdu-dispatch", "delog", "rsa2048-gen", "rsa4096", "admin-app"], optional = true } piv-authenticator = { version = "0.3.1", features = ["apdu-dispatch", "delog"], optional = true } provisioner-app = { path = "../provisioner-app", optional = true } se05x = { version = "0.0.1", optional = true} @@ -41,17 +43,17 @@ provisioner = ["provisioner-app", "trussed/clients-5"] # apps secrets-app = ["dep:secrets-app", "backend-auth"] -webcrypt = ["dep:webcrypt", "backend-auth", "backend-rsa", "backend-staging", "trussed-staging/hmacsha256p256"] +backend-staging-hmacsha256p256 = ["trussed-staging/hmacsha256p256"] +webcrypt = ["dep:webcrypt", "backend-auth", "backend-rsa", "backend-staging-hmacsha256p256"] fido-authenticator = ["dep:fido-authenticator", "usbd-ctaphid"] -opcard = ["dep:opcard", "backend-rsa", "backend-auth", "backend-staging"] -piv-authenticator = ["dep:piv-authenticator", "backend-rsa", "backend-auth", "backend-staging"] +opcard = ["dep:opcard", "backend-rsa", "backend-auth"] +piv-authenticator = ["dep:piv-authenticator", "backend-rsa", "backend-auth"] se050-test-app = ["se050", "admin-app/se050"] se050 = ["trussed-se050-backend", "dep:se05x"] # backends backend-auth = ["trussed-auth"] backend-rsa = ["trussed-rsa-alloc"] -backend-staging = ["trussed-staging"] log-all = ["admin-app/log-all", "fido-authenticator?/log-all", "secrets-app?/log-all", "webcrypt?/log-all", "opcard?/log-all", "provisioner-app?/log-all"] diff --git a/components/apps/src/dispatch.rs b/components/apps/src/dispatch.rs index 27bf144b..96cb8f08 100644 --- a/components/apps/src/dispatch.rs +++ b/components/apps/src/dispatch.rs @@ -9,11 +9,9 @@ use trussed::{ Platform, }; -#[cfg(any( - feature = "backend-auth", - feature = "backend-rsa", - feature = "backend-staging" -))] +use littlefs2::{path, path::Path}; + +use if_chain::if_chain; use trussed::{ api::{reply, request}, backend::Backend as _, @@ -26,7 +24,9 @@ use embedded_hal::blocking::delay::DelayUs; #[cfg(feature = "se050")] use se05x::{se05x::Se05X, t1::I2CForT1}; #[cfg(feature = "se050")] -use trussed_se050_backend::{manage::ManageExtension, Context as Se050Context, Se050Backend}; +use trussed_se050_backend::{ + manage::ManageExtension as Se050ManageExtension, Context as Se050Context, Se050Backend, +}; #[cfg(feature = "backend-auth")] use trussed_auth::{AuthBackend, AuthContext, AuthExtension, MAX_HW_KEY_LEN}; @@ -34,22 +34,20 @@ use trussed_auth::{AuthBackend, AuthContext, AuthExtension, MAX_HW_KEY_LEN}; #[cfg(feature = "backend-rsa")] use trussed_rsa_alloc::SoftwareRsa; -#[cfg(feature = "backend-staging")] use trussed_staging::{ - streaming::ChunkedExtension, wrap_key_to_file::WrapKeyToFileExtension, StagingBackend, - StagingContext, + manage::ManageExtension, streaming::ChunkedExtension, wrap_key_to_file::WrapKeyToFileExtension, + StagingBackend, StagingContext, }; -#[cfg(all(feature = "webcrypt", feature = "backend-staging"))] +#[cfg(feature = "webcrypt")] use trussed_staging::hmacsha256p256::HmacSha256P256Extension; pub struct Dispatch { #[cfg(feature = "backend-auth")] auth: AuthBackend, - #[cfg(feature = "backend-staging")] staging: StagingBackend, #[cfg(feature = "se050")] - se050: Option>, + se050: Option>, #[cfg(not(feature = "se050"))] __: PhantomData<(T, D)>, } @@ -58,12 +56,39 @@ pub struct Dispatch { pub struct DispatchContext { #[cfg(feature = "backend-auth")] auth: AuthContext, - #[cfg(feature = "backend-staging")] staging: StagingContext, #[cfg(feature = "se050")] se050: Se050Context, } +fn should_preserve_file(file: &Path) -> bool { + // We save all "special" objects, with an ID that is representable by a `u8` + + const DIRS: &[&Path] = &[path!("x5c"), path!("ctr"), path!("sec"), path!("pub")]; + + let mut components = file.iter(); + if_chain! { + if components.next() == Some("/".into()); + if components.next().is_some(); + if let Some(intermediary) = components.next(); + if DIRS.contains(&&*intermediary); + if let Some(file_name) = components.next(); + if components.next().is_none(); + if file_name.as_ref().len() <=2; + then { + true + } else { + false + } + } +} + +fn build_staging_backend() -> StagingBackend { + let mut backend = StagingBackend::new(); + backend.manage.should_preserve_file = |file, _location| should_preserve_file(file); + backend +} + impl Dispatch { pub fn new( auth_location: Location, @@ -74,8 +99,7 @@ impl Dispatch { Self { #[cfg(feature = "backend-auth")] auth: AuthBackend::new(auth_location), - #[cfg(feature = "backend-staging")] - staging: StagingBackend::new(), + staging: build_staging_backend(), #[cfg(feature = "se050")] se050: se050.map(trussed_se050_backend::Se050Backend::new), #[cfg(not(feature = "se050"))] @@ -91,8 +115,7 @@ impl Dispatch { ) -> Self { Self { auth: AuthBackend::with_hw_key(auth_location, hw_key), - #[cfg(feature = "backend-staging")] - staging: StagingBackend::new(), + staging: build_staging_backend(), #[cfg(feature = "se050")] se050: se050.map(trussed_se050_backend::Se050Backend::new), #[cfg(not(feature = "se050"))] @@ -142,11 +165,11 @@ impl ExtensionDispatch for Dispatch { } #[cfg(feature = "backend-rsa")] Backend::SoftwareRsa => SoftwareRsa.request(&mut ctx.core, &mut (), request, resources), - #[cfg(feature = "backend-staging")] Backend::Staging => { self.staging .request(&mut ctx.core, &mut ctx.backends.staging, request, resources) } + Backend::StagingManage => Err(TrussedError::RequestNotAvailable), #[cfg(feature = "se050")] Backend::Se050 => self .se050 @@ -179,7 +202,6 @@ impl ExtensionDispatch for Dispatch { }, #[cfg(feature = "backend-rsa")] Backend::SoftwareRsa => Err(TrussedError::RequestNotAvailable), - #[cfg(feature = "backend-staging")] Backend::Staging => match extension { Extension::Chunked => { ExtensionImpl::::extension_request_serialized( @@ -199,7 +221,7 @@ impl ExtensionDispatch for Dispatch { resources, ) } - #[cfg(feature = "webcrypt")] + #[cfg(feature = "backend-staging-hmacsha256p256")] Extension::HmacShaP256 => { ExtensionImpl::::extension_request_serialized( &mut self.staging, @@ -212,19 +234,31 @@ impl ExtensionDispatch for Dispatch { #[allow(unreachable_patterns)] _ => Err(TrussedError::RequestNotAvailable), }, - #[cfg(feature = "se050")] - Backend::Se050 => match extension { - Extension::Se050Manage => { + Backend::StagingManage => match extension { + Extension::Manage => { ExtensionImpl::::extension_request_serialized( - self.se050.as_mut().ok_or(TrussedError::GeneralError)?, + &mut self.staging, &mut ctx.core, - &mut ctx.backends.se050, + &mut ctx.backends.staging, request, resources, ) } _ => Err(TrussedError::RequestNotAvailable), }, + #[cfg(feature = "se050")] + Backend::Se050 => match extension { + Extension::Se050Manage => ExtensionImpl::< + trussed_se050_backend::manage::ManageExtension, + >::extension_request_serialized( + self.se050.as_mut().ok_or(TrussedError::GeneralError)?, + &mut ctx.core, + &mut ctx.backends.se050, + request, + resources, + ), + _ => Err(TrussedError::RequestNotAvailable), + }, _ => Err(TrussedError::RequestNotAvailable), } } @@ -236,8 +270,9 @@ pub enum Backend { Auth, #[cfg(feature = "backend-rsa")] SoftwareRsa, - #[cfg(feature = "backend-staging")] Staging, + /// Separate BackendId to prevent non-priviledged apps from accessing the manage Extension + StagingManage, #[cfg(feature = "se050")] Se050, } @@ -246,11 +281,10 @@ pub enum Backend { pub enum Extension { #[cfg(feature = "backend-auth")] Auth, - #[cfg(feature = "backend-staging")] Chunked, - #[cfg(feature = "backend-staging")] WrapKeyToFile, - #[cfg(feature = "backend-staging")] + Manage, + #[cfg(feature = "backend-staging-hmacsha256p256")] HmacShaP256, #[cfg(feature = "se050")] Se050Manage, @@ -261,14 +295,13 @@ impl From for u8 { match extension { #[cfg(feature = "backend-auth")] Extension::Auth => 0, - #[cfg(feature = "backend-staging")] Extension::Chunked => 1, - #[cfg(feature = "backend-staging")] Extension::WrapKeyToFile => 2, - #[cfg(feature = "backend-staging")] - Extension::HmacShaP256 => 3, + Extension::Manage => 3, + #[cfg(feature = "backend-staging-hmacsha256p256")] + Extension::HmacShaP256 => 4, #[cfg(feature = "se050")] - Extension::Se050Manage => 4, + Extension::Se050Manage => 5, } } } @@ -280,14 +313,13 @@ impl TryFrom for Extension { match id { #[cfg(feature = "backend-auth")] 0 => Ok(Extension::Auth), - #[cfg(feature = "backend-staging")] 1 => Ok(Extension::Chunked), - #[cfg(feature = "backend-staging")] 2 => Ok(Extension::WrapKeyToFile), - #[cfg(feature = "backend-staging")] - 3 => Ok(Extension::HmacShaP256), + 3 => Ok(Extension::Manage), + #[cfg(feature = "backend-staging-hmacsha256p256")] + 4 => Ok(Extension::HmacShaP256), #[cfg(feature = "se050")] - 4 => Ok(Extension::Se050Manage), + 5 => Ok(Extension::Se050Manage), _ => Err(TrussedError::InternalError), } } @@ -300,30 +332,55 @@ impl ExtensionId for Dispatch { const ID: Self::Id = Self::Id::Auth; } -#[cfg(feature = "backend-staging")] impl ExtensionId for Dispatch { type Id = Extension; const ID: Self::Id = Self::Id::Chunked; } -#[cfg(feature = "backend-staging")] impl ExtensionId for Dispatch { type Id = Extension; const ID: Self::Id = Self::Id::WrapKeyToFile; } -#[cfg(all(feature = "backend-staging", feature = "webcrypt"))] +#[cfg(feature = "backend-staging-hmacsha256p256")] impl ExtensionId for Dispatch { type Id = Extension; const ID: Self::Id = Self::Id::HmacShaP256; } -#[cfg(feature = "se050")] impl ExtensionId for Dispatch { type Id = Extension; + const ID: Self::Id = Self::Id::Manage; +} + +#[cfg(feature = "se050")] +impl ExtensionId for Dispatch { + type Id = Extension; + const ID: Self::Id = Self::Id::Se050Manage; } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn file_preserve() { + assert!(should_preserve_file(path!("/fido/sec/00"))); + assert!(should_preserve_file(path!("/fido/x5c/00"))); + assert!(should_preserve_file(path!("/fido/sec/01"))); + assert!(should_preserve_file(path!("/fido/x5c/01"))); + assert!(should_preserve_file(path!("/attn/pub/00"))); + assert!(should_preserve_file(path!("/attn/sec/01"))); + assert!(should_preserve_file(path!("/attn/sec/02"))); + assert!(should_preserve_file(path!("/attn/sec/03"))); + assert!(should_preserve_file(path!("/attn/x5c/01"))); + assert!(should_preserve_file(path!("/attn/x5c/02"))); + assert!(should_preserve_file(path!("/attn/x5c/03"))); + assert!(!should_preserve_file(path!("/fido/dat/sec/00"))); + } +} diff --git a/components/apps/src/lib.rs b/components/apps/src/lib.rs index ee671a72..601e4a96 100644 --- a/components/apps/src/lib.rs +++ b/components/apps/src/lib.rs @@ -12,14 +12,15 @@ use core::marker::PhantomData; use ctaphid_dispatch::app::App as CtaphidApp; #[cfg(feature = "se050")] use embedded_hal::blocking::delay::DelayUs; +use littlefs2::path; use serde::{Deserialize, Serialize}; use trussed::{ backend::BackendId, client::ClientBuilder, interrupt::InterruptFlag, platform::Syscall, - store::filestore::ClientFilestore, ClientImplementation, Platform, Service, + store::filestore::ClientFilestore, types::Path, ClientImplementation, Platform, Service, }; -use admin_app::ConfigValueMut; pub use admin_app::Reboot; +use admin_app::{ConfigValueMut, ResetSignalAllocation}; use trussed::types::Location; #[cfg(feature = "webcrypt")] @@ -47,6 +48,16 @@ impl admin_app::Config for Config { _ => None, } } + + fn reset_client_id( + &self, + key: &str, + ) -> Option<(&'static Path, &'static ResetSignalAllocation)> { + match key { + "opcard" => Some((path!("opcard"), &OPCARD_RESET_SIGNAL)), + _ => None, + } + } } #[derive(Debug, Default, PartialEq, Deserialize, Serialize)] @@ -422,6 +433,7 @@ impl App for AdminApp { fn backends(runner: &R, _config: &()) -> &'static [BackendId] { const BACKENDS_ADMIN: &[BackendId] = &[ + BackendId::Custom(Backend::StagingManage), #[cfg(feature = "se050-test-app")] BackendId::Custom(Backend::Se050), BackendId::Core, @@ -520,6 +532,8 @@ impl App for SecretsApp { } } +static OPCARD_RESET_SIGNAL: ResetSignalAllocation = ResetSignalAllocation::new(); + #[cfg(feature = "opcard")] impl App for OpcardApp { const CLIENT_ID: &'static str = "opcard"; @@ -535,6 +549,7 @@ impl App for OpcardApp { options.manufacturer = 0x000Fu16.to_be_bytes(); options.serial = [uuid[0], uuid[1], uuid[2], uuid[3]]; options.storage = trussed::types::Location::External; + options.reset_signal = Some(&OPCARD_RESET_SIGNAL); Self::new(trussed, options) } fn backends(runner: &R, _: &()) -> &'static [BackendId] {