Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `types::Path` re-export of `littlefs2::path::Path`.
- Reduced stack usage of `Service::process`.
- Added the `Aes256Gcm` mechanism.
- Added mldsa44 mechanism implementation.

### Changed

Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ repository.workspace = true
[dependencies]
trussed-core = "0.2.2"

critical-section = "1"

# general
bitflags = { version = "2.1" }
# const-oid = "0.4.5"
Expand Down Expand Up @@ -71,6 +73,7 @@ salty = "0.3"
p384 = { version = "0.13.0", optional = true, default-features = false, features = ["sha384", "ecdh", "ecdsa"] }
p521 = { version = "0.13.3", optional = true, default-features = false, features = ["sha512", "ecdh", "ecdsa"] }
ecdsa = { version = "0.16.9", optional = true, default-features = false }
libcrux-ml-dsa = { version = "0.0.9", optional = true, default-features = false, features = ["mldsa44"] }

[dev-dependencies]
# Testing
Expand Down Expand Up @@ -127,6 +130,7 @@ hmac-blake2s = ["trussed-core/hmac-blake2s", "blake2"]
hmac-sha1 = ["trussed-core/hmac-sha1", "sha-1"]
hmac-sha256 = ["trussed-core/hmac-sha256"]
hmac-sha512 = ["trussed-core/hmac-sha512"]
mldsa44 = ["trussed-core/mldsa44", "dep:libcrux-ml-dsa"]
p256 = ["trussed-core/p256"]
p384 = ["trussed-core/p384", "dep:p384"]
p521 = ["trussed-core/p521", "dep:p521", "dep:ecdsa"]
Expand Down Expand Up @@ -155,7 +159,6 @@ management-client = ["trussed-core/management-client"]
ui-client = ["trussed-core/ui-client"]

test-attestation-cert-ids = []
test-increased-interchange-size = []

[[test]]
name = "aead"
Expand Down
196 changes: 196 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,199 @@ impl<S: Syscall, E> FilesystemClient for ClientImplementation<'_, S, E> {}
impl<S: Syscall, E> ManagementClient for ClientImplementation<'_, S, E> {}
#[cfg(feature = "ui-client")]
impl<S: Syscall, E> UiClient for ClientImplementation<'_, S, E> {}

// =========================================================================
// Multiplexed clients
// =========================================================================
//
// `MultiplexedClient` shares a single `TrussedRequester` (Requester half of
// one `interchange::Channel`) across N apps. Each app's request is tagged
// with a `ClientTag` so the Service-side `process_multiplexed` knows which
// `Context<C>` + backends to dispatch with.
//
// Synchronisation:
// - The tag is held in an `AtomicU8` (`CurrentTagCell`), naturally `Sync`.
// - The shared `Requester` is held in `Mutex<RefCell<Option<...>>>` via
// `critical_section`. Each access disables interrupts only for the
// duration of the short closure (set tag + write request, or read
// response). `syscall.syscall()` runs outside the critical section so
// `OS_EVENT` fires immediately afterwards.

use core::cell::RefCell;
use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use critical_section::Mutex;

/// Identifies which multiplexed client is the source of the in-flight request.
pub type ClientTag = u8;

/// Identifies the active multiplexed client. Written by `MultiplexedClient::request`
/// and read by `Service::process_multiplexed` to look up the matching context.
pub struct CurrentTagCell(AtomicU8);

impl CurrentTagCell {
pub const fn new() -> Self {
Self(AtomicU8::new(0))
}
pub fn set(&self, tag: ClientTag) {
self.0.store(tag, Ordering::Relaxed);
}
pub fn get(&self) -> ClientTag {
self.0.load(Ordering::Relaxed)
}
}

impl Default for CurrentTagCell {
fn default() -> Self {
Self::new()
}
}

/// Cheap "response ready" flag. Set by `Service::process_multiplexed` after
/// `respond()` so clients can avoid entering a critical section in the spin
/// loop until there's actually a response to pick up. Cleared inside the
/// client's `poll()` when the response is taken.
pub static RESPONSE_READY: AtomicBool = AtomicBool::new(false);

