Skip to content

Commit

Permalink
Move serde_extensions module into trussed-core
Browse files Browse the repository at this point in the history
  • Loading branch information
robin-nitrokey committed Nov 4, 2024
1 parent 06cb5b5 commit 20c0dde
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 134 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ license = "Apache-2.0 OR MIT"
heapless = "0.7"
heapless-bytes = "0.3"
littlefs2-core = { version = "0.1", features = ["serde"] }
postcard = "0.7.0"
rand_core = "0.6"
serde = { version = "1.0", default-features = false, features = ["derive"] }

Expand Down Expand Up @@ -39,7 +40,7 @@ generic-array = "0.14.4"
heapless = { workspace = true, features = ["serde"] }
hex-literal = "0.4.1"
nb = "1"
postcard = "0.7.0"
postcard.workspace = true
rand_core.workspace = true
serde.workspace = true
zeroize = { version = "1.2", default-features = false, features = ["zeroize_derive"] }
Expand Down Expand Up @@ -82,7 +83,7 @@ trussed-derive = { path = "derive" }

[features]
default = ["default-mechanisms", "default-syscalls", "clients-5"]
serde-extensions = []
serde-extensions = ["trussed-core/serde-extensions"]
std = []
verbose-tests = ["littlefs2/ll-assertions"]
verbose-lfs = ["littlefs2/ll-assertions", "littlefs2/ll-trace"]
Expand Down
2 changes: 2 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ license.workspace = true
heapless.workspace = true
heapless-bytes.workspace = true
littlefs2-core.workspace = true
postcard.workspace = true
rand_core.workspace = true
serde.workspace = true

Expand All @@ -19,3 +20,4 @@ serde-indexed = "0.1"
[features]
crypto-client-attest = []
counter-client = []
serde-extensions = []
3 changes: 1 addition & 2 deletions core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ pub struct FutureResult<'c, T, C: ?Sized>
where
C: PollClient,
{
// TODO: make private
pub client: &'c mut C,
pub(crate) client: &'c mut C,
__: PhantomData<T>,
}

Expand Down
2 changes: 2 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ pub mod client;
pub mod config;
pub mod error;
pub mod interrupt;
#[cfg(feature = "serde-extensions")]
pub mod serde_extensions;
pub mod types;
146 changes: 146 additions & 0 deletions core/src/serde_extensions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//! Extensions to the core Trussed syscalls.
//!
//! *Requires the `serde-extensions` feature.*
//!
//! This module makes it possible to add additional syscalls to Trussed by implementing the
//! [`Extension`][] trait. Extension requests and replies are serialized to
//! [`Request::SerdeExtension`][] and [`Reply::SerdeExtension`][].
//!
//! [`Request::SerdeExtension`]: `crate::api::Request::SerdeExtension`
//! [`Reply::SerdeExtension`]: `crate::api::Reply::SerdeExtension`
use core::{marker::PhantomData, task::Poll};

use serde::{de::DeserializeOwned, Serialize};

use crate::{
api::{reply, request},
client::{ClientError, FutureResult, PollClient},
error::Error,
types::Bytes,
};

/// A Trussed API extension.
pub trait Extension {
/// The requests supported by this extension.
type Request: DeserializeOwned + Serialize;
/// The replies supported by this extension.
type Reply: DeserializeOwned + Serialize;

/// Serialize an extension request.
///
/// Requests that are serialized with this function can be deserialized with
/// [`Extension::deserialize_request`][]. The format is not guaranteed to be stable over
/// crate releases.
#[inline(never)]
fn serialize_request(
id: u8,
request: &Self::Request,
) -> Result<request::SerdeExtension, ClientError> {
postcard::to_vec(request)
.map(Bytes::from)
.map(|request| request::SerdeExtension { id, request })
.map_err(|_| ClientError::SerializationFailed)
}

/// Deserialize an extension request.
///
/// This function can be used to deserialize requests that have been serialized with
/// [`Extension::serialize_request`][]. The format is not guaranteed to be stable over
/// crate releases.
#[inline(never)]
fn deserialize_request(request: &request::SerdeExtension) -> Result<Self::Request, Error> {
postcard::from_bytes(&request.request).map_err(|_| Error::InvalidSerializedRequest)
}

/// Serialize an extension reply.
///
/// Replies that are serialized with this function can be deserialized with
/// [`Extension::deserialize_reply`][]. The format is not guaranteed to be stable over
/// crate releases.
#[inline(never)]
fn serialize_reply(reply: &Self::Reply) -> Result<reply::SerdeExtension, Error> {
postcard::to_vec(reply)
.map(Bytes::from)
.map(|reply| reply::SerdeExtension { reply })
.map_err(|_| Error::ReplySerializationFailure)
}

/// Deserialize an extension reply.
///
/// This function can be used to deserialize replies that have been serialized with
/// [`Extension::serialize_reply`][]. The format is not guaranteed to be stable over
/// crate releases.
#[inline(never)]
fn deserialize_reply(reply: &reply::SerdeExtension) -> Result<Self::Reply, Error> {
postcard::from_bytes(&reply.reply).map_err(|_| Error::InvalidSerializedReply)
}
}

