crypto/
lib.rs

1// SPDX-FileCopyrightText: 2024 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4pub mod error;
5pub mod messages;
6pub mod sha2;
7
8pub use messages::AesMode;
9use server::{xous::MemoryRange, CheckedConn, CheckedPermissions, MessageAllowed};
10#[cfg(keyos)]
11use server::{ScalarEventHandler, ServerContext};
12pub use sha2::{
13    Sha256StreamingContext, ShaAlgo, ShaPermissions, ShaStreamingContext, SHA224_HASH_SIZE, SHA256_HASH_SIZE,
14    SHA384_HASH_SIZE, SHA512_HASH_SIZE,
15};
16
17use crate::error::{CryptoError, ShamirError};
18use crate::messages::*;
19
20pub const AES_BLOCK_SIZE: usize = 16;
21
22#[macro_export]
23macro_rules! use_api {
24    () => {
25        mod crypto_permissions {
26            use crypto::messages::*;
27            #[derive(Clone, Default, server::Permissions)]
28            #[server_name = "os/crypto"]
29            pub struct CryptoPermissions;
30        }
31        type CryptoApi = crypto::CryptoApi<crypto_permissions::CryptoPermissions>;
32    };
33}
34
35#[derive(Default)]
36pub struct CryptoApi<P: CheckedPermissions> {
37    pub(crate) conn: CheckedConn<P>,
38}
39
40pub struct AesContext<P: CheckedPermissions + MessageAllowed<AesClear>> {
41    conn: CheckedConn<P>,
42    id: u8,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
46pub enum Direction {
47    Encrypt,
48    Decrypt,
49}
50
51impl<P: CheckedPermissions> CryptoApi<P> {
52    pub fn setup_aes(&self, key: &[u8], mode: AesMode) -> Result<AesContext<P>, CryptoError>
53    where
54        P: MessageAllowed<AesSetup>,
55        P: MessageAllowed<AesClear>,
56    {
57        let result = self.conn.send_blocking_archive(AesSetup { key: key.into(), mode });
58        Ok(AesContext { id: result? as u8, conn: self.conn.clone() })
59    }
60
61    /// Encrypt/decrypt using DMA on the provided memory ranges directly.
62    /// Source and destination pointer and length do not need to be page-aligned, but they need to be
63    /// word-aligned.
64    /// Cache on the source buffer needs to be cleaned before the operation, and invalidated
65    /// on the destination after the operation.
66    ///
67    /// Returns synchronously after pre-DMA validation: an `Err` here means the DMA never
68    /// started. Once `Ok(())` is returned, completion is signalled via a `DiskEncryptComplete`
69    /// event to the persistent subscription.
70    ///
71    /// # Safety
72    /// Caller has to make sure both buffers stay mapped for the duration of the operation.
73    #[cfg(keyos)]
74    pub unsafe fn disk_encrypt_unsafe(
75        &self,
76        tweak: [u8; 16],
77        j: usize,
78        src: MemoryRange,
79        dst: MemoryRange,
80        direction: Direction,
81    ) -> Result<(), CryptoError>
82    where
83        P: MessageAllowed<DiskEncryptUnsafe>,
84    {
85        self.conn.send_blocking_archive(DiskEncryptUnsafe {
86            tweak,
87            j,
88            src: src.as_ptr() as usize,
89            dst: dst.as_ptr() as usize,
90            len: src.len().min(dst.len()),
91            direction,
92        })
93    }
94
95    /// Subscribe to `DiskEncryptComplete` events. Must be called once at startup.
96    #[cfg(keyos)]
97    pub fn subscribe_disk_encrypt_complete<SR>(
98        &self,
99        context: &mut ServerContext<SR>,
100    ) -> Result<(), CryptoError>
101    where
102        P: MessageAllowed<SubscribeDiskEncryptComplete>,
103        SR: ScalarEventHandler<DiskEncryptComplete>,
104    {
105        self.conn.subscribe_scalar(SubscribeDiskEncryptComplete, context)
106    }
107
108    pub fn hmac224(&self, key: Vec<u8>, data: Vec<u8>) -> Result<Vec<u8>, CryptoError>
109    where
110        P: MessageAllowed<Hmac>,
111    {
112        self.conn.send_blocking_archive(Hmac { algo: ShaAlgo::Sha224, key, data })
113    }
114
115    pub fn hmac256(&self, key: Vec<u8>, data: Vec<u8>) -> Result<Vec<u8>, CryptoError>
116    where
117        P: MessageAllowed<Hmac>,
118    {
119        self.conn.send_blocking_archive(Hmac { algo: ShaAlgo::Sha256, key, data })
120    }
121
122    pub fn hmac384(&self, key: Vec<u8>, data: Vec<u8>) -> Result<Vec<u8>, CryptoError>
123    where
124        P: MessageAllowed<Hmac>,
125    {
126        self.conn.send_blocking_archive(Hmac { algo: ShaAlgo::Sha384, key, data })
127    }
128
129    pub fn hmac512(&self, key: Vec<u8>, data: Vec<u8>) -> Result<Vec<u8>, CryptoError>
130    where
131        P: MessageAllowed<Hmac>,
132    {
133        self.conn.send_blocking_archive(Hmac { algo: ShaAlgo::Sha512, key, data })
134    }
135
136    pub fn split_secret(
137        &self,
138        secret: Vec<u8>,
139        num_shares: usize,
140        threshold: usize,
141    ) -> Result<Vec<Vec<u8>>, ShamirError>
142    where
143        P: MessageAllowed<ShamirSplit>,
144    {
145        self.conn.send_blocking_archive(ShamirSplit { secret, num_shares, threshold })
146    }
147
148    pub fn recover_secret(&self, indexes: Vec<usize>, shares: Vec<Vec<u8>>) -> Result<Vec<u8>, ShamirError>
149    where
150        P: MessageAllowed<ShamirRecover>,
151    {
152        self.conn.send_blocking_archive(ShamirRecover { indexes, shares })
153    }
154}
155
156impl<P: CheckedPermissions + MessageAllowed<AesClear>> AesContext<P> {
157    pub fn execute(
158        &self,
159        buf: MemoryRange,
160        offset: usize,
161        len: usize,
162        direction: Direction,
163    ) -> Result<usize, CryptoError>
164    where
165        P: MessageAllowed<AesExecute>,
166    {
167        self.conn.lend_mut(AesExecute { buf, transfer_id: self.id, len, direction, offset })
168    }
169
170    pub fn add_aad(&self, aad: &[u8]) -> Result<usize, CryptoError>
171    where
172        P: MessageAllowed<AesAad>,
173    {
174        self.conn.send_blocking_archive(AesAad { transfer_id: self.id, aad: aad.into() })
175    }
176
177    pub fn gcm_tag(&self) -> Result<[u8; 16], CryptoError>
178    where
179        P: MessageAllowed<AesGcmTag>,
180    {
181        self.conn.send_blocking_archive(AesGcmTag { transfer_id: self.id })
182    }
183}
184
185impl<P: CheckedPermissions + MessageAllowed<AesClear>> Drop for AesContext<P> {
186    fn drop(&mut self) { self.conn.try_send_scalar(AesClear(self.id)).ok(); }
187}