Skip to main content

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_cfg)]
31#![feature(macro_metavar_expr)]
32#![feature(never_type)]
33#![feature(try_blocks)]
34
35extern crate alloc;
36
37use alloc::borrow::Cow;
38#[cfg(any(feature = "device", feature = "host"))]
39use alloc::boxed::Box;
40
41#[cfg(feature = "host")]
42pub use connection::*;
43use wasefire_error::Error;
44use wasefire_wire::Wire;
45#[cfg(feature = "host")]
46use wasefire_wire::Yoke;
47
48pub mod applet;
49#[cfg(feature = "host")]
50pub mod bundle;
51pub mod common;
52#[cfg(feature = "host")]
53mod connection;
54pub mod platform;
55pub mod transfer;
56
57/// Service description.
58pub trait Service: 'static {
59    #[cfg(feature = "host")]
60    const NAME: &str;
61    /// Range of versions implementing this service.
62    #[cfg(feature = "host")]
63    const VERSIONS: Versions;
64    type Request<'a>: Wire<'a>;
65    type Response<'a>: Wire<'a>;
66    #[cfg(feature = "host")]
67    fn request(x: Self::Request<'_>) -> Api<'_, Request>;
68    #[cfg(feature = "serde")]
69    fn response(x: Self::Response<'_>) -> Api<'_, Response>;
70}
71
72#[derive(Debug)]
73pub enum Request {}
74impl sealed::Direction for Request {
75    type Type<'a, T: Service> = T::Request<'a>;
76}
77
78#[derive(Debug)]
79#[cfg(any(feature = "_descriptor", feature = "serde"))]
80pub enum Response {}
81#[cfg(any(feature = "_descriptor", feature = "serde"))]
82impl sealed::Direction for Response {
83    type Type<'a, T: Service> = T::Response<'a>;
84}
85
86mod sealed {
87    pub trait Direction: 'static {
88        type Type<'a, T: crate::Service>: wasefire_wire::Wire<'a>;
89    }
90}
91
92#[derive(Wire)]
93#[wire(static = T, range = 2)]
94pub enum ApiResult<'a, T: Service> {
95    #[wire(tag = 0)]
96    Ok(T::Response<'a>),
97    #[wire(tag = 1)]
98    Err(Error),
99}
100
101#[cfg(feature = "host")]
102impl Api<'_, Request> {
103    pub fn encode(&self) -> Result<Box<[u8]>, Error> {
104        wasefire_wire::encode(self)
105    }
106}
107
108#[cfg(feature = "device")]
109impl<'a> Api<'a, Request> {
110    pub fn decode(data: &'a [u8]) -> Result<Self, Error> {
111        wasefire_wire::decode(data)
112    }
113}
114
115impl<T: Service> ApiResult<'_, T> {
116    #[cfg(feature = "device")]
117    pub fn encode(&self) -> Result<Box<[u8]>, Error> {
118        wasefire_wire::encode(self)
119    }
120
121    #[cfg(feature = "host")]
122    pub fn decode(data: &[u8]) -> Result<ApiResult<'_, T>, Error> {
123        wasefire_wire::decode(data)
124    }
125
126    #[cfg(feature = "host")]
127    pub fn decode_yoke(data: Box<[u8]>) -> Result<Yoke<ApiResult<'static, T>>, Error> {
128        wasefire_wire::decode_yoke(data)
129    }
130}
131
132#[derive(Debug, Copy, Clone, Wire)]
133#[cfg(any(feature = "host", feature = "_descriptor"))]
134pub struct Versions {
135    pub min: u32,
136    pub max: Option<u32>,
137}
138
139#[cfg(feature = "host")]
140impl Versions {
141    pub fn contains(&self, version: u32) -> Result<bool, Error> {
142        match (self.min, self.max) {
143            // Deprecated service. The device needs to be within the range.
144            (min, Some(max)) => Ok((min ..= max).contains(&version)),
145            // The device is too old.
146            (min, None) if version < min => Ok(false),
147            // The device is newer than the host, so we don't know if the service has been
148            // deprecated for that device.
149            (_, None) if VERSION < version => Err(Error::world(wasefire_error::Code::OutOfBounds)),
150            // The device is older than the host but recent enough for the service.
151            _ => Ok(true),
152        }
153    }
154}
155
156#[derive(Debug, Clone, Wire)]
157#[cfg(feature = "_descriptor")]
158pub struct Descriptor {
159    pub tag: u32,
160    pub versions: Versions,
161}
162
163#[cfg(feature = "_descriptor")]
164impl core::fmt::Display for Descriptor {
165    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
166        let Descriptor { tag, versions: Versions { min, max } } = *self;
167        match max {
168            Some(max) => write!(f, "{tag} [{min} - {max}]"),
169            None => write!(f, "{tag} [{min} -]"),
170        }
171    }
172}
173
174#[cfg(all(test, feature = "serde"))]
175fn _api_serde() {
176    fn is_serializable<T: serde::Serialize>() {}
177    fn is_deserializable_owned<T>()
178    where for<'a> T: serde::Deserialize<'a> {
179    }
180    is_serializable::<Api<Request>>();
181    is_deserializable_owned::<Api<Request>>();
182    is_serializable::<Api<Response>>();
183    is_deserializable_owned::<Api<Response>>();
184}
185
186macro_rules! api {
187    ($(#![$api:meta])* $(
188        $(#[doc = $doc:literal])*
189        $tag:literal [$min:literal - $($max:literal)?] $Name:ident: $request:ty => $response:ty,
190    )* next $next:literal [$version:literal - ]) => {
191        $(#[$api])* #[derive(Debug, Wire)]
192        #[wire(static = T)]
193        #[cfg_attr(feature = "host", wire(range = $next))]
194        #[cfg_attr(not(feature = "_exhaustive"), non_exhaustive)]
195        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
196        pub enum Api<'a, T: sealed::Direction> {
197            $(
198                $(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[wire(tag = $tag)]
199                $Name(T::Type<'a, $Name>),
200            )*
201        }
202        $(
203            $(#[doc = $doc])* $(#[cfg(feature = "host")] ${ignore($max)})? #[derive(Debug)]
204            pub enum $Name {}
205            $(#[cfg(feature = "host")] ${ignore($max)})?
206            impl Service for $Name {
207                #[cfg(feature = "host")]
208                const NAME: &str = stringify!($Name);
209                #[cfg(feature = "host")]
210                const VERSIONS: Versions = api!(versions $min $($max)?);
211                type Request<'a> = $request;
212                type Response<'a> = $response;
213                #[cfg(feature = "host")]
214                fn request(x: Self::Request<'_>) -> Api<'_, Request> { Api::$Name(x) }
215                #[cfg(feature = "serde")]
216                fn response(x: Self::Response<'_>) -> Api<'_, Response> {
217                    #[allow(unreachable_code)] Api::$Name(x)
218                }
219            }
220        )*
221        /// Device API version (or maximum supported device API version for host).
222        pub const VERSION: u32 = $version - 1;
223        #[cfg(feature = "_descriptor")]
224        pub const DESCRIPTORS: &'static [Descriptor] = &[
225            $(
226                $(#[cfg(feature = "host")] ${ignore($max)})?
227                Descriptor { tag: $tag, versions: api!(versions $min $($max)?) },
228            )*
229        ];
230    };
231
232    (max) => (None);
233    (max $max:literal) => (Some($max));
234
235    (versions $min:literal $($max:literal)?) => (Versions { min: $min, max: api!(max $($max)?) });
236}
237
238api! {
239    //! Protocol API parametric over the message direction.
240    //!
241    //! Deprecated variants are only available to the host (to support older devices).
242
243    /// Returns the device API version.
244    0 [0 -] ApiVersion: () => u32,
245
246    /// Sends a request to an applet.
247    1 [0 -] AppletRequest: applet::Request<'a> => (),
248
249    /// Reads a response from an applet.
250    2 [0 -] AppletResponse: applet::AppletId => Option<Cow<'a, [u8]>>,
251
252    /// Reboots the platform.
253    3 [0 -] PlatformReboot: () => !,
254
255    /// Starts a direct tunnel with an applet.
256    4 [0 -] AppletTunnel: applet::Tunnel<'a> => (),
257
258    /// (deprecated) Returns platform information (e.g. serial and version).
259    ///
260    /// This message is deprecated in favor of [`_PlatformInfo1`].
261    5 [1 - 4] _PlatformInfo0: () => platform::_Info0<'a>,
262
263    /// Calls a vendor-specific platform command.
264    6 [2 -] PlatformVendor: Cow<'a, [u8]> => Cow<'a, [u8]>,
265
266    /// (deprecated) Returns the metadata for platform update.
267    ///
268    /// This message is deprecated in favor of [`PlatformVendor`], which is the message for
269    /// vendor-specific messages.
270    7 [3 - 4] _PlatformUpdateMetadata: () => Cow<'a, [u8]>,
271
272    /// (deprecated) Updates the platform.
273    ///
274    /// This message is deprecated in favor of [`PlatformUpdate`].
275    8 [3 - 6] _PlatformUpdate0: transfer::_Request0<'a> => (),
276
277    /// (deprecated) Installs an applet.
278    ///
279    /// This message is deprecated in favor of [`_AppletInstall1`].
280    9 [4 - 6] _AppletInstall0: transfer::_Request0<'a> => (),
281
282    /// (deprecated) Uninstalls an applet.
283    ///
284    /// This message is deprecated in favor of [`_AppletInstall1`] with an empty transfer.
285    10 [4 - 6] _AppletUninstall0: () => (),
286
287    /// Returns the exit status of an applet, if not running.
288    11 [4 -] AppletExitStatus: applet::AppletId => Option<applet::ExitStatus>,
289
290    /// Locks a platform until reboot.
291    ///
292    /// This is useful for testing purposes by locking a platform before flashing a new one.
293    12 [4 -] PlatformLock: () => (),
294
295    /// (deprecated) Returns platform information.
296    ///
297    /// This message is deprecated in favor of [`_PlatformInfo2`].
298    13 [5 - 8] _PlatformInfo1: () => platform::_Info1<'a>,
299
300    /// Clears the store for the platform and all applets.
301    ///
302    /// The argument is the number of keys to protect. Using zero will clear all entries.
303    14 [6 -] PlatformClearStore: usize => (),
304
305    /// Updates the platform.
306    15 [7 -] PlatformUpdate: transfer::Request<'a> => transfer::Response,
307
308    /// (deprecated) Installs or uninstalls an applet.
309    ///
310    /// This message is deprecated in favor of [`AppletInstall2`].
311    16 [7 - 10] _AppletInstall1: transfer::Request<'a> => transfer::Response,
312
313    /// Reboots an applet.
314    17 [8 -] AppletReboot: applet::AppletId => (),
315
316    /// (deprecated) Returns information about the platform.
317    ///
318    /// This message is deprecated in favor of [`PlatformInfo3`].
319    18 [9 - 9] _PlatformInfo2: () => platform::_Info2<'a>,
320
321    /// Returns information about the platform.
322    19 [10 -] PlatformInfo3: () => platform::Info3<'a>,
323
324    /// Returns the metadata of an applet.
325    20 [11 -] AppletMetadata0: applet::AppletId => applet::Metadata0<'a>,
326
327    /// Installs or uninstalls an applet.
328    ///
329    /// The payload must be the concatenation of the applet, its metadata (serialized from
330    /// [`applet::Metadata0`]), and 4 big-endian bytes encoding the size (in bytes) of the applet.
331    21 [11 -] AppletInstall2: transfer::Request<'a> => transfer::Response,
332
333    next 22 [12 -]
334}