wasefire_protocol/
lib.rs

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Wasefire platform protocol.
16//!
17//! This crate defines a high-level protocol between a host and a device. The host initiates
18//! requests and the device responds. Requests and responses use the same [`Api`] but with a
19//! different type parameter: `Api<Request>` and `Api<Response>` respectively. However, while the
20//! host sends an `Api<Request>`, the device responds with an `ApiResult<T>` where `T` is the
21//! service of the request.
22//!
23//! This high-level protocol is eventually wrapped in a lower-level protocol for a given transport,
24//! for example USB. The host should provide enough time for the device to respond, but should
25//! eventually move on in case no response will ever come, for example when the device is reset
26//! before sending the response. Reciprocally, the device should accept a new request from the host
27//! and cancel the request it was processing if any.
28
29#![no_std]
30#![feature(doc_auto_cfg)]
31#![feature(macro_metavar_expr)]
32#![feature(never_type)]
33
34extern crate alloc;
35
36#[cfg(any(feature = "device", feature = "host"))]
37use alloc::boxed::Box;
38
39#[cfg(feature = "host")]
40pub use connection::*;
41use wasefire_error::Error;
42use wasefire_wire::Wire;
43#[cfg(feature = "host")]
44use wasefire_wire::Yoke;
45
46pub mod applet;
47#[cfg(feature = "host")]
48mod connection;
49pub mod platform;
50pub mod transfer;
51
52/// Service description.
53pub trait Service: 'static {
54    #[cfg(feature = "host")]
55    const NAME: &str;
56    /// Range of versions implementing this service.
57    #[cfg(feature = "host")]
58    const VERSIONS: Versions;
59    type Request<'a>: Wire<'a>;
60    type Response<'a>: Wire<'a>;
61    #[cfg(feature = "host")]
62    fn request(x: Self::Request<'_>) -> Api<'_, Request>;
63}
64
65#[derive(Debug)]
66pub enum Request {}
67impl sealed::Direction for Request {
68    type Type<'a, T: Service> = T::Request<'a>;
69}
70
71#[derive(Debug)]
72#[cfg(feature = "_descriptor")]
73pub enum Response {}
74#[cfg(feature = "_descriptor")]
75impl sealed::Direction for Response {
76    type Type<'a, T: Service> = T::Response<'a>;
77}
78
79mod sealed {
80    pub trait Direction: 'static {
81        type Type<'a, T: crate::Service>: wasefire_wire::Wire<'a>;
82    }
83}
84
85#[derive(Wire)]
86#[wire(static = T, range = 2)]
87pub enum ApiResult<'a, T: Service> {
88    #[wire(tag = 0)]
89    Ok(T::Response<'a>),
90    #[wire(tag = 1)]
91    Err(Error),
92}
93
94#[cfg(feature = "host")]
95impl Api<'_, Request> {
96    pub fn encode(&self) -> Result<Box<[u8]>, Error> {
97        wasefire_wire::encode(self)
98    }
99}
100
101#[cfg(feature = "device")]
102impl<'a> Api<'a, Request> {
103    pub fn decode(data: &'a [u8]) -> Result<Self, Error> {
104        wasefire_wire::decode(data)
105    }
106}
107
108impl<T: Service> ApiResult<'_, T> {
109    #[cfg(feature = "device")]
110    pub fn encode(&self) -> Result<Box<[u8]>, Error> {
111        wasefire_wire::encode(self)
112    }
113
114    #[cfg(feature = "host")]
115    pub fn decode(data: &[u8]) -> Result<ApiResult<'_, T>, Error> {
116        wasefire_wire::decode(data)
117    }
118
119    #[cfg(feature = "host")]
120    pub fn decode_yoke(data: Box<[u8]>) -> Result<Yoke<ApiResult<'static, T>>, Error> {
121        wasefire_wire::decode_yoke(data)
122    }
123}
124
125#[derive(Debug, Copy, Clone, Wire)]
126#[cfg(any(feature = "host", feature = "_descriptor"))]
127pub struct Versions {
128    pub min: u32,
129    pub max: Option<u32>,
130}
131
132#[cfg(feature = "host")]
133impl Versions {
134    pub fn contains(&self, version: u32) -> Result<bool, Error> {
135        match (self.min, self.max) {
136            // Deprecated service. The device needs to be within the range.
137            (min, Some(max)) => Ok((min ..= max).contains(&version)),
138            // The device is too old.
139            (min, None) if version < min => Ok(false),
140            // The device is newer than the host, so we don't know if the service has been
141            // deprecated for that device.
142            (_, None) if VERSION < version => Err(Error::world(wasefire_error::Code::OutOfBounds)),
143            // The device is older than the host but recent enough for the service.
144            _ => Ok(true),
145        }
146    }
147}
148
149#[derive(Debug, Clone, Wire)]
150#[cfg(feature = "_descriptor")]
151pub struct Descriptor {
152    pub tag: u32,
153    pub versions: Versions,
154}
155
156#[cfg(feature = "_descriptor")]
157impl core::fmt::Display for Descriptor {
158    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159        let Descriptor { tag, versions: Versions { min, max } } = *self;
160        match max {
161            Some(max) => write!(f, "{tag} [{min} - {max}]"),
162            None => write!(f, "{tag} [{min} -]"),
163        }
164    }
165}
166
167macro_rules! api {
168    ($(#![$api:meta])* $(
169        $(#[doc = $doc:literal])*
170        $tag:literal [$min:literal - $($max:literal)?] $Name:ident: $request:ty => $response:ty,
171    )* next $next:literal [$version:literal - ]) => {
172        $(#[$api])* #[derive(Debug, Wire)]
173        #[wire(static = T)]
174        #[cfg_attr(feature = "host", wire(range = $next))]
175        #[cfg_attr(not(feature = "_exhaustive"), non_exhaustive)]
176        pub enum Api<'a, T: sealed::Direction> {
177            $(
178                $(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[wire(tag = $tag)]
179                $Name(T::Type<'a, $Name>),
180            )*
181        }
182        $(
183            $(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[derive(Debug)]
184            pub enum $Name {}
185            $(#[cfg(feature = "host")] ${ignore($max)})?
186            impl Service for $Name {
187                #[cfg(feature = "host")]
188                const NAME: &str = stringify!($Name);
189                #[cfg(feature = "host")]
190                const VERSIONS: Versions = api!(versions $min $($max)?);
191                type Request<'a> = $request;
192                type Response<'a> = $response;
193                #[cfg(feature = "host")]
194                fn request(x: Self::Request<'_>) -> Api<'_, Request> { Api::$Name(x) }
195            }
196        )*
197        /// Device API version (or maximum supported device API version for host).
198        pub const VERSION: u32 = $version - 1;
199        #[cfg(feature = "_descriptor")]
200        pub const DESCRIPTORS: &'static [Descriptor] = &[
201            $(
202                $(#[cfg(feature = "host")] ${ignore($max)})?
203                Descriptor { tag: $tag, versions: api!(versions $min $($max)?) },
204            )*
205        ];
206    };
207
208    (max) => (None);
209    (max $max:literal) => (Some($max));
210
211    (versions $min:literal $($max:literal)?) => (Versions { min: $min, max: api!(max $($max)?) });
212}
213
214api! {
215    //! Protocol API parametric over the message direction.
216    //!
217    //! Deprecated variants are only available to the host (to support older devices).
218
219    /// Returns the device API version.
220    0 [0 -] ApiVersion: () => u32,
221
222    /// Sends a request to an applet.
223    1 [0 -] AppletRequest: applet::Request<'a> => (),
224
225    /// Reads a response from an applet.
226    2 [0 -] AppletResponse: applet::AppletId => Option<&'a [u8]>,
227
228    /// Reboots the platform.
229    3 [0 -] PlatformReboot: () => !,
230
231    /// Starts a direct tunnel with an applet.
232    4 [0 -] AppletTunnel: applet::Tunnel<'a> => (),
233
234    /// (deprecated) Returns platform information (e.g. serial and version).
235    ///
236    /// This message is deprecated in favor of [`PlatformInfo`].
237    5 [1 - 4] _PlatformInfo0: () => platform::_Info0<'a>,
238
239    /// Calls a vendor-specific platform command.
240    6 [2 -] PlatformVendor: &'a [u8] => &'a [u8],
241
242    /// (deprecated) Returns the metadata for platform update.
243    ///
244    /// This message is deprecated in favor of [`PlatformVendor`], which is the message for
245    /// vendor-specific messages.
246    7 [3 - 4] _PlatformUpdateMetadata: () => &'a [u8],
247
248    /// (deprecated) Updates the platform.
249    ///
250    /// This message is deprecated in favor of [`PlatformUpdate`].
251    8 [3 - 6] _PlatformUpdate0: transfer::_Request0<'a> => (),
252
253    /// (deprecated) Installs an applet.
254    ///
255    /// This message is deprecated in favor of [`AppletInstall`].
256    9 [4 - 6] _AppletInstall0: transfer::_Request0<'a> => (),
257
258    /// (deprecated) Uninstalls an applet.
259    ///
260    /// This message is deprecated in favor of [`AppletInstall`] with an empty transfer.
261    10 [4 - 6] _AppletUninstall0: () => (),
262
263    /// Returns the exit status of an applet, if not running.
264    11 [4 -] AppletExitStatus: applet::AppletId => Option<applet::ExitStatus>,
265
266    /// Locks a platform until reboot.
267    ///
268    /// This is useful for testing purposes by locking a platform before flashing a new one.
269    12 [4 -] PlatformLock: () => (),
270
271    /// Returns platform information (serial, version, running side, opposite version, etc).
272    13 [5 -] PlatformInfo: () => platform::Info<'a>,
273
274    /// Clears the store for the platform and all applets.
275    ///
276    /// The argument is the number of keys to protect. Using zero will clear all entries.
277    14 [6 -] PlatformClearStore: usize => (),
278
279    /// Updates the platform.
280    15 [7 -] PlatformUpdate: transfer::Request<'a> => transfer::Response,
281
282    /// Installs or uninstalls an applet.
283    16 [7 -] AppletInstall: transfer::Request<'a> => transfer::Response,
284
285    /// Reboots an applet.
286    17 [8 -] AppletReboot: applet::AppletId => (),
287
288    next 18 [9 -]
289}