/// Executes extension requests.
///
/// Instead of using this trait directly, extensions should define their own traits that extend
/// this trait and use the `extension` function to execute extension requests.
pub trait ExtensionClient<E: Extension>: PollClient {
/// Returns the ID for the `E` extension as defined by the runner.
fn id() -> u8;

/// Executes an extension request.
///
/// Applications should not call this method directly and instead use a trait provided by the
/// extension.
fn extension<Rq, Rp>(&mut self, request: Rq) -> ExtensionResult<'_, E, Rp, Self>
where
Rq: Into<E::Request>,
Rp: TryFrom<E::Reply, Error = Error>,
{
let request = E::serialize_request(Self::id(), &request.into())?;
self.request(request).map(From::from)
}
}

/// A result returned by [`ExtensionClient`][] and clients using it.
pub type ExtensionResult<'a, E, T, C> = Result<ExtensionFutureResult<'a, E, T, C>, ClientError>;

#[must_use = "Syscalls must be polled with the `syscall` macro"]
/// A future of an [`ExtensionResult`][].
pub struct ExtensionFutureResult<'c, E, T, C: ?Sized> {
client: &'c mut C,
__: PhantomData<(E, T)>,
}

impl<'c, E, T, C: ?Sized> ExtensionFutureResult<'c, E, T, C> {
fn new(client: &'c mut C) -> Self {
Self {
client,
__: PhantomData,
}
}
}

impl<'c, E, T, C> ExtensionFutureResult<'c, E, T, C>
where
E: Extension,
T: TryFrom<E::Reply, Error = Error>,
C: PollClient,
{
pub fn poll(&mut self) -> Poll<Result<T, Error>> {
self.client.poll().map(|result| {
result.and_then(|reply| {
let reply = reply::SerdeExtension::try_from(reply)?;
let reply: E::Reply = E::deserialize_reply(&reply)?;
reply.try_into()
})
})
}
}

impl<'c, E, T, C> From<FutureResult<'c, reply::SerdeExtension, C>>
for ExtensionFutureResult<'c, E, T, C>
where
C: PollClient + ?Sized,
{
fn from(result: FutureResult<'c, reply::SerdeExtension, C>) -> Self {
Self::new(result.client)
}
}
135 changes: 5 additions & 130 deletions src/serde_extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,76 +13,19 @@
//!
//! See `tests/serde_extensions.rs` for an example.
use core::{marker::PhantomData, task::Poll};

use crate::{
api::{reply, request, Reply, Request},
backend::{Backend, CoreOnly, Dispatch, NoId, OptionalBackend},
client::{ClientError, ClientImplementation, FutureResult, PollClient},
client::ClientImplementation,
error::Error,
platform::{Platform, Syscall},
service::ServiceResources,
types::{self, Bytes, Context, CoreContext},
types::{self, Context, CoreContext},
};

use serde::{de::DeserializeOwned, Serialize};

