Skip to main content
KeyOS API Reference

crypto/
messages.rs

1// SPDX-FileCopyrightText: 2024 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::fmt;
5
6use server::{xous::MemoryRange, SimpleMemoryMessage};
7use zeroize::{Zeroize, ZeroizeOnDrop};
8
9use crate::error::{CryptoError, ShamirError};
10use crate::Direction;
11
12#[derive(
13    Clone, server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Zeroize, ZeroizeOnDrop,
14)]
15#[response(Result<usize, CryptoError>)]
16pub struct AesSetup {
17    pub key: Vec<u8>,
18    pub mode: AesMode,
19}
20
21impl fmt::Debug for AesSetup {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        f.debug_struct("AesSetup")
24            .field("key", &format_args!("<{} bytes>", self.key.len()))
25            .field("mode", &self.mode)
26            .finish()
27    }
28}
29
30#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Zeroize, ZeroizeOnDrop)]
31pub enum AesMode {
32    Ecb,
33    Cbc { iv: [u8; 16] },
34    Ctr { iv: [u8; 16] },
35    Gcm { iv: [u8; 12] },
36}
37
38#[derive(Debug, server::Message)]
39#[response(Result<usize, CryptoError>)]
40pub struct AesExecute {
41    pub buf: MemoryRange,
42    pub transfer_id: u8,
43    pub direction: Direction,
44    pub offset: usize,
45    pub len: usize,
46}
47
48const OFFSET_OFFSET: usize = 9;
49const TRANSFER_ID_OFFSET: usize = 1;
50const DECRYPT_FLAG: usize = 1;
51
52impl From<AesExecute> for SimpleMemoryMessage {
53    fn from(value: AesExecute) -> Self {
54        Self {
55            buf: value.buf,
56            arg1: value.len,
57            arg2: (value.offset << OFFSET_OFFSET)
58                | ((value.transfer_id as usize) << TRANSFER_ID_OFFSET)
59                | match value.direction {
60                    Direction::Encrypt => 0,
61                    Direction::Decrypt => DECRYPT_FLAG,
62                },
63        }
64    }
65}
66
67impl From<SimpleMemoryMessage> for AesExecute {
68    fn from(value: SimpleMemoryMessage) -> Self {
69        Self {
70            buf: value.buf,
71            len: value.arg1,
72            transfer_id: (value.arg2 >> TRANSFER_ID_OFFSET) as u8,
73            direction: if value.arg2 & DECRYPT_FLAG != 0 { Direction::Decrypt } else { Direction::Encrypt },
74            offset: (value.arg2 >> OFFSET_OFFSET),
75        }
76    }
77}
78
79#[derive(Debug, server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
80#[response(Result<usize, CryptoError>)]
81pub struct AesAad {
82    pub transfer_id: u8,
83    pub aad: Vec<u8>,
84}
85
86#[derive(Debug, server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
87#[response(Result<[u8;16], CryptoError>)]
88pub struct AesGcmTag {
89    pub transfer_id: u8,
90}
91
92#[derive(Debug, server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
93#[response(Result<(), CryptoError>)]
94pub struct DiskEncryptUnsafe {
95    pub tweak: [u8; 16],
96    pub j: usize,
97    pub src: usize,
98    pub dst: usize,
99    pub len: usize,
100    pub direction: Direction,
101}
102
103#[cfg(keyos)]
104#[derive(Debug, server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
105#[event(DiskEncryptComplete)]
106#[error(CryptoError)]
107pub struct SubscribeDiskEncryptComplete;
108
109/// Fired when a previously-accepted `DiskEncryptUnsafe` finishes.
110/// This event has no struct body because pre-DMA failures are reported
111/// synchronously, and DMA completion cannot fail without taking down the system.
112#[cfg(keyos)]
113#[derive(Debug, Clone, Copy)]
114pub struct DiskEncryptComplete;
115
116#[cfg(keyos)]
117impl server::AsScalar<1> for DiskEncryptComplete {
118    fn as_scalar(&self) -> [u32; 1] { [0] }
119}
120
121#[cfg(keyos)]
122impl server::FromScalar<1> for DiskEncryptComplete {
123    fn from_scalar(_: [u32; 1]) -> Self { Self }
124}
125
126#[derive(Debug, server::Message)]
127pub struct AesClear(pub u8);
128
129pub use crate::sha2::ShaAlgo;
130
131/// Allocate a server-side SHA context slot (or overwrite an existing one) and seed it
132/// with the supplied hash state. `context_id = None` on first use; the server allocates
133/// a slot and returns its id. Subsequent calls with the returned id overwrite the state.
134#[derive(server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
135#[response(Result<usize, CryptoError>)]
136pub struct ShaSetContext {
137    /// `None` → allocate a new slot; `Some(id)` → overwrite existing slot.
138    pub context_id: Option<usize>,
139    pub algo: ShaAlgo,
140    /// Intermediate hash state in standard digest byte order (BE per word).
141    /// SHA-224/256 use the first 32 bytes; SHA-384/512 use all 64.
142    pub hash_state: [u8; 64],
143}
144
145impl fmt::Debug for ShaSetContext {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        f.debug_struct("ShaSetContext")
148            .field("context_id", &self.context_id)
149            .field("algo", &self.algo)
150            .field("hash_state", &"<redacted>")
151            .finish()
152    }
153}
154
155/// Feed a block-aligned chunk of data into the hardware SHA engine via DMA.
156/// `buf` must be page-aligned; `length` must be a multiple of the algo's block size.
157/// Data always starts at offset 0 in `buf`.
158#[derive(Debug, server::Message)]
159#[response(Result<usize, CryptoError>)]
160pub struct ShaUpdate {
161    pub context_id: usize,
162    pub buf: MemoryRange,
163    pub length: usize,
164}
165
166impl From<ShaUpdate> for SimpleMemoryMessage {
167    fn from(value: ShaUpdate) -> Self { Self { buf: value.buf, arg1: value.context_id, arg2: value.length } }
168}
169
170impl From<SimpleMemoryMessage> for ShaUpdate {
171    fn from(value: SimpleMemoryMessage) -> Self {
172        Self { context_id: value.arg1, buf: value.buf, length: value.arg2 }
173    }
174}
175
176/// Retrieve the current intermediate hash state for a context slot.
177#[derive(Debug, server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
178#[response(Result<ShaContextSnapshot, CryptoError>)]
179pub struct ShaGetContext {
180    pub context_id: usize,
181}
182
183/// Snapshot of a server-side SHA context (returned by `ShaGetContext`).
184#[derive(Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
185pub struct ShaContextSnapshot {
186    pub algo: ShaAlgo,
187    pub hash_state: [u8; 64],
188}
189
190impl fmt::Debug for ShaContextSnapshot {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        f.debug_struct("ShaContextSnapshot")
193            .field("algo", &self.algo)
194            .field("hash_state", &"<redacted>")
195            .finish()
196    }
197}
198
199/// Release a server-side SHA context slot.
200#[derive(Debug, server::Message)]
201pub struct ShaDrop(pub usize);
202
203#[derive(server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
204#[response(Result<Vec<u8>, CryptoError>)]
205pub struct Hmac {
206    pub algo: ShaAlgo,
207    pub key: Vec<u8>,
208    pub data: Vec<u8>,
209}
210
211impl fmt::Debug for Hmac {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        f.debug_struct("Hmac")
214            .field("algo", &self.algo)
215            .field("key", &format_args!("<{} bytes>", self.key.len()))
216            .field("data", &format_args!("<{} bytes>", self.data.len()))
217            .finish()
218    }
219}
220
221#[derive(server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
222#[response(Result<Vec<Vec<u8>>, ShamirError>)]
223pub struct ShamirSplit {
224    pub secret: Vec<u8>,
225    pub num_shares: usize,
226    pub threshold: usize,
227}
228
229impl fmt::Debug for ShamirSplit {
230    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231        f.debug_struct("ShamirSplit")
232            .field("secret", &format_args!("<{} bytes>", self.secret.len()))
233            .field("num_shares", &self.num_shares)
234            .field("threshold", &self.threshold)
235            .finish()
236    }
237}
238
239#[derive(server::Message, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
240#[response(Result<Vec<u8>, ShamirError>)]
241pub struct ShamirRecover {
242    pub indexes: Vec<usize>,
243    pub shares: Vec<Vec<u8>>,
244}
245
246impl fmt::Debug for ShamirRecover {
247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248        f.debug_struct("ShamirRecover")
249            .field("indexes", &self.indexes)
250            .field("shares", &format_args!("<{} shares>", self.shares.len()))
251            .finish()
252    }
253}