From 6661d7d708cd1855ac2ad558878be3b147177258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 17 Sep 2026 16:35:50 +0200 Subject: [PATCH 1/3] Use ResetSignal from trussed-core This allows removing the `opcard` dependency on `admin-app` This PR also removes the strum crate and instead uses a simple declarative macro like we already do in many other crates --- Cargo.toml | 4 +- src/config.rs | 150 ++++++++++++++------------------------------------ src/lib.rs | 5 +- 3 files changed, 46 insertions(+), 113 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e16cbe..24e1182 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,6 @@ iso7816 = "0.2" littlefs2 = { version = "0.8", optional = true } littlefs2-core = { version = "0.1", features = ["heapless-bytes05"] } serde = { version = "1.0.180", default-features = false } -strum_macros = "0.25.2" trussed = { version = "0.2", default-features = false } trussed-core = { version = "0.2", features = ["crypto-client", "filesystem-client", "management-client", "ui-client"] } @@ -43,3 +42,6 @@ log-error = [] # Utils to test migration migration-tests = ["dep:littlefs2"] + +[patch.crates-io] +trussed-core = { git = "https://github.com/trussed-dev/trussed.git", rev = "bc01fd6728f3b1efef2eb12adcce5acff558f6bb"} diff --git a/src/config.rs b/src/config.rs index 2687267..dfae67b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,116 +1,20 @@ use core::{ fmt::{self, Display, Formatter, Write as _}, str::FromStr, - sync::atomic::{AtomicU8, Ordering}, }; use cbor_smol::{cbor_deserialize, cbor_serialize_to}; use heapless::{string::StringView, VecView}; use littlefs2_core::{path, Path}; use serde::{de::DeserializeOwned, Serialize}; -use strum_macros::FromRepr; use trussed::store::Filestore; use trussed_core::{ + reset_signal::ResetSignalAllocation, try_syscall, types::{Location, Message}, FilesystemClient, }; -#[derive(Debug)] -/// Structure meant to be stored in a `static` to signal applications that they have been factory-resetted by the admin app -/// -/// It is expected to have one such structure for each application supporting factory-reset by the admin-app -/// -/// ```rust,ignore -///# use admin_app::{ResetSignalAllocation, ConfigValueMut}; -///# use littlefs2::{path::Path, path}; -/// #[derive(Default, PartialEq, serde::Deserialize, serde::Serialize)] -/// struct Config { -/// use_new_backend: bool, -///}; -/// static OPCARD_RESET: ResetSignalAllocation = ResetSignalAllocation::new(); -/// impl admin_app::Config for Config { -/// fn field(&mut self, key: &str) -> Option> { -/// match key { -/// "opcard.use_new_backend" => Some(ConfigValueMut::Bool(&mut self.use_new_backend)), -/// _ => None, -/// } -/// } -/// /// Client ID to factory-reset if the associated configuration option is changed -/// fn reset_client_id(&self, key: &str) -> Option<(&'static Path, &'static ResetSignalAllocation)> { -/// match key { -/// "opcard" => Some((path!("opcard"), &OPCARD_RESET)), -/// "opcard.use_new_backend" =>Some((path!("opcard"), &OPCARD_RESET)), -/// _ => None, -/// } -/// } -/// } -/// ``` -pub struct ResetSignalAllocation(AtomicU8); - -impl Default for ResetSignalAllocation { - fn default() -> Self { - Self::new() - } -} - -impl ResetSignalAllocation { - pub const fn new() -> Self { - Self(AtomicU8::new(ResetSignal::None as u8)) - } - - pub fn load(&self) -> ResetSignal { - let v = self.0.load(Ordering::Relaxed); - ResetSignal::from_repr(v).expect("A reset signal value") - } - - pub fn set_factory_reset(&self) -> bool { - self.0 - .compare_exchange( - ResetSignal::None as u8, - ResetSignal::FactoryReset as u8, - Ordering::Relaxed, - Ordering::Relaxed, - ) - .is_ok() - } - - pub fn set_config_changed(&self) { - self.0 - .store(ResetSignal::ConfigChanged as u8, Ordering::Relaxed) - } - - /// Factory reset can be acknowledged so that the application can restart working - /// - /// A configuration change cannot be acknowledged as it requires a power cycle to be taken into account. - pub fn ack_factory_reset(&self) -> bool { - self.0 - .compare_exchange( - ResetSignal::FactoryReset as u8, - ResetSignal::None as u8, - Ordering::Relaxed, - Ordering::Relaxed, - ) - .is_ok() - } -} - -#[derive(Debug, FromRepr, Default)] -#[repr(u8)] -pub enum ResetSignal { - #[default] - /// The App can continue operating - None, - /// The app has had it state factory reseted by the admin app - /// - /// It should delete any runtime state it is currently holding, then [`acknowledge`](ResetSignalAllocation::ack_factory_reset) the reset and continue working. - FactoryReset, - /// A configuration relevant to the application has been changed. - /// - /// The application must reject all incoming request and store no persistent state until a power cycle. - ConfigChanged, -} - const LOCATION: Location = Location::Internal; const FILENAME: &Path = path!("config"); @@ -270,19 +174,49 @@ impl<'a> Display for ConfigValueMut<'a> { } } -#[derive(Debug, FromRepr)] -#[repr(u8)] -pub enum ConfigError { - ReadFailed = 1, - WriteFailed = 2, - DeserializationFailed = 3, - SerializationFailed = 4, - InvalidKey = 5, - InvalidValue = 6, - DataTooLong = 7, - NotConfirmed = 8, +macro_rules! enum_u8 { + ( + $(#[$outer:meta])* + $vis:vis enum $name:ident { + $($(#[$attr:meta])* $var:ident = $num:expr),+ + $(,)* + } + ) => { + $(#[$outer])* + #[repr(u8)] + $vis enum $name { + $( + $(#[$attr])* + $var = $num, + )* + } + + impl $name { + const fn from_repr(val: u8) -> Option<$name> { + match val { + $( + $num => Some($name::$var), + )* + _ => None, + } + } + } + } } +enum_u8!( + #[derive(Debug)] + pub enum ConfigError { + ReadFailed = 1, + WriteFailed = 2, + DeserializationFailed = 3, + SerializationFailed = 4, + InvalidKey = 5, + InvalidValue = 6, + DataTooLong = 7, + NotConfirmed = 8, + } +); const _: () = assert!( ConfigError::from_repr(0).is_none(), "ConfigError may not have a variant with discriminant zero as zero indicates success.", diff --git a/src/lib.rs b/src/lib.rs index 7e5ec23..78729b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,10 +17,7 @@ mod config; pub mod migrations; pub use admin::{App, Data, StatusBytes}; -pub use config::{ - Config, ConfigError, ConfigField, ConfigValueMut, FieldType, ResetConfigResult, ResetSignal, - ResetSignalAllocation, -}; +pub use config::{Config, ConfigError, ConfigField, ConfigValueMut, FieldType, ResetConfigResult}; use trussed_manage::ManageClient; #[cfg(feature = "se050")] use trussed_se050_manage::Se050ManageClient; From 1680038235c7963c2e74cfc663082eec32470baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 17 Sep 2026 18:07:21 +0200 Subject: [PATCH 2/3] Use released trussed-core --- Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 24e1182..f61d14f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ littlefs2 = { version = "0.8", optional = true } littlefs2-core = { version = "0.1", features = ["heapless-bytes05"] } serde = { version = "1.0.180", default-features = false } trussed = { version = "0.2", default-features = false } -trussed-core = { version = "0.2", features = ["crypto-client", "filesystem-client", "management-client", "ui-client"] } +trussed-core = { version = "0.2.3", features = ["crypto-client", "filesystem-client", "management-client", "ui-client"] } embedded-hal = { version = "0.2.7", optional = true } hex-literal = "0.4.1" @@ -42,6 +42,3 @@ log-error = [] # Utils to test migration migration-tests = ["dep:littlefs2"] - -[patch.crates-io] -trussed-core = { git = "https://github.com/trussed-dev/trussed.git", rev = "bc01fd6728f3b1efef2eb12adcce5acff558f6bb"} From eb4fe4303286e5c66ce03d72f1801d4f4325ee41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 17 Sep 2026 18:22:50 +0200 Subject: [PATCH 3/3] Prepare release 0.5.0 --- CHANGELOG.md | 8 +++++++- Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5e0aa9..bdac14e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -[Unreleased]: https://github.com/trussed-dev/admin-app/compare/0.4.0...HEAD +[Unreleased]: https://github.com/trussed-dev/admin-app/compare/0.5.0...HEAD - +## [0.5.0] 2026-09-16 + +[0.5.0]: https://github.com/trussed-dev/admin-app/compare/0.4.0...0.5.0 + +- Remove `ResetSignal`. It is now in `trussed-core`. + ## [0.4.0] 2026-09-16 [0.4.0]: https://github.com/trussed-dev/admin-app/compare/0.3.0...0.4.0 diff --git a/Cargo.toml b/Cargo.toml index f61d14f..418ff74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "admin-app" -version = "0.4.0" +version = "0.5.0" authors = ["Conor Patrick ", "Nicolas Stalder "] repository = "https://github.com/solokeys/admin-app" edition = "2021"