/// A Trussed API extension.
pub trait Extension {
/// The requests supported by this extension.
type Request: DeserializeOwned + Serialize;
/// The replies supported by this extension.
type Reply: DeserializeOwned + Serialize;

/// Serialize an extension request.
///
/// Requests that are serialized with this function can be deserialized with
/// [`Extension::deserialize_request`][]. The format is not guaranteed to be stable over
/// crate releases.
#[inline(never)]
fn serialize_request(
id: u8,
request: &Self::Request,
) -> Result<request::SerdeExtension, ClientError> {
postcard::to_vec(request)
.map(Bytes::from)
.map(|request| request::SerdeExtension { id, request })
.map_err(|_| ClientError::SerializationFailed)
}

/// Deserialize an extension request.
///
/// This function can be used to deserialize requests that have been serialized with
/// [`Extension::serialize_request`][]. The format is not guaranteed to be stable over
/// crate releases.
#[inline(never)]
fn deserialize_request(request: &request::SerdeExtension) -> Result<Self::Request, Error> {
postcard::from_bytes(&request.request).map_err(|_| Error::InvalidSerializedRequest)
}

/// Serialize an extension reply.
///
/// Replies that are serialized with this function can be deserialized with
/// [`Extension::deserialize_reply`][]. The format is not guaranteed to be stable over
/// crate releases.
#[inline(never)]
fn serialize_reply(reply: &Self::Reply) -> Result<reply::SerdeExtension, Error> {
postcard::to_vec(reply)
.map(Bytes::from)
.map(|reply| reply::SerdeExtension { reply })
.map_err(|_| Error::ReplySerializationFailure)
}

/// Deserialize an extension reply.
///
/// This function can be used to deserialize replies that have been serialized with
/// [`Extension::serialize_reply`][]. The format is not guaranteed to be stable over
/// crate releases.
#[inline(never)]
fn deserialize_reply(reply: &reply::SerdeExtension) -> Result<Self::Reply, Error> {
postcard::from_bytes(&reply.reply).map_err(|_| Error::InvalidSerializedReply)
}
}
pub use trussed_core::serde_extensions::{
Extension, ExtensionClient, ExtensionFutureResult, ExtensionResult,
};

/// Dispatches extension requests to custom backends.
pub trait ExtensionDispatch {
Expand Down Expand Up @@ -198,28 +141,6 @@ pub trait ExtensionId<E> {
const ID: Self::Id;
}

/// Executes extension requests.
///
/// Instead of using this trait directly, extensions should define their own traits that extend
/// this trait and use the `extension` function to execute extension requests.
pub trait ExtensionClient<E: Extension>: PollClient {
/// Returns the ID for the `E` extension as defined by the runner, see [`ExtensionId`][].
fn id() -> u8;

/// Executes an extension request.
///
/// Applications should not call this method directly and instead use a trait provided by the
/// extension.
fn extension<Rq, Rp>(&mut self, request: Rq) -> ExtensionResult<'_, E, Rp, Self>
where
Rq: Into<E::Request>,
Rp: TryFrom<E::Reply, Error = Error>,
{
let request = E::serialize_request(Self::id(), &request.into())?;
self.request(request).map(From::from)
}
}

impl<E, S, I> ExtensionClient<E> for ClientImplementation<S, I>
where
E: Extension,
Expand All @@ -230,49 +151,3 @@ where
I::ID.into()
}
}

/// A result returned by [`ExtensionClient`][] and clients using it.
pub type ExtensionResult<'a, E, T, C> = Result<ExtensionFutureResult<'a, E, T, C>, ClientError>;

#[must_use = "Syscalls must be polled with the `syscall` macro"]
/// A future of an [`ExtensionResult`][].
pub struct ExtensionFutureResult<'c, E, T, C: ?Sized> {
client: &'c mut C,
__: PhantomData<(E, T)>,
}

impl<'c, E, T, C: ?Sized> ExtensionFutureResult<'c, E, T, C> {
fn new(client: &'c mut C) -> Self {
Self {
client,
__: PhantomData,
}
}
}

impl<'c, E, T, C> ExtensionFutureResult<'c, E, T, C>
where
E: Extension,
T: TryFrom<E::Reply, Error = Error>,
C: PollClient,
{
pub fn poll(&mut self) -> Poll<Result<T, Error>> {
self.client.poll().map(|result| {
result.and_then(|reply| {
let reply = reply::SerdeExtension::try_from(reply)?;
let reply: E::Reply = E::deserialize_reply(&reply)?;
reply.try_into()
})
})
}
}

impl<'c, E, T, C> From<FutureResult<'c, reply::SerdeExtension, C>>
for ExtensionFutureResult<'c, E, T, C>
where
C: PollClient + ?Sized,
{
fn from(result: FutureResult<'c, reply::SerdeExtension, C>) -> Self {
Self::new(result.client)
}
}

0 comments on commit 20c0dde

Please sign in to comment.