/// Holds the shared `TrussedRequester` for multiplexed clients. Initialised
/// once at boot from the runner; accessed via short critical-section closures.
pub struct SharedRequesterCell(Mutex<RefCell<Option<TrussedRequester<'static>>>>);

impl SharedRequesterCell {
pub const fn new() -> Self {
Self(Mutex::new(RefCell::new(None)))
}
/// Install the requester. Call once at boot from the runner.
pub fn init(&self, requester: TrussedRequester<'static>) {
critical_section::with(|cs| {
*self.0.borrow(cs).borrow_mut() = Some(requester);
});
}
/// Run `f` against the requester inside a critical section. Panics if
/// the cell has not been initialised yet.
pub fn with_mut<R>(&self, f: impl FnOnce(&mut TrussedRequester<'static>) -> R) -> R {
critical_section::with(|cs| {
let mut r = self.0.borrow(cs).borrow_mut();
f(r.as_mut().expect("SharedRequesterCell not initialised"))
})
}
}

impl Default for SharedRequesterCell {
fn default() -> Self {
Self::new()
}
}

/// Client that funnels requests through a shared `TrussedRequester` and
/// tags each request with a `ClientTag` so the Service can route to the
/// right context. Implements the same client traits as `ClientImplementation`.
pub struct MultiplexedClient<S, D = CoreOnly> {
syscall: S,
shared: &'static SharedRequesterCell,
current_tag: &'static CurrentTagCell,
tag: ClientTag,
interrupt: Option<&'static InterruptFlag>,
pending: Option<u8>,
_marker: PhantomData<D>,
}

impl<S, D> MultiplexedClient<S, D>
where
S: Syscall,
{
pub fn new(
shared: &'static SharedRequesterCell,
current_tag: &'static CurrentTagCell,
tag: ClientTag,
syscall: S,
interrupt: Option<&'static InterruptFlag>,
) -> Self {
Self {
shared,
current_tag,
tag,
syscall,
interrupt,
pending: None,
_marker: PhantomData,
}
}
}

impl<S, D> PollClient for MultiplexedClient<S, D>
where
S: Syscall,
{
fn poll(&mut self) -> Poll<Result<Reply, Error>> {
// Cheap fast-path: if no response has been published yet, skip the
// critical section entirely. The Service sets `RESPONSE_READY` after
// `respond()`; we clear it once we've taken the response.
if !RESPONSE_READY.load(Ordering::Acquire) {
return Poll::Pending;
}
let taken = self.shared.with_mut(|r| (r.take_response(), r.state()));
if taken.0.is_some() {
RESPONSE_READY.store(false, Ordering::Release);
}
match taken.0 {
Some(reply) => match reply {
Ok(reply) => {
if Some(u8::from(&reply)) == self.pending {
self.pending = None;
Poll::Ready(Ok(reply))
} else {
info!(
"got: {:?}, expected: {:?}",
Some(u8::from(&reply)),
self.pending
);
Poll::Ready(Err(Error::InternalError))
}
}
Err(error) => {
self.pending = None;
Poll::Ready(Err(error))
}
},
None => {
debug_assert_ne!(
taken.1,
interchange::State::Idle,
"requests can't be cancelled"
);
Poll::Pending
}
}
}

fn request<Rq: RequestVariant>(&mut self, req: Rq) -> ClientResult<'_, Rq::Reply, Self> {
if self.pending.is_some() {
return Err(ClientError::Pending);
}
self.current_tag.set(self.tag);
let request = req.into();
self.pending = Some(u8::from(&request));
self.shared.with_mut(|r| r.request(request).unwrap());
self.syscall.syscall();
Ok(FutureResult::new(self))
}

fn interrupt(&self) -> Option<&'static InterruptFlag> {
self.interrupt
}
}

#[cfg(feature = "certificate-client")]
impl<S: Syscall, D> CertificateClient for MultiplexedClient<S, D> {}
#[cfg(feature = "crypto-client")]
impl<S: Syscall, D> CryptoClient for MultiplexedClient<S, D> {}
#[cfg(feature = "counter-client")]
impl<S: Syscall, D> CounterClient for MultiplexedClient<S, D> {}
#[cfg(feature = "filesystem-client")]
impl<S: Syscall, D> FilesystemClient for MultiplexedClient<S, D> {}
#[cfg(feature = "management-client")]
impl<S: Syscall, D> ManagementClient for MultiplexedClient<S, D> {}
#[cfg(feature = "ui-client")]
impl<S: Syscall, D> UiClient for MultiplexedClient<S, D> {}
#[cfg(feature = "all-clients")]
impl<S: Syscall, D> Client for MultiplexedClient<S, D> {}
36 changes: 35 additions & 1 deletion src/client/mechanisms.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::ClientImplementation;
use super::{ClientImplementation, MultiplexedClient};
use crate::platform::Syscall;

