From cac4038bd9c6c226a3a8f8d7ec9f60aca85e2a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 23 Mar 2023 17:30:07 +0100 Subject: [PATCH] Store all persistent data according to the option --- src/lib.rs | 37 +++++++------------------ src/state.rs | 78 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 60 insertions(+), 55 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 38aa7af..5dde67b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,7 @@ use core::convert::TryInto; use flexiber::EncodableHeapless; use heapless_bytes::Bytes; use iso7816::{Data, Status}; -use trussed::types::{KeySerialization, Location, StorageAttributes}; +use trussed::types::{KeySerialization, Location, PathBuf, StorageAttributes}; use trussed::{client, syscall, try_syscall}; use constants::*; @@ -105,7 +105,7 @@ where fn load(&mut self) -> core::result::Result, Status> { Ok(LoadedAuthenticator { - state: self.state.load(&mut self.trussed)?, + state: self.state.load(&mut self.trussed, self.options.storage)?, trussed: &mut self.trussed, options: &mut self.options, }) @@ -193,30 +193,13 @@ where } YubicoPivExtension::Reset => { - let persistent_state = self.state.persistent(&mut self.trussed)?; + let this = self.load()?; // TODO: find out what all needs resetting :) - persistent_state.reset_pin(&mut self.trussed); - persistent_state.reset_puk(&mut self.trussed); - persistent_state.reset_administration_key(&mut self.trussed); - self.state.volatile.app_security_status.pin_verified = false; - self.state.volatile.app_security_status.puk_verified = false; - self.state - .volatile - .app_security_status - .administrator_verified = false; - - try_syscall!(self.trussed.remove_file( - trussed::types::Location::Internal, - trussed::types::PathBuf::from(b"printed-information"), - )) - .ok(); - - try_syscall!(self.trussed.remove_file( - trussed::types::Location::Internal, - trussed::types::PathBuf::from(b"authentication-key.x5c"), - )) - .ok(); + for location in [Location::Volatile, Location::External, Location::Internal] { + try_syscall!(this.trussed.delete_all(location)).ok(); + try_syscall!(this.trussed.remove_dir_all(location, PathBuf::new())).ok(); + } } YubicoPivExtension::SetManagementKey(touch_policy) => { @@ -939,7 +922,7 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> let offset = reply.len(); match container { Container::KeyHistoryObject => self.get_key_history_object(reply.lend())?, - _ => match ContainerStorage(container).load(self.trussed)? { + _ => match ContainerStorage(container).load(self.trussed, self.options.storage)? { Some(data) => reply.expand(&data)?, None => return Err(Status::NotFound), }, @@ -969,7 +952,7 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> }; use state::ContainerStorage; - ContainerStorage(container).save(self.trussed, data) + ContainerStorage(container).save(self.trussed, data, self.options.storage) } fn reset_retry_counter(&mut self, data: ResetRetryCounter) -> Result { @@ -999,7 +982,7 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> use state::ContainerStorage; for c in RETIRED_CERTS { - if ContainerStorage(c).exists(self.trussed)? { + if ContainerStorage(c).exists(self.trussed, self.options.storage)? { num_certs += 1; } } diff --git a/src/state.rs b/src/state.rs index 5b8d64d..507829e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -188,9 +188,13 @@ pub struct State { } impl State { - pub fn load(&mut self, client: &mut impl trussed::Client) -> Result, Status> { + pub fn load( + &mut self, + client: &mut impl trussed::Client, + storage: Location, + ) -> Result, Status> { if self.persistent.is_none() { - self.persistent = Some(Persistent::load_or_initialize(client)?); + self.persistent = Some(Persistent::load_or_initialize(client, storage)?); } Ok(LoadedState { volatile: &mut self.volatile, @@ -201,8 +205,9 @@ impl State { pub fn persistent( &mut self, client: &mut impl trussed::Client, + storage: Location, ) -> Result<&mut Persistent, Status> { - Ok(self.load(client)?.persistent) + Ok(self.load(client, storage)?.persistent) } pub fn new() -> Self { @@ -216,6 +221,11 @@ pub struct LoadedState<'t> { pub persistent: &'t mut Persistent, } +/// exists only to please serde, which doesn't accept enum variants in `#[serde(default=…)]` +fn volatile() -> Location { + Location::Volatile +} + #[derive(Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct Persistent { pub keys: Keys, @@ -230,6 +240,8 @@ pub struct Persistent { // pin_hash: Option<[u8; 16]>, // Ideally, we'd dogfood a "Monotonic Counter" from `trussed`. timestamp: u32, + #[serde(skip, default = "volatile")] + storage: Location, } #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -443,7 +455,7 @@ impl Persistent { let id = syscall!(client.unsafe_inject_key( alg.mechanism(), management_key, - trussed::types::Location::Internal, + self.storage, KeySerialization::Raw )) .key; @@ -471,7 +483,7 @@ impl Persistent { ) -> KeyId { let id = syscall!(client.generate_key( alg.key_mechanism(), - StorageAttributes::default().set_persistence(Location::Internal) + StorageAttributes::default().set_persistence(self.storage) )) .key; let old = self.set_asymmetric_key(key, id, alg); @@ -482,13 +494,13 @@ impl Persistent { id } - pub fn initialize(client: &mut impl trussed::Client) -> Self { + pub fn initialize(client: &mut impl trussed::Client, storage: Location) -> Self { info!("initializing PIV state"); let administration = KeyWithAlg { id: syscall!(client.unsafe_inject_key( YUBICO_DEFAULT_MANAGEMENT_KEY_ALG.mechanism(), YUBICO_DEFAULT_MANAGEMENT_KEY, - trussed::types::Location::Internal, + storage, KeySerialization::Raw )) .key, @@ -498,7 +510,7 @@ impl Persistent { let authentication = KeyWithAlg { id: syscall!(client.generate_key( Mechanism::P256, - StorageAttributes::new().set_persistence(Location::Internal) + StorageAttributes::new().set_persistence(storage) )) .key, alg: AsymmetricAlgorithms::P256, @@ -521,6 +533,7 @@ impl Persistent { .save( client, &guid_file[2..], // Remove the unnecessary 53 tag + storage, ) .ok(); @@ -541,34 +554,35 @@ impl Persistent { pin: Pin::try_from(Self::DEFAULT_PIN).unwrap(), puk: Puk::try_from(Self::DEFAULT_PUK).unwrap(), timestamp: 0, + // In case of forgotten to rebind, ensure the bug is found + storage: Location::Volatile, }; state.save(client); state } - pub fn load_or_initialize(client: &mut impl trussed::Client) -> Result { + pub fn load_or_initialize( + client: &mut impl trussed::Client, + storage: Location, + ) -> Result { // todo: can't seem to combine load + initialize without code repetition - let data = load_if_exists(client, Location::Internal, &PathBuf::from(Self::FILENAME))?; + let data = load_if_exists(client, storage, &PathBuf::from(Self::FILENAME))?; let Some(bytes) = data else { - return Ok( Self::initialize(client)); + return Ok( Self::initialize(client, storage)); }; - let parsed = trussed::cbor_deserialize(&bytes).map_err(|_err| { + let mut parsed: Self = trussed::cbor_deserialize(&bytes).map_err(|_err| { error!("{_err:?}"); Status::UnspecifiedPersistentExecutionError })?; + parsed.storage = storage; Ok(parsed) } pub fn save(&mut self, client: &mut impl trussed::Client) { let data: trussed::types::Message = trussed::cbor_serialize_bytes(&self).unwrap(); - syscall!(client.write_file( - Location::Internal, - PathBuf::from(Self::FILENAME), - data, - None, - )); + syscall!(client.write_file(self.storage, PathBuf::from(Self::FILENAME), data, None,)); } pub fn timestamp(&mut self, client: &mut impl trussed::Client) -> u32 { @@ -661,8 +675,12 @@ impl ContainerStorage { } } - pub fn exists(self, client: &mut impl trussed::Client) -> Result { - match try_syscall!(client.entry_metadata(Location::Internal, self.path())) { + pub fn exists( + self, + client: &mut impl trussed::Client, + storage: Location, + ) -> Result { + match try_syscall!(client.entry_metadata(storage, self.path())) { Ok(Metadata { metadata: None }) => Ok(false), Ok(Metadata { metadata: Some(metadata), @@ -686,22 +704,26 @@ impl ContainerStorage { pub fn load( self, client: &mut impl trussed::Client, + storage: Location, ) -> Result>, Status> { - load_if_exists(client, Location::Internal, &self.path()) + load_if_exists(client, storage, &self.path()) .map(|data| data.or_else(|| self.default().map(Bytes::from))) } - pub fn save(self, client: &mut impl trussed::Client, bytes: &[u8]) -> Result<(), Status> { + pub fn save( + self, + client: &mut impl trussed::Client, + bytes: &[u8], + storage: Location, + ) -> Result<(), Status> { let msg = Bytes::from(heapless::Vec::try_from(bytes).map_err(|_| { error!("Buffer full"); Status::IncorrectDataParameter })?); - try_syscall!(client.write_file(Location::Internal, self.path(), msg, None)).map_err( - |_err| { - error!("Failed to store data: {_err:?}"); - Status::UnspecifiedNonpersistentExecutionError - }, - )?; + try_syscall!(client.write_file(storage, self.path(), msg, None)).map_err(|_err| { + error!("Failed to store data: {_err:?}"); + Status::UnspecifiedNonpersistentExecutionError + })?; Ok(()) } }