Expand description

Client interface for applications.

The API methods (such as GenerateKey, Sign, Verify,…) are implemented by a variety of mechanisms (such as Ed255, X255, Chacha8Poly1305, HmacSha256,…).

The ClientImplementation structure in this module offers only one general request method:

pub fn request<'c, T: From<Reply>>(&'c mut self, req: impl Into<Request>)
  -> ClientResult<'c, T, Self>

For convenience, the Client trait expands the API methods, keeping the mechanism general, e.g.:

// use trussed::Client as _;
fn sign<'c>(&'c mut self,
  mechanism: Mechanism,
  key: KeyId,
  data: &[u8],
  format: SignatureSerialization
) -> ClientResult<'c, reply::Sign, Self>;

For further convenience, each mechanism has a corresponding trait of the same name, e.g., Ed255, which also specializes the mechanism, e.g.

// use trussed::client::Ed255 as _;
fn sign_ed255<'c>(&'c mut self, key: &KeyId, message: &[u8])
  -> ClientResult<'c, reply::Sign, Self>

Pick your poison :)

Details

The lower-level workings of ClientResult are currently a hand-rolled / semi-horrible pseudo-Future implementation; this will likely be replaced by a proper core::future::Future with something like the direct-executor.

The lifetimes indicate that the ClientResult takes ownership of the unique reference to the client itself for the length of its own lifetime. That is, once the call to Trussed completes (success or failure), there is no use for the ClientResult anymore, so due to lexical lifetimes, the ClientImplementation can be used again.

What does always happen is that each client has an Interchange with the service, in which it places the api::Request (a Rust enum), and then uses the Syscall implementation, to trigger processing by the Trussed service.

In practice, in embedded Syscall is implemented by pending a hardware interrupt for the service, which runs at a higher interrupt priority. For PC testing, the service itself has a Syscall implementation (“call thyself”). In both cases, the caller is blocked until processing completes.

All the same, to unpack the “result” it is suggested to use the syscall! macro, which returns the Reply corresponding to the Request. Example:

let secret_key = syscall!(client.generate_x255_secret_key(Internal)).key;

This syscall! can fail (by panicking) in two ways:

  • logic error: clients are only allowed to make one syscall to the Trussed service at once, then they must wait for a response. By the above, this case cannot happen in practice.
  • processing error: some methods are naturally fallible; for example, a public key that is to be imported via Deserialize may be invalid for the mechanism (such things are always checked).

In this second case (probably in all cases when programming defensively, e.g. one possible trussed::error::Error is HostMemory, which means out of RAM), the try_syscall! macro should be used instead, which does not unwrap the inner Result type.

In terms of the Result<FutureResult<'c, T, C>, ClientError> return type of the Client::request method, the outer Result corresponds to the logic error (see trussed::client::ClientError) for possible causes.

The processing error corresponds to the Result<From<Reply, trussed::error::Error>> which is the Ready variant of the core::task::Poll struct returns by the FutureResult’s poll method. Possible causes are listed in trussed::error::Error.

Re-exports

pub use crate::platform::Syscall;
pub use mechanisms::*;

Modules

Structs

The client implementation client applications actually receive.

Enums

Traits

Read/Write + Delete certificates

All-in-one trait bounding on the sub-traits.

Create counters, increment existing counters.

Trussed Client interface that Trussed apps can rely on.

Read/Write/Delete files, iterate over directories.

All the other methods that are fit to expose.

Lowest level interface, use one of the higher level ones.

User-interfacing functionality.

Type Definitions