pub use trussed_core::mechanisms::*;
Expand All @@ -24,6 +24,9 @@ impl<S: Syscall, E> HmacSha256 for ClientImplementation<'_, S, E> {}
#[cfg(feature = "hmac-sha512")]
impl<S: Syscall, E> HmacSha512 for ClientImplementation<'_, S, E> {}

#[cfg(feature = "mldsa44")]
impl<S: Syscall, E> Mldsa44 for ClientImplementation<'_, S, E> {}

#[cfg(feature = "ed255")]
impl<S: Syscall, E> Ed255 for ClientImplementation<'_, S, E> {}

Expand All @@ -47,3 +50,34 @@ impl<S: Syscall, E> Totp for ClientImplementation<'_, S, E> {}

#[cfg(feature = "x255")]
impl<S: Syscall, E> X255 for ClientImplementation<'_, S, E> {}

#[cfg(feature = "aes256-cbc")]
impl<S: Syscall, E> Aes256Cbc for MultiplexedClient<S, E> {}
#[cfg(feature = "chacha8-poly1305")]
impl<S: Syscall, E> Chacha8Poly1305 for MultiplexedClient<S, E> {}
#[cfg(feature = "hmac-blake2s")]
impl<S: Syscall, E> HmacBlake2s for MultiplexedClient<S, E> {}
#[cfg(feature = "hmac-sha1")]
impl<S: Syscall, E> HmacSha1 for MultiplexedClient<S, E> {}
#[cfg(feature = "hmac-sha256")]
impl<S: Syscall, E> HmacSha256 for MultiplexedClient<S, E> {}
#[cfg(feature = "hmac-sha512")]
impl<S: Syscall, E> HmacSha512 for MultiplexedClient<S, E> {}
#[cfg(feature = "mldsa44")]
impl<S: Syscall, E> Mldsa44 for MultiplexedClient<S, E> {}
#[cfg(feature = "ed255")]
impl<S: Syscall, E> Ed255 for MultiplexedClient<S, E> {}
#[cfg(feature = "p256")]
impl<S: Syscall, E> P256 for MultiplexedClient<S, E> {}
#[cfg(feature = "p384")]
impl<S: Syscall, E> P384 for MultiplexedClient<S, E> {}
#[cfg(feature = "p521")]
impl<S: Syscall, E> P521 for MultiplexedClient<S, E> {}
#[cfg(feature = "sha256")]
impl<S: Syscall, E> Sha256 for MultiplexedClient<S, E> {}
#[cfg(feature = "tdes")]
impl<S: Syscall, E> Tdes for MultiplexedClient<S, E> {}
#[cfg(feature = "totp")]
impl<S: Syscall, E> Totp for MultiplexedClient<S, E> {}
#[cfg(feature = "x255")]
impl<S: Syscall, E> X255 for MultiplexedClient<S, E> {}
5 changes: 5 additions & 0 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ pub enum Kind {
BrainpoolP512R1,
X255,
Secp256k1,
/// 32-byte ML-DSA seed (FIPS 204 KeyGen_internal `xi`); the full
/// signing/verifying key pair is re-expanded on demand.
Mldsa44Seed,
}

bitflags::bitflags! {
Expand Down Expand Up @@ -222,6 +225,7 @@ impl Kind {
Kind::BrainpoolP384R1 => 13,
Kind::BrainpoolP512R1 => 14,
Kind::Secp256k1 => 15,
Kind::Mldsa44Seed => 16,
}
}

Expand All @@ -242,6 +246,7 @@ impl Kind {
13 => Kind::BrainpoolP384R1,
14 => Kind::BrainpoolP512R1,
15 => Kind::Secp256k1,
16 => Kind::Mldsa44Seed,
_ => return Err(Error::InvalidSerializedKey),
})
}
Expand Down
5 changes: 5 additions & 0 deletions src/mechanisms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ pub struct HmacSha512;
#[cfg(feature = "hmac-sha512")]
mod hmacsha512;

#[cfg(feature = "mldsa44")]
pub struct Mldsa44;
#[cfg(feature = "mldsa44")]
mod mldsa44;

#[cfg(feature = "p256")]
pub struct P256;
#[cfg(feature = "p256")]
Expand Down
Loading